Move sshUserName from Account to AccountExternalId

We remove the secondary unique column sshUserName and store it in
the AccountExternalId entity instead.  This change is necessary to
support databases which do not allow mulitiple key attributes for
an entity.

Change-Id: I20076a05f2ea083da6044a4f1ed2f0672e85739a
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-12-31 09:20:17 -08:00
parent 8df7cf7689
commit f2bab2afc9
29 changed files with 484 additions and 171 deletions

View File

@@ -28,6 +28,7 @@ import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.ChangeUserName;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.FactoryModule;
@@ -130,7 +131,11 @@ public class WebModule extends FactoryModule {
SINGLETON);
bind(GerritConfig.class).toProvider(GerritConfigProvider.class).in(
SINGLETON);
bind(AccountManager.class).in(SINGLETON);
bind(AccountManager.class);
bind(ChangeUserName.CurrentUser.class);
factory(ChangeUserName.Factory.class);
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
HttpRemotePeerProvider.class).in(RequestScoped.class);

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.httpd.auth.become;
import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_USERNAME;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.httpd.WebSession;
@@ -154,12 +156,21 @@ public class BecomeAnyAccountLoginServlet extends HttpServlet {
return null;
}
private AuthResult auth(final AccountExternalId account) {
if (account != null) {
return new AuthResult(account.getAccountId(), null, false);
}
return null;
}
private AuthResult bySshUserName(final HttpServletResponse rsp,
final String userName) {
try {
final ReviewDb db = schema.open();
try {
return auth(db.accounts().bySshUserName(userName));
AccountExternalId.Key key =
new AccountExternalId.Key(SCHEME_USERNAME, userName);
return auth(db.accountExternalIds().get(key));
} finally {
db.close();
}

View File

@@ -191,9 +191,9 @@ class GitWebServlet extends HttpServlet {
}
if (gerritConfig.getSshdAddress() != null) {
String sshAddr = gerritConfig.getSshdAddress();
p.print("if ($ENV{'GERRIT_SSH_USER_NAME'}) {\n");
p.print("if ($ENV{'GERRIT_USER_NAME'}) {\n");
p.print(" push @git_base_url_list, join('', 'ssh://'");
p.print(", $ENV{'GERRIT_SSH_USER_NAME'}");
p.print(", $ENV{'GERRIT_USER_NAME'}");
p.print(", '@'");
if (sshAddr.startsWith("*:") || "".equals(sshAddr)) {
p.print(", $ENV{'SERVER_NAME'}");
@@ -452,8 +452,8 @@ class GitWebServlet extends HttpServlet {
String remoteUser = null;
if (project.getCurrentUser() instanceof IdentifiedUser) {
final IdentifiedUser u = (IdentifiedUser) project.getCurrentUser();
final String user = u.getAccount().getSshUserName();
env.set("GERRIT_SSH_USER_NAME", user);
final String user = u.getUserName();
env.set("GERRIT_USER_NAME", user);
if (user != null && !user.isEmpty()) {
remoteUser = user;
} else {

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.httpd.rpc;
import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_USERNAME;
import com.google.gerrit.common.data.AccountDashboardInfo;
import com.google.gerrit.common.data.ChangeInfo;
import com.google.gerrit.common.data.ChangeListService;
@@ -470,8 +472,12 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements
Set<Account.Id> result = new HashSet<Account.Id>();
String a = userName;
String b = userName + "\u9fa5";
addAll(result, db.accounts().suggestBySshUserName(a, b, 10));
addAll(result, db.accounts().suggestByFullName(a, b, 10));
for (AccountExternalId extId : db.accountExternalIds().suggestByKey(
new AccountExternalId.Key(SCHEME_USERNAME, a),
new AccountExternalId.Key(SCHEME_USERNAME, b), 10)) {
result.add(extId.getAccountId());
}
for (AccountExternalId extId : db.accountExternalIds()
.suggestByEmailAddress(a, b, 10)) {
result.add(extId.getAccountId());

View File

@@ -45,6 +45,15 @@ import java.util.concurrent.Callable;
* operation completed successfully.
*/
public abstract class Handler<T> implements Callable<T> {
public static <T> Handler<T> wrap(final Callable<T> r) {
return new Handler<T>() {
@Override
public T call() throws Exception {
return r.call();
}
};
}
/**
* Run the operation and pass the result to the callback.
*

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.httpd.rpc.account;
import com.google.gerrit.httpd.rpc.RpcServletModule;
import com.google.gerrit.httpd.rpc.UiRpcModule;
import com.google.gerrit.server.account.ChangeUserName;
import com.google.gerrit.server.config.FactoryModule;
public class AccountModule extends RpcServletModule {

View File

@@ -17,10 +17,10 @@ package com.google.gerrit.httpd.rpc.account;
import com.google.gerrit.common.data.AccountSecurity;
import com.google.gerrit.common.errors.ContactInformationStoreException;
import com.google.gerrit.common.errors.InvalidSshKeyException;
import com.google.gerrit.common.errors.InvalidSshUserNameException;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountAgreement;
import com.google.gerrit.reviewdb.AccountExternalId;
@@ -30,11 +30,13 @@ import com.google.gerrit.reviewdb.ContactInformation;
import com.google.gerrit.reviewdb.ContributorAgreement;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.ChangeUserName;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.contact.ContactStore;
@@ -58,17 +60,14 @@ import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
class AccountSecurityImpl extends BaseServiceImplementation implements
AccountSecurity {
private static final Pattern SSH_USER_NAME_PATTERN = Pattern.compile(Account.SSH_USER_NAME_PATTERN);
private final Logger log = LoggerFactory.getLogger(getClass());
private final ContactStore contactStore;
private final AuthConfig authConfig;
private final Realm realm;
private final Provider<IdentifiedUser> user;
private final RegisterNewEmailSender.Factory registerNewEmailFactory;
private final SshKeyCache sshKeyCache;
private final AccountByEmailCache byEmailCache;
@@ -76,6 +75,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
private final AccountManager accountManager;
private final boolean useContactInfo;
private final ChangeUserName.CurrentUser changeUserNameFactory;
private final DeleteExternalIds.Factory deleteExternalIdsFactory;
private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
private final MyGroupsFactory.Factory myGroupsFactory;
@@ -83,10 +83,11 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
@Inject
AccountSecurityImpl(final Provider<ReviewDb> schema,
final Provider<CurrentUser> currentUser, final ContactStore cs,
final AuthConfig ac, final Realm r,
final AuthConfig ac, final Realm r, final Provider<IdentifiedUser> u,
final RegisterNewEmailSender.Factory esf, final SshKeyCache skc,
final AccountByEmailCache abec, final AccountCache uac,
final AccountManager am,
final ChangeUserName.CurrentUser changeUserNameFactory,
final DeleteExternalIds.Factory deleteExternalIdsFactory,
final ExternalIdDetailFactory.Factory externalIdDetailFactory,
final MyGroupsFactory.Factory myGroupsFactory) {
@@ -94,6 +95,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
contactStore = cs;
authConfig = ac;
realm = r;
user = u;
registerNewEmailFactory = esf;
sshKeyCache = skc;
byEmailCache = abec;
@@ -102,6 +104,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
useContactInfo = contactStore != null && contactStore.isEnabled();
this.changeUserNameFactory = changeUserNameFactory;
this.deleteExternalIdsFactory = deleteExternalIdsFactory;
this.externalIdDetailFactory = externalIdDetailFactory;
this.myGroupsFactory = myGroupsFactory;
@@ -110,7 +113,8 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
public void mySshKeys(final AsyncCallback<List<AccountSshKey>> callback) {
run(callback, new Action<List<AccountSshKey>>() {
public List<AccountSshKey> run(ReviewDb db) throws OrmException {
return db.accountSshKeys().byAccount(getAccountId()).toList();
IdentifiedUser u = user.get();
return db.accountSshKeys().byAccount(u.getAccountId()).toList();
}
});
}
@@ -120,7 +124,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
run(callback, new Action<AccountSshKey>() {
public AccountSshKey run(final ReviewDb db) throws OrmException, Failure {
int max = 0;
final Account.Id me = getAccountId();
final Account.Id me = user.get().getAccountId();
for (final AccountSshKey k : db.accountSshKeys().byAccount(me)) {
max = Math.max(max, k.getKey().get());
}
@@ -132,7 +136,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
throw new Failure(e);
}
db.accountSshKeys().insert(Collections.singleton(key));
uncacheSshKeys(me);
uncacheSshKeys();
return key;
}
});
@@ -142,7 +146,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
final AsyncCallback<VoidResult> callback) {
run(callback, new Action<VoidResult>() {
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
final Account.Id me = getAccountId();
final Account.Id me = user.get().getAccountId();
for (final AccountSshKey.Id keyId : ids) {
if (!me.equals(keyId.getParentKey()))
throw new Failure(new NoSuchEntityException());
@@ -153,7 +157,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
final Transaction txn = db.beginTransaction();
db.accountSshKeys().delete(k, txn);
txn.commit();
uncacheSshKeys(me);
uncacheSshKeys();
}
return VoidResult.INSTANCE;
@@ -161,56 +165,18 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
});
}
private void uncacheSshKeys(final Account.Id me) {
uncacheSshKeys(accountCache.get(me).getAccount().getSshUserName());
}
private void uncacheSshKeys(final String userName) {
sshKeyCache.evict(userName);
private void uncacheSshKeys() {
sshKeyCache.evict(user.get().getUserName());
}
@Override
public void changeSshUserName(final String newName,
final AsyncCallback<VoidResult> callback) {
if (!realm.allowsEdit(Account.FieldName.SSH_USER_NAME)) {
if (realm.allowsEdit(Account.FieldName.USER_NAME)) {
Handler.wrap(changeUserNameFactory.create(newName)).to(callback);
} else {
callback.onFailure(new NameAlreadyUsedException());
return;
}
run(callback, new Action<VoidResult>() {
@Override
public VoidResult run(ReviewDb db) throws OrmException, Failure {
final Account me = db.accounts().get(getAccountId());
if (me == null) {
throw new Failure(new NoSuchEntityException());
}
if (newName != null && !SSH_USER_NAME_PATTERN.matcher(newName).matches()) {
throw new Failure(new InvalidSshUserNameException());
}
final Account other;
if (newName != null) {
other = db.accounts().bySshUserName(newName);
} else {
other = null;
}
if (other != null) {
if (other.getId().equals(me.getId())) {
return VoidResult.INSTANCE;
} else {
throw new Failure(new NameAlreadyUsedException());
}
}
final String oldName = me.getSshUserName();
me.setSshUserName(newName);
db.accounts().update(Collections.singleton(me));
uncacheSshKeys(oldName);
uncacheSshKeys(newName);
accountCache.evict(me.getId());
return VoidResult.INSTANCE;
}
});
}
public void myExternalIds(AsyncCallback<List<AccountExternalId>> callback) {
@@ -231,7 +197,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
final ContactInformation info, final AsyncCallback<Account> callback) {
run(callback, new Action<Account>() {
public Account run(ReviewDb db) throws OrmException, Failure {
final Account me = db.accounts().get(getAccountId());
final Account me = db.accounts().get(user.get().getAccountId());
final String oldEmail = me.getPreferredEmail();
if (realm.allowsEdit(Account.FieldName.FULL_NAME)) {
me.setFullName(name != null && !name.isEmpty() ? name : null);
@@ -278,7 +244,8 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
}
final AccountAgreement a =
new AccountAgreement(new AccountAgreement.Key(getAccountId(), id));
new AccountAgreement(new AccountAgreement.Key(user.get()
.getAccountId(), id));
if (cla.isAutoVerify()) {
a.review(AccountAgreement.Status.VERIFIED, null);
}
@@ -318,7 +285,8 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
callback.onFailure(new IllegalStateException("Invalid token"));
return;
}
accountManager.link(getAccountId(), AuthRequest.forEmail(newEmail));
accountManager.link(user.get().getAccountId(), AuthRequest
.forEmail(newEmail));
callback.onSuccess(VoidResult.INSTANCE);
} catch (XsrfException e) {
callback.onFailure(e);

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.httpd.rpc.account;
import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_USERNAME;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.AccountExternalId;
@@ -58,7 +60,11 @@ class ExternalIdDetailFactory extends Handler<List<AccountExternalId>> {
// establish this web session, and if only if an identity was
// actually used to establish this web session.
//
e.setCanDelete(last != null && !last.equals(e.getKey()));
if (e.isScheme(SCHEME_USERNAME)) {
e.setCanDelete(false);
} else {
e.setCanDelete(last != null && !last.equals(e.getKey()));
}
}
return ids;
}