Merge changes from topic "case-insensitive-usernames"
* changes:
Add option to delete external IDs to `gerrit set-account`
Allow to delete externalIds also in scheme username
ChangeExternalIdCaseSensitivity: Add --dryrun option
ChangeExternalIdCaseSensitivity: Allow to migrate to case sensitive
Add tool to migrate external IDs to work case insensitively
Add auth.userNameCaseInsensitive option
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 2a019ca..3da69df 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -477,6 +477,11 @@
`+refs/heads/sandbox/${username}/*+`. If you do, it's also recommended
you grant the users the push force permission to be able to clean up
stale branches.
+If link:config-gerrit.html#auth.userNameCaseInsensitive[auth.userNameCaseInsensitive]
+is enabled, the `${username}` is still case sensitive and will use
+the capitalization used during account creation. This is done, since
+git branches are case sensitive, so that sandbox branches containing
+`${username}` are still reachable by the users.
[[category_delete]]
=== Delete Reference
diff --git a/Documentation/cmd-set-account.txt b/Documentation/cmd-set-account.txt
index 6808e017..02eaf83 100644
--- a/Documentation/cmd-set-account.txt
+++ b/Documentation/cmd-set-account.txt
@@ -14,7 +14,8 @@
[--delete-ssh-key - | <KEY> | ALL]
[--generate-http-password]
[--http-password <PASSWORD>]
- [--clear-http-password] <USER>
+ [--clear-http-password]
+ [--delete-external-id <EXTERNALID>] <USER>
--
== DESCRIPTION
@@ -106,6 +107,13 @@
--clear-http-password::
Clear the HTTP password for the user account.
+--delete-external-id::
+ Delete an external ID from a user's account if it exists.
+ If the external ID provided is 'ALL', all associated
+ external IDs are deleted from this account.
+ May be supplied more than once to remove multiple external
+ IDs from an account in a single command execution.
+
== EXAMPLES
Add an email and SSH key to `watcher`'s account:
diff --git a/Documentation/config-accounts.txt b/Documentation/config-accounts.txt
index 2f226536..7a7cef2 100644
--- a/Documentation/config-accounts.txt
+++ b/Documentation/config-accounts.txt
@@ -297,6 +297,13 @@
This ensures that an external ID is used only once (e.g. an external ID can
never be assigned to multiple accounts at a point in time).
+By default, the SHA-1 sum is computed preserving the case of the external ID. If
+auth.userNameCaseInsensitive` is set to `true`, the SHA-1 sum of external IDs
+in the `gerrit:` and `username:` schemes are computed from the all lowercase
+external ID. This enables case insensitive username handling. The case of the
+external ID is however preserved by using the original capitalization in the
+note content.
+
The following commands show how to find the SHA-1 of an external ID:
----
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 90c69b6..9e73f6f 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -652,6 +652,32 @@
+
By default this is set to false.
+[[auth.userNameCaseInsensitive]]auth.userNameCaseInsensitive::
++
+If set the username will be handled case insensitively but case preserving,
+i.e. a user can login with `johndoe` or `JohnDoe` for the same account
+created for `JohnDoe`. The form of the username used during account creation
+will be used wherever the username is displayed. Sandbox branches created
+for a user can also only be created for this original form.
++
+Note, that this does not work for all existing accounts, if they were
+not originally created with all lowercase, since the note keys of the
+external IDs will not match the new scheme. For more details refer to
+the link:config-accounts.html#external-ids[External ID documentation].
++
+Gerrit provides the
+link:pgm-ChangeExternalIdCaseSensitivity.html[ChangeExternalIdCaseSensitivity tool]
+to migrate existing accounts to match the new scheme.
++
+Naturally, if there were two accounts only different in capitalization,
+e.g. `johndoe` and `JohnDoe`, the account `JohnDoe` will not be able
+to authenticate anymore after setting this option. If such duplicate
+accounts exist the migration tool will fail, since the newly computed
+note name would be identical and thus conflict. These duplicates thus
+have to be deleted manually by deleting the respective external ID.
++
+Default is false.
+
[[auth.enableRunAs]]auth.enableRunAs::
+
If true HTTP REST APIs will accept the `X-Gerrit-RunAs` HTTP request
diff --git a/Documentation/pgm-ChangeExternalIdCaseSensitivity.txt b/Documentation/pgm-ChangeExternalIdCaseSensitivity.txt
new file mode 100644
index 0000000..1fb4b97
--- /dev/null
+++ b/Documentation/pgm-ChangeExternalIdCaseSensitivity.txt
@@ -0,0 +1,71 @@
+= ChangeExternalIdCaseSensitivity
+
+== NAME
+ChangeExternalIdCaseSensitivity - Convert `username` and `gerrit`
+external IDs to be handled case insensitively
+
+== SYNOPSIS
+[verse]
+--
+_java_ -jar gerrit.war _ChangeExternalIdCaseSensitivity_
+ -d <SITE_PATH>
+ [--batch]
+ [--dryrun]
+--
+
+== DESCRIPTION
+Convert `username` and `gerrit` external IDs to be handled case
+insensitively or case sensitively. This is done by recomputing
+the name of the note from the sha1 sum of the all lowercase
+external ID key or of the key with its original capitalization
+respectively.
+
+The tool uses the `auth.userNameCaseInsensitive` option to determine,
+whether the migration should be performed to case insensitive or case sensitive
+usernames, i.e. if the option is set to `false`, migration will be performed to
+make external IDs case insensitive and if set to `true` to case sensitive.
+
+== OPTIONS
+
+-d::
+--site-path::
+ Path of the Gerrit site
+
+--batch::
+ No user interaction is required. The tool won't ask for confirmation before migrating.
+
+--dryrun::
+ Whether to perform the conversion without persisting it.
+
+== CONTEXT
+This command can only be run offline with direct access to the server's
+site.
+
+== EXAMPLES
+To convert the external IDs to be case insensitive:
+
+----
+ $ git config -f $SITE/etc/gerrit.config --get auth.userNameCaseInsensitive
+ > false
+ $ java -jar gerrit.war ChangeExternalIdCaseSensitivity -d site_path
+----
+
+To convert the external IDs to be case sensitive again:
+
+----
+ $ git config -f $SITE/etc/gerrit.config --get auth.userNameCaseInsensitive
+ > true
+ $ java -jar gerrit.war ChangeExternalIdCaseSensitivity -d site_path
+----
+
+
+== SEE ALSO
+
+* Configuration parameter link:config-gerrit.html#auth.userNameCaseInsensitive[auth.userNameCaseInsensitive]
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-index.txt b/Documentation/pgm-index.txt
index dde0231..8f4cbda 100644
--- a/Documentation/pgm-index.txt
+++ b/Documentation/pgm-index.txt
@@ -38,6 +38,9 @@
link:pgm-LocalUsernamesToLowerCase.html[LocalUsernamesToLowerCase]::
Convert the local username of every account to lower case.
+link:pgm-ChangeExternalIdCaseSensitivity.html[ChangeExternalIdCaseSensitivity]::
+ Convert external IDs to be case insensitive.
+
link:pgm-MigrateAccountPatchReviewDb.html[MigrateAccountPatchReviewDb]::
Migrates AccountPatchReviewDb from one database backend to another.
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 3510305..a613c7e 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1779,7 +1779,16 @@
Only external ids belonging to the caller may be deleted. Users that have
link:access-control.html#capability_modifyAccount[Modify Account] can delete
-external ids that belong to other accounts.
+external ids that belong to other accounts. External ids in the 'username:'
+scheme can only be deleted by users that have
+link:access-control.html#capability_administrateServer[Administrate Server]
+or both
+link:access-control.html#capability_maintainServer[Maintain Server] and
+link:access-control.html#capability__modifyAccount[Modify Account]
+since the user may not be able to login anymore, after the removal of the
+external id with scheme 'username:'. Users cannot delete their own external id
+with scheme 'username:' in order to prevent they can lock themselves out
+since they may not be able to login anymore.
.Request
----
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index aa13339..c67991d 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.ServiceUserClassifier;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.group.db.GroupDelta;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.notedb.Sequences;
@@ -52,18 +53,21 @@
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final GroupCache groupCache;
private final Provider<GroupsUpdate> groupsUpdateProvider;
+ private final ExternalIdFactory externalIdFactory;
@Inject
AccountCreator(
Sequences sequences,
@ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider,
GroupCache groupCache,
- @ServerInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
+ @ServerInitiated Provider<GroupsUpdate> groupsUpdateProvider,
+ ExternalIdFactory externalIdFactory) {
accounts = new HashMap<>();
this.sequences = sequences;
this.accountsUpdateProvider = accountsUpdateProvider;
this.groupCache = groupCache;
this.groupsUpdateProvider = groupsUpdateProvider;
+ this.externalIdFactory = externalIdFactory;
}
public synchronized TestAccount create(
@@ -84,11 +88,11 @@
String httpPass = null;
if (username != null) {
httpPass = "http-pass";
- extIds.add(ExternalId.createUsername(username, id, httpPass));
+ extIds.add(externalIdFactory.createUsername(username, id, httpPass));
}
if (email != null) {
- extIds.add(ExternalId.createEmail(id, email));
+ extIds.add(externalIdFactory.createEmail(id, email));
}
accountsUpdateProvider
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index 3763f9a..c6457a4 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -27,6 +27,7 @@
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AccountsUpdate.ConfigureDeltaFromState;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import java.io.IOException;
@@ -44,13 +45,18 @@
private final Accounts accounts;
private final AccountsUpdate accountsUpdate;
private final Sequences seq;
+ private final ExternalIdFactory externalIdFactory;
@Inject
public AccountOperationsImpl(
- Accounts accounts, @ServerInitiated AccountsUpdate accountsUpdate, Sequences seq) {
+ Accounts accounts,
+ @ServerInitiated AccountsUpdate accountsUpdate,
+ Sequences seq,
+ ExternalIdFactory externalIdFactory) {
this.accounts = accounts;
this.accountsUpdate = accountsUpdate;
this.seq = seq;
+ this.externalIdFactory = externalIdFactory;
}
@Override
@@ -72,7 +78,7 @@
return createdAccount.account().id();
}
- private static void initAccountDelta(
+ private void initAccountDelta(
AccountDelta.Builder builder, TestAccountCreation accountCreation, Account.Id accountId) {
accountCreation.fullname().ifPresent(builder::setFullName);
accountCreation.preferredEmail().ifPresent(e -> setPreferredEmail(builder, accountId, e));
@@ -84,19 +90,19 @@
.secondaryEmails()
.forEach(
secondaryEmail ->
- builder.addExternalId(ExternalId.createEmail(accountId, secondaryEmail)));
+ builder.addExternalId(externalIdFactory.createEmail(accountId, secondaryEmail)));
}
- private static void setPreferredEmail(
+ private void setPreferredEmail(
AccountDelta.Builder builder, Account.Id accountId, String preferredEmail) {
builder
.setPreferredEmail(preferredEmail)
- .addExternalId(ExternalId.createEmail(accountId, preferredEmail));
+ .addExternalId(externalIdFactory.createEmail(accountId, preferredEmail));
}
- private static void setUsername(
+ private void setUsername(
AccountDelta.Builder builder, Account.Id accountId, String username, String httpPassword) {
- builder.addExternalId(ExternalId.createUsername(username, accountId, httpPassword));
+ builder.addExternalId(externalIdFactory.createUsername(username, accountId, httpPassword));
}
private class PerAccountOperationsImpl implements PerAccountOperations {
@@ -202,14 +208,14 @@
.collect(toImmutableSet()));
builder.addExternalIds(
newSecondaryEmails.stream()
- .map(secondaryEmail -> ExternalId.createEmail(accountId, secondaryEmail))
+ .map(secondaryEmail -> externalIdFactory.createEmail(accountId, secondaryEmail))
.collect(toImmutableSet()));
if (accountUpdate.preferredEmail().isPresent()) {
builder.addExternalId(
- ExternalId.createEmail(accountId, accountUpdate.preferredEmail().get()));
+ externalIdFactory.createEmail(accountId, accountUpdate.preferredEmail().get()));
} else if (accountState.account().preferredEmail() != null) {
builder.addExternalId(
- ExternalId.createEmail(accountId, accountState.account().preferredEmail()));
+ externalIdFactory.createEmail(accountId, accountState.account().preferredEmail()));
}
}
diff --git a/java/com/google/gerrit/auth/ldap/LdapRealm.java b/java/com/google/gerrit/auth/ldap/LdapRealm.java
index 9305914..9a9f309 100644
--- a/java/com/google/gerrit/auth/ldap/LdapRealm.java
+++ b/java/com/google/gerrit/auth/ldap/LdapRealm.java
@@ -32,6 +32,7 @@
import com.google.gerrit.server.account.EmailExpander;
import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.auth.AuthenticationUnavailableException;
import com.google.gerrit.server.auth.NoSuchUserException;
@@ -346,10 +347,12 @@
static class UserLoader extends CacheLoader<String, Optional<Account.Id>> {
private final ExternalIds externalIds;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
- UserLoader(ExternalIds externalIds) {
+ UserLoader(ExternalIds externalIds, ExternalIdKeyFactory externalIdKeyFactory) {
this.externalIds = externalIds;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -358,7 +361,7 @@
TraceContext.newTimer(
"Loading account for username", Metadata.builder().username(username).build())) {
return externalIds
- .get(ExternalId.Key.create(SCHEME_GERRIT, username))
+ .get(externalIdKeyFactory.create(SCHEME_GERRIT, username))
.map(ExternalId::accountId);
}
}
diff --git a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index 9477cb6..71dff97 100644
--- a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.UrlFormatter;
import com.google.gerrit.server.query.account.InternalAccountQuery;
@@ -62,17 +63,20 @@
private final IdentifiedUser.GenericFactory userFactory;
private final int maxTrustDepth;
private final ImmutableMap<Long, Fingerprint> trusted;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
Factory(
@GerritServerConfig Config cfg,
Provider<InternalAccountQuery> accountQueryProvider,
IdentifiedUser.GenericFactory userFactory,
- DynamicItem<UrlFormatter> urlFormatter) {
+ DynamicItem<UrlFormatter> urlFormatter,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.accountQueryProvider = accountQueryProvider;
this.urlFormatter = urlFormatter;
this.userFactory = userFactory;
this.maxTrustDepth = cfg.getInt("receive", null, "maxTrustDepth", 0);
+ this.externalIdKeyFactory = externalIdKeyFactory;
String[] strs = cfg.getStringList("receive", null, "trustedKey");
if (strs.length != 0) {
@@ -103,6 +107,7 @@
private final Provider<InternalAccountQuery> accountQueryProvider;
private final DynamicItem<UrlFormatter> urlFormatter;
private final IdentifiedUser.GenericFactory userFactory;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
private IdentifiedUser expectedUser;
@@ -113,6 +118,7 @@
if (factory.trusted != null) {
enableTrust(factory.maxTrustDepth, factory.trusted);
}
+ this.externalIdKeyFactory = factory.externalIdKeyFactory;
}
/**
@@ -247,7 +253,8 @@
return sb.toString();
}
- static ExternalId.Key toExtIdKey(PGPPublicKey key) {
- return ExternalId.Key.create(SCHEME_GPGKEY, BaseEncoding.base16().encode(key.getFingerprint()));
+ ExternalId.Key toExtIdKey(PGPPublicKey key) {
+ return externalIdKeyFactory.create(
+ SCHEME_GPGKEY, BaseEncoding.base16().encode(key.getFingerprint()));
}
}
diff --git a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index 1be37f5..e0c921d 100644
--- a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -32,6 +32,7 @@
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.mail.send.DeleteKeySender;
import com.google.inject.Inject;
@@ -53,6 +54,7 @@
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final ExternalIds externalIds;
private final DeleteKeySender.Factory deleteKeySenderFactory;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
DeleteGpgKey(
@@ -60,12 +62,14 @@
Provider<PublicKeyStore> storeProvider,
@UserInitiated Provider<AccountsUpdate> accountsUpdateProvider,
ExternalIds externalIds,
- DeleteKeySender.Factory deleteKeySenderFactory) {
+ DeleteKeySender.Factory deleteKeySenderFactory,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.serverIdent = serverIdent;
this.storeProvider = storeProvider;
this.accountsUpdateProvider = accountsUpdateProvider;
this.externalIds = externalIds;
this.deleteKeySenderFactory = deleteKeySenderFactory;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -73,7 +77,8 @@
throws RestApiException, PGPException, IOException, ConfigInvalidException {
PGPPublicKey key = rsrc.getKeyRing().getPublicKey();
String fingerprint = BaseEncoding.base16().encode(key.getFingerprint());
- Optional<ExternalId> extId = externalIds.get(ExternalId.Key.create(SCHEME_GPGKEY, fingerprint));
+ Optional<ExternalId> extId =
+ externalIds.get(externalIdKeyFactory.create(SCHEME_GPGKEY, fingerprint));
if (!extId.isPresent()) {
throw new ResourceNotFoundException(fingerprint);
}
diff --git a/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 1b5e06a..d46b344 100644
--- a/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -53,6 +53,8 @@
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.mail.send.AddKeySender;
import com.google.gerrit.server.mail.send.DeleteKeySender;
@@ -93,6 +95,8 @@
private final ExternalIds externalIds;
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final RetryHelper retryHelper;
+ private final ExternalIdFactory externalIdFactory;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
PostGpgKeys(
@@ -105,7 +109,9 @@
Provider<InternalAccountQuery> accountQueryProvider,
ExternalIds externalIds,
@UserInitiated Provider<AccountsUpdate> accountsUpdateProvider,
- RetryHelper retryHelper) {
+ RetryHelper retryHelper,
+ ExternalIdFactory externalIdFactory,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.serverIdent = serverIdent;
this.self = self;
this.storeProvider = storeProvider;
@@ -116,6 +122,8 @@
this.externalIds = externalIds;
this.accountsUpdateProvider = accountsUpdateProvider;
this.retryHelper = retryHelper;
+ this.externalIdFactory = externalIdFactory;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -140,7 +148,7 @@
throw new ResourceConflictException("GPG key already associated with another account");
}
} else {
- newExtIds.add(ExternalId.create(extIdKey, rsrc.getUser().getAccountId()));
+ newExtIds.add(externalIdFactory.create(extIdKey, rsrc.getUser().getAccountId()));
}
}
@@ -287,7 +295,7 @@
}
private ExternalId.Key toExtIdKey(byte[] fp) {
- return ExternalId.Key.create(SCHEME_GPGKEY, BaseEncoding.base16().encode(fp));
+ return externalIdKeyFactory.create(SCHEME_GPGKEY, BaseEncoding.base16().encode(fp));
}
private Account getAccountByExternalId(ExternalId.Key extIdKey) {
diff --git a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
index de989ac..a421139 100644
--- a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -76,17 +76,23 @@
private final AccountCache accountCache;
private final AccountManager accountManager;
private final AuthConfig authConfig;
+ private final AuthRequest.Factory authRequestFactory;
+ private final PasswordVerifier passwordVerifier;
@Inject
ProjectBasicAuthFilter(
DynamicItem<WebSession> session,
AccountCache accountCache,
AccountManager accountManager,
- AuthConfig authConfig) {
+ AuthConfig authConfig,
+ AuthRequest.Factory authRequestFactory,
+ PasswordVerifier passwordVerifier) {
this.session = session;
this.accountCache = accountCache;
this.accountManager = accountManager;
this.authConfig = authConfig;
+ this.authRequestFactory = authRequestFactory;
+ this.passwordVerifier = passwordVerifier;
}
@Override
@@ -155,7 +161,7 @@
GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
|| gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
- if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
+ if (passwordVerifier.checkPassword(who.externalIds(), username, password)) {
logger.atFine().log(
"HTTP:%s %s username/password authentication succeeded",
req.getMethod(), req.getRequestURI());
@@ -167,7 +173,7 @@
return failAuthentication(rsp, username, req);
}
- AuthRequest whoAuth = AuthRequest.forUser(username);
+ AuthRequest whoAuth = authRequestFactory.createForUser(username);
whoAuth.setPassword(password);
try {
@@ -177,7 +183,7 @@
"HTTP:%s %s Realm authentication succeeded", req.getMethod(), req.getRequestURI());
return true;
} catch (NoSuchUserException e) {
- if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
+ if (passwordVerifier.checkPassword(who.externalIds(), username, password)) {
return succeedAuthentication(who, null);
}
logger.atWarning().withCause(e).log(authenticationFailedMsg(username, req));
diff --git a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
index dab36c4..fa53053 100644
--- a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
@@ -76,6 +76,7 @@
private final AccountManager accountManager;
private final String gitOAuthProvider;
private final boolean userNameToLowerCase;
+ private final AuthRequest.Factory authRequestFactory;
private String defaultAuthPlugin;
private String defaultAuthProvider;
@@ -86,13 +87,15 @@
DynamicMap<OAuthLoginProvider> pluginsProvider,
AccountCache accountCache,
AccountManager accountManager,
- @GerritServerConfig Config gerritConfig) {
+ @GerritServerConfig Config gerritConfig,
+ AuthRequest.Factory authRequestFactory) {
this.session = session;
this.loginProviders = pluginsProvider;
this.accountCache = accountCache;
this.accountManager = accountManager;
this.gitOAuthProvider = gerritConfig.getString("auth", null, "gitOAuthProvider");
this.userNameToLowerCase = gerritConfig.getBoolean("auth", null, "userNameToLowerCase", false);
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -162,7 +165,7 @@
}
Account account = who.get().account();
- AuthRequest authRequest = AuthRequest.forExternalUser(authInfo.username);
+ AuthRequest authRequest = authRequestFactory.createForExternalUser(authInfo.username);
authRequest.setEmailAddress(account.preferredEmail());
authRequest.setDisplayName(account.fullName());
authRequest.setPassword(authInfo.tokenOrSecret);
diff --git a/java/com/google/gerrit/httpd/WebModule.java b/java/com/google/gerrit/httpd/WebModule.java
index e416075..0645aac 100644
--- a/java/com/google/gerrit/httpd/WebModule.java
+++ b/java/com/google/gerrit/httpd/WebModule.java
@@ -75,6 +75,10 @@
listener().toInstance(registerInParentInjectors());
install(UniversalWebLoginFilter.module());
+
+ // Static injection was unfortunately the best solution in this place. However, it is to be
+ // avoided if possible.
+ requestStaticInjection(WebSessionManager.Val.class);
}
private void installAuthModule() {
diff --git a/java/com/google/gerrit/httpd/WebSessionManager.java b/java/com/google/gerrit/httpd/WebSessionManager.java
index c0900ec..87bf3a6 100644
--- a/java/com/google/gerrit/httpd/WebSessionManager.java
+++ b/java/com/google/gerrit/httpd/WebSessionManager.java
@@ -32,6 +32,7 @@
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -186,6 +187,8 @@
public static final class Val implements Serializable {
static final long serialVersionUID = 2L;
+ @Inject private static transient ExternalIdKeyFactory externalIdKeyFactory;
+
private transient Account.Id accountId;
private transient long refreshCookieAt;
private transient boolean persistentCookie;
@@ -295,7 +298,7 @@
persistentCookie = readVarInt32(in) != 0;
continue;
case 4:
- externalId = ExternalId.Key.parse(readString(in));
+ externalId = externalIdKeyFactory.parse(readString(in));
continue;
case 5:
sessionId = readString(in);
diff --git a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 97bb44b..2f760f0 100644
--- a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -31,7 +31,7 @@
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.query.account.InternalAccountQuery;
import com.google.gerrit.util.http.CacheHeaders;
import com.google.inject.Inject;
@@ -62,6 +62,8 @@
private final AccountManager accountManager;
private final SiteHeaderFooter headers;
private final Provider<InternalAccountQuery> queryProvider;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
BecomeAnyAccountLoginServlet(
@@ -70,13 +72,17 @@
AccountCache ac,
AccountManager am,
SiteHeaderFooter shf,
- Provider<InternalAccountQuery> qp) {
+ Provider<InternalAccountQuery> qp,
+ ExternalIdKeyFactory eikf,
+ AuthRequest.Factory arf) {
webSession = ws;
accounts = a;
accountCache = ac;
accountManager = am;
headers = shf;
queryProvider = qp;
+ externalIdKeyFactory = eikf;
+ authRequestFactory = arf;
}
@Override
@@ -220,7 +226,8 @@
private AuthResult create() throws IOException {
try {
return accountManager.authenticate(
- new AuthRequest(ExternalId.Key.create(SCHEME_UUID, UUID.randomUUID().toString())));
+ authRequestFactory.create(
+ externalIdKeyFactory.create(SCHEME_UUID, UUID.randomUUID().toString())));
} catch (AccountException e) {
getServletContext().log("cannot create new account", e);
return null;
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java b/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
index e20c9b9..acb3282 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
@@ -26,6 +26,7 @@
import com.google.gerrit.httpd.RemoteUserUtil;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.util.http.CacheHeaders;
import com.google.gerrit.util.http.RequestUtil;
@@ -64,10 +65,16 @@
private final String emailHeader;
private final String externalIdHeader;
private final boolean userNameToLowerCase;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
- HttpAuthFilter(DynamicItem<WebSession> webSession, AuthConfig authConfig) throws IOException {
+ HttpAuthFilter(
+ DynamicItem<WebSession> webSession,
+ AuthConfig authConfig,
+ ExternalIdKeyFactory externalIdKeyFactory)
+ throws IOException {
this.sessionProvider = webSession;
+ this.externalIdKeyFactory = externalIdKeyFactory;
final String pageName = "LoginRedirect.html";
final String doc = HtmlDomUtil.readFile(getClass(), pageName);
@@ -124,9 +131,9 @@
return false;
}
- private static boolean correctUser(String user, WebSession session) {
+ private boolean correctUser(String user, WebSession session) {
Optional<ExternalId.Key> id = session.getUser().getLastLoginExternalIdKey();
- return id.map(i -> i.equals(ExternalId.Key.create(SCHEME_GERRIT, user))).orElse(false);
+ return id.map(i -> i.equals(externalIdKeyFactory.create(SCHEME_GERRIT, user))).orElse(false);
}
String getRemoteUser(HttpServletRequest req) {
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java b/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
index 1b7e477..53f33b5 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
@@ -28,7 +28,7 @@
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.util.http.CacheHeaders;
import com.google.inject.Inject;
@@ -61,6 +61,8 @@
private final AccountManager accountManager;
private final HttpAuthFilter authFilter;
private final AuthConfig authConfig;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
HttpLoginServlet(
@@ -68,12 +70,16 @@
final CanonicalWebUrl urlProvider,
final AccountManager accountManager,
final HttpAuthFilter authFilter,
- final AuthConfig authConfig) {
+ final AuthConfig authConfig,
+ final ExternalIdKeyFactory externalIdKeyFactory,
+ final AuthRequest.Factory authRequestFactory) {
this.webSession = webSession;
this.urlProvider = urlProvider;
this.accountManager = accountManager;
this.authFilter = authFilter;
this.authConfig = authConfig;
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -109,7 +115,7 @@
return;
}
- final AuthRequest areq = AuthRequest.forUser(user);
+ final AuthRequest areq = authRequestFactory.createForUser(user);
areq.setDisplayName(authFilter.getRemoteDisplayname(req));
areq.setEmailAddress(authFilter.getRemoteEmail(req));
final AuthResult arsp;
@@ -154,7 +160,7 @@
throws AccountException, IOException, ConfigInvalidException {
accountManager.updateLink(
arsp.getAccountId(),
- new AuthRequest(ExternalId.Key.create(SCHEME_EXTERNAL, remoteAuthToken)));
+ authRequestFactory.create(externalIdKeyFactory.create(SCHEME_EXTERNAL, remoteAuthToken)));
}
private void replace(Document doc, String name, String value) {
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertAuthFilter.java b/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertAuthFilter.java
index 40807c0..820c7a2 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertAuthFilter.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertAuthFilter.java
@@ -42,12 +42,16 @@
private final DynamicItem<WebSession> webSession;
private final AccountManager accountManager;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
HttpsClientSslCertAuthFilter(
- final DynamicItem<WebSession> webSession, AccountManager accountManager) {
+ final DynamicItem<WebSession> webSession,
+ AccountManager accountManager,
+ final AuthRequest.Factory authRequestFactory) {
this.webSession = webSession;
this.accountManager = accountManager;
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -70,7 +74,7 @@
} else {
throw new ServletException("Couldn't extract username from your certificate");
}
- final AuthRequest areq = AuthRequest.forUser(userName);
+ final AuthRequest areq = authRequestFactory.createForUser(userName);
final AuthResult arsp;
try {
arsp = accountManager.authenticate(areq);
diff --git a/java/com/google/gerrit/httpd/auth/ldap/LdapLoginServlet.java b/java/com/google/gerrit/httpd/auth/ldap/LdapLoginServlet.java
index a09866e..6caa760 100644
--- a/java/com/google/gerrit/httpd/auth/ldap/LdapLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/ldap/LdapLoginServlet.java
@@ -56,17 +56,20 @@
private final DynamicItem<WebSession> webSession;
private final CanonicalWebUrl urlProvider;
private final SiteHeaderFooter headers;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
LdapLoginServlet(
AccountManager accountManager,
DynamicItem<WebSession> webSession,
CanonicalWebUrl urlProvider,
- SiteHeaderFooter headers) {
+ SiteHeaderFooter headers,
+ AuthRequest.Factory authRequestFactory) {
this.accountManager = accountManager;
this.webSession = webSession;
this.urlProvider = urlProvider;
this.headers = headers;
+ this.authRequestFactory = authRequestFactory;
}
private void sendForm(
@@ -115,7 +118,7 @@
return;
}
- AuthRequest areq = AuthRequest.forUser(username);
+ AuthRequest areq = authRequestFactory.createForUser(username);
areq.setPassword(password);
AuthResult ares;
diff --git a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
index 70ed79b..a3f8fbda 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
+++ b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
@@ -35,7 +35,7 @@
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.servlet.SessionScoped;
@@ -65,6 +65,8 @@
private Account.Id accountId;
private String redirectToken;
private boolean linkMode;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
OAuthSession(
@@ -72,13 +74,17 @@
Provider<IdentifiedUser> identifiedUser,
AccountManager accountManager,
CanonicalWebUrl urlProvider,
- OAuthTokenCache tokenCache) {
+ OAuthTokenCache tokenCache,
+ ExternalIdKeyFactory externalIdKeyFactory,
+ AuthRequest.Factory authRequestFactory) {
this.state = generateRandomState();
this.identifiedUser = identifiedUser;
this.webSession = webSession;
this.accountManager = accountManager;
this.urlProvider = urlProvider;
this.tokenCache = tokenCache;
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ this.authRequestFactory = authRequestFactory;
}
boolean isLoggedIn() {
@@ -126,7 +132,7 @@
private void authenticateAndRedirect(
HttpServletRequest req, HttpServletResponse rsp, OAuthToken token) throws IOException {
- AuthRequest areq = new AuthRequest(ExternalId.Key.parse(user.getExternalId()));
+ AuthRequest areq = authRequestFactory.create(externalIdKeyFactory.parse(user.getExternalId()));
AuthResult arsp;
try {
String claimedIdentifier = user.getClaimedIdentity();
diff --git a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
index b987c68..df0062c 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
@@ -32,8 +32,9 @@
import com.google.gerrit.server.IdentifiedUser;
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.AuthResult;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.servlet.SessionScoped;
@@ -63,18 +64,24 @@
private OAuthUserInfo user;
private String redirectToken;
private boolean linkMode;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
OAuthSessionOverOpenID(
DynamicItem<WebSession> webSession,
Provider<IdentifiedUser> identifiedUser,
AccountManager accountManager,
- CanonicalWebUrl urlProvider) {
+ CanonicalWebUrl urlProvider,
+ ExternalIdKeyFactory externalIdKeyFactory,
+ AuthRequest.Factory authRequestFactory) {
this.state = generateRandomState();
this.webSession = webSession;
this.identifiedUser = identifiedUser;
this.accountManager = accountManager;
this.urlProvider = urlProvider;
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ this.authRequestFactory = authRequestFactory;
}
boolean isLoggedIn() {
@@ -117,8 +124,7 @@
private void authenticateAndRedirect(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
com.google.gerrit.server.account.AuthRequest areq =
- new com.google.gerrit.server.account.AuthRequest(
- ExternalId.Key.parse(user.getExternalId()));
+ authRequestFactory.create(externalIdKeyFactory.parse(user.getExternalId()));
AuthResult arsp;
try {
String claimedIdentifier = user.getClaimedIdentity();
diff --git a/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java b/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
index b685011..cf3562f 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
@@ -28,7 +28,7 @@
import com.google.gerrit.server.UrlEncoded;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.auth.openid.OpenIdProviderPattern;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.ConfigUtil;
@@ -92,6 +92,8 @@
private final ConsumerManager manager;
private final List<OpenIdProviderPattern> allowedOpenIDs;
private final List<String> openIdDomains;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+ private final com.google.gerrit.server.account.AuthRequest.Factory authRequestFactory;
/** Maximum age, in seconds, before forcing re-authentication of account. */
private final int papeMaxAuthAge;
@@ -104,7 +106,9 @@
@GerritServerConfig Config config,
AuthConfig ac,
AccountManager am,
- ProxyProperties proxyProperties) {
+ ProxyProperties proxyProperties,
+ ExternalIdKeyFactory externalIdKeyFactory,
+ com.google.gerrit.server.account.AuthRequest.Factory authRequestFactory) {
if (proxyProperties.getProxyUrl() != null) {
final org.openid4java.util.ProxyProperties proxy = new org.openid4java.util.ProxyProperties();
@@ -132,6 +136,8 @@
"maxOpenIdSessionAge",
-1,
TimeUnit.SECONDS);
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ this.authRequestFactory = authRequestFactory;
}
@SuppressWarnings("unchecked")
@@ -310,7 +316,7 @@
}
final com.google.gerrit.server.account.AuthRequest areq =
- new com.google.gerrit.server.account.AuthRequest(ExternalId.Key.parse(openidIdentifier));
+ authRequestFactory.create(externalIdKeyFactory.parse(openidIdentifier));
if (sregRsp != null) {
areq.setDisplayName(sregRsp.getAttributeValue("fullname"));
@@ -388,8 +394,7 @@
// was missing due to a bug in Gerrit. Link the claimed.
//
final com.google.gerrit.server.account.AuthRequest linkReq =
- new com.google.gerrit.server.account.AuthRequest(
- ExternalId.Key.parse(claimedIdentifier));
+ authRequestFactory.create(externalIdKeyFactory.parse(claimedIdentifier));
linkReq.setDisplayName(areq.getDisplayName());
linkReq.setEmailAddress(areq.getEmailAddress());
accountManager.link(actualId.get(), linkReq);
@@ -425,8 +430,7 @@
webSession.get().login(arsp, remember);
if (arsp.isNew() && claimedIdentifier != null) {
final com.google.gerrit.server.account.AuthRequest linkReq =
- new com.google.gerrit.server.account.AuthRequest(
- ExternalId.Key.parse(claimedIdentifier));
+ authRequestFactory.create(externalIdKeyFactory.parse(claimedIdentifier));
linkReq.setDisplayName(areq.getDisplayName());
linkReq.setEmailAddress(areq.getEmailAddress());
accountManager.link(arsp.getAccountId(), linkReq);
diff --git a/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java b/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java
new file mode 100644
index 0000000..fbc6065
--- /dev/null
+++ b/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java
@@ -0,0 +1,194 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.pgm;
+
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.pgm.init.api.ConsoleUI;
+import com.google.gerrit.pgm.util.SiteProgram;
+import com.google.gerrit.server.account.externalids.DisabledExternalIdCache;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdNotes;
+import com.google.gerrit.server.account.externalids.ExternalIdUpsertPreprocessor;
+import com.google.gerrit.server.account.externalids.ExternalIds;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
+import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.util.Collection;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.kohsuke.args4j.Option;
+
+/**
+ * Changes the case sensitivity of `username:` and `gerrit:` external IDs by recomputing the SHA-1
+ * sums used as note names.
+ */
+public class ChangeExternalIdCaseSensitivity extends SiteProgram {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Option(name = "--batch", usage = "Don't ask for confirmation before migrating.")
+ private boolean batch;
+
+ @Option(name = "--dryrun", usage = "Do a dryrun of the migration.")
+ private boolean dryrun;
+
+ private final LifecycleManager manager = new LifecycleManager();
+ private final TextProgressMonitor monitor = new TextProgressMonitor();
+
+ private Config globalConfig;
+ private boolean isUserNameCaseInsensitive;
+ private ConsoleUI ui;
+
+ @Inject private GitRepositoryManager repoManager;
+ @Inject private AllUsersName allUsersName;
+ @Inject private Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory;
+ @Inject private ExternalIdNotes.FactoryNoReindex externalIdNotesFactory;
+ @Inject private ExternalIds externalIds;
+ @Inject private ExternalIdFactory externalIdFactory;
+
+ @Override
+ public int run() throws Exception {
+ mustHaveValidSite();
+ ui = ConsoleUI.getInstance(batch);
+
+ Injector dbInjector = createDbInjector();
+ manager.add(dbInjector, dbInjector.createChildInjector(NoteDbSchemaVersionCheck.module()));
+ dbInjector
+ .createChildInjector(
+ new FactoryModule() {
+ @Override
+ protected void configure() {
+ bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
+ factory(MetaDataUpdate.InternalFactory.class);
+ DynamicMap.mapOf(binder(), ExternalIdUpsertPreprocessor.class);
+
+ // The ChangeExternalIdCaseSensitivity program needs to access all external IDs only
+ // once to update them. After the update they are not accessed again. Hence the
+ // LocalUsernamesToLowerCase program doesn't benefit from caching external IDs and
+ // the external ID cache can be disabled.
+ install(DisabledExternalIdCache.module());
+ }
+ })
+ .injectMembers(this);
+ globalConfig = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
+
+ this.isUserNameCaseInsensitive =
+ globalConfig.getBoolean("auth", "userNameCaseInsensitive", false);
+
+ String message =
+ "auth.userNameCaseInsensitive is set to %b. "
+ + "External IDs will be migrated to be case %ssensitive. Continue?";
+ if (!ui.yesno(
+ true, message, isUserNameCaseInsensitive, isUserNameCaseInsensitive ? "" : "in")) {
+ return 0;
+ }
+
+ Collection<ExternalId> todo = externalIds.all();
+ monitor.beginTask("Converting external ID note names", todo.size());
+
+ manager.start();
+ try {
+ try (Repository repo = repoManager.openRepository(allUsersName)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(repo);
+ for (ExternalId extId : todo) {
+ recomputeExternalIdNoteId(extIdNotes, extId);
+ monitor.update(1);
+ }
+ if (!dryrun) {
+ try (MetaDataUpdate metaDataUpdate =
+ metaDataUpdateServerFactory.get().create(allUsersName)) {
+ metaDataUpdate.setMessage(
+ String.format(
+ "Migration to case %ssensitive usernames",
+ isUserNameCaseInsensitive ? "" : "in"));
+ extIdNotes.commit(metaDataUpdate);
+ }
+ }
+ }
+ } finally {
+ manager.stop();
+ monitor.endTask();
+ }
+
+ int exitCode;
+ if (!dryrun) {
+ updateGerritConfig();
+
+ exitCode = reindexAccounts();
+ } else {
+ exitCode = 0;
+ }
+ return exitCode;
+ }
+
+ private void recomputeExternalIdNoteId(ExternalIdNotes extIdNotes, ExternalId extId)
+ throws DuplicateKeyException, IOException {
+ if (extId.isScheme(SCHEME_GERRIT) || extId.isScheme(SCHEME_USERNAME)) {
+ ExternalId.Key updatedKey =
+ ExternalId.Key.create(extId.key().scheme(), extId.key().id(), !isUserNameCaseInsensitive);
+ if (!extId.key().sha1().getName().equals(updatedKey.sha1().getName())) {
+ logger.atInfo().log("Converting note name of external ID: %s", extId.key());
+ ExternalId updatedExtId =
+ externalIdFactory.create(
+ updatedKey, extId.accountId(), extId.email(), extId.password(), extId.blobId());
+ extIdNotes.replace(extId, updatedExtId);
+ }
+ }
+ }
+
+ private void updateGerritConfig() throws IOException, ConfigInvalidException {
+ logger.atInfo().log("Setting auth.userNameCaseInsensitive to true in gerrit.config.");
+ FileBasedConfig config =
+ new FileBasedConfig(
+ globalConfig, getSitePath().resolve("etc/gerrit.config").toFile(), FS.DETECTED);
+ config.load();
+ config.setBoolean("auth", null, "userNameCaseInsensitive", !isUserNameCaseInsensitive);
+ config.save();
+ }
+
+ private int reindexAccounts() throws Exception {
+ monitor.beginTask("Reindex accounts", ProgressMonitor.UNKNOWN);
+ String[] reindexArgs = {
+ "--site-path", getSitePath().toString(), "--index", AccountSchemaDefinitions.NAME
+ };
+ logger.atInfo().log(
+ "Migration complete, reindexing accounts with: reindex %s", String.join(" ", reindexArgs));
+ Reindex reindexPgm = new Reindex();
+ int exitCode = reindexPgm.main(reindexArgs);
+ monitor.endTask();
+ return exitCode;
+ }
+}
diff --git a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
index 8e2f70f..f651994 100644
--- a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
+++ b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
@@ -22,6 +22,7 @@
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.server.account.externalids.DisabledExternalIdCache;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AllUsersName;
@@ -52,6 +53,7 @@
@Inject private AllUsersName allUsersName;
@Inject private Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory;
@Inject private ExternalIdNotes.FactoryNoReindex externalIdNotesFactory;
+ @Inject private ExternalIdFactory externalIdFactory;
@Inject private ExternalIds externalIds;
@Override
@@ -105,7 +107,7 @@
String localUserLowerCase = localUser.toLowerCase(Locale.US);
if (!localUser.equals(localUserLowerCase)) {
ExternalId extIdLowerCase =
- ExternalId.create(
+ externalIdFactory.create(
SCHEME_GERRIT,
localUserLowerCase,
extId.accountId(),
diff --git a/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java b/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
index 9519653..e2a1f04 100644
--- a/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
@@ -18,6 +18,7 @@
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.SitePaths;
@@ -39,12 +40,18 @@
private final InitFlags flags;
private final SitePaths site;
private final AllUsersName allUsers;
+ private final ExternalIdFactory externalIdFactory;
@Inject
- public ExternalIdsOnInit(InitFlags flags, SitePaths site, AllUsersNameOnInitProvider allUsers) {
+ public ExternalIdsOnInit(
+ InitFlags flags,
+ SitePaths site,
+ AllUsersNameOnInitProvider allUsers,
+ ExternalIdFactory externalIdFactory) {
this.flags = flags;
this.site = site;
this.allUsers = new AllUsersName(allUsers.get());
+ this.externalIdFactory = externalIdFactory;
}
public synchronized void insert(String commitMessage, Collection<ExternalId> extIds)
@@ -52,7 +59,8 @@
File path = getPath();
if (path != null) {
try (Repository allUsersRepo = new FileRepository(path)) {
- ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, allUsersRepo);
+ ExternalIdNotes extIdNotes =
+ ExternalIdNotes.loadNoCacheUpdate(allUsers, allUsersRepo, externalIdFactory);
extIdNotes.insert(extIds);
try (MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsers, allUsersRepo)) {
diff --git a/java/com/google/gerrit/pgm/init/InitAdminUser.java b/java/com/google/gerrit/pgm/init/InitAdminUser.java
index 2e32066..d6a0133 100644
--- a/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.account.AccountSshKey;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gerrit.server.index.group.GroupIndex;
@@ -52,6 +53,7 @@
private final ExternalIdsOnInit externalIds;
private final SequencesOnInit sequencesOnInit;
private final GroupsOnInit groupsOnInit;
+ private final ExternalIdFactory externalIdFactory;
private AccountIndexCollection accountIndexCollection;
private GroupIndexCollection groupIndexCollection;
@@ -63,7 +65,8 @@
VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory,
ExternalIdsOnInit externalIds,
SequencesOnInit sequencesOnInit,
- GroupsOnInit groupsOnInit) {
+ GroupsOnInit groupsOnInit,
+ ExternalIdFactory externalIdFactory) {
this.flags = flags;
this.ui = ui;
this.accounts = accounts;
@@ -71,6 +74,7 @@
this.externalIds = externalIds;
this.sequencesOnInit = sequencesOnInit;
this.groupsOnInit = groupsOnInit;
+ this.externalIdFactory = externalIdFactory;
}
@Override
@@ -107,10 +111,10 @@
String email = readEmail(sshKey);
List<ExternalId> extIds = new ArrayList<>(2);
- extIds.add(ExternalId.createUsername(username, id, httpPassword));
+ extIds.add(externalIdFactory.createUsername(username, id, httpPassword));
if (email != null) {
- extIds.add(ExternalId.createEmail(id, email));
+ extIds.add(externalIdFactory.createEmail(id, email));
}
externalIds.insert("Add external IDs for initial admin user", extIds);
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 09ddcc8..188f30b 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -39,7 +39,7 @@
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.account.ServiceUserClassifierImpl;
-import com.google.gerrit.server.account.externalids.ExternalIdModule;
+import com.google.gerrit.server.account.externalids.ExternalIdCacheModule;
import com.google.gerrit.server.approval.ApprovalCacheImpl;
import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.cache.h2.H2CacheModule;
@@ -172,7 +172,7 @@
modules.add(new DefaultPermissionBackendModule());
modules.add(new DefaultMemoryCacheModule());
modules.add(new H2CacheModule());
- modules.add(new ExternalIdModule());
+ modules.add(new ExternalIdCacheModule());
modules.add(new GroupModule());
modules.add(new NoteDbModule());
modules.add(AccountCacheImpl.module());
diff --git a/java/com/google/gerrit/server/account/AccountCacheImpl.java b/java/com/google/gerrit/server/account/AccountCacheImpl.java
index defa4c7..093af68 100644
--- a/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -24,7 +24,7 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.AllUsersName;
@@ -76,6 +76,7 @@
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
private final DefaultPreferencesCache defaultPreferenceCache;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
AccountCacheImpl(
@@ -84,12 +85,14 @@
LoadingCache<CachedAccountDetails.Key, CachedAccountDetails> accountDetailsCache,
GitRepositoryManager repoManager,
AllUsersName allUsersName,
- DefaultPreferencesCache defaultPreferenceCache) {
+ DefaultPreferencesCache defaultPreferenceCache,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.externalIds = externalIds;
this.accountDetailsCache = accountDetailsCache;
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.defaultPreferenceCache = defaultPreferenceCache;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -140,7 +143,7 @@
public Optional<AccountState> getByUsername(String username) {
try {
return externalIds
- .get(ExternalId.Key.create(SCHEME_USERNAME, username))
+ .get(externalIdKeyFactory.create(SCHEME_USERNAME, username))
.map(e -> get(e.accountId()))
.orElseGet(Optional::empty);
} catch (IOException e) {
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index 5a89a862..987e7e3 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -36,6 +36,8 @@
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.auth.NoSuchUserException;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -77,6 +79,8 @@
private final GroupsUpdate.Factory groupsUpdateFactory;
private final boolean autoUpdateAccountActiveStatus;
private final SetInactiveFlag setInactiveFlag;
+ private final ExternalIdFactory externalIdFactory;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@VisibleForTesting
@Inject
@@ -92,7 +96,9 @@
ProjectCache projectCache,
ExternalIds externalIds,
GroupsUpdate.Factory groupsUpdateFactory,
- SetInactiveFlag setInactiveFlag) {
+ SetInactiveFlag setInactiveFlag,
+ ExternalIdFactory externalIdFactory,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.sequences = sequences;
this.accounts = accounts;
this.accountsUpdateProvider = accountsUpdateProvider;
@@ -108,12 +114,14 @@
this.autoUpdateAccountActiveStatus =
cfg.getBoolean("auth", "autoUpdateAccountActiveStatus", false);
this.setInactiveFlag = setInactiveFlag;
+ this.externalIdFactory = externalIdFactory;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
/** @return user identified by this external identity string */
public Optional<Account.Id> lookup(String externalId) throws AccountException {
try {
- return externalIds.get(ExternalId.Key.parse(externalId)).map(ExternalId::accountId);
+ return externalIds.get(externalIdKeyFactory.parse(externalId)).map(ExternalId::accountId);
} catch (IOException e) {
throw new AccountException("Cannot lookup account " + externalId, e);
}
@@ -229,7 +237,7 @@
String oldEmail = extId.email();
if (newEmail != null && !newEmail.equals(oldEmail)) {
ExternalId extIdWithNewEmail =
- ExternalId.create(extId.key(), extId.accountId(), newEmail, extId.password());
+ externalIdFactory.create(extId.key(), extId.accountId(), newEmail, extId.password());
checkEmailNotUsed(extId.accountId(), extIdWithNewEmail);
accountUpdates.add(u -> u.replaceExternalId(extId, extIdWithNewEmail));
@@ -273,7 +281,7 @@
logger.atFine().log("Assigning new Id %s to account", newId);
ExternalId extId =
- ExternalId.createWithEmail(who.getExternalIdKey(), newId, who.getEmailAddress());
+ externalIdFactory.createWithEmail(who.getExternalIdKey(), newId, who.getEmailAddress());
logger.atFine().log("Created external Id: %s", extId);
checkEmailNotUsed(newId, extId);
ExternalId userNameExtId =
@@ -348,7 +356,7 @@
"Cannot assign user name \"%s\" to account %s; name does not conform.",
username, accountId));
}
- return ExternalId.create(SCHEME_USERNAME, username, accountId);
+ return externalIdFactory.create(SCHEME_USERNAME, username, accountId);
}
private void checkEmailNotUsed(Account.Id accountId, ExternalId extIdToBeCreated)
@@ -414,7 +422,7 @@
update(who, extId);
} else {
ExternalId newExtId =
- ExternalId.createWithEmail(who.getExternalIdKey(), to, who.getEmailAddress());
+ externalIdFactory.createWithEmail(who.getExternalIdKey(), to, who.getEmailAddress());
checkEmailNotUsed(to, newExtId);
accountsUpdateProvider
.get()
diff --git a/java/com/google/gerrit/server/account/AccountModule.java b/java/com/google/gerrit/server/account/AccountModule.java
new file mode 100644
index 0000000..c1305cf
--- /dev/null
+++ b/java/com/google/gerrit/server/account/AccountModule.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.inject.AbstractModule;
+
+public class AccountModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(AuthRequest.Factory.class);
+ }
+}
diff --git a/java/com/google/gerrit/server/account/AuthRequest.java b/java/com/google/gerrit/server/account/AuthRequest.java
index ddb54a6..50ed532 100644
--- a/java/com/google/gerrit/server/account/AuthRequest.java
+++ b/java/com/google/gerrit/server/account/AuthRequest.java
@@ -21,6 +21,9 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import java.util.Optional;
/**
@@ -32,31 +35,52 @@
* not all OpenID providers return them, and not all non-OpenID systems can use them.
*/
public class AuthRequest {
- /** Create a request for a local username, such as from LDAP. */
- public static AuthRequest forUser(String username) {
- AuthRequest r = new AuthRequest(ExternalId.Key.create(SCHEME_GERRIT, username));
- r.setUserName(username);
- return r;
+ @Singleton
+ public static class Factory {
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+
+ @Inject
+ public Factory(ExternalIdKeyFactory externalIdKeyFactory) {
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ }
+
+ public AuthRequest create(ExternalId.Key externalIdKey) {
+ return new AuthRequest(externalIdKey, externalIdKeyFactory);
+ }
+
+ /** Create a request for a local username, such as from LDAP. */
+ public AuthRequest createForUser(String username) {
+ AuthRequest r =
+ new AuthRequest(
+ externalIdKeyFactory.create(SCHEME_GERRIT, username), externalIdKeyFactory);
+ r.setUserName(username);
+ return r;
+ }
+
+ /** Create a request for an external username. */
+ public AuthRequest createForExternalUser(String username) {
+ AuthRequest r =
+ new AuthRequest(
+ externalIdKeyFactory.create(SCHEME_EXTERNAL, username), externalIdKeyFactory);
+ r.setUserName(username);
+ return r;
+ }
+
+ /**
+ * Create a request for an email address registration.
+ *
+ * <p>This type of request should be used only to attach a new email address to an existing user
+ * account.
+ */
+ public AuthRequest createForEmail(String email) {
+ AuthRequest r =
+ new AuthRequest(externalIdKeyFactory.create(SCHEME_MAILTO, email), externalIdKeyFactory);
+ r.setEmailAddress(email);
+ return r;
+ }
}
- /** Create a request for an external username. */
- public static AuthRequest forExternalUser(String username) {
- AuthRequest r = new AuthRequest(ExternalId.Key.create(SCHEME_EXTERNAL, username));
- r.setUserName(username);
- return r;
- }
-
- /**
- * Create a request for an email address registration.
- *
- * <p>This type of request should be used only to attach a new email address to an existing user
- * account.
- */
- public static AuthRequest forEmail(String email) {
- AuthRequest r = new AuthRequest(ExternalId.Key.create(SCHEME_MAILTO, email));
- r.setEmailAddress(email);
- return r;
- }
+ private final ExternalIdKeyFactory externalIdKeyFactory;
private ExternalId.Key externalId;
private String password;
@@ -69,8 +93,9 @@
private boolean authProvidesAccountActiveStatus;
private boolean active;
- public AuthRequest(ExternalId.Key externalId) {
+ private AuthRequest(ExternalId.Key externalId, ExternalIdKeyFactory externalIdKeyFactory) {
this.externalId = externalId;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
public ExternalId.Key getExternalIdKey() {
@@ -86,7 +111,7 @@
public void setLocalUser(String localUser) {
if (externalId.isScheme(SCHEME_GERRIT)) {
- externalId = ExternalId.Key.create(SCHEME_GERRIT, localUser);
+ externalId = externalIdKeyFactory.create(SCHEME_GERRIT, localUser);
}
}
diff --git a/java/com/google/gerrit/server/account/externalids/AllExternalIds.java b/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
index f1fc5cb..e718bcb 100644
--- a/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
+++ b/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
@@ -68,7 +68,8 @@
ExternalIdProto.Builder b =
ExternalIdProto.newBuilder()
.setKey(externalId.key().get())
- .setAccountId(externalId.accountId().get());
+ .setAccountId(externalId.accountId().get())
+ .setIsCaseInsensitive(externalId.isCaseInsensitive());
if (externalId.email() != null) {
b.setEmail(externalId.email());
}
@@ -91,7 +92,7 @@
private static ExternalId toExternalId(ObjectIdConverter idConverter, ExternalIdProto proto) {
return ExternalId.create(
- ExternalId.Key.parse(proto.getKey()),
+ ExternalId.Key.parse(proto.getKey(), proto.getIsCaseInsensitive()),
Account.id(proto.getAccountId()),
// ExternalId treats null and empty strings the same, so no need to distinguish here.
proto.getEmail(),
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index 268812c..bbee1b2 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -17,35 +17,28 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.git.ObjectIds;
-import com.google.gerrit.server.account.HashedPassword;
import java.io.Serializable;
import java.util.Collection;
+import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
@AutoValue
public abstract class ExternalId implements Serializable {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
// If these regular expressions are modified the same modifications should be done to the
// corresponding regular expressions in the
// com.google.gerrit.client.account.UsernameField class.
@@ -106,10 +99,10 @@
private static final long serialVersionUID = 1L;
- private static final String EXTERNAL_ID_SECTION = "externalId";
- private static final String ACCOUNT_ID_KEY = "accountId";
- private static final String EMAIL_KEY = "email";
- private static final String PASSWORD_KEY = "password";
+ static final String EXTERNAL_ID_SECTION = "externalId";
+ static final String ACCOUNT_ID_KEY = "accountId";
+ static final String EMAIL_KEY = "email";
+ static final String PASSWORD_KEY = "password";
/**
* Scheme used for {@link AuthType#LDAP}, {@link AuthType#CLIENT_SSL_CERT_LDAP}, {@link
@@ -118,6 +111,8 @@
* <p>The name {@code gerrit:} was a very poor choice.
*
* <p>Scheme names must not contain colons (':').
+ *
+ * <p>Will be handled case insensitive, if auth.userNameCaseInsensitive = true.
*/
public static final String SCHEME_GERRIT = "gerrit";
@@ -127,7 +122,11 @@
/** Scheme used to represent only an email address. */
public static final String SCHEME_MAILTO = "mailto";
- /** Scheme for the username used to authenticate an account, e.g. over SSH. */
+ /**
+ * Scheme for the username used to authenticate an account, e.g. over SSH.
+ *
+ * <p>Will be handled case insensitive, if auth.userNameCaseInsensitive = true.
+ */
public static final String SCHEME_USERNAME = "username";
/** Scheme used for GPG public keys. */
@@ -154,10 +153,12 @@
*
* @param scheme the scheme name, must not contain colons (':'), can be {@code null}
* @param id the external ID, must not contain colons (':')
+ * @param isCaseInsensitive whether the external ID key is matched case insensitively
* @return the created external ID key
*/
- public static Key create(@Nullable String scheme, String id) {
- return new AutoValue_ExternalId_Key(Strings.emptyToNull(scheme), id);
+ @VisibleForTesting
+ public static Key create(@Nullable String scheme, String id, boolean isCaseInsensitive) {
+ return new AutoValue_ExternalId_Key(Strings.emptyToNull(scheme), id, isCaseInsensitive);
}
/**
@@ -165,18 +166,21 @@
*
* @return the parsed external ID key
*/
- public static Key parse(String externalId) {
+ @VisibleForTesting
+ public static Key parse(String externalId, boolean isCaseInsensitive) {
int c = externalId.indexOf(':');
if (c < 1 || c >= externalId.length() - 1) {
- return create(null, externalId);
+ return create(null, externalId, isCaseInsensitive);
}
- return create(externalId.substring(0, c), externalId.substring(c + 1));
+ return create(externalId.substring(0, c), externalId.substring(c + 1), isCaseInsensitive);
}
public abstract @Nullable String scheme();
public abstract String id();
+ public abstract boolean isCaseInsensitive();
+
public boolean isScheme(String scheme) {
return scheme.equals(scheme());
}
@@ -187,7 +191,8 @@
*/
@SuppressWarnings("deprecation") // Use Hashing.sha1 for compatibility.
public ObjectId sha1() {
- return ObjectId.fromRaw(Hashing.sha1().hashString(get(), UTF_8).asBytes());
+ String keyString = isCaseInsensitive() ? get().toLowerCase(Locale.US) : get();
+ return ObjectId.fromRaw(Hashing.sha1().hashString(keyString, UTF_8).asBytes());
}
/**
@@ -209,100 +214,26 @@
return get();
}
+ @Override
+ public final boolean equals(Object obj) {
+ if (!(obj instanceof ExternalId.Key)) {
+ return false;
+ }
+ ExternalId.Key o = (ExternalId.Key) obj;
+
+ return sha1().equals(o.sha1());
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(sha1());
+ }
+
public static ImmutableSet<ExternalId.Key> from(Collection<ExternalId> extIds) {
return extIds.stream().map(ExternalId::key).collect(toImmutableSet());
}
}
- /**
- * Creates an external ID.
- *
- * @param scheme the scheme name, must not contain colons (':')
- * @param id the external ID, must not contain colons (':')
- * @param accountId the ID of the account to which the external ID belongs
- * @return the created external ID
- */
- public static ExternalId create(String scheme, String id, Account.Id accountId) {
- return create(Key.create(scheme, id), accountId, null, null);
- }
-
- /**
- * Creates an external ID.
- *
- * @param scheme the scheme name, must not contain colons (':')
- * @param id the external ID, must not contain colons (':')
- * @param accountId the ID of the account to which the external ID belongs
- * @param email the email of the external ID, may be {@code null}
- * @param hashedPassword the hashed password of the external ID, may be {@code null}
- * @return the created external ID
- */
- public static ExternalId create(
- String scheme,
- String id,
- Account.Id accountId,
- @Nullable String email,
- @Nullable String hashedPassword) {
- return create(Key.create(scheme, id), accountId, email, hashedPassword);
- }
-
- public static ExternalId create(Key key, Account.Id accountId) {
- return create(key, accountId, null, null);
- }
-
- public static ExternalId create(
- Key key, Account.Id accountId, @Nullable String email, @Nullable String hashedPassword) {
- return create(
- key, accountId, Strings.emptyToNull(email), Strings.emptyToNull(hashedPassword), null);
- }
-
- public static ExternalId createWithPassword(
- Key key, Account.Id accountId, @Nullable String email, @Nullable String plainPassword) {
- plainPassword = Strings.emptyToNull(plainPassword);
- String hashedPassword =
- plainPassword != null ? HashedPassword.fromPassword(plainPassword).encode() : null;
- return create(key, accountId, email, hashedPassword);
- }
-
- /**
- * Create a external ID for a username (scheme "username").
- *
- * @param id the external ID, must not contain colons (':')
- * @param accountId the ID of the account to which the external ID belongs
- * @param plainPassword the plain HTTP password, may be {@code null}
- * @return the created external ID
- */
- public static ExternalId createUsername(
- String id, Account.Id accountId, @Nullable String plainPassword) {
- return createWithPassword(Key.create(SCHEME_USERNAME, id), accountId, null, plainPassword);
- }
-
- /**
- * Creates an external ID with an email.
- *
- * @param scheme the scheme name, must not contain colons (':')
- * @param id the external ID, must not contain colons (':')
- * @param accountId the ID of the account to which the external ID belongs
- * @param email the email of the external ID, may be {@code null}
- * @return the created external ID
- */
- public static ExternalId createWithEmail(
- String scheme, String id, Account.Id accountId, @Nullable String email) {
- return createWithEmail(Key.create(scheme, id), accountId, email);
- }
-
- public static ExternalId createWithEmail(Key key, Account.Id accountId, @Nullable String email) {
- return create(key, accountId, Strings.emptyToNull(email), null);
- }
-
- public static ExternalId createEmail(Account.Id accountId, String email) {
- return createWithEmail(SCHEME_MAILTO, email, accountId, requireNonNull(email));
- }
-
- static ExternalId create(ExternalId extId, @Nullable ObjectId blobId) {
- return new AutoValue_ExternalId(
- extId.key(), extId.accountId(), extId.email(), extId.password(), blobId);
- }
-
@VisibleForTesting
public static ExternalId create(
Key key,
@@ -311,111 +242,20 @@
@Nullable String hashedPassword,
@Nullable ObjectId blobId) {
return new AutoValue_ExternalId(
- key, accountId, Strings.emptyToNull(email), Strings.emptyToNull(hashedPassword), blobId);
- }
-
- /**
- * Parses an external ID from a byte array that contain the external ID as an Git config file
- * text.
- *
- * <p>The Git config must have exactly one externalId subsection with an accountId and optionally
- * email and password:
- *
- * <pre>
- * [externalId "username:jdoe"]
- * accountId = 1003407
- * email = jdoe@example.com
- * password = bcrypt:4:LCbmSBDivK/hhGVQMfkDpA==:XcWn0pKYSVU/UJgOvhidkEtmqCp6oKB7
- * </pre>
- */
- public static ExternalId parse(String noteId, byte[] raw, ObjectId blobId)
- throws ConfigInvalidException {
- requireNonNull(blobId);
-
- Config externalIdConfig = new Config();
- try {
- externalIdConfig.fromText(new String(raw, UTF_8));
- } catch (ConfigInvalidException e) {
- throw invalidConfig(noteId, e.getMessage());
- }
-
- Set<String> externalIdKeys = externalIdConfig.getSubsections(EXTERNAL_ID_SECTION);
- if (externalIdKeys.size() != 1) {
- throw invalidConfig(
- noteId,
- String.format(
- "Expected exactly 1 '%s' section, found %d",
- EXTERNAL_ID_SECTION, externalIdKeys.size()));
- }
-
- String externalIdKeyStr = Iterables.getOnlyElement(externalIdKeys);
- Key externalIdKey = Key.parse(externalIdKeyStr);
- if (externalIdKey == null) {
- throw invalidConfig(noteId, String.format("External ID %s is invalid", externalIdKeyStr));
- }
-
- if (!externalIdKey.sha1().getName().equals(noteId)) {
- throw invalidConfig(
- noteId,
- String.format(
- "SHA1 of external ID '%s' does not match note ID '%s'", externalIdKeyStr, noteId));
- }
-
- String email = externalIdConfig.getString(EXTERNAL_ID_SECTION, externalIdKeyStr, EMAIL_KEY);
- String password =
- externalIdConfig.getString(EXTERNAL_ID_SECTION, externalIdKeyStr, PASSWORD_KEY);
- int accountId = readAccountId(noteId, externalIdConfig, externalIdKeyStr);
-
- return create(
- externalIdKey,
- Account.id(accountId),
+ key,
+ accountId,
+ key.isCaseInsensitive(),
Strings.emptyToNull(email),
- Strings.emptyToNull(password),
+ Strings.emptyToNull(hashedPassword),
blobId);
}
- private static int readAccountId(String noteId, Config externalIdConfig, String externalIdKeyStr)
- throws ConfigInvalidException {
- String accountIdStr =
- externalIdConfig.getString(EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY);
- if (accountIdStr == null) {
- throw invalidConfig(
- noteId,
- String.format(
- "Value for '%s.%s.%s' is missing, expected account ID",
- EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY));
- }
-
- try {
- int accountId =
- externalIdConfig.getInt(EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY, -1);
- if (accountId < 0) {
- throw invalidConfig(
- noteId,
- String.format(
- "Value %s for '%s.%s.%s' is invalid, expected account ID",
- accountIdStr, EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY));
- }
- return accountId;
- } catch (IllegalArgumentException e) {
- String msg =
- String.format(
- "Value %s for '%s.%s.%s' is invalid, expected account ID",
- accountIdStr, EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY);
- logger.atSevere().withCause(e).log(msg);
- throw invalidConfig(noteId, msg);
- }
- }
-
- private static ConfigInvalidException invalidConfig(String noteId, String message) {
- return new ConfigInvalidException(
- String.format("Invalid external ID config for note '%s': %s", noteId, message));
- }
-
public abstract Key key();
public abstract Account.Id accountId();
+ public abstract boolean isCaseInsensitive();
+
public abstract @Nullable String email();
public abstract @Nullable String password();
@@ -456,13 +296,14 @@
ExternalId o = (ExternalId) obj;
return Objects.equals(key(), o.key())
&& Objects.equals(accountId(), o.accountId())
+ && Objects.equals(isCaseInsensitive(), o.isCaseInsensitive())
&& Objects.equals(email(), o.email())
&& Objects.equals(password(), o.password());
}
@Override
public final int hashCode() {
- return Objects.hash(key(), accountId(), email(), password());
+ return Objects.hash(key(), accountId(), isCaseInsensitive(), email(), password());
}
/**
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
index 902c18b..5d81a25 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
@@ -73,6 +73,7 @@
private final Timer0 reloadDifferential;
private final boolean enablePartialReloads;
private final boolean isPersistentCache;
+ private final ExternalIdFactory externalIdFactory;
@Inject
ExternalIdCacheLoader(
@@ -82,7 +83,8 @@
@Named(ExternalIdCacheImpl.CACHE_NAME)
Provider<Cache<ObjectId, AllExternalIds>> externalIdCache,
MetricMaker metricMaker,
- @GerritServerConfig Config config) {
+ @GerritServerConfig Config config,
+ ExternalIdFactory externalIdFactory) {
this.externalIdReader = externalIdReader;
this.externalIdCache = externalIdCache;
this.gitRepositoryManager = gitRepositoryManager;
@@ -105,6 +107,7 @@
config.getBoolean("cache", ExternalIdCacheImpl.CACHE_NAME, "enablePartialReloads", true);
this.isPersistentCache =
config.getInt("cache", ExternalIdCacheImpl.CACHE_NAME, "diskLimit", 0) > 0;
+ this.externalIdFactory = externalIdFactory;
}
@Override
@@ -216,7 +219,7 @@
* @param additions map of name to blob ID for each external ID that should be added
* @param removals set of name {@link ObjectId}s that should be removed
*/
- private static AllExternalIds buildAllExternalIds(
+ private AllExternalIds buildAllExternalIds(
Repository repo,
AllExternalIds oldExternalIds,
Map<ObjectId, ObjectId> additions,
@@ -245,7 +248,7 @@
ExternalId parsedExternalId;
try {
parsedExternalId =
- ExternalId.parse(
+ externalIdFactory.parse(
nameToBlob.getKey().name(),
reader.open(nameToBlob.getValue()).getCachedBytes(),
nameToBlob.getValue());
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheModule.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheModule.java
new file mode 100644
index 0000000..f0ad1b2
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheModule.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.serialize.ObjectIdCacheSerializer;
+import com.google.inject.TypeLiteral;
+import java.time.Duration;
+import org.eclipse.jgit.lib.ObjectId;
+
+public class ExternalIdCacheModule extends CacheModule {
+ @Override
+ protected void configure() {
+ persist(ExternalIdCacheImpl.CACHE_NAME, ObjectId.class, new TypeLiteral<AllExternalIds>() {})
+ // The cached data is potentially pretty large and we are always only interested
+ // in the latest value. However, due to a race condition, it is possible for different
+ // threads to observe different values of the meta ref, and hence request different keys
+ // from the cache. Extend the cache size by 1 to cover this case, but expire the extra
+ // object after a short period of time, since it may be a potentially large amount of
+ // memory.
+ // When loading a new value because the primary data advanced, we want to leverage the old
+ // cache state to recompute only what changed. This doesn't affect cache size though as
+ // Guava calls the loader first and evicts later on.
+ .maximumWeight(2)
+ .expireFromMemoryAfterAccess(Duration.ofMinutes(1))
+ .loader(ExternalIdCacheLoader.class)
+ .diskLimit(-1)
+ .version(1)
+ .keySerializer(ObjectIdCacheSerializer.INSTANCE)
+ .valueSerializer(AllExternalIds.Serializer.INSTANCE);
+
+ bind(ExternalIdCacheImpl.class);
+ bind(ExternalIdCache.class).to(ExternalIdCacheImpl.class);
+ }
+}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java b/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java
new file mode 100644
index 0000000..0c96f58
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java
@@ -0,0 +1,314 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.server.account.HashedPassword;
+import com.google.gerrit.server.account.externalids.ExternalId.Key;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+
+@Singleton
+public class ExternalIdFactory {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+
+ @Inject
+ public ExternalIdFactory(ExternalIdKeyFactory externalIdKeyFactory) {
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ }
+
+ /**
+ * Creates an external ID.
+ *
+ * @param scheme the scheme name, must not contain colons (':'). E.g. {@link
+ * ExternalId#SCHEME_USERNAME}.
+ * @param id the external ID, must not contain colons (':')
+ * @param accountId the ID of the account to which the external ID belongs
+ * @return the created external ID
+ */
+ public ExternalId create(String scheme, String id, Account.Id accountId) {
+ return create(externalIdKeyFactory.create(scheme, id), accountId, null, null);
+ }
+
+ /**
+ * Creates an external ID.
+ *
+ * @param scheme the scheme name, must not contain colons (':'). E.g. {@link
+ * ExternalId#SCHEME_USERNAME}.
+ * @param id the external ID, must not contain colons (':')
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @param hashedPassword the hashed password of the external ID, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId create(
+ String scheme,
+ String id,
+ Account.Id accountId,
+ @Nullable String email,
+ @Nullable String hashedPassword) {
+ return create(externalIdKeyFactory.create(scheme, id), accountId, email, hashedPassword);
+ }
+
+ /**
+ * Creates an external ID.
+ *
+ * @param key the external Id key
+ * @param accountId the ID of the account to which the external ID belongs
+ * @return the created external ID
+ */
+ public ExternalId create(Key key, Account.Id accountId) {
+ return create(key, accountId, null, null);
+ }
+
+ /**
+ * Creates an external ID.
+ *
+ * @param key the external Id key
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @param hashedPassword the hashed password of the external ID, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId create(
+ Key key, Account.Id accountId, @Nullable String email, @Nullable String hashedPassword) {
+ return create(
+ key, accountId, Strings.emptyToNull(email), Strings.emptyToNull(hashedPassword), null);
+ }
+
+ /**
+ * Creates an external ID adding a hashed password computed from a plain password.
+ *
+ * @param key the external Id key
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @param plainPassword the plain HTTP password, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId createWithPassword(
+ Key key, Account.Id accountId, @Nullable String email, @Nullable String plainPassword) {
+ plainPassword = Strings.emptyToNull(plainPassword);
+ String hashedPassword =
+ plainPassword != null ? HashedPassword.fromPassword(plainPassword).encode() : null;
+ return create(key, accountId, email, hashedPassword);
+ }
+
+ /**
+ * Create a external ID for a username (scheme "username").
+ *
+ * @param id the external ID, must not contain colons (':')
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param plainPassword the plain HTTP password, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId createUsername(
+ String id, Account.Id accountId, @Nullable String plainPassword) {
+ return createWithPassword(
+ externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, id),
+ accountId,
+ null,
+ plainPassword);
+ }
+
+ /**
+ * Creates an external ID with an email.
+ *
+ * @param scheme the scheme name, must not contain colons (':'). E.g. {@link
+ * ExternalId#SCHEME_USERNAME}.
+ * @param id the external ID, must not contain colons (':')
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId createWithEmail(
+ String scheme, String id, Account.Id accountId, @Nullable String email) {
+ return createWithEmail(externalIdKeyFactory.create(scheme, id), accountId, email);
+ }
+
+ /**
+ * Creates an external ID with an email.
+ *
+ * @param key the external Id key
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId createWithEmail(Key key, Account.Id accountId, @Nullable String email) {
+ return create(key, accountId, Strings.emptyToNull(email), null);
+ }
+
+ /**
+ * Creates an external ID using the `mailto`-scheme.
+ *
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @return the created external ID
+ */
+ public ExternalId createEmail(Account.Id accountId, String email) {
+ return createWithEmail(ExternalId.SCHEME_MAILTO, email, accountId, requireNonNull(email));
+ }
+
+ ExternalId create(ExternalId extId, @Nullable ObjectId blobId) {
+ return create(extId.key(), extId.accountId(), extId.email(), extId.password(), blobId);
+ }
+
+ /**
+ * Creates an external ID.
+ *
+ * @param key the external Id key
+ * @param accountId the ID of the account to which the external ID belongs
+ * @param email the email of the external ID, may be {@code null}
+ * @param hashedPassword the hashed password of the external ID, may be {@code null}
+ * @param blobId the ID of the note blob in the external IDs branch that stores this external ID.
+ * {@code null} if the external ID was created in code and is not yet stored in Git.
+ * @return the created external ID
+ */
+ public ExternalId create(
+ Key key,
+ Account.Id accountId,
+ @Nullable String email,
+ @Nullable String hashedPassword,
+ @Nullable ObjectId blobId) {
+ return ExternalId.create(
+ key, accountId, Strings.emptyToNull(email), Strings.emptyToNull(hashedPassword), blobId);
+ }
+
+ /**
+ * Parses an external ID from a byte array that contains the external ID as a Git config file
+ * text.
+ *
+ * <p>The Git config must have exactly one externalId subsection with an accountId and optionally
+ * email and password:
+ *
+ * <pre>
+ * [externalId "username:jdoe"]
+ * accountId = 1003407
+ * email = jdoe@example.com
+ * password = bcrypt:4:LCbmSBDivK/hhGVQMfkDpA==:XcWn0pKYSVU/UJgOvhidkEtmqCp6oKB7
+ * </pre>
+ *
+ * @param noteId the SHA-1 sum of the external ID used as the note's ID
+ * @param raw a byte array that contains the external ID as a Git config file text.
+ * @param blobId the ID of the note blob in the external IDs branch that stores this external ID.
+ * {@code null} if the external ID was created in code and is not yet stored in Git.
+ * @return the parsed external ID
+ */
+ public ExternalId parse(String noteId, byte[] raw, ObjectId blobId)
+ throws ConfigInvalidException {
+ requireNonNull(blobId);
+
+ Config externalIdConfig = new Config();
+ try {
+ externalIdConfig.fromText(new String(raw, UTF_8));
+ } catch (ConfigInvalidException e) {
+ throw invalidConfig(noteId, e.getMessage());
+ }
+
+ Set<String> externalIdKeys = externalIdConfig.getSubsections(ExternalId.EXTERNAL_ID_SECTION);
+ if (externalIdKeys.size() != 1) {
+ throw invalidConfig(
+ noteId,
+ String.format(
+ "Expected exactly 1 '%s' section, found %d",
+ ExternalId.EXTERNAL_ID_SECTION, externalIdKeys.size()));
+ }
+
+ String externalIdKeyStr = Iterables.getOnlyElement(externalIdKeys);
+ Key externalIdKey = externalIdKeyFactory.parse(externalIdKeyStr);
+ if (externalIdKey == null) {
+ throw invalidConfig(noteId, String.format("External ID %s is invalid", externalIdKeyStr));
+ }
+
+ if (!externalIdKey.sha1().getName().equals(noteId)) {
+ throw invalidConfig(
+ noteId,
+ String.format(
+ "SHA1 of external ID '%s' does not match note ID '%s'", externalIdKeyStr, noteId));
+ }
+
+ String email =
+ externalIdConfig.getString(
+ ExternalId.EXTERNAL_ID_SECTION, externalIdKeyStr, ExternalId.EMAIL_KEY);
+ String password =
+ externalIdConfig.getString(
+ ExternalId.EXTERNAL_ID_SECTION, externalIdKeyStr, ExternalId.PASSWORD_KEY);
+ int accountId = readAccountId(noteId, externalIdConfig, externalIdKeyStr);
+
+ return create(
+ externalIdKey,
+ Account.id(accountId),
+ Strings.emptyToNull(email),
+ Strings.emptyToNull(password),
+ blobId);
+ }
+
+ private static int readAccountId(String noteId, Config externalIdConfig, String externalIdKeyStr)
+ throws ConfigInvalidException {
+ String accountIdStr =
+ externalIdConfig.getString(
+ ExternalId.EXTERNAL_ID_SECTION, externalIdKeyStr, ExternalId.ACCOUNT_ID_KEY);
+ if (accountIdStr == null) {
+ throw invalidConfig(
+ noteId,
+ String.format(
+ "Value for '%s.%s.%s' is missing, expected account ID",
+ ExternalId.EXTERNAL_ID_SECTION, externalIdKeyStr, ExternalId.ACCOUNT_ID_KEY));
+ }
+
+ try {
+ int accountId =
+ externalIdConfig.getInt(
+ ExternalId.EXTERNAL_ID_SECTION, externalIdKeyStr, ExternalId.ACCOUNT_ID_KEY, -1);
+ if (accountId < 0) {
+ throw invalidConfig(
+ noteId,
+ String.format(
+ "Value %s for '%s.%s.%s' is invalid, expected account ID",
+ accountIdStr,
+ ExternalId.EXTERNAL_ID_SECTION,
+ externalIdKeyStr,
+ ExternalId.ACCOUNT_ID_KEY));
+ }
+ return accountId;
+ } catch (IllegalArgumentException e) {
+ String msg =
+ String.format(
+ "Value %s for '%s.%s.%s' is invalid, expected account ID",
+ accountIdStr,
+ ExternalId.EXTERNAL_ID_SECTION,
+ externalIdKeyStr,
+ ExternalId.ACCOUNT_ID_KEY);
+ logger.atSevere().withCause(e).log(msg);
+ throw invalidConfig(noteId, msg);
+ }
+ }
+
+ private static ConfigInvalidException invalidConfig(String noteId, String message) {
+ return new ConfigInvalidException(
+ String.format("Invalid external ID config for note '%s': %s", noteId, message));
+ }
+}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java b/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
new file mode 100644
index 0000000..37c2604
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ExternalIdKeyFactory {
+ private final boolean isUserNameCaseInsensitive;
+
+ @Inject
+ public ExternalIdKeyFactory(AuthConfig authConfig) {
+ this.isUserNameCaseInsensitive = authConfig.isUserNameCaseInsensitive();
+ }
+
+ /**
+ * Creates an external ID key.
+ *
+ * @param scheme the scheme name, must not contain colons (':'). E.g. {@link
+ * ExternalId#SCHEME_USERNAME}.
+ * @param id the external ID, must not contain colons (':')
+ * @return the created external ID key
+ */
+ public ExternalId.Key create(@Nullable String scheme, String id) {
+ if (scheme != null
+ && (scheme.equals(ExternalId.SCHEME_USERNAME) || scheme.equals(ExternalId.SCHEME_GERRIT))) {
+ return ExternalId.Key.create(scheme, id, isUserNameCaseInsensitive);
+ }
+
+ return ExternalId.Key.create(scheme, id, false);
+ }
+
+ /**
+ * Parses an external ID key from its String representation
+ *
+ * @param externalId String representation of external ID key (e.g. username:johndoe)
+ * @return the external Id key object
+ */
+ public ExternalId.Key parse(String externalId) {
+ int c = externalId.indexOf(':');
+ if (c < 1 || c >= externalId.length() - 1) {
+ return create(null, externalId);
+ }
+ return create(externalId.substring(0, c), externalId.substring(c + 1));
+ }
+}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java b/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java
index 3e5d7b8..da7b357 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Android Open Source Project
+// Copyright (C) 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,34 +14,13 @@
package com.google.gerrit.server.account.externalids;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.serialize.ObjectIdCacheSerializer;
-import com.google.inject.TypeLiteral;
-import java.time.Duration;
-import org.eclipse.jgit.lib.ObjectId;
+import com.google.inject.AbstractModule;
-public class ExternalIdModule extends CacheModule {
+public class ExternalIdModule extends AbstractModule {
@Override
protected void configure() {
- persist(ExternalIdCacheImpl.CACHE_NAME, ObjectId.class, new TypeLiteral<AllExternalIds>() {})
- // The cached data is potentially pretty large and we are always only interested
- // in the latest value. However, due to a race condition, it is possible for different
- // threads to observe different values of the meta ref, and hence request different keys
- // from the cache. Extend the cache size by 1 to cover this case, but expire the extra
- // object after a short period of time, since it may be a potentially large amount of
- // memory.
- // When loading a new value because the primary data advanced, we want to leverage the old
- // cache state to recompute only what changed. This doesn't affect cache size though as
- // Guava calls the loader first and evicts later on.
- .maximumWeight(2)
- .expireFromMemoryAfterAccess(Duration.ofMinutes(1))
- .loader(ExternalIdCacheLoader.class)
- .diskLimit(-1)
- .version(1)
- .keySerializer(ObjectIdCacheSerializer.INSTANCE)
- .valueSerializer(AllExternalIds.Serializer.INSTANCE);
-
- bind(ExternalIdCacheImpl.class);
- bind(ExternalIdCache.class).to(ExternalIdCacheImpl.class);
+ bind(ExternalIdFactory.class);
+ bind(ExternalIdKeyFactory.class);
+ bind(PasswordVerifier.class);
}
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
index 435a524..2b9c00a9 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
@@ -99,16 +99,19 @@
protected final MetricMaker metricMaker;
protected final AllUsersName allUsersName;
protected final DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors;
+ protected final ExternalIdFactory externalIdFactory;
protected ExternalIdNotesLoader(
ExternalIdCache externalIdCache,
MetricMaker metricMaker,
AllUsersName allUsersName,
- DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors) {
+ DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors,
+ ExternalIdFactory externalIdFactory) {
this.externalIdCache = externalIdCache;
this.metricMaker = metricMaker;
this.allUsersName = allUsersName;
this.upsertPreprocessors = upsertPreprocessors;
+ this.externalIdFactory = externalIdFactory;
}
/**
@@ -199,22 +202,25 @@
Provider<AccountIndexer> accountIndexer,
MetricMaker metricMaker,
AllUsersName allUsersName,
- DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors) {
- super(externalIdCache, metricMaker, allUsersName, upsertPreprocessors);
+ DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors,
+ ExternalIdFactory externalIdFactory) {
+ super(externalIdCache, metricMaker, allUsersName, upsertPreprocessors, externalIdFactory);
this.accountIndexer = accountIndexer;
}
@Override
public ExternalIdNotes load(Repository allUsersRepo)
throws IOException, ConfigInvalidException {
- return new ExternalIdNotes(metricMaker, allUsersName, allUsersRepo, upsertPreprocessors)
+ return new ExternalIdNotes(
+ metricMaker, allUsersName, allUsersRepo, upsertPreprocessors, externalIdFactory)
.load();
}
@Override
public ExternalIdNotes load(Repository allUsersRepo, @Nullable ObjectId rev)
throws IOException, ConfigInvalidException {
- return new ExternalIdNotes(metricMaker, allUsersName, allUsersRepo, upsertPreprocessors)
+ return new ExternalIdNotes(
+ metricMaker, allUsersName, allUsersRepo, upsertPreprocessors, externalIdFactory)
.load(rev);
}
@@ -232,14 +238,16 @@
ExternalIdCache externalIdCache,
MetricMaker metricMaker,
AllUsersName allUsersName,
- DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors) {
- super(externalIdCache, metricMaker, allUsersName, upsertPreprocessors);
+ DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors,
+ ExternalIdFactory externalIdFactory) {
+ super(externalIdCache, metricMaker, allUsersName, upsertPreprocessors, externalIdFactory);
}
@Override
public ExternalIdNotes load(Repository allUsersRepo)
throws IOException, ConfigInvalidException {
- return new ExternalIdNotes(metricMaker, allUsersName, allUsersRepo, upsertPreprocessors)
+ return new ExternalIdNotes(
+ metricMaker, allUsersName, allUsersRepo, upsertPreprocessors, externalIdFactory)
.setNoReindex()
.load();
}
@@ -247,7 +255,8 @@
@Override
public ExternalIdNotes load(Repository allUsersRepo, @Nullable ObjectId rev)
throws IOException, ConfigInvalidException {
- return new ExternalIdNotes(metricMaker, allUsersName, allUsersRepo, upsertPreprocessors)
+ return new ExternalIdNotes(
+ metricMaker, allUsersName, allUsersRepo, upsertPreprocessors, externalIdFactory)
.setNoReindex()
.load(rev);
}
@@ -269,10 +278,17 @@
* @return read-only {@link ExternalIdNotes} instance
*/
public static ExternalIdNotes loadReadOnly(
- AllUsersName allUsersName, Repository allUsersRepo, @Nullable ObjectId rev)
+ AllUsersName allUsersName,
+ Repository allUsersRepo,
+ @Nullable ObjectId rev,
+ ExternalIdFactory externalIdFactory)
throws IOException, ConfigInvalidException {
return new ExternalIdNotes(
- new DisabledMetricMaker(), allUsersName, allUsersRepo, DynamicMap.emptyMap())
+ new DisabledMetricMaker(),
+ allUsersName,
+ allUsersRepo,
+ DynamicMap.emptyMap(),
+ externalIdFactory)
.setReadOnly()
.setNoCacheUpdate()
.setNoReindex()
@@ -290,10 +306,14 @@
* @return {@link ExternalIdNotes} instance that doesn't updates caches on save
*/
public static ExternalIdNotes loadNoCacheUpdate(
- AllUsersName allUsersName, Repository allUsersRepo)
+ AllUsersName allUsersName, Repository allUsersRepo, ExternalIdFactory externalIdFactory)
throws IOException, ConfigInvalidException {
return new ExternalIdNotes(
- new DisabledMetricMaker(), allUsersName, allUsersRepo, DynamicMap.emptyMap())
+ new DisabledMetricMaker(),
+ allUsersName,
+ allUsersRepo,
+ DynamicMap.emptyMap(),
+ externalIdFactory)
.setNoCacheUpdate()
.setNoReindex()
.load();
@@ -304,6 +324,7 @@
private final Repository repo;
private final DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors;
private final CallerFinder callerFinder;
+ private final ExternalIdFactory externalIdFactory;
private NoteMap noteMap;
private ObjectId oldRev;
@@ -334,7 +355,8 @@
MetricMaker metricMaker,
AllUsersName allUsersName,
Repository allUsersRepo,
- DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors) {
+ DynamicMap<ExternalIdUpsertPreprocessor> upsertPreprocessors,
+ ExternalIdFactory externalIdFactory) {
this.updateCount =
metricMaker.newCounter(
"notedb/external_id_update_count",
@@ -355,6 +377,7 @@
// 3. direct callers
.addTarget(ExternalIdNotes.class)
.build();
+ this.externalIdFactory = externalIdFactory;
}
public ExternalIdNotes setAfterReadRevision(Runnable afterReadRevision) {
@@ -434,7 +457,7 @@
try (RevWalk rw = new RevWalk(repo)) {
ObjectId noteDataId = noteMap.get(noteId);
byte[] raw = readNoteData(rw, noteDataId);
- return Optional.of(ExternalId.parse(noteId.name(), raw, noteDataId));
+ return Optional.of(externalIdFactory.parse(noteId.name(), raw, noteDataId));
}
}
@@ -468,7 +491,7 @@
for (Note note : noteMap) {
byte[] raw = readNoteData(rw, note.getData());
try {
- b.add(ExternalId.parse(note.getName(), raw, note.getData()));
+ b.add(externalIdFactory.parse(note.getName(), raw, note.getData()));
} catch (ConfigInvalidException | RuntimeException e) {
logger.atSevere().withCause(e).log(
"Ignoring invalid external ID note %s", note.getName());
@@ -840,8 +863,7 @@
*
* <p>If the external ID already exists, it is overwritten.
*/
- private static ExternalId upsert(
- RevWalk rw, ObjectInserter ins, NoteMap noteMap, ExternalId extId)
+ private ExternalId upsert(RevWalk rw, ObjectInserter ins, NoteMap noteMap, ExternalId extId)
throws IOException, ConfigInvalidException {
ObjectId noteId = extId.key().sha1();
Config c = new Config();
@@ -859,7 +881,7 @@
byte[] raw = c.toText().getBytes(UTF_8);
ObjectId noteData = ins.insert(OBJ_BLOB, raw);
noteMap.set(noteId, noteData);
- return ExternalId.create(extId, noteData);
+ return externalIdFactory.create(extId, noteData);
}
/**
@@ -868,7 +890,7 @@
* @throws IllegalStateException is thrown if there is an existing external ID that has the same
* key, but otherwise doesn't match the specified external ID.
*/
- private static void remove(RevWalk rw, NoteMap noteMap, ExternalId extId)
+ private void remove(RevWalk rw, NoteMap noteMap, ExternalId extId)
throws IOException, ConfigInvalidException {
ObjectId noteId = extId.key().sha1();
if (!noteMap.contains(noteId)) {
@@ -877,7 +899,7 @@
ObjectId noteDataId = noteMap.get(noteId);
byte[] raw = readNoteData(rw, noteDataId);
- ExternalId actualExtId = ExternalId.parse(noteId.name(), raw, noteDataId);
+ ExternalId actualExtId = externalIdFactory.parse(noteId.name(), raw, noteDataId);
checkState(
extId.equals(actualExtId),
"external id %s should be removed, but it doesn't match the actual external id %s",
@@ -894,8 +916,7 @@
* @return the external ID that was removed, {@code null} if no external ID with the specified key
* exists
*/
- @Nullable
- private static ExternalId remove(
+ private ExternalId remove(
RevWalk rw, NoteMap noteMap, ExternalId.Key extIdKey, Account.Id expectedAccountId)
throws IOException, ConfigInvalidException {
ObjectId noteId = extIdKey.sha1();
@@ -905,7 +926,7 @@
ObjectId noteDataId = noteMap.get(noteId);
byte[] raw = readNoteData(rw, noteDataId);
- ExternalId extId = ExternalId.parse(noteId.name(), raw, noteDataId);
+ ExternalId extId = externalIdFactory.parse(noteId.name(), raw, noteDataId);
if (expectedAccountId != null) {
checkState(
expectedAccountId.equals(extId.accountId()),
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java b/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
index f2505fa..0d715ae 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
@@ -69,10 +69,14 @@
private boolean failOnLoad = false;
private final Timer0 readAllLatency;
private final Timer0 readSingleLatency;
+ private final ExternalIdFactory externalIdFactory;
@Inject
ExternalIdReader(
- GitRepositoryManager repoManager, AllUsersName allUsersName, MetricMaker metricMaker) {
+ GitRepositoryManager repoManager,
+ AllUsersName allUsersName,
+ MetricMaker metricMaker,
+ ExternalIdFactory externalIdFactory) {
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.readAllLatency =
@@ -87,6 +91,7 @@
new Description("Latency for reading a single external ID from NoteDb.")
.setCumulative()
.setUnit(Units.MILLISECONDS));
+ this.externalIdFactory = externalIdFactory;
}
@VisibleForTesting
@@ -106,7 +111,7 @@
try (Timer0.Context ctx = readAllLatency.start();
Repository repo = repoManager.openRepository(allUsersName)) {
- return ExternalIdNotes.loadReadOnly(allUsersName, repo, null).all();
+ return ExternalIdNotes.loadReadOnly(allUsersName, repo, null, externalIdFactory).all();
}
}
@@ -125,7 +130,7 @@
try (Timer0.Context ctx = readAllLatency.start();
Repository repo = repoManager.openRepository(allUsersName)) {
- return ExternalIdNotes.loadReadOnly(allUsersName, repo, rev).all();
+ return ExternalIdNotes.loadReadOnly(allUsersName, repo, rev, externalIdFactory).all();
}
}
@@ -135,7 +140,7 @@
try (Timer0.Context ctx = readSingleLatency.start();
Repository repo = repoManager.openRepository(allUsersName)) {
- return ExternalIdNotes.loadReadOnly(allUsersName, repo, null).get(key);
+ return ExternalIdNotes.loadReadOnly(allUsersName, repo, null, externalIdFactory).get(key);
}
}
@@ -146,7 +151,7 @@
try (Timer0.Context ctx = readSingleLatency.start();
Repository repo = repoManager.openRepository(allUsersName)) {
- return ExternalIdNotes.loadReadOnly(allUsersName, repo, rev).get(key);
+ return ExternalIdNotes.loadReadOnly(allUsersName, repo, rev, externalIdFactory).get(key);
}
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java b/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java
index 6a4da09..cf0e5d3 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java
@@ -43,29 +43,32 @@
private final AllUsersName allUsers;
private final AccountCache accountCache;
private final OutgoingEmailValidator validator;
+ private final ExternalIdFactory externalIdFactory;
@Inject
ExternalIdsConsistencyChecker(
GitRepositoryManager repoManager,
AllUsersName allUsers,
AccountCache accountCache,
- OutgoingEmailValidator validator) {
+ OutgoingEmailValidator validator,
+ ExternalIdFactory externalIdFactory) {
this.repoManager = repoManager;
this.allUsers = allUsers;
this.accountCache = accountCache;
this.validator = validator;
+ this.externalIdFactory = externalIdFactory;
}
public List<ConsistencyProblemInfo> check() throws IOException, ConfigInvalidException {
try (Repository repo = repoManager.openRepository(allUsers)) {
- return check(ExternalIdNotes.loadReadOnly(allUsers, repo, null));
+ return check(ExternalIdNotes.loadReadOnly(allUsers, repo, null, externalIdFactory));
}
}
public List<ConsistencyProblemInfo> check(ObjectId rev)
throws IOException, ConfigInvalidException {
try (Repository repo = repoManager.openRepository(allUsers)) {
- return check(ExternalIdNotes.loadReadOnly(allUsers, repo, rev));
+ return check(ExternalIdNotes.loadReadOnly(allUsers, repo, rev, externalIdFactory));
}
}
@@ -79,7 +82,7 @@
for (Note note : noteMap) {
byte[] raw = ExternalIdNotes.readNoteData(rw, note.getData());
try {
- ExternalId extId = ExternalId.parse(note.getName(), raw, note.getData());
+ ExternalId extId = externalIdFactory.parse(note.getName(), raw, note.getData());
problems.addAll(validateExternalId(extId));
if (extId.email() != null) {
diff --git a/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
index 3f2f774..33443c1 100644
--- a/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
+++ b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
@@ -20,21 +20,30 @@
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.server.account.HashedPassword;
+import com.google.inject.Inject;
import java.util.Collection;
/** Checks if a given username and password match a user's external IDs. */
public class PasswordVerifier {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+
+ @Inject
+ public PasswordVerifier(ExternalIdKeyFactory externalIdKeyFactory) {
+ this.externalIdKeyFactory = externalIdKeyFactory;
+ }
+
/** Returns {@code true} if there is an external ID matching both the username and password. */
- public static boolean checkPassword(
+ public boolean checkPassword(
Collection<ExternalId> externalIds, String username, @Nullable String password) {
if (password == null) {
return false;
}
for (ExternalId id : externalIds) {
// Only process the "username:$USER" entry, which is unique.
- if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
+ if (!id.isScheme(SCHEME_USERNAME)
+ || !id.key().equals(externalIdKeyFactory.create(SCHEME_USERNAME, username))) {
continue;
}
diff --git a/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
index b8040f7..a42afc3 100644
--- a/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
+++ b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
@@ -45,7 +45,9 @@
rw,
ident,
(ins, noteMap) -> {
- ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), accountId);
+ ExternalId extId =
+ ExternalId.create(
+ ExternalId.Key.parse(externalId, false), accountId, null, null, null);
ObjectId noteId = extId.key().sha1();
Config c = new Config();
extId.writeToConfig(c);
@@ -65,8 +67,10 @@
rw,
ident,
(ins, noteMap) -> {
- ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), accountId);
- ObjectId noteId = ExternalId.Key.parse(externalId + "x").sha1();
+ ExternalId extId =
+ ExternalId.create(
+ ExternalId.Key.parse(externalId, false), accountId, null, null, null);
+ ObjectId noteId = ExternalId.Key.parse(externalId + "x", false).sha1();
Config c = new Config();
extId.writeToConfig(c);
byte[] raw = c.toText().getBytes(UTF_8);
@@ -83,7 +87,7 @@
rw,
ident,
(ins, noteMap) -> {
- ObjectId noteId = ExternalId.Key.parse(externalId).sha1();
+ ObjectId noteId = ExternalId.Key.parse(externalId, false).sha1();
byte[] raw = "bad-config".getBytes(UTF_8);
ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
noteMap.set(noteId, dataBlob);
@@ -98,7 +102,7 @@
rw,
ident,
(ins, noteMap) -> {
- ObjectId noteId = ExternalId.Key.parse(externalId).sha1();
+ ObjectId noteId = ExternalId.Key.parse(externalId, false).sha1();
byte[] raw = "".getBytes(UTF_8);
ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
noteMap.set(noteId, dataBlob);
diff --git a/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index 73a970b..5df4d28 100644
--- a/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -44,12 +44,14 @@
private final AccountResolver accountResolver;
private final AccountManager accountManager;
private final AuthType authType;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
public AccountIdHandler(
AccountResolver accountResolver,
AccountManager accountManager,
AuthConfig authConfig,
+ AuthRequest.Factory authRequestFactory,
@Assisted CmdLineParser parser,
@Assisted OptionDef option,
@Assisted Setter<Account.Id> setter) {
@@ -57,6 +59,7 @@
this.accountResolver = accountResolver;
this.accountManager = accountManager;
this.authType = authConfig.getAuthType();
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -105,7 +108,7 @@
}
try {
- AuthRequest req = AuthRequest.forUser(user);
+ AuthRequest req = authRequestFactory.createForUser(user);
req.setSkipAuthentication(true);
return accountManager.authenticate(req).getAccountId();
} catch (AccountException e) {
diff --git a/java/com/google/gerrit/server/auth/InternalAuthBackend.java b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
index 2f8886b..ce536f6 100644
--- a/java/com/google/gerrit/server/auth/InternalAuthBackend.java
+++ b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
@@ -26,11 +26,14 @@
public class InternalAuthBackend implements AuthBackend {
private final AccountCache accountCache;
private final AuthConfig authConfig;
+ private final PasswordVerifier passwordVerifier;
@Inject
- InternalAuthBackend(AccountCache accountCache, AuthConfig authConfig) {
+ InternalAuthBackend(
+ AccountCache accountCache, AuthConfig authConfig, PasswordVerifier passwordVerifier) {
this.accountCache = accountCache;
this.authConfig = authConfig;
+ this.passwordVerifier = passwordVerifier;
}
@Override
@@ -63,7 +66,7 @@
+ ": account inactive or not provisioned in Gerrit");
}
- if (!PasswordVerifier.checkPassword(who.externalIds(), username, req.getPassword().get())) {
+ if (!passwordVerifier.checkPassword(who.externalIds(), username, req.getPassword().get())) {
throw new InvalidCredentialsException();
}
return new AuthUser(AuthUser.UUID.create(username), username);
diff --git a/java/com/google/gerrit/server/config/AuthConfig.java b/java/com/google/gerrit/server/config/AuthConfig.java
index de57d04..2ac551d 100644
--- a/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/java/com/google/gerrit/server/config/AuthConfig.java
@@ -64,6 +64,7 @@
private final boolean cookieSecure;
private final SignedToken emailReg;
private final boolean allowRegisterNewEmail;
+ private final boolean userNameCaseInsensitive;
private GitBasicAuthPolicy gitBasicAuthPolicy;
@Inject
@@ -95,6 +96,7 @@
useContributorAgreements = cfg.getBoolean("auth", "contributoragreements", false);
userNameToLowerCase = cfg.getBoolean("auth", "userNameToLowerCase", false);
allowRegisterNewEmail = cfg.getBoolean("auth", "allowRegisterNewEmail", true);
+ userNameCaseInsensitive = cfg.getBoolean("auth", "userNameCaseInsensitive", false);
if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP
&& authType != AuthType.LDAP
@@ -237,6 +239,11 @@
return userNameToLowerCase;
}
+ /** Whether user name should be matched case insenitive */
+ public boolean isUserNameCaseInsensitive() {
+ return userNameCaseInsensitive;
+ }
+
public GitBasicAuthPolicy getGitBasicAuthPolicy() {
return gitBasicAuthPolicy;
}
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 48c03a0..ef1c0ae 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -92,6 +92,7 @@
import com.google.gerrit.server.account.AccountDeactivator;
import com.google.gerrit.server.account.AccountExternalIdCreator;
import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AccountModule;
import com.google.gerrit.server.account.AccountTagProvider;
import com.google.gerrit.server.account.AccountVisibilityProvider;
import com.google.gerrit.server.account.CapabilityCollection;
@@ -101,6 +102,7 @@
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
import com.google.gerrit.server.account.ServiceUserClassifierImpl;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
+import com.google.gerrit.server.account.externalids.ExternalIdCacheModule;
import com.google.gerrit.server.account.externalids.ExternalIdModule;
import com.google.gerrit.server.account.externalids.ExternalIdUpsertPreprocessor;
import com.google.gerrit.server.approval.ApprovalCacheImpl;
@@ -266,8 +268,10 @@
install(SubmitRequirementsEvaluatorImpl.module());
install(new AccessControlModule());
+ install(new AccountModule());
install(new CmdLineParserModule());
install(new EmailModule());
+ install(new ExternalIdCacheModule());
install(new ExternalIdModule());
install(new GitModule());
install(new GroupDbModule());
diff --git a/java/com/google/gerrit/server/mail/EmailTokenVerifier.java b/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
index 2ff5fc3..ead4c06 100644
--- a/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
+++ b/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
@@ -56,10 +56,13 @@
class ParsedToken {
private final Account.Id accountId;
private final String emailAddress;
+ private final AuthRequest.Factory authRequestFactory;
- public ParsedToken(Account.Id accountId, String emailAddress) {
+ public ParsedToken(
+ Account.Id accountId, String emailAddress, AuthRequest.Factory authRequestFactory) {
this.accountId = accountId;
this.emailAddress = emailAddress;
+ this.authRequestFactory = authRequestFactory;
}
public Account.Id getAccountId() {
@@ -71,7 +74,7 @@
}
public AuthRequest toAuthRequest() {
- return AuthRequest.forEmail(getEmailAddress());
+ return authRequestFactory.createForEmail(getEmailAddress());
}
@Override
diff --git a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
index 77be665..bdfaf6d 100644
--- a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
+++ b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
@@ -19,6 +19,7 @@
import com.google.common.io.BaseEncoding;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.mail.send.RegisterNewEmailSender;
import com.google.inject.AbstractModule;
@@ -31,6 +32,7 @@
@Singleton
public class SignedTokenEmailTokenVerifier implements EmailTokenVerifier {
private final SignedToken emailRegistrationToken;
+ private final AuthRequest.Factory authRequestFactory;
public static class Module extends AbstractModule {
@Override
@@ -40,8 +42,9 @@
}
@Inject
- SignedTokenEmailTokenVerifier(AuthConfig config) {
+ SignedTokenEmailTokenVerifier(AuthConfig config, AuthRequest.Factory authRequestFactory) {
emailRegistrationToken = config.getEmailRegistrationToken();
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -77,7 +80,7 @@
}
Account.Id id = Account.Id.tryParse(matcher.group(1)).orElseThrow(InvalidTokenException::new);
String newEmail = matcher.group(2);
- return new ParsedToken(id, newEmail);
+ return new ParsedToken(id, newEmail, authRequestFactory);
}
private void checkEmailRegistrationToken() {
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index b7f8b45..1d67009 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -31,6 +31,7 @@
import com.google.gerrit.index.query.InternalQuery;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.inject.Inject;
@@ -46,12 +47,16 @@
public class InternalAccountQuery extends InternalQuery<AccountState, InternalAccountQuery> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final ExternalIdKeyFactory externalIdKeyFactory;
+
@Inject
InternalAccountQuery(
AccountQueryProcessor queryProcessor,
AccountIndexCollection indexes,
- IndexConfig indexConfig) {
+ IndexConfig indexConfig,
+ ExternalIdKeyFactory externalIdKeyFactory) {
super(queryProcessor, indexes, indexConfig);
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
public List<AccountState> byDefault(String query, boolean canSeeSecondaryEmails) {
@@ -59,7 +64,7 @@
}
public List<AccountState> byExternalId(String scheme, String id) {
- return byExternalId(ExternalId.Key.create(scheme, id));
+ return byExternalId(externalIdKeyFactory.create(scheme, id));
}
public List<AccountState> byExternalId(ExternalId.Key externalId) {
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index baa2951..b4946c4 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -44,6 +44,7 @@
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.group.GroupResolver;
import com.google.gerrit.server.group.db.GroupDelta;
@@ -85,6 +86,7 @@
private final Provider<GroupsUpdate> groupsUpdate;
private final OutgoingEmailValidator validator;
private final AuthConfig authConfig;
+ private final ExternalIdFactory externalIdFactory;
@Inject
CreateAccount(
@@ -97,7 +99,8 @@
PluginSetContext<AccountExternalIdCreator> externalIdCreators,
@UserInitiated Provider<GroupsUpdate> groupsUpdate,
OutgoingEmailValidator validator,
- AuthConfig authConfig) {
+ AuthConfig authConfig,
+ ExternalIdFactory externalIdFactory) {
this.seq = seq;
this.groupResolver = groupResolver;
this.authorizedKeys = authorizedKeys;
@@ -108,6 +111,7 @@
this.groupsUpdate = groupsUpdate;
this.validator = validator;
this.authConfig = authConfig;
+ this.externalIdFactory = externalIdFactory;
}
@Override
@@ -142,10 +146,10 @@
if (!validator.isValid(input.email)) {
throw new BadRequestException("invalid email address");
}
- extIds.add(ExternalId.createEmail(accountId, input.email));
+ extIds.add(externalIdFactory.createEmail(accountId, input.email));
}
- extIds.add(ExternalId.createUsername(username, accountId, input.httpPassword));
+ extIds.add(externalIdFactory.createUsername(username, accountId, input.httpPassword));
externalIdCreators.runEach(c -> extIds.addAll(c.create(accountId, username, input.email)));
try {
diff --git a/java/com/google/gerrit/server/restapi/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
index 6ee4539..70fbb26 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -83,6 +83,7 @@
private final OutgoingEmailValidator validator;
private final MessageIdGenerator messageIdGenerator;
private final boolean isDevMode;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
CreateEmail(
@@ -94,7 +95,8 @@
RegisterNewEmailSender.Factory registerNewEmailFactory,
PutPreferred putPreferred,
OutgoingEmailValidator validator,
- MessageIdGenerator messageIdGenerator) {
+ MessageIdGenerator messageIdGenerator,
+ AuthRequest.Factory authRequestFactory) {
this.self = self;
this.realm = realm;
this.permissionBackend = permissionBackend;
@@ -104,6 +106,7 @@
this.validator = validator;
this.isDevMode = authConfig.getAuthType() == DEVELOPMENT_BECOME_ANY_ACCOUNT;
this.messageIdGenerator = messageIdGenerator;
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -151,7 +154,7 @@
logger.atWarning().log("skipping email validation in developer mode");
}
try {
- accountManager.link(user.getAccountId(), AuthRequest.forEmail(email));
+ accountManager.link(user.getAccountId(), authRequestFactory.createForEmail(email));
} catch (AccountException e) {
throw new ResourceConflictException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
index 445a5d6..e099a70 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
@@ -18,6 +18,8 @@
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
@@ -29,6 +31,7 @@
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -56,17 +59,20 @@
private final AccountManager accountManager;
private final ExternalIds externalIds;
private final Provider<CurrentUser> self;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
DeleteExternalIds(
PermissionBackend permissionBackend,
AccountManager accountManager,
ExternalIds externalIds,
- Provider<CurrentUser> self) {
+ Provider<CurrentUser> self,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.permissionBackend = permissionBackend;
this.accountManager = accountManager;
this.externalIds = externalIds;
this.self = self;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -87,15 +93,24 @@
List<ExternalId> toDelete = new ArrayList<>();
Optional<ExternalId.Key> last = resource.getUser().getLastLoginExternalIdKey();
for (String externalIdStr : extIds) {
- ExternalId id = externalIdMap.get(ExternalId.Key.parse(externalIdStr));
+ ExternalId id = externalIdMap.get(externalIdKeyFactory.parse(externalIdStr));
if (id == null) {
throw new UnprocessableEntityException(
String.format("External id %s does not exist", externalIdStr));
}
- if ((!id.isScheme(SCHEME_USERNAME))
- && (!last.isPresent() || (!last.get().equals(id.key())))) {
+ if (!last.isPresent() || !last.get().equals(id.key())) {
+ if (id.isScheme(SCHEME_USERNAME)) {
+ if (self.get().hasSameAccountId(resource.getUser())) {
+ throw new AuthException("User cannot delete its own externalId in 'username:' scheme");
+ }
+ permissionBackend
+ .currentUser()
+ .checkAny(
+ ImmutableSet.of(
+ GlobalPermission.ADMINISTRATE_SERVER, GlobalPermission.MAINTAIN_SERVER));
+ }
toDelete.add(id);
} else {
throw new ResourceConflictException(
diff --git a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index 2427def..9361e27 100644
--- a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -34,6 +34,8 @@
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.mail.send.HttpPasswordUpdateSender;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -77,6 +79,8 @@
private final ExternalIds externalIds;
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final HttpPasswordUpdateSender.Factory httpPasswordUpdateSenderFactory;
+ private final ExternalIdFactory externalIdFactory;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
PutHttpPassword(
@@ -84,12 +88,16 @@
PermissionBackend permissionBackend,
ExternalIds externalIds,
@UserInitiated Provider<AccountsUpdate> accountsUpdateProvider,
- HttpPasswordUpdateSender.Factory httpPasswordUpdateSenderFactory) {
+ HttpPasswordUpdateSender.Factory httpPasswordUpdateSenderFactory,
+ ExternalIdFactory externalIdFactory,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.self = self;
this.permissionBackend = permissionBackend;
this.externalIds = externalIds;
this.accountsUpdateProvider = accountsUpdateProvider;
this.httpPasswordUpdateSenderFactory = httpPasswordUpdateSenderFactory;
+ this.externalIdFactory = externalIdFactory;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -125,7 +133,7 @@
String userName =
user.getUserName().orElseThrow(() -> new ResourceConflictException("username must be set"));
Optional<ExternalId> optionalExtId =
- externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, userName));
+ externalIds.get(externalIdKeyFactory.create(SCHEME_USERNAME, userName));
ExternalId extId = optionalExtId.orElseThrow(ResourceNotFoundException::new);
accountsUpdateProvider
.get()
@@ -134,7 +142,7 @@
extId.accountId(),
u ->
u.updateExternalId(
- ExternalId.createWithPassword(
+ externalIdFactory.createWithPassword(
extId.key(), extId.accountId(), extId.email(), newPassword)));
try {
diff --git a/java/com/google/gerrit/server/restapi/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
index 32b5ff2..aee0b78 100644
--- a/java/com/google/gerrit/server/restapi/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -62,17 +63,20 @@
private final PermissionBackend permissionBackend;
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final ExternalIds externalIds;
+ private final ExternalIdFactory externalIdFactory;
@Inject
PutPreferred(
Provider<CurrentUser> self,
PermissionBackend permissionBackend,
@ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider,
- ExternalIds externalIds) {
+ ExternalIds externalIds,
+ ExternalIdFactory externalIdFactory) {
this.self = self;
this.permissionBackend = permissionBackend;
this.accountsUpdateProvider = accountsUpdateProvider;
this.externalIds = externalIds;
+ this.externalIdFactory = externalIdFactory;
}
@Override
@@ -137,7 +141,8 @@
}
// claim the email now
- u.addExternalId(ExternalId.createEmail(a.account().id(), preferredEmail));
+ u.addExternalId(
+ externalIdFactory.createEmail(a.account().id(), preferredEmail));
matchingEmail = preferredEmail;
} else {
// Realm says that the email doesn't belong to the user. This can only happen as
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index 05bf1fd..f295389 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -34,6 +34,8 @@
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -65,6 +67,8 @@
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final SshKeyCache sshKeyCache;
private final Realm realm;
+ private final ExternalIdFactory externalIdFactory;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
PutUsername(
@@ -73,13 +77,17 @@
ExternalIds externalIds,
@ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider,
SshKeyCache sshKeyCache,
- Realm realm) {
+ Realm realm,
+ ExternalIdFactory externalIdFactory,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.self = self;
this.permissionBackend = permissionBackend;
this.externalIds = externalIds;
this.accountsUpdateProvider = accountsUpdateProvider;
this.sshKeyCache = sshKeyCache;
this.realm = realm;
+ this.externalIdFactory = externalIdFactory;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -107,14 +115,14 @@
throw new UnprocessableEntityException("invalid username");
}
- ExternalId.Key key = ExternalId.Key.create(SCHEME_USERNAME, input.username);
+ ExternalId.Key key = externalIdKeyFactory.create(SCHEME_USERNAME, input.username);
try {
accountsUpdateProvider
.get()
.update(
"Set Username via API",
accountId,
- u -> u.addExternalId(ExternalId.create(key, accountId, null, null)));
+ u -> u.addExternalId(externalIdFactory.create(key, accountId, null, null)));
} catch (DuplicateKeyException dupeErr) {
// If we are using this identity, don't report the exception.
Optional<ExternalId> other = externalIds.get(key);
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index f44abec..4cb2f47 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -94,6 +94,7 @@
private final AccountCache accountCache;
private final AccountLoader.Factory infoFactory;
private final Provider<GroupsUpdate> groupsUpdateProvider;
+ private final AuthRequest.Factory authRequestFactory;
@Inject
AddMembers(
@@ -102,13 +103,15 @@
AccountResolver accountResolver,
AccountCache accountCache,
AccountLoader.Factory infoFactory,
- @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
+ @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider,
+ AuthRequest.Factory authRequestFactory) {
this.accountManager = accountManager;
this.authType = authConfig.getAuthType();
this.accountResolver = accountResolver;
this.accountCache = accountCache;
this.infoFactory = infoFactory;
this.groupsUpdateProvider = groupsUpdateProvider;
+ this.authRequestFactory = authRequestFactory;
}
@Override
@@ -190,7 +193,7 @@
}
try {
- AuthRequest req = AuthRequest.forUser(user);
+ AuthRequest req = authRequestFactory.createForUser(user);
req.setSkipAuthentication(true);
return accountCache
.get(accountManager.authenticate(req).getAccountId())
diff --git a/java/com/google/gerrit/sshd/SshKeyCacheImpl.java b/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
index d5f0ee8..628a050 100644
--- a/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.account.AccountSshKey;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.logging.Metadata;
@@ -97,11 +98,16 @@
static class Loader extends CacheLoader<String, Iterable<SshKeyCacheEntry>> {
private final ExternalIds externalIds;
private final VersionedAuthorizedKeys.Accessor authorizedKeys;
+ private final ExternalIdKeyFactory externalIdKeyFactory;
@Inject
- Loader(ExternalIds externalIds, VersionedAuthorizedKeys.Accessor authorizedKeys) {
+ Loader(
+ ExternalIds externalIds,
+ VersionedAuthorizedKeys.Accessor authorizedKeys,
+ ExternalIdKeyFactory externalIdKeyFactory) {
this.externalIds = externalIds;
this.authorizedKeys = authorizedKeys;
+ this.externalIdKeyFactory = externalIdKeyFactory;
}
@Override
@@ -111,7 +117,7 @@
"Loading SSH keys for account with username",
Metadata.builder().username(username).build())) {
Optional<ExternalId> user =
- externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, username));
+ externalIds.get(externalIdKeyFactory.create(SCHEME_USERNAME, username));
if (!user.isPresent()) {
return NO_SUCH_USER;
}
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 43a1670..0c286ca 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -15,6 +15,7 @@
package com.google.gerrit.sshd.commands;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
import com.google.gerrit.common.RawInputUtil;
@@ -36,6 +37,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountSshKey;
+import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -43,6 +45,7 @@
import com.google.gerrit.server.restapi.account.CreateEmail;
import com.google.gerrit.server.restapi.account.DeleteActive;
import com.google.gerrit.server.restapi.account.DeleteEmail;
+import com.google.gerrit.server.restapi.account.DeleteExternalIds;
import com.google.gerrit.server.restapi.account.DeleteSshKey;
import com.google.gerrit.server.restapi.account.GetEmails;
import com.google.gerrit.server.restapi.account.GetSshKeys;
@@ -122,10 +125,18 @@
@Option(name = "--generate-http-password", usage = "generate a new HTTP password for the account")
private boolean generateHttpPassword;
+ @Option(
+ name = "--delete-external-id",
+ metaVar = "EXTERNALID",
+ usage = "external id to delete from the account")
+ private List<String> externalIdsToDelete = new ArrayList<>();
+
@Inject private IdentifiedUser.GenericFactory genericUserFactory;
@Inject private CreateEmail createEmail;
+ @Inject private DeleteExternalIds deleteExternalIds;
+
@Inject private GetEmails getEmails;
@Inject private DeleteEmail deleteEmail;
@@ -150,6 +161,8 @@
@Inject private Provider<CurrentUser> userProvider;
+ @Inject private ExternalIds externalIds;
+
private AccountResource rsrc;
@Override
@@ -210,6 +223,9 @@
"--preferred-email and --delete-email options are mutually "
+ "exclusive for the same email address.");
}
+ if (externalIdsToDelete.contains("ALL")) {
+ externalIdsToDelete = Collections.singletonList("ALL");
+ }
}
private void setAccount() throws Failure {
@@ -265,6 +281,10 @@
if (!deleteSshKeys.isEmpty()) {
deleteSshKeys(deleteSshKeys);
}
+
+ for (String externalId : externalIdsToDelete) {
+ deleteExternalId(externalId);
+ }
} catch (RestApiException e) {
throw die(e.getMessage());
} catch (Exception e) {
@@ -355,4 +375,21 @@
}
return sshKeys;
}
+
+ private void deleteExternalId(String externalId)
+ throws IOException, RestApiException, ConfigInvalidException, PermissionBackendException {
+ List<String> ids;
+ if (externalId.equals("ALL")) {
+ ids =
+ externalIds.byAccount(rsrc.getUser().getAccountId()).stream()
+ .map(e -> e.key().get())
+ .collect(toList());
+ if (ids.isEmpty()) {
+ throw new ResourceNotFoundException("Account has no external Ids");
+ }
+ } else {
+ ids = Collections.singletonList(externalId);
+ }
+ deleteExternalIds.apply(rsrc, ids);
+ }
}
diff --git a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
index 44d5cea..77df46c 100644
--- a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
+++ b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
@@ -46,6 +46,7 @@
@Inject private IdentifiedUser.GenericFactory userFactory;
@Inject private SchemaCreator schemaCreator;
@Inject private ThreadLocalRequestContext requestContext;
+ @Inject private AuthRequest.Factory authRequestFactory;
private LifecycleManager lifecycle;
@@ -99,7 +100,8 @@
schemaCreator.create();
// The first user is added to the "Administrators" group. See AccountManager#create().
- setApiUser(accountManager.authenticate(AuthRequest.forUser("admin")).getAccountId());
+ setApiUser(
+ accountManager.authenticate(authRequestFactory.createForUser("admin")).getAccountId());
// Inject target members after setting API user, so it can @Inject request-scoped objects if it
// wants.
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 8982a2b..1da2176c 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -131,6 +131,8 @@
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -229,6 +231,8 @@
@Inject private VersionedAuthorizedKeys.Accessor authorizedKeys;
@Inject private ExtensionRegistry extensionRegistry;
@Inject private PluginSetContext<ExceptionHook> exceptionHooks;
+ @Inject private ExternalIdKeyFactory externalIdKeyFactory;
+ @Inject private ExternalIdFactory externalIdFactory;
@Inject protected Emails emails;
@@ -372,8 +376,8 @@
accountIndexedCounter.assertReindexOf(accountId, 1);
assertThat(externalIds.byAccount(accountId))
.containsExactly(
- ExternalId.createUsername(input.username, accountId, null),
- ExternalId.createEmail(accountId, input.email));
+ externalIdFactory.createUsername(input.username, accountId, null),
+ externalIdFactory.createEmail(accountId, input.email));
}
}
@@ -426,7 +430,7 @@
public void createAtomically() throws Exception {
Account.Id accountId = Account.id(seq.nextAccountId());
String fullName = "Foo";
- ExternalId extId = ExternalId.createEmail(accountId, "foo@example.com");
+ ExternalId extId = externalIdFactory.createEmail(accountId, "foo@example.com");
AccountState accountState =
accountsUpdateProvider
.get()
@@ -1278,11 +1282,11 @@
admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse(extId1), admin.id(), email))
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(extId1), admin.id(), email))
.addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse(extId2), admin.id(), email)));
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(extId2), admin.id(), email)));
accountIndexedCounter.assertReindexOf(admin);
assertThat(
gApi.accounts().self().getExternalIds().stream()
@@ -1318,8 +1322,8 @@
admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse(ldapExternalId), admin.id(), ldapEmail)));
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(ldapExternalId), admin.id(), ldapEmail)));
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
.contains(ldapExternalId);
@@ -1353,11 +1357,13 @@
admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse(nonLdapExternalId), admin.id(), nonLdapEMail))
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(nonLdapExternalId),
+ admin.id(),
+ nonLdapEMail))
.addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse(ldapExternalId), admin.id(), ldapEmail)));
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(ldapExternalId), admin.id(), ldapEmail)));
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
.containsAtLeast(ldapExternalId, nonLdapExternalId);
@@ -1420,8 +1426,8 @@
admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse("foo:bar"), admin.id(), email)));
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:bar"), admin.id(), email)));
assertEmail(emails.getAccountFor(email), admin);
// wrong case doesn't match
@@ -1737,7 +1743,7 @@
.update(
"Add External ID",
user.id(),
- u -> u.addExternalId(ExternalId.create("foo", "myId", user.id())));
+ u -> u.addExternalId(externalIdFactory.create("foo", "myId", user.id())));
accountIndexedCounter.assertReindexOf(user);
TestKey key = validKeyWithSecondUserId();
@@ -2040,7 +2046,7 @@
.update(
"Delete External ID",
account.id(),
- u -> u.deleteExternalId(ExternalId.createEmail(account.id(), email)));
+ u -> u.deleteExternalId(externalIdFactory.createEmail(account.id(), email)));
expectedProblems.add(
new ConsistencyProblemInfo(
ConsistencyProblemInfo.Status.ERROR,
@@ -2375,7 +2381,7 @@
.update();
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId extIdA1 = ExternalId.create("foo", "A-1", accountId);
+ ExternalId extIdA1 = externalIdFactory.create("foo", "A-1", accountId);
accountsUpdateProvider
.get()
.insert("Create Test Account", accountId, u -> u.addExternalId(extIdA1));
@@ -2383,7 +2389,7 @@
AtomicInteger bgCounterA1 = new AtomicInteger(0);
AtomicInteger bgCounterA2 = new AtomicInteger(0);
PersonIdent ident = serverIdent.get();
- ExternalId extIdA2 = ExternalId.create("foo", "A-2", accountId);
+ ExternalId extIdA2 = externalIdFactory.create("foo", "A-2", accountId);
AccountsUpdate update =
new AccountsUpdate(
repoManager,
@@ -2424,8 +2430,8 @@
.collect(toSet()))
.containsExactly(extIdA1.key().get());
- ExternalId extIdB1 = ExternalId.create("foo", "B-1", accountId);
- ExternalId extIdB2 = ExternalId.create("foo", "B-2", accountId);
+ ExternalId extIdB1 = externalIdFactory.create("foo", "B-1", accountId);
+ ExternalId extIdB2 = externalIdFactory.create("foo", "B-2", accountId);
Optional<AccountState> updatedAccount =
update.update(
"Update External ID",
@@ -2488,23 +2494,24 @@
// Manually inserting/updating/deleting an external ID of the user makes the index document
// stale.
try (Repository repo = repoManager.openRepository(allUsers)) {
- ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, repo);
+ ExternalIdNotes extIdNotes =
+ ExternalIdNotes.loadNoCacheUpdate(allUsers, repo, externalIdFactory);
- ExternalId.Key key = ExternalId.Key.create("foo", "foo");
- extIdNotes.insert(ExternalId.create(key, accountId));
+ ExternalId.Key key = externalIdKeyFactory.create("foo", "foo");
+ extIdNotes.insert(externalIdFactory.create(key, accountId));
try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
extIdNotes.commit(update);
}
assertStaleAccountAndReindex(accountId);
- extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, repo);
- extIdNotes.upsert(ExternalId.createWithEmail(key, accountId, "foo@example.com"));
+ extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, repo, externalIdFactory);
+ extIdNotes.upsert(externalIdFactory.createWithEmail(key, accountId, "foo@example.com"));
try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
extIdNotes.commit(update);
}
assertStaleAccountAndReindex(accountId);
- extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, repo);
+ extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, repo, externalIdFactory);
extIdNotes.delete(accountId, key);
try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
extIdNotes.commit(update);
@@ -2765,9 +2772,11 @@
String extId1String = "foo:bar";
String extId2String = "foo:baz";
ExternalId extId1 =
- ExternalId.createWithEmail(ExternalId.Key.parse(extId1String), admin.id(), "1@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(extId1String), admin.id(), "1@foo.com");
ExternalId extId2 =
- ExternalId.createWithEmail(ExternalId.Key.parse(extId2String), user.id(), "2@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(extId2String), user.id(), "2@foo.com");
ObjectId revBefore;
try (Repository repo = repoManager.openRepository(allUsers)) {
@@ -2807,9 +2816,11 @@
@Test
public void externalIdBatchUpdates_fail_sameAccount() {
ExternalId extId1 =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id(), "1@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
ExternalId extId2 =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:baz"), user.id(), "2@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:baz"), user.id(), "2@foo.com");
AccountsUpdate.UpdateArguments ua1 =
new AccountsUpdate.UpdateArguments(
@@ -2828,9 +2839,11 @@
@Test
public void externalIdBatchUpdates_fail_duplicateKey() {
ExternalId extIdAdmin =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id(), "1@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
ExternalId extIdUser =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), user.id(), "2@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:bar"), user.id(), "2@foo.com");
AccountsUpdate.UpdateArguments ua1 =
new AccountsUpdate.UpdateArguments(
@@ -2848,9 +2861,11 @@
@Test
public void externalIdBatchUpdates_commitMsg_multipleAccounts() throws Exception {
ExternalId extId1 =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id(), "1@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
ExternalId extId2 =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:baz"), user.id(), "2@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:baz"), user.id(), "2@foo.com");
AccountsUpdate.UpdateArguments ua1 =
new AccountsUpdate.UpdateArguments(
@@ -2872,7 +2887,8 @@
@Test
public void externalIdBatchUpdates_commitMsg_singleAccount() throws Exception {
ExternalId extId =
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id(), "1@foo.com");
+ externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
accountsUpdateProvider.get().update("foobar", admin.id(), (a, u) -> u.addExternalId(extId));
@@ -3029,7 +3045,7 @@
account.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(name("test"), email, account.id(), email)));
+ externalIdFactory.createWithEmail(name("test"), email, account.id(), email)));
accountIndexedCounter.assertReindexOf(account);
requestScopeOperations.setApiUser(account.id());
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index b41a2f3..7e23f0e 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -37,6 +37,8 @@
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.SetInactiveFlag;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -62,14 +64,17 @@
@Inject private SshKeyCache sshKeyCache;
@Inject private GroupsUpdate.Factory groupsUpdateFactory;
@Inject private SetInactiveFlag setInactiveFlag;
+ @Inject private AuthRequest.Factory authRequestFactory;
+ @Inject private ExternalIdFactory externalIdFactory;
+ @Inject private ExternalIdKeyFactory externalIdKeyFactory;
@Test
public void authenticateNewAccountWithEmail() throws Exception {
String email = "foo@example.com";
- ExternalId.Key mailtoExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_MAILTO, email);
+ ExternalId.Key mailtoExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_MAILTO, email);
assertNoSuchExternalIds(mailtoExtIdKey);
- AuthRequest who = AuthRequest.forEmail(email);
+ AuthRequest who = authRequestFactory.createForEmail(email);
AuthResult authResult = accountManager.authenticate(who);
assertAuthResultForNewAccount(authResult, mailtoExtIdKey);
assertExternalId(mailtoExtIdKey, email);
@@ -78,11 +83,12 @@
@Test
public void authenticateNewAccountWithUsername() throws Exception {
String username = "foo";
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
- ExternalId.Key usernameExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_USERNAME, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key usernameExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, username);
assertNoSuchExternalIds(gerritExtIdKey, usernameExtIdKey);
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
AuthResult authResult = accountManager.authenticate(who);
assertAuthResultForNewAccount(authResult, gerritExtIdKey);
assertExternalIdsWithoutEmail(gerritExtIdKey, usernameExtIdKey);
@@ -91,11 +97,12 @@
@Test
public void authenticateNewAccountWithUsernameAndEmail() throws Exception {
String username = "foo";
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
- ExternalId.Key usernameExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_USERNAME, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key usernameExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, username);
assertNoSuchExternalIds(gerritExtIdKey, usernameExtIdKey);
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
String email = "foo@example.com";
who.setEmailAddress(email);
AuthResult authResult = accountManager.authenticate(who);
@@ -107,12 +114,14 @@
@Test
public void authenticateNewAccountWithExternalUser() throws Exception {
String username = "foo";
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
- ExternalId.Key usernameExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_USERNAME, username);
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key usernameExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
assertNoSuchExternalIds(externalExtIdKey, usernameExtIdKey, gerritExtIdKey);
- AuthRequest who = AuthRequest.forExternalUser(username);
+ AuthRequest who = authRequestFactory.createForExternalUser(username);
AuthResult authResult = accountManager.authenticate(who);
assertAuthResultForNewAccount(authResult, externalExtIdKey);
assertExternalIdsWithoutEmail(externalExtIdKey, usernameExtIdKey);
@@ -122,12 +131,14 @@
@Test
public void authenticateNewAccountWithExternalUserAndEmail() throws Exception {
String username = "foo";
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
- ExternalId.Key usernameExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_USERNAME, username);
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key usernameExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
assertNoSuchExternalIds(externalExtIdKey, usernameExtIdKey, gerritExtIdKey);
- AuthRequest who = AuthRequest.forExternalUser(username);
+ AuthRequest who = authRequestFactory.createForExternalUser(username);
String email = "foo@example.com";
who.setEmailAddress(email);
AuthResult authResult = accountManager.authenticate(who);
@@ -141,13 +152,13 @@
public void authenticateWithEmail() throws Exception {
String email = "foo@example.com";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key mailtoExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_MAILTO, email);
+ ExternalId.Key mailtoExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_MAILTO, email);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(mailtoExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(mailtoExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forEmail(email);
+ AuthRequest who = authRequestFactory.createForEmail(email);
AuthResult authResult = accountManager.authenticate(who);
assertAuthResultForExistingAccount(authResult, accountId, mailtoExtIdKey);
}
@@ -156,13 +167,13 @@
public void authenticateWithUsername() throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
AuthResult authResult = accountManager.authenticate(who);
assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
}
@@ -171,13 +182,14 @@
public void authenticateWithExternalUser() throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(externalExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(externalExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forExternalUser(username);
+ AuthRequest who = authRequestFactory.createForExternalUser(username);
AuthResult authResult = accountManager.authenticate(who);
assertAuthResultForExistingAccount(authResult, accountId, externalExtIdKey);
}
@@ -187,15 +199,16 @@
String username = "foo";
String email = "foo@example.com";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
u ->
u.setPreferredEmail(email)
- .addExternalId(ExternalId.createWithEmail(gerritExtIdKey, accountId, email)));
+ .addExternalId(
+ externalIdFactory.createWithEmail(gerritExtIdKey, accountId, email)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
String newEmail = "bar@example.com";
who.setEmailAddress(newEmail);
AuthResult authResult = accountManager.authenticate(who);
@@ -233,23 +246,26 @@
projectCache,
externalIds,
groupsUpdateFactory,
- setInactiveFlag));
+ setInactiveFlag,
+ externalIdFactory,
+ externalIdKeyFactory));
}
private void authenticateWithUsernameAndUpdateDisplayName(AccountManager am) throws Exception {
String username = "foo";
String email = "foo@example.com";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
u ->
u.setFullName("Initial Name")
.setPreferredEmail(email)
- .addExternalId(ExternalId.createWithEmail(gerritExtIdKey, accountId, email)));
+ .addExternalId(
+ externalIdFactory.createWithEmail(gerritExtIdKey, accountId, email)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
String newName = "Updated Name";
who.setDisplayName(newName);
AuthResult authResult = am.authenticate(who);
@@ -263,12 +279,12 @@
@Test
public void cannotAuthenticateWithOrphanedExtId() throws Exception {
String username = "foo";
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
assertNoSuchExternalIds(gerritExtIdKey);
// Create orphaned SCHEME_GERRIT external ID.
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId gerritExtId = ExternalId.create(gerritExtIdKey, accountId);
+ ExternalId gerritExtId = externalIdFactory.create(gerritExtIdKey, accountId);
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = extIdNotesFactory.load(allUsersRepo);
@@ -276,7 +292,7 @@
extIdNotes.commit(md);
}
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.authenticate(who));
assertThat(thrown).hasMessageThat().contains("Authentication error, account not found");
@@ -286,13 +302,13 @@
public void cannotAuthenticateWithInactiveAccount() throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.setActive(false).addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.setActive(false).addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.authenticate(who));
assertThat(thrown).hasMessageThat().contains("Authentication error, account inactive");
@@ -303,13 +319,13 @@
throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.setActive(false).addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.setActive(false).addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
who.setActive(true);
who.setAuthProvidesAccountActiveStatus(true);
AccountException thrown =
@@ -323,13 +339,13 @@
throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.setActive(false).addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.setActive(false).addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
who.setActive(true);
who.setAuthProvidesAccountActiveStatus(true);
AuthResult authResult = accountManager.authenticate(who);
@@ -344,13 +360,13 @@
throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
who.setActive(false);
who.setAuthProvidesAccountActiveStatus(true);
AuthResult authResult = accountManager.authenticate(who);
@@ -366,13 +382,13 @@
throws Exception {
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
who.setActive(false);
who.setAuthProvidesAccountActiveStatus(true);
AccountException thrown =
@@ -391,15 +407,17 @@
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.createWithEmail(externalExtIdKey, accountId, email)));
+ u ->
+ u.addExternalId(externalIdFactory.createWithEmail(externalExtIdKey, accountId, email)));
// Try to authenticate with this email to create a new account with a SCHEME_MAILTO external ID.
// Expect that this fails because the email is already assigned to the other account.
- AuthRequest who = AuthRequest.forEmail(email);
+ AuthRequest who = authRequestFactory.createForEmail(email);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.authenticate(who));
assertThat(thrown)
@@ -414,15 +432,17 @@
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.createWithEmail(externalExtIdKey, accountId, email)));
+ u ->
+ u.addExternalId(externalIdFactory.createWithEmail(externalExtIdKey, accountId, email)));
// Try to authenticate with a new username and claim the same email.
// Expect that this fails because the email is already assigned to the other account.
- AuthRequest who = AuthRequest.forUser("bar");
+ AuthRequest who = authRequestFactory.createForUser("bar");
who.setEmailAddress(email);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.authenticate(who));
@@ -439,25 +459,29 @@
// Create an account with a SCHEME_GERRIT external ID and an email.
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
u ->
u.setPreferredEmail(email)
- .addExternalId(ExternalId.createWithEmail(gerritExtIdKey, accountId, email)));
+ .addExternalId(
+ externalIdFactory.createWithEmail(gerritExtIdKey, accountId, email)));
// Create another account with an SCHEME_EXTERNAL external ID that occupies the new email.
Account.Id accountId2 = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, "bar");
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, "bar");
accountsUpdate.insert(
"Create Test Account",
accountId2,
- u -> u.addExternalId(ExternalId.createWithEmail(externalExtIdKey, accountId2, newEmail)));
+ u ->
+ u.addExternalId(
+ externalIdFactory.createWithEmail(externalExtIdKey, accountId2, newEmail)));
// Try to authenticate and update the email for the first account.
// Expect that this fails because the new email is already assigned to the other account.
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
who.setEmailAddress(newEmail);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.authenticate(who));
@@ -482,21 +506,21 @@
// Create an account with a SCHEME_GERRIT external ID
String username = "foo";
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
Account.Id accountId = Account.id(seq.nextAccountId());
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
// Add the additional mail external ID with SCHEME_EMAIL
- accountManager.link(accountId, AuthRequest.forEmail(email));
+ accountManager.link(accountId, authRequestFactory.createForEmail(email));
// Try to authenticate and update the email for the account.
// Expect that this to succeed because even if the email already exist
// it is associated to the same account-id and thus is not really
// a duplicate but simply a promotion of external id to preferred email.
- AuthRequest who = AuthRequest.forUser(username);
+ AuthRequest who = authRequestFactory.createForUser(username);
who.setEmailAddress(email);
AuthResult authResult = accountManager.authenticate(who);
@@ -519,20 +543,20 @@
// Create an account with a SCHEME_GERRIT external ID and no email
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
+ ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
// Check that email is not used yet.
String email = "foo@example.com";
- ExternalId.Key mailtoExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_MAILTO, email);
+ ExternalId.Key mailtoExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_MAILTO, email);
assertNoSuchExternalIds(mailtoExtIdKey);
// Link the email to the account.
// Expect that a MAILTO external ID is created.
- AuthRequest who = AuthRequest.forEmail(email);
+ AuthRequest who = authRequestFactory.createForEmail(email);
AuthResult authResult = accountManager.link(accountId, who);
assertAuthResultForExistingAccount(authResult, accountId, mailtoExtIdKey);
assertExternalId(mailtoExtIdKey, accountId, email);
@@ -543,17 +567,18 @@
// Create an account with a SCHEME_GERRIT external ID and no email
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
u ->
u.addExternalId(
- ExternalId.createWithEmail(externalExtIdKey, accountId, "old@example.com")));
+ externalIdFactory.createWithEmail(externalExtIdKey, accountId, "old@example.com")));
// Link the email to the existing SCHEME_EXTERNAL external ID, but with a new email.
// Expect that the email of the existing external ID is updated.
- AuthRequest who = AuthRequest.forExternalUser(username);
+ AuthRequest who = authRequestFactory.createForExternalUser(username);
String newEmail = "new@example.com";
who.setEmailAddress(newEmail);
AuthResult authResult = accountManager.link(accountId, who);
@@ -566,24 +591,26 @@
// Create an account with a SCHEME_EXTERNAL external ID
String username1 = "foo";
Account.Id accountId1 = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey1 = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username1);
+ ExternalId.Key externalExtIdKey1 =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username1);
accountsUpdate.insert(
"Create Test Account",
accountId1,
- u -> u.addExternalId(ExternalId.create(externalExtIdKey1, accountId1)));
+ u -> u.addExternalId(externalIdFactory.create(externalExtIdKey1, accountId1)));
// Create another account with a SCHEME_EXTERNAL external ID
String username2 = "bar";
Account.Id accountId2 = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey2 = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username2);
+ ExternalId.Key externalExtIdKey2 =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username2);
accountsUpdate.insert(
"Create Test Account",
accountId2,
- u -> u.addExternalId(ExternalId.create(externalExtIdKey2, accountId2)));
+ u -> u.addExternalId(externalIdFactory.create(externalExtIdKey2, accountId2)));
// Try to link external ID of the first account to the second account.
// Expect that this fails because the external ID is already assigned to the first account.
- AuthRequest who = AuthRequest.forExternalUser(username1);
+ AuthRequest who = authRequestFactory.createForExternalUser(username1);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.link(accountId2, who));
assertThat(thrown)
@@ -598,24 +625,27 @@
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.createWithEmail(externalExtIdKey, accountId, email)));
+ u ->
+ u.addExternalId(externalIdFactory.createWithEmail(externalExtIdKey, accountId, email)));
// Create another account with a SCHEME_GERRIT external ID and no email
String username2 = "foo";
Account.Id accountId2 = Account.id(seq.nextAccountId());
- ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username2);
+ ExternalId.Key gerritExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username2);
accountsUpdate.insert(
"Create Test Account",
accountId2,
- u -> u.addExternalId(ExternalId.create(gerritExtIdKey, accountId2)));
+ u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId2)));
// Try to link the email to the second account (via a new MAILTO external ID) and expect that
// this fails because the email is already assigned to the first account.
- AuthRequest who = AuthRequest.forEmail(email);
+ AuthRequest who = authRequestFactory.createForEmail(email);
AccountException thrown =
assertThrows(AccountException.class, () -> accountManager.link(accountId2, who));
assertThat(thrown)
@@ -630,13 +660,15 @@
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
Account.Id accountId = Account.id(seq.nextAccountId());
- ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
+ ExternalId.Key externalExtIdKey =
+ externalIdKeyFactory.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
accountId,
- u -> u.addExternalId(ExternalId.createWithEmail(externalExtIdKey, accountId, email)));
+ u ->
+ u.addExternalId(externalIdFactory.createWithEmail(externalExtIdKey, accountId, email)));
- AuthRequest who = AuthRequest.forEmail(email);
+ AuthRequest who = authRequestFactory.createForEmail(email);
AuthResult result = accountManager.link(accountId, who);
assertThat(result.isNew()).isFalse();
assertThat(result.getAccountId().get()).isEqualTo(accountId.get());
diff --git a/javatests/com/google/gerrit/acceptance/pgm/ChangeExternalIdCaseSensitivityIT.java b/javatests/com/google/gerrit/acceptance/pgm/ChangeExternalIdCaseSensitivityIT.java
new file mode 100644
index 0000000..83df896
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/pgm/ChangeExternalIdCaseSensitivityIT.java
@@ -0,0 +1,239 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.pgm;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.StandaloneSiteTest;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdNotes;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.junit.After;
+import org.junit.Test;
+
+@NoHttpd
+public class ChangeExternalIdCaseSensitivityIT extends StandaloneSiteTest {
+
+ private static final boolean CASE_SENSITIVE = false;
+ private static final boolean CASE_INSENSITIVE = true;
+
+ private ServerContext ctx;
+ private ExternalIdNotes extIdNotes;
+ private ExternalIdFactory extIdFactory;
+ private MetaDataUpdate md;
+ private FileBasedConfig config;
+
+ @After
+ public void cleanup() throws Exception {
+ if (ctx != null) {
+ ctx.close();
+ }
+ }
+
+ @Test
+ public void externalIdNoteNameIsMigratedToCaseInsensitive() throws Exception {
+ prepareExternalIdNotes(CASE_SENSITIVE);
+
+ ctx.close();
+ runChangeExternalIdCaseSensitivity();
+ ctx = startServer();
+ extIdNotes = getExternalIdNotes(ctx);
+
+ assertExternalIdNotes(CASE_INSENSITIVE);
+ }
+
+ @Test
+ public void externalIdNoteNameIsMigratedToCaseSensitive() throws Exception {
+ prepareExternalIdNotes(CASE_INSENSITIVE);
+
+ ctx.close();
+ runChangeExternalIdCaseSensitivity();
+ ctx = startServer();
+ extIdNotes = getExternalIdNotes(ctx);
+
+ assertExternalIdNotes(CASE_SENSITIVE);
+ }
+
+ @Test
+ public void migrationFailsWithDuplicates() throws Exception {
+ prepareExternalIdNotes(CASE_SENSITIVE);
+ extIdNotes.insert(extIdFactory.create(SCHEME_USERNAME, "JohnDoe", Account.id(1)));
+ extIdNotes.commit(md);
+
+ assertThat(extIdNotes.get(ExternalId.Key.parse("username:johndoe", false)).isPresent())
+ .isTrue();
+ assertThat(extIdNotes.get(ExternalId.Key.parse("username:JohnDoe", false)).isPresent())
+ .isTrue();
+
+ ctx.close();
+ assertThrows(DuplicateExternalIdKeyException.class, () -> runChangeExternalIdCaseSensitivity());
+ ctx = startServer();
+ extIdNotes = getExternalIdNotes(ctx);
+
+ assertExternalIdNotes(CASE_SENSITIVE);
+ assertThat(extIdNotes.get(ExternalId.Key.parse("username:johndoe", false)).isPresent())
+ .isTrue();
+ assertThat(extIdNotes.get(ExternalId.Key.parse("username:JohnDoe", false)).isPresent())
+ .isTrue();
+ }
+
+ @Test
+ public void userNameCaseInsensitiveOptionIsSwitched() throws Exception {
+ configureUserNameCaseInsensitive(CASE_SENSITIVE);
+ assertThat(config.getBoolean("auth", "userNameCaseInsensitive", false)).isFalse();
+ runChangeExternalIdCaseSensitivity();
+ config.load();
+ assertThat(config.getBoolean("auth", "userNameCaseInsensitive", false)).isTrue();
+ runChangeExternalIdCaseSensitivity();
+ config.load();
+ assertThat(config.getBoolean("auth", "userNameCaseInsensitive", false)).isFalse();
+ }
+
+ @Test
+ public void dryrunDoesNotPersistChanges() throws Exception {
+ prepareExternalIdNotes(CASE_SENSITIVE);
+ ctx.close();
+ runGerrit("ChangeExternalIdCaseSensitivity", "-d", sitePaths.site_path.toString(), "--dryrun");
+
+ config.load();
+ assertThat(config.getBoolean("auth", "userNameCaseInsensitive", false)).isFalse();
+
+ ctx = startServer();
+ assertExternalIdNotes(CASE_SENSITIVE);
+ }
+
+ private void prepareExternalIdNotes(boolean userNameCaseInsensitive) throws Exception {
+ configureUserNameCaseInsensitive(userNameCaseInsensitive);
+ initSite();
+ ctx = startServer();
+ extIdFactory = ctx.getInjector().getInstance(ExternalIdFactory.class);
+ Project.NameKey allUsers = ctx.getInjector().getInstance(AllUsersName.class);
+ extIdNotes = getExternalIdNotes(ctx, allUsers);
+ md = getMetaDataUpdate(ctx, allUsers);
+
+ extIdNotes.insert(extIdFactory.create(SCHEME_USERNAME, "johndoe", Account.id(0)));
+ extIdNotes.insert(extIdFactory.create(SCHEME_GERRIT, "johndoe", Account.id(0)));
+
+ extIdNotes.insert(extIdFactory.create(SCHEME_USERNAME, "JaneDoe", Account.id(1)));
+ extIdNotes.insert(extIdFactory.create(SCHEME_GERRIT, "JaneDoe", Account.id(1)));
+
+ extIdNotes.insert(extIdFactory.create(SCHEME_MAILTO, "Jane@Doe.com", Account.id(1)));
+ extIdNotes.insert(extIdFactory.create(SCHEME_UUID, "Abc123", Account.id(1)));
+ extIdNotes.insert(extIdFactory.create(SCHEME_GPGKEY, "Abc123", Account.id(1)));
+ extIdNotes.insert(extIdFactory.create(SCHEME_EXTERNAL, "saml/JaneDoe", Account.id(1)));
+ extIdNotes.commit(md);
+
+ assertExternalIdNotes(userNameCaseInsensitive);
+ }
+
+ private void assertExternalIdNotes(boolean userNameCaseInsensitive) throws Exception {
+ assertThat(
+ extIdNotes
+ .get(ExternalId.Key.parse("username:johndoe", userNameCaseInsensitive))
+ .isPresent())
+ .isTrue();
+ assertThat(
+ extIdNotes
+ .get(ExternalId.Key.parse("username:JaneDoe", !userNameCaseInsensitive))
+ .isPresent())
+ .isFalse();
+ assertThat(
+ extIdNotes
+ .get(ExternalId.Key.parse("username:JaneDoe", userNameCaseInsensitive))
+ .isPresent())
+ .isTrue();
+
+ assertThat(
+ extIdNotes
+ .get(ExternalId.Key.parse("gerrit:johndoe", userNameCaseInsensitive))
+ .isPresent())
+ .isTrue();
+ assertThat(
+ extIdNotes
+ .get(ExternalId.Key.parse("gerrit:JaneDoe", !userNameCaseInsensitive))
+ .isPresent())
+ .isFalse();
+ assertThat(
+ extIdNotes
+ .get(ExternalId.Key.parse("gerrit:JaneDoe", userNameCaseInsensitive))
+ .isPresent())
+ .isTrue();
+
+ assertThat(extIdNotes.get(ExternalId.Key.parse("mailto:Jane@Doe.com", false)).isPresent())
+ .isTrue();
+ assertThat(extIdNotes.get(ExternalId.Key.parse("uuid:Abc123", false)).isPresent()).isTrue();
+ assertThat(extIdNotes.get(ExternalId.Key.parse("gpgkey:Abc123", false)).isPresent()).isTrue();
+ assertThat(extIdNotes.get(ExternalId.Key.parse("external:saml/JaneDoe", false)).isPresent())
+ .isTrue();
+ }
+
+ private void configureUserNameCaseInsensitive(boolean userNameCaseInsensitive)
+ throws IOException, ConfigInvalidException {
+ config = new FileBasedConfig(baseConfig, sitePaths.gerrit_config.toFile(), FS.DETECTED);
+ config.load();
+ config.setBoolean("auth", null, "userNameCaseInsensitive", userNameCaseInsensitive);
+ config.save();
+ if (userNameCaseInsensitive) {
+ assertThat(config.getBoolean("auth", "userNameCaseInsensitive", false)).isTrue();
+ } else {
+ assertThat(config.getBoolean("auth", "userNameCaseInsensitive", false)).isFalse();
+ }
+ }
+
+ private void initSite() throws Exception {
+ runGerrit("init", "-d", sitePaths.site_path.toString(), "--show-stack-trace");
+ }
+
+ private void runChangeExternalIdCaseSensitivity() throws Exception {
+ runGerrit("ChangeExternalIdCaseSensitivity", "-d", sitePaths.site_path.toString(), "--batch");
+ }
+
+ private static ExternalIdNotes getExternalIdNotes(ServerContext ctx) throws Exception {
+ return getExternalIdNotes(ctx, ctx.getInjector().getInstance(AllUsersName.class));
+ }
+
+ private static ExternalIdNotes getExternalIdNotes(ServerContext ctx, Project.NameKey allUsers)
+ throws Exception {
+ GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
+ ExternalIdNotes.FactoryNoReindex extIdNotesFactory =
+ ctx.getInjector().getInstance(ExternalIdNotes.FactoryNoReindex.class);
+ return extIdNotesFactory.load(repoManager.openRepository(allUsers));
+ }
+
+ private static MetaDataUpdate getMetaDataUpdate(ServerContext ctx, Project.NameKey allUsers)
+ throws Exception {
+ MetaDataUpdate.Server metaDataUpdateFactory =
+ ctx.getInjector().getInstance(MetaDataUpdate.Server.class);
+ return metaDataUpdateFactory.create(allUsers);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
index 8feac20..d055875 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
@@ -40,6 +40,8 @@
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AuthConfig;
@@ -63,6 +65,8 @@
@Inject private ExternalIds externalIds;
@Inject private Provider<Emails> emails;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExternalIdFactory externalIdFactory;
+ @Inject private ExternalIdKeyFactory externalIdKeyFactory;
@Test
public void addEmail() throws Exception {
@@ -138,7 +142,7 @@
admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(
+ externalIdFactory.createWithEmail(
ExternalId.SCHEME_EXTERNAL, "foo", admin.id(), email)));
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
@@ -182,7 +186,7 @@
public void setPreferredEmailToEmailFromCustomRealmThatDoesntExistAsExternalId()
throws Exception {
String email = "foo@example.com";
- ExternalId.Key mailtoExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_MAILTO, email);
+ ExternalId.Key mailtoExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_MAILTO, email);
assertThat(externalIds.get(mailtoExtIdKey)).isEmpty();
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
@@ -200,7 +204,7 @@
@Test
public void setPreferredEmailToEmailFromCustomRealmThatBelongsToOtherAccount() throws Exception {
- ExternalId mailToExtId = ExternalId.createEmail(user.id(), user.email());
+ ExternalId mailToExtId = externalIdFactory.createEmail(user.id(), user.email());
assertThat(externalIds.get(mailToExtId.key())).isPresent();
Context oldCtx =
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index c41c9d8..cd123aa 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -21,6 +21,8 @@
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID;
@@ -37,6 +39,8 @@
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
@@ -53,7 +57,10 @@
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIdReader;
import com.google.gerrit.server.account.externalids.ExternalIds;
@@ -92,6 +99,8 @@
@Inject private ExternalIdNotes.Factory externalIdNotesFactory;
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExternalIdKeyFactory externalIdKeyFactory;
+ @Inject private ExternalIdFactory externalIdFactory;
@ConfigSuite.Default
public static Config partialCacheReloadingEnabled() {
@@ -249,19 +258,60 @@
gApi.accounts()
.self()
.deleteExternalIds(
- ImmutableList.of(ExternalId.Key.create(SCHEME_MAILTO, preferredEmail).get()));
+ ImmutableList.of(externalIdKeyFactory.create(SCHEME_MAILTO, preferredEmail).get()));
assertThat(gApi.accounts().self().get().email).isNull();
}
@Test
- public void deleteExternalIds_Conflict() throws Exception {
+ public void deleteExternalIdOfUsernameByNonAdminForbidden() throws Exception {
List<String> toDelete = new ArrayList<>();
String externalIdStr = "username:" + user.username();
toDelete.add(externalIdStr);
- RestResponse response = userRestSession.post("/accounts/self/external.ids:delete", toDelete);
- response.assertConflict();
- assertThat(response.getEntityContent())
- .isEqualTo(String.format("External id %s cannot be deleted", externalIdStr));
+ RestResponse response =
+ userRestSession.post("/accounts/" + admin.id() + "/external.ids:delete", toDelete);
+ response.assertForbidden();
+ }
+
+ @Test
+ public void deleteExternalIdOfUsernameSelfForbidden() throws Exception {
+ List<String> toDelete = new ArrayList<>();
+ String externalIdStr = "username:" + admin.username();
+ toDelete.add(externalIdStr);
+ RestResponse response = adminRestSession.post("/accounts/self/external.ids:delete", toDelete);
+ response.assertForbidden();
+ }
+
+ @Test
+ public void deleteExternalIdOfUsernameByAdmin() throws Exception {
+ List<String> toDelete = new ArrayList<>();
+ String externalIdStr = "username:" + user.username();
+ toDelete.add(externalIdStr);
+ RestResponse response =
+ adminRestSession.post("/accounts/" + user.id() + "/external.ids:delete", toDelete);
+ response.assertNoContent();
+ List<AccountExternalIdInfo> results = gApi.accounts().id(user.id().get()).getExternalIds();
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).identity).isEqualTo("mailto:user1@example.com");
+ }
+
+ @Test
+ public void deleteExternalIdOfUsernameMaintainServer() throws Exception {
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.MAINTAIN_SERVER).group(REGISTERED_USERS))
+ .add(allowCapability(GlobalCapability.MODIFY_ACCOUNT).group(REGISTERED_USERS))
+ .update();
+
+ List<String> toDelete = new ArrayList<>();
+ TestAccount user2 = accountCreator.user2();
+ String externalIdStr = "username:" + user2.username();
+ toDelete.add(externalIdStr);
+ RestResponse response =
+ userRestSession.post("/accounts/" + user2.id() + "/external.ids:delete", toDelete);
+ response.assertNoContent();
+ List<AccountExternalIdInfo> results = gApi.accounts().id(user2.id().get()).getExternalIds();
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).identity).isEqualTo("mailto:user2@example.com");
}
@Test
@@ -525,12 +575,12 @@
// create valid external IDs
insertExtId(
- ExternalId.createWithPassword(
- ExternalId.Key.parse(nextId(scheme, i)),
+ externalIdFactory.createWithPassword(
+ externalIdKeyFactory.parse(nextId(scheme, i)),
admin.id(),
"admin.other@example.com",
"secret-password"));
- insertExtId(ExternalId.createEmail(admin.id(), "admin.other@example.com"));
+ insertExtId(externalIdFactory.createEmail(admin.id(), "admin.other@example.com"));
insertExtId(createExternalIdWithOtherCaseEmail(nextId(scheme, i)));
}
@@ -630,29 +680,30 @@
}
private ExternalId createExternalIdWithOtherCaseEmail(String externalId) {
- return ExternalId.createWithPassword(
- ExternalId.Key.parse(externalId),
+ return externalIdFactory.createWithPassword(
+ externalIdKeyFactory.parse(externalId),
admin.id(),
admin.email().toUpperCase(Locale.US),
"password");
}
private ExternalId createExternalIdForNonExistingAccount(String externalId) {
- return ExternalId.create(ExternalId.Key.parse(externalId), Account.id(1));
+ return externalIdFactory.create(externalIdKeyFactory.parse(externalId), Account.id(1));
}
private ExternalId createExternalIdWithInvalidEmail(String externalId) {
- return ExternalId.createWithEmail(
- ExternalId.Key.parse(externalId), admin.id(), "invalid-email");
+ return externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(externalId), admin.id(), "invalid-email");
}
private ExternalId createExternalIdWithDuplicateEmail(String externalId) {
- return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), user.id(), admin.email());
+ return externalIdFactory.createWithEmail(
+ externalIdKeyFactory.parse(externalId), user.id(), admin.email());
}
private ExternalId createExternalIdWithBadPassword(String username) {
- return ExternalId.create(
- ExternalId.Key.create(SCHEME_USERNAME, username),
+ return externalIdFactory.create(
+ externalIdKeyFactory.create(SCHEME_USERNAME, username),
admin.id(),
null,
"non-hashed-password-is-not-allowed");
@@ -664,14 +715,14 @@
@Test
public void readExternalIdWithAccountIdThatCanBeExpressedInKiB() throws Exception {
- ExternalId.Key extIdKey = ExternalId.Key.parse("foo:bar");
+ ExternalId.Key extIdKey = externalIdKeyFactory.parse("foo:bar");
Account.Id accountId = Account.id(1024 * 100);
accountsUpdateProvider
.get()
.insert(
"Create Account with Bad External ID",
accountId,
- u -> u.addExternalId(ExternalId.create(extIdKey, accountId)));
+ u -> u.addExternalId(externalIdFactory.create(extIdKey, accountId)));
Optional<ExternalId> extId = externalIds.get(extIdKey);
assertThat(extId.map(ExternalId::accountId)).hasValue(accountId);
}
@@ -681,7 +732,7 @@
Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id()));
try (AutoCloseable ctx = createFailOnLoadContext()) {
// insert external ID
- ExternalId extId = ExternalId.create("foo", "bar", admin.id());
+ ExternalId extId = externalIdFactory.create("foo", "bar", admin.id());
insertExtId(extId);
expectedExtIds.add(extId);
assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
@@ -689,7 +740,7 @@
// update external ID
expectedExtIds.remove(extId);
ExternalId extId2 =
- ExternalId.createWithEmail("foo", "bar", admin.id(), "foo.bar@example.com");
+ externalIdFactory.createWithEmail("foo", "bar", admin.id(), "foo.bar@example.com");
accountsUpdateProvider
.get()
.update("Update External ID", admin.id(), u -> u.updateExternalId(extId2));
@@ -711,7 +762,7 @@
try (AutoCloseable ctx = createFailOnLoadContext()) {
// update external ID branch so that external IDs need to be reloaded
- insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
+ insertExtIdBehindGerritsBack(externalIdFactory.create("foo", "bar", admin.id()));
assertThrows(IOException.class, () -> externalIds.byAccount(admin.id()));
}
@@ -723,7 +774,7 @@
try (AutoCloseable ctx = createFailOnLoadContext()) {
// update external ID branch so that external IDs need to be reloaded
- insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
+ insertExtIdBehindGerritsBack(externalIdFactory.create("foo", "bar", admin.id()));
assertThrows(IOException.class, () -> externalIds.byEmail(admin.email()));
}
@@ -732,7 +783,7 @@
@Test
public void byAccountUpdateExternalIdsBehindGerritsBack() throws Exception {
Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id()));
- ExternalId newExtId = ExternalId.create("foo", "bar", admin.id());
+ ExternalId newExtId = externalIdFactory.create("foo", "bar", admin.id());
insertExtIdBehindGerritsBack(newExtId);
expectedExternalIds.add(newExtId);
assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExternalIds);
@@ -740,10 +791,10 @@
@Test
public void unsetEmail() throws Exception {
- ExternalId extId = ExternalId.createWithEmail("x", "1", user.id(), "x@example.com");
+ ExternalId extId = externalIdFactory.createWithEmail("x", "1", user.id(), "x@example.com");
insertExtId(extId);
- ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id());
+ ExternalId extIdWithoutEmail = externalIdFactory.create("x", "1", user.id());
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -757,10 +808,11 @@
@Test
public void unsetHttpPassword() throws Exception {
ExternalId extId =
- ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id(), null, "secret");
+ externalIdFactory.createWithPassword(
+ externalIdKeyFactory.create("y", "1"), user.id(), null, "secret");
insertExtId(extId);
- ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id());
+ ExternalId extIdWithoutPassword = externalIdFactory.create("y", "1", user.id());
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -771,6 +823,75 @@
}
}
+ @Test
+ @GerritConfig(name = "auth.userNameCaseInsensitive", value = "true")
+ public void createCaseInsensitiveExternalId_DuplicateKey() throws Exception {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ testCaseInsensitiveExternalIdKey(md, extIdNotes, SCHEME_USERNAME, "JohnDoe", Account.id(42));
+ assertThrows(
+ DuplicateExternalIdKeyException.class,
+ () ->
+ extIdNotes.insert(
+ externalIdFactory.create(SCHEME_USERNAME, "johndoe", Account.id(23))));
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "auth.userNameCaseInsensitive", value = "true")
+ public void createCaseInsensitiveExternalId_SchemeWithUsername() throws Exception {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+
+ testCaseInsensitiveExternalIdKey(md, extIdNotes, SCHEME_USERNAME, "janedoe", Account.id(66));
+ testCaseInsensitiveExternalIdKey(md, extIdNotes, SCHEME_GERRIT, "JaneDoe", Account.id(66));
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "auth.userNameCaseInsensitive", value = "true")
+ public void createCaseSensitiveExternalId_SchemeWithoutUsername() throws Exception {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+
+ testCaseSensitiveExternalIdKey(md, extIdNotes, SCHEME_MAILTO, "Jane@doe.com", Account.id(66));
+ testCaseSensitiveExternalIdKey(md, extIdNotes, SCHEME_UUID, "1234ABCD", Account.id(66));
+ testCaseSensitiveExternalIdKey(md, extIdNotes, SCHEME_GPGKEY, "1234ABCD", Account.id(66));
+ }
+ }
+
+ private void testCaseSensitiveExternalIdKey(
+ MetaDataUpdate md, ExternalIdNotes extIdNotes, String scheme, String id, Account.Id accountId)
+ throws DuplicateExternalIdKeyException, IOException, ConfigInvalidException {
+ ExternalId extId = externalIdFactory.create(scheme, id, accountId);
+ extIdNotes.insert(extId);
+ extIdNotes.commit(md);
+ assertThat(extIdNotes.get(externalIdKeyFactory.create(scheme, id)).get().accountId().get())
+ .isEqualTo(accountId.get());
+ assertThat(extIdNotes.get(externalIdKeyFactory.create(scheme, id.toLowerCase())).isPresent())
+ .isFalse();
+ }
+
+ private void testCaseInsensitiveExternalIdKey(
+ MetaDataUpdate md, ExternalIdNotes extIdNotes, String scheme, String id, Account.Id accountId)
+ throws DuplicateExternalIdKeyException, IOException, ConfigInvalidException {
+ ExternalId extId = externalIdFactory.create(scheme, id, accountId);
+ extIdNotes.insert(extId);
+ extIdNotes.commit(md);
+ assertThat(extIdNotes.get(externalIdKeyFactory.create(scheme, id)).get().accountId().get())
+ .isEqualTo(accountId.get());
+ assertThat(
+ extIdNotes
+ .get(externalIdKeyFactory.create(scheme, id.toLowerCase()))
+ .get()
+ .accountId()
+ .get())
+ .isEqualTo(accountId.get());
+ }
+
private boolean isPartialCacheReloadingEnabled() {
return cfg.getBoolean("cache", "external_ids_map", "enablePartialReloads", true);
}
@@ -796,7 +917,8 @@
private void insertExtIdBehindGerritsBack(ExternalId extId) throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
// Inserting an external ID "behind Gerrit's back" means that the caches are not updated.
- ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsers, repo);
+ ExternalIdNotes extIdNotes =
+ ExternalIdNotes.loadNoCacheUpdate(allUsers, repo, externalIdFactory);
extIdNotes.insert(extId);
try (MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, repo)) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java b/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
index e05d0db..f46cf0c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
@@ -18,6 +18,7 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.extensions.api.accounts.UsernameInput;
import org.junit.Test;
@@ -42,6 +43,16 @@
}
@Test
+ @GerritConfig(name = "auth.userNameCaseInsensitive", value = "true")
+ public void setExistingCaseInsensitive_Conflict() throws Exception {
+ UsernameInput in = new UsernameInput();
+ in.username = admin.username().toUpperCase();
+ adminRestSession
+ .put("/accounts/" + accountCreator.create().id().get() + "/username", in)
+ .assertConflict();
+ }
+
+ @Test
public void setNew_MethodNotAllowed() throws Exception {
UsernameInput in = new UsernameInput();
in.username = "newUsername";
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
index 0b2cba9..13353bd 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
@@ -29,7 +29,7 @@
import com.google.gerrit.gpg.testing.TestKey;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.junit.Test;
@@ -43,6 +43,7 @@
public class AccountsRestApiBindingsIT extends AbstractDaemonTest {
@Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExternalIdFactory externalIdFactory;
/**
* Account REST endpoints to be tested, each URL contains a placeholder for the account
@@ -166,7 +167,7 @@
admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(name("test"), email, admin.id(), email)));
+ externalIdFactory.createWithEmail(name("test"), email, admin.id(), email)));
requestScopeOperations.setApiUser(admin.id());
gApi.accounts()
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
index c7beb2d..0cfa0f8 100644
--- a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -23,6 +23,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
@@ -160,6 +161,22 @@
}
@Test
+ @GerritConfig(name = "auth.userNameCaseInsensitive", value = "true")
+ public void byUsernameCaseInsensitive() throws Exception {
+ String existingUsername = "myusername";
+ Account.Id idWithUsername = accountOperations.newAccount().username(existingUsername).create();
+
+ String existingMixedCaseUsername = "MyMixedCaseUsername";
+ Account.Id idWithMixedCaseUsername =
+ accountOperations.newAccount().username(existingMixedCaseUsername).create();
+
+ assertThat(resolve(existingUsername)).containsExactly(idWithUsername);
+ assertThat(resolve(existingMixedCaseUsername)).containsExactly(idWithMixedCaseUsername);
+ assertThat(resolve(existingMixedCaseUsername.toLowerCase()))
+ .containsExactly(idWithMixedCaseUsername);
+ }
+
+ @Test
public void byNameAndEmail() throws Exception {
String email = name("user@example.com");
Account.Id idWithEmail = accountOperations.newAccount().preferredEmail(email).create();
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java
index 0fc42ff..277c0e6 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIdUpsertPreprocessor;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -49,6 +50,7 @@
@Inject private Sequences sequences;
@Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
@Inject private ExternalIdNotes.Factory extIdNotesFactory;
+ @Inject private ExternalIdFactory extIdFactory;
public static class Module extends AbstractModule {
@Override
@@ -70,7 +72,7 @@
@Test
public void insertAccount() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId = ExternalId.create("foo", "bar", id);
+ ExternalId extId = extIdFactory.create("foo", "bar", id);
accountsUpdateProvider.get().insert("test", id, u -> u.addExternalId(extId));
assertThat(testPreprocessor.upserted).containsExactly(extId);
}
@@ -78,8 +80,8 @@
@Test
public void replaceByKeys() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId1 = ExternalId.create("foo", "bar1", id);
- ExternalId extId2 = ExternalId.create("foo", "bar2", id);
+ ExternalId extId1 = extIdFactory.create("foo", "bar1", id);
+ ExternalId extId2 = extIdFactory.create("foo", "bar2", id);
accountsUpdateProvider.get().insert("test", id, u -> u.addExternalId(extId1));
testPreprocessor.reset();
@@ -95,7 +97,7 @@
@Test
public void insert() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId = ExternalId.create("foo", "bar", id);
+ ExternalId extId = extIdFactory.create("foo", "bar", id);
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
@@ -109,7 +111,7 @@
@Test
public void upsert() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId = ExternalId.create("foo", "bar", id);
+ ExternalId extId = extIdFactory.create("foo", "bar", id);
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
@@ -123,8 +125,8 @@
@Test
public void replace() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId1 = ExternalId.create("foo", "bar1", id);
- ExternalId extId2 = ExternalId.create("foo", "bar2", id);
+ ExternalId extId1 = extIdFactory.create("foo", "bar1", id);
+ ExternalId extId2 = extIdFactory.create("foo", "bar2", id);
accountsUpdateProvider.get().insert("test", id, u -> u.addExternalId(extId1));
testPreprocessor.reset();
@@ -140,8 +142,8 @@
@Test
public void replace_viaAccountsUpdate() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId1 = ExternalId.create("foo", "bar", id, "email1@foo", "hash");
- ExternalId extId2 = ExternalId.create("foo", "bar", id, "email2@foo", "hash");
+ ExternalId extId1 = extIdFactory.create("foo", "bar", id, "email1@foo", "hash");
+ ExternalId extId2 = extIdFactory.create("foo", "bar", id, "email2@foo", "hash");
accountsUpdateProvider.get().insert("test", id, u -> u.addExternalId(extId1));
testPreprocessor.reset();
@@ -152,7 +154,7 @@
@Test
public void blockUpsert() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId = ExternalId.create("foo", "bar", id);
+ ExternalId extId = extIdFactory.create("foo", "bar", id);
testPreprocessor.throwException = true;
StorageException e =
assertThrows(
@@ -165,8 +167,8 @@
@Test
public void blockUpsert_replace() throws Exception {
Account.Id id = Account.id(sequences.nextAccountId());
- ExternalId extId1 = ExternalId.create("foo", "bar", id, "email1@foo", "hash");
- ExternalId extId2 = ExternalId.create("foo", "bar", id, "email2@foo", "hash");
+ ExternalId extId1 = extIdFactory.create("foo", "bar", id, "email1@foo", "hash");
+ ExternalId extId2 = extIdFactory.create("foo", "bar", id, "email2@foo", "hash");
accountsUpdateProvider.get().insert("test", id, u -> u.addExternalId(extId1));
assertThat(accounts.get(id).get().externalIds()).containsExactly(extId1);
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
index 9e4907c..46687e3 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
@@ -44,7 +44,7 @@
import com.google.gerrit.server.PropertyMap;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupMembership;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -70,6 +70,7 @@
@Inject private ChangeNotes.Factory changeNotesFactory;
@Inject private ExternalUser.Factory externalUserFactory;
@Inject private GroupOperations groupOperations;
+ @Inject private ExternalIdKeyFactory externalIdKeyFactory;
@Before
public void setUp() {
@@ -295,7 +296,7 @@
ExternalUser createUserInGroup(String userId, String groupId) {
return externalUserFactory.create(
ImmutableSet.of(),
- ImmutableSet.of(ExternalId.Key.parse("company-auth:" + groupId + "-" + userId)),
+ ImmutableSet.of(externalIdKeyFactory.parse("company-auth:" + groupId + "-" + userId)),
PropertyMap.EMPTY);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SetAccountIT.java b/javatests/com/google/gerrit/acceptance/ssh/SetAccountIT.java
new file mode 100644
index 0000000..a82876e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/SetAccountIT.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.ssh;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.server.account.externalids.ExternalIds;
+import com.google.inject.Inject;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+@UseSsh
+@NoHttpd
+public class SetAccountIT extends AbstractDaemonTest {
+ @Inject private ExternalIds externalIds;
+ @Inject private ProjectOperations projectOperations;
+
+ @Test
+ public void setAccount_deleteExternalId_all() throws Exception {
+ TestAccount testAccount = accountCreator.create("user1", "user1@example.com", null, null);
+ adminSshSession.exec("gerrit set-account --delete-external-id ALL user1");
+ adminSshSession.assertSuccess();
+ assertThat(externalIds.byAccount(testAccount.id()).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void setAccount_deleteExternalId_single() throws Exception {
+ TestAccount testAccount = accountCreator.create("user2", "user2@example.com", null, null);
+ List<String> extIdKeys = getExternalIdKeys(testAccount);
+ assertThat(extIdKeys.contains("username:user2")).isTrue();
+ assertThat(extIdKeys.contains("mailto:user2@example.com")).isTrue();
+ adminSshSession.exec("gerrit set-account --delete-external-id username:user2 user2");
+ adminSshSession.assertSuccess();
+ extIdKeys = getExternalIdKeys(testAccount);
+ assertThat(extIdKeys.contains("username:user3")).isFalse();
+ assertThat(extIdKeys.contains("mailto:user3@example.com")).isFalse();
+ }
+
+ @Test
+ public void setAccount_deleteExternalId_multiple() throws Exception {
+ TestAccount testAccount = accountCreator.create("user3", "user3@example.com", null, null);
+ List<String> extIdKeys = getExternalIdKeys(testAccount);
+ assertThat(extIdKeys.contains("username:user3")).isTrue();
+ assertThat(extIdKeys.contains("mailto:user3@example.com")).isTrue();
+ adminSshSession.exec(
+ "gerrit set-account --delete-external-id username:user3 --delete-external-id mailto:user3@example.com user3");
+ adminSshSession.assertSuccess();
+ extIdKeys = getExternalIdKeys(testAccount);
+ assertThat(extIdKeys.contains("username:user3")).isFalse();
+ assertThat(extIdKeys.contains("mailto:user3@example.com")).isFalse();
+ }
+
+ @Test
+ public void setAccount_deleteExternalId_byUser() throws Exception {
+ userSshSession.exec("gerrit set-account --delete-external-id mailto:admin@example.com admin");
+ userSshSession.assertFailure();
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.MODIFY_ACCOUNT).group(REGISTERED_USERS))
+ .update();
+ userSshSession.exec("gerrit set-account --delete-external-id mailto:admin@example.com admin");
+ userSshSession.assertSuccess();
+ userSshSession.exec("gerrit set-account --delete-external-id username:admin admin");
+ userSshSession.assertFailure();
+ }
+
+ private List<String> getExternalIdKeys(TestAccount account) throws Exception {
+ return externalIds.byAccount(account.id()).stream()
+ .map(e -> e.key().get())
+ .collect(Collectors.toList());
+ }
+}
diff --git a/javatests/com/google/gerrit/auth/ldap/LdapRealmTest.java b/javatests/com/google/gerrit/auth/ldap/LdapRealmTest.java
index 7a61626..0883033 100644
--- a/javatests/com/google/gerrit/auth/ldap/LdapRealmTest.java
+++ b/javatests/com/google/gerrit/auth/ldap/LdapRealmTest.java
@@ -30,6 +30,7 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
@@ -44,6 +45,7 @@
public final class LdapRealmTest {
@Inject private LdapRealm ldapRealm = null;
+ @Inject private ExternalIdFactory externalIdFactory;
@Before
public void setUpInjector() throws Exception {
@@ -67,7 +69,7 @@
}
private ExternalId id(String scheme, String id) {
- return ExternalId.create(scheme, id, Account.id(1000));
+ return externalIdFactory.create(scheme, id, Account.id(1000));
}
private boolean accountBelongsToRealm(ExternalId... ids) {
diff --git a/javatests/com/google/gerrit/auth/oauth/OAuthRealmTest.java b/javatests/com/google/gerrit/auth/oauth/OAuthRealmTest.java
index 1af78e3..3ec6f28 100644
--- a/javatests/com/google/gerrit/auth/oauth/OAuthRealmTest.java
+++ b/javatests/com/google/gerrit/auth/oauth/OAuthRealmTest.java
@@ -24,6 +24,7 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -34,6 +35,7 @@
public final class OAuthRealmTest {
@Inject private OAuthRealm oauthRealm = null;
+ @Inject private ExternalIdFactory externalIdFactory;
@Before
public void setUpInjector() throws Exception {
@@ -42,7 +44,7 @@
}
private ExternalId id(String scheme, String id) {
- return ExternalId.create(scheme, id, Account.id(1000));
+ return externalIdFactory.create(scheme, id, Account.id(1000));
}
private boolean accountBelongsToRealm(ExternalId... ids) {
diff --git a/javatests/com/google/gerrit/auth/openid/OpenIdRealmTest.java b/javatests/com/google/gerrit/auth/openid/OpenIdRealmTest.java
index 05b0ec0..f83409b 100644
--- a/javatests/com/google/gerrit/auth/openid/OpenIdRealmTest.java
+++ b/javatests/com/google/gerrit/auth/openid/OpenIdRealmTest.java
@@ -25,6 +25,7 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -35,6 +36,7 @@
public final class OpenIdRealmTest {
@Inject private OpenIdRealm openidRealm = null;
+ @Inject private ExternalIdFactory extIdFactory;
@Before
public void setUpInjector() throws Exception {
@@ -43,7 +45,7 @@
}
private ExternalId id(String scheme, String id) {
- return ExternalId.create(scheme, id, Account.id(1000));
+ return extIdFactory.create(scheme, id, Account.id(1000));
}
private boolean accountBelongsToRealm(ExternalId... ids) {
diff --git a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index 45b3419..05e9808 100644
--- a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -15,7 +15,6 @@
package com.google.gerrit.gpg;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.gpg.GerritPublicKeyChecker.toExtIdKey;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithSecondUserId;
import static com.google.gerrit.gpg.testing.TestTrustKeys.keyA;
@@ -39,6 +38,7 @@
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.testing.InMemoryModule;
@@ -76,6 +76,10 @@
@Inject private ThreadLocalRequestContext requestContext;
+ @Inject private AuthRequest.Factory authRequestFactory;
+
+ @Inject private ExternalIdFactory externalIdFactory;
+
private LifecycleManager lifecycle;
private Account.Id userId;
private IdentifiedUser user;
@@ -101,7 +105,7 @@
lifecycle.start();
schemaCreator.create();
- userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
+ userId = accountManager.authenticate(authRequestFactory.createForUser("user")).getAccountId();
// Note: does not match any key in TestKeys.
accountsUpdateProvider
.get()
@@ -121,7 +125,7 @@
}
private IdentifiedUser addUser(String name) throws Exception {
- AuthRequest req = AuthRequest.forUser(name);
+ AuthRequest req = authRequestFactory.createForUser(name);
Account.Id id = accountManager.authenticate(req).getAccountId();
return userFactory.create(id);
}
@@ -202,16 +206,18 @@
reloadUser();
TestKey key = validKeyWithSecondUserId();
- PublicKeyChecker checker = checkerFactory.create(user, store).disableTrust();
+ GerritPublicKeyChecker checker =
+ (GerritPublicKeyChecker) checkerFactory.create(user, store).disableTrust();
assertProblems(
checker.check(key.getPublicKey()),
Status.BAD,
"No identities found for user; check http://test/settings#Identities");
- checker = checkerFactory.create().setStore(store).disableTrust();
+ checker = (GerritPublicKeyChecker) checkerFactory.create().setStore(store).disableTrust();
assertProblems(
checker.check(key.getPublicKey()), Status.BAD, "Key is not associated with any users");
- insertExtId(ExternalId.create(toExtIdKey(key.getPublicKey()), user.getAccountId()));
+ insertExtId(
+ externalIdFactory.create(checker.toExtIdKey(key.getPublicKey()), user.getAccountId()));
assertProblems(checker.check(key.getPublicKey()), Status.BAD, "No identities found for user");
}
@@ -362,13 +368,15 @@
private void add(PGPPublicKeyRing kr, IdentifiedUser user) throws Exception {
Account.Id id = user.getAccountId();
List<ExternalId> newExtIds = new ArrayList<>(2);
- newExtIds.add(ExternalId.create(toExtIdKey(kr.getPublicKey()), id));
+ GerritPublicKeyChecker checker =
+ (GerritPublicKeyChecker) checkerFactory.create(user, store).disableTrust();
+ newExtIds.add(externalIdFactory.create(checker.toExtIdKey(kr.getPublicKey()), id));
String userId = Iterators.getOnlyElement(kr.getPublicKey().getUserIDs(), null);
if (userId != null) {
String email = PushCertificateIdent.parse(userId).getEmailAddress();
assertThat(email).contains("@");
- newExtIds.add(ExternalId.createEmail(id, email));
+ newExtIds.add(externalIdFactory.createEmail(id, email));
}
store.add(kr);
@@ -401,7 +409,7 @@
}
private void addExternalId(String scheme, String id, String email) throws Exception {
- insertExtId(ExternalId.createWithEmail(scheme, id, user.getAccountId(), email));
+ insertExtId(externalIdFactory.createWithEmail(scheme, id, user.getAccountId(), email));
}
private void insertExtId(ExternalId extId) throws Exception {
diff --git a/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java b/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java
index 2f0fafa..162a171 100644
--- a/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java
+++ b/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java
@@ -32,8 +32,12 @@
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
import com.google.gerrit.util.http.testutil.FakeHttpServletResponse;
@@ -62,12 +66,6 @@
private static final String AUTH_PASSWORD = "jd123";
private static final String GERRIT_COOKIE_KEY = "GerritAccount";
private static final String AUTH_COOKIE_VALUE = "gerritcookie";
- private static final ExternalId AUTH_USER_PASSWORD_EXTERNAL_ID =
- ExternalId.createWithPassword(
- ExternalId.Key.create(ExternalId.SCHEME_USERNAME, AUTH_USER),
- AUTH_ACCOUNT_ID,
- null,
- AUTH_PASSWORD);
@Mock private DynamicItem<WebSession> webSessionItem;
@@ -93,14 +91,23 @@
private FakeHttpServletRequest req;
private HttpServletResponse res;
private AuthResult authSuccessful;
+ private ExternalIdFactory extIdFactory;
+ private ExternalIdKeyFactory extIdKeyFactory;
+ private PasswordVerifier pwdVerifier;
+ private AuthRequest.Factory authRequestFactory;
@Before
public void setUp() throws Exception {
req = new FakeHttpServletRequest("gerrit.example.com", 80, "", "");
res = new FakeHttpServletResponse();
+ extIdKeyFactory = new ExternalIdKeyFactory(authConfig);
+ extIdFactory = new ExternalIdFactory(extIdKeyFactory);
+ authRequestFactory = new AuthRequest.Factory(extIdKeyFactory);
+ pwdVerifier = new PasswordVerifier(extIdKeyFactory);
+
authSuccessful =
- new AuthResult(AUTH_ACCOUNT_ID, ExternalId.Key.create("username", AUTH_USER), false);
+ new AuthResult(AUTH_ACCOUNT_ID, extIdKeyFactory.create("username", AUTH_USER), false);
doReturn(Optional.of(accountState)).when(accountCache).getByUsername(AUTH_USER);
doReturn(Optional.of(accountState)).when(accountCache).get(AUTH_ACCOUNT_ID);
doReturn(account).when(accountState).account();
@@ -121,7 +128,13 @@
res.setStatus(HttpServletResponse.SC_OK);
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
@@ -136,7 +149,13 @@
res.setStatus(HttpServletResponse.SC_OK);
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
@@ -155,7 +174,13 @@
doReturn(GitBasicAuthPolicy.LDAP).when(authConfig).getGitBasicAuthPolicy();
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
@@ -175,7 +200,13 @@
res.setStatus(HttpServletResponse.SC_OK);
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
@@ -216,7 +247,13 @@
doReturn(GitBasicAuthPolicy.LDAP).when(authConfig).getGitBasicAuthPolicy();
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
@@ -235,7 +272,13 @@
doReturn(GitBasicAuthPolicy.LDAP).when(authConfig).getGitBasicAuthPolicy();
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
@@ -255,7 +298,13 @@
res.setStatus(HttpServletResponse.SC_OK);
ProjectBasicAuthFilter basicAuthFilter =
- new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+ new ProjectBasicAuthFilter(
+ webSessionItem,
+ accountCache,
+ accountManager,
+ authConfig,
+ authRequestFactory,
+ pwdVerifier);
basicAuthFilter.doFilter(req, res, chain);
}
@@ -278,9 +327,13 @@
}
private void initMockedUsernamePasswordExternalId() {
- doReturn(ImmutableSet.builder().add(AUTH_USER_PASSWORD_EXTERNAL_ID).build())
- .when(accountState)
- .externalIds();
+ ExternalId extId =
+ extIdFactory.createWithPassword(
+ extIdKeyFactory.create(ExternalId.SCHEME_USERNAME, AUTH_USER),
+ AUTH_ACCOUNT_ID,
+ null,
+ AUTH_PASSWORD);
+ doReturn(ImmutableSet.builder().add(extId).build()).when(accountState).externalIds();
}
private void requestBasicAuth(FakeHttpServletRequest fakeReq) {
diff --git a/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java b/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
index 814df03..058384e 100644
--- a/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
+++ b/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
@@ -25,12 +25,26 @@
import com.google.gerrit.server.account.externalids.AllExternalIds.Serializer;
import com.google.gerrit.server.cache.proto.Cache.AllExternalIdsProto;
import com.google.gerrit.server.cache.proto.Cache.AllExternalIdsProto.ExternalIdProto;
+import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.TypeLiteral;
+import java.lang.reflect.Type;
import java.util.Arrays;
import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
public class AllExternalIdsTest {
+ private AuthConfig authConfig;
+ private ExternalIdFactory externalIdFactory;
+
+ @Before
+ public void setUp() throws Exception {
+ authConfig = Mockito.mock(AuthConfig.class);
+ Mockito.when(authConfig.isUserNameCaseInsensitive()).thenReturn(false);
+ externalIdFactory = new ExternalIdFactory(new ExternalIdKeyFactory(authConfig));
+ }
+
@Test
public void serializeEmptyExternalIds() throws Exception {
assertRoundTrip(allExternalIds(), AllExternalIdsProto.getDefaultInstance());
@@ -42,10 +56,10 @@
Account.Id accountId2 = Account.id(1002);
assertRoundTrip(
allExternalIds(
- ExternalId.create("scheme1", "id1", accountId1),
- ExternalId.create("scheme2", "id2", accountId1),
- ExternalId.create("scheme2", "id3", accountId2),
- ExternalId.create("scheme3", "id4", accountId2)),
+ externalIdFactory.create("scheme1", "id1", accountId1),
+ externalIdFactory.create("scheme2", "id2", accountId1),
+ externalIdFactory.create("scheme2", "id3", accountId2),
+ externalIdFactory.create("scheme3", "id4", accountId2)),
AllExternalIdsProto.newBuilder()
.addExternalId(
ExternalIdProto.newBuilder().setKey("scheme1:id1").setAccountId(1001).build())
@@ -61,7 +75,7 @@
@Test
public void serializeExternalIdWithEmail() throws Exception {
assertRoundTrip(
- allExternalIds(ExternalId.createEmail(Account.id(1001), "foo@example.com")),
+ allExternalIds(externalIdFactory.createEmail(Account.id(1001), "foo@example.com")),
AllExternalIdsProto.newBuilder()
.addExternalId(
ExternalIdProto.newBuilder()
@@ -75,7 +89,7 @@
public void serializeExternalIdWithPassword() throws Exception {
assertRoundTrip(
allExternalIds(
- ExternalId.create("scheme", "id", Account.id(1001), null, "hashed password")),
+ externalIdFactory.create("scheme", "id", Account.id(1001), null, "hashed password")),
AllExternalIdsProto.newBuilder()
.addExternalId(
ExternalIdProto.newBuilder()
@@ -89,8 +103,8 @@
public void serializeExternalIdWithBlobId() throws Exception {
assertRoundTrip(
allExternalIds(
- ExternalId.create(
- ExternalId.create("scheme", "id", Account.id(1001)),
+ externalIdFactory.create(
+ externalIdFactory.create("scheme", "id", Account.id(1001)),
ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))),
AllExternalIdsProto.newBuilder()
.addExternalId(
@@ -121,12 +135,14 @@
public void externalIdMethods() {
assertThatSerializedClass(ExternalId.class)
.hasAutoValueMethods(
- ImmutableMap.of(
- "key", ExternalId.Key.class,
- "accountId", Account.Id.class,
- "email", String.class,
- "password", String.class,
- "blobId", ObjectId.class));
+ ImmutableMap.<String, Type>builder()
+ .put("key", ExternalId.Key.class)
+ .put("accountId", Account.Id.class)
+ .put("isCaseInsensitive", boolean.class)
+ .put("email", String.class)
+ .put("password", String.class)
+ .put("blobId", ObjectId.class)
+ .build());
}
private static AllExternalIds allExternalIds(ExternalId... externalIds) {
diff --git a/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java b/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java
index 5717e78..fab90d4 100644
--- a/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java
+++ b/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java
@@ -28,6 +28,7 @@
import com.google.gerrit.server.account.externalids.testing.ExternalIdTestUtil;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
+import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -56,12 +57,18 @@
private GitRepositoryManager repoManager = new InMemoryRepositoryManager();
private ExternalIdReader externalIdReader;
private ExternalIdReader externalIdReaderSpy;
+ private AuthConfig authConfig;
+ private ExternalIdFactory externalIdFactory;
@Before
public void setUp() throws Exception {
+ authConfig = Mockito.mock(AuthConfig.class);
+ Mockito.when(authConfig.isUserNameCaseInsensitive()).thenReturn(false);
+ externalIdFactory = new ExternalIdFactory(new ExternalIdKeyFactory(authConfig));
externalIdCache = CacheBuilder.newBuilder().build();
repoManager.createRepository(ALL_USERS).close();
- externalIdReader = new ExternalIdReader(repoManager, ALL_USERS, new DisabledMetricMaker());
+ externalIdReader =
+ new ExternalIdReader(repoManager, ALL_USERS, new DisabledMetricMaker(), externalIdFactory);
externalIdReaderSpy = Mockito.spy(externalIdReader);
loader = createLoader(true);
}
@@ -151,7 +158,8 @@
ObjectId head =
modifyExternalId(
externalId(1, 1),
- ExternalId.create("fooschema", "bar1", Account.id(1), "foo@bar.com", "password"));
+ externalIdFactory.create(
+ "fooschema", "bar1", Account.id(1), "foo@bar.com", "password"));
assertThat(allFromGit(head).byAccount().size()).isEqualTo(1);
externalIdCache.put(firstState, allFromGit(firstState));
@@ -212,7 +220,8 @@
externalIdReaderSpy,
Providers.of(externalIdCache),
new DisabledMetricMaker(),
- cfg);
+ cfg,
+ externalIdFactory);
}
private AllExternalIds allFromGit(ObjectId revision) throws Exception {
@@ -256,13 +265,14 @@
}
private ExternalId externalId(int key, int accountId) {
- return ExternalId.create("fooschema", "bar" + key, Account.id(accountId));
+ return externalIdFactory.create("fooschema", "bar" + key, Account.id(accountId));
}
private ObjectId performExternalIdUpdate(Consumer<ExternalIdNotes> update) throws Exception {
try (Repository repo = repoManager.openRepository(ALL_USERS)) {
PersonIdent updater = new PersonIdent("Foo bar", "foo@bar.com");
- ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(ALL_USERS, repo);
+ ExternalIdNotes extIdNotes =
+ ExternalIdNotes.loadNoCacheUpdate(ALL_USERS, repo, externalIdFactory);
update.accept(extIdNotes);
try (MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, repo)) {
diff --git a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
index cd28ac4..5e3be9a 100644
--- a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
+++ b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
@@ -71,6 +71,7 @@
@Inject private ProjectConfig.Factory projectConfigFactory;
@Inject private GerritApi gApi;
@Inject private ProjectOperations projectOperations;
+ @Inject private AuthRequest.Factory authRequestFactory;
private LifecycleManager lifecycle;
private Account.Id userId;
@@ -87,7 +88,7 @@
lifecycle.start();
schemaCreator.create();
- userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
+ userId = accountManager.authenticate(authRequestFactory.createForUser("user")).getAccountId();
user = userFactory.create(userId);
requestContext.setContext(() -> user);
diff --git a/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java b/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java
index 92a5fbe..d16efc3 100644
--- a/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java
@@ -56,25 +56,33 @@
.build();
ExternalId extId1 =
ExternalId.create(
- ExternalId.Key.create(ExternalId.SCHEME_MAILTO, "foo.bar@example.com"),
+ ExternalId.Key.create(ExternalId.SCHEME_MAILTO, "foo.bar@example.com", false),
id,
"foo.bar@example.com",
null,
ObjectId.fromString("1b9a0cf038ea38a0ab08617c39aa8e28413a27ca"));
ExternalId extId2 =
ExternalId.create(
- ExternalId.Key.create(ExternalId.SCHEME_USERNAME, "foo"),
+ ExternalId.Key.create(ExternalId.SCHEME_USERNAME, "foo", false),
id,
null,
"secret",
ObjectId.fromString("5b3a73dc9a668a5b89b5f049225261e3e3291d1a"));
+ ExternalId extId3 =
+ ExternalId.create(
+ ExternalId.Key.create(ExternalId.SCHEME_USERNAME, "Bar", true),
+ id,
+ null,
+ "secret",
+ ObjectId.fromString("483ea804e84282e15ddcdd1d15a797eb4796a760"));
List<String> values =
toStrings(
AccountField.EXTERNAL_ID_STATE.get(
- AccountState.forAccount(account, ImmutableSet.of(extId1, extId2))));
+ AccountState.forAccount(account, ImmutableSet.of(extId1, extId2, extId3))));
String expectedValue1 = extId1.key().sha1().name() + ":" + extId1.blobId().name();
String expectedValue2 = extId2.key().sha1().name() + ":" + extId2.blobId().name();
- assertThat(values).containsExactly(expectedValue1, expectedValue2);
+ String expectedValue3 = extId3.key().sha1().name() + ":" + extId3.blobId().name();
+ assertThat(values).containsExactly(expectedValue1, expectedValue2, expectedValue3);
}
private List<String> toStrings(Iterable<byte[]> values) {
diff --git a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
index 1035fe7..5daf68e 100644
--- a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
+++ b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
@@ -64,6 +64,7 @@
@Inject protected AllProjectsName allProjects;
@Inject private CommitsCollection commits;
@Inject private ProjectOperations projectOperations;
+ @Inject private AuthRequest.Factory authRequestFactory;
private TestRepository<InMemoryRepository> repo;
private Project.NameKey project;
@@ -72,7 +73,8 @@
public void setUp() throws Exception {
setUpPermissions();
- Account.Id user = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
+ Account.Id user =
+ accountManager.authenticate(authRequestFactory.createForUser("user")).getAccountId();
testEnvironment.setApiUser(user);
project = projectOperations.newProject().create();
repo = new TestRepository<>(repoManager.openRepository(project));
diff --git a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index b7be40b..16f7199 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -65,6 +65,7 @@
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
@@ -139,6 +140,10 @@
@Inject protected ExternalIds externalIds;
+ @Inject private ExternalIdKeyFactory externalIdKeyFactory;
+
+ @Inject protected AuthRequest.Factory authRequestFactory;
+
protected LifecycleManager lifecycle;
protected Injector injector;
protected AccountInfo currentUserInfo;
@@ -659,7 +664,7 @@
List<AccountExternalIdInfo> externalIdInfos = gApi.accounts().self().getExternalIds();
List<ByteArrayWrapper> blobs = new ArrayList<>();
for (AccountExternalIdInfo info : externalIdInfos) {
- Optional<ExternalId> extId = externalIds.get(ExternalId.Key.parse(info.identity));
+ Optional<ExternalId> extId = externalIds.get(externalIdKeyFactory.parse(info.identity));
assertThat(extId).isPresent();
blobs.add(new ByteArrayWrapper(extId.get().toByteArray()));
}
@@ -772,9 +777,10 @@
private Account.Id createAccount(String username, String fullName, String email, boolean active)
throws Exception {
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
- Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
+ Account.Id id =
+ accountManager.authenticate(authRequestFactory.createForUser(username)).getAccountId();
if (email != null) {
- accountManager.link(id, AuthRequest.forEmail(email));
+ accountManager.link(id, authRequestFactory.createForEmail(email));
}
accountsUpdate
.get()
@@ -791,7 +797,7 @@
private void addEmails(AccountInfo account, String... emails) throws Exception {
Account.Id id = Account.id(account._accountId);
for (String email : emails) {
- accountManager.link(id, AuthRequest.forEmail(email));
+ accountManager.link(id, authRequestFactory.createForEmail(email));
}
accountIndexer.index(id);
}
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index a5e7ede..92cbc41 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -105,7 +105,7 @@
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.VersionedAccountQueries;
-import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.change.NotifyResolver;
@@ -192,6 +192,8 @@
@Inject protected ProjectCache projectCache;
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected IdentifiedUser.GenericFactory identifiedUserFactory;
+ @Inject protected AuthRequest.Factory authRequestFactory;
+ @Inject protected ExternalIdFactory externalIdFactory;
@Inject private ProjectConfig.Factory projectConfigFactory;
@Inject private ProjectOperations projectOperations;
@@ -226,14 +228,16 @@
protected void setUpDatabase() throws Exception {
schemaCreator.create();
- userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
+ userId = accountManager.authenticate(authRequestFactory.createForUser("user")).getAccountId();
String email = "user@example.com";
accountsUpdate
.get()
.update(
"Add Email",
userId,
- u -> u.addExternalId(ExternalId.createEmail(userId, email)).setPreferredEmail(email));
+ u ->
+ u.addExternalId(externalIdFactory.createEmail(userId, email))
+ .setPreferredEmail(email));
resetUser();
}
@@ -417,7 +421,7 @@
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
Change change2 = insert(repo, newChange(repo), user2);
// No private changes.
@@ -585,7 +589,7 @@
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
Change change2 = insert(repo, newChange(repo), user2);
assertQuery("is:owner", change1);
@@ -699,7 +703,7 @@
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
Change change2 = insert(repo, newChange(repo), user2);
Change change3 = insert(repo, newChange(repo), user2);
gApi.changes().id(change3.getId().get()).current().review(ReviewInput.approve());
@@ -989,7 +993,7 @@
@Test
public void byLabel() throws Exception {
- accountManager.authenticate(AuthRequest.forUser("anotheruser"));
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser"));
TestRepository<Repo> repo = createProject("repo");
ChangeInserter ins = newChange(repo);
ChangeInserter ins2 = newChange(repo);
@@ -1203,7 +1207,7 @@
}
private Account.Id createAccount(String name) throws Exception {
- return accountManager.authenticate(AuthRequest.forUser(name)).getAccountId();
+ return accountManager.authenticate(authRequestFactory.createForUser(name)).getAccountId();
}
@Test
@@ -1363,7 +1367,7 @@
TestRepository<Repo> repo = createProject("repo");
Change change = insert(repo, newChange(repo), userId);
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
for (int i = 0; i < 5; i++) {
insert(repo, newChange(repo), user2);
}
@@ -1376,7 +1380,7 @@
public void filterOutAllResults() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
for (int i = 0; i < 5; i++) {
insert(repo, newChange(repo), user2);
}
@@ -2162,11 +2166,11 @@
Account.Id user3 = createAccount("user3");
// Explicitly authenticate user2 and user3 so that display name gets set
- AuthRequest authRequest = AuthRequest.forUser("user2");
+ AuthRequest authRequest = authRequestFactory.createForUser("user2");
authRequest.setDisplayName("Another User");
authRequest.setEmailAddress("user2@example.com");
accountManager.authenticate(authRequest);
- authRequest = AuthRequest.forUser("user3");
+ authRequest = authRequestFactory.createForUser("user3");
authRequest.setDisplayName("Another User");
authRequest.setEmailAddress("user3@example.com");
accountManager.authenticate(authRequest);
@@ -2237,7 +2241,10 @@
Change change2 = insert(repo, newChange(repo));
int user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId().get();
+ accountManager
+ .authenticate(authRequestFactory.createForUser("anotheruser"))
+ .getAccountId()
+ .get();
ReviewInput input = new ReviewInput();
input.message = "toplevel";
@@ -2318,7 +2325,7 @@
gApi.changes().id(change2.getId().get()).current().createDraft(in);
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
assertQuery("has:draft", change2, change1);
@@ -2375,7 +2382,7 @@
gApi.accounts().self().starChange(change2.getId().toString());
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
assertQuery("has:star", change2, change1);
assertQuery("star:star", change2, change1);
@@ -2393,7 +2400,7 @@
Change change3 = insert(repo, newChange(repo));
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
requestContext.setContext(newRequestContext(user2));
gApi.accounts().self().starChange(change1.getId().toString());
@@ -2415,7 +2422,7 @@
public void byIgnore() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
Change change1 = insert(repo, newChange(repo), user2);
Change change2 = insert(repo, newChange(repo), user2);
@@ -2438,7 +2445,7 @@
Change change1 = insert(repo, newChange(repo));
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
Change change2 = insert(repo, newChange(repo), user2);
ReviewInput input = new ReviewInput();
@@ -2556,7 +2563,7 @@
gApi.changes().id(change1.getId().get()).current().review(new ReviewInput().message("comment"));
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
requestContext.setContext(newRequestContext(user2));
gApi.changes().id(change2.getId().get()).current().review(new ReviewInput().message("comment"));
@@ -2622,7 +2629,7 @@
public void byReviewed() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Account.Id otherUser =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChange(repo));
@@ -2642,9 +2649,12 @@
@Test
public void reviewerin() throws Exception {
- Account.Id user1 = accountManager.authenticate(AuthRequest.forUser("user1")).getAccountId();
- Account.Id user2 = accountManager.authenticate(AuthRequest.forUser("user2")).getAccountId();
- Account.Id user3 = accountManager.authenticate(AuthRequest.forUser("user3")).getAccountId();
+ Account.Id user1 =
+ accountManager.authenticate(authRequestFactory.createForUser("user1")).getAccountId();
+ Account.Id user2 =
+ accountManager.authenticate(authRequestFactory.createForUser("user2")).getAccountId();
+ Account.Id user3 =
+ accountManager.authenticate(authRequestFactory.createForUser("user3")).getAccountId();
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo));
@@ -3442,7 +3452,7 @@
AttentionSetInput input = new AttentionSetInput(userId.toString(), "reason 1");
gApi.changes().id(change.getChangeId()).addToAttentionSet(input);
Account.Id user2Id =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
// Add the second user as cc to ensure that user took part of the change and can be added to the
// attention set.
@@ -3494,7 +3504,7 @@
.isEqualTo("Unknown named destination: foo");
Account.Id anotherUserId =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
String destination1 = "refs/heads/master\trepo1";
String destination2 = "refs/heads/master\trepo2";
String destination3 = "refs/heads/master\trepo1\nrefs/heads/master\trepo2";
@@ -3571,7 +3581,7 @@
Change change2 = insert(repo, newChangeForBranch(repo, "stable"));
Account.Id anotherUserId =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
String queryListText =
"query1\tproject:repo\n"
+ "query2\tproject:repo status:open\n"
@@ -3673,7 +3683,7 @@
@Test
public void selfSucceedsForInactiveAccount() throws Exception {
Account.Id user2 =
- accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
TestRepository<Repo> repo = createProject("repo");
Change change = insert(repo, newChange(repo));
@@ -4053,9 +4063,10 @@
private Account.Id createAccount(String username, String fullName, String email, boolean active)
throws Exception {
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
- Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
+ Account.Id id =
+ accountManager.authenticate(authRequestFactory.createForUser(username)).getAccountId();
if (email != null) {
- accountManager.link(id, AuthRequest.forEmail(email));
+ accountManager.link(id, authRequestFactory.createForEmail(email));
}
accountsUpdate
.get()
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index f392747..568b5a0 100644
--- a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -106,6 +106,8 @@
@Inject protected GroupIndexCollection indexes;
+ @Inject protected AuthRequest.Factory authRequestFactory;
+
@Inject private GroupIndexCollection groupIndexes;
protected LifecycleManager lifecycle;
@@ -397,9 +399,10 @@
private Account.Id createAccountOutsideRequestContext(
String username, String fullName, String email, boolean active) throws Exception {
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
- Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
+ Account.Id id =
+ accountManager.authenticate(authRequestFactory.createForUser(username)).getAccountId();
if (email != null) {
- accountManager.link(id, AuthRequest.forEmail(email));
+ accountManager.link(id, authRequestFactory.createForEmail(email));
}
accountsUpdate
.get()
diff --git a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
index 2317c7e..60d1655 100644
--- a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -104,6 +104,8 @@
@Inject protected AllUsersName allUsers;
+ @Inject protected AuthRequest.Factory authRequestFactory;
+
protected LifecycleManager lifecycle;
protected Injector injector;
protected AccountInfo currentUserInfo;
@@ -309,9 +311,10 @@
private Account.Id createAccount(String username, String fullName, String email, boolean active)
throws Exception {
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
- Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
+ Account.Id id =
+ accountManager.authenticate(authRequestFactory.createForUser(username)).getAccountId();
if (email != null) {
- accountManager.link(id, AuthRequest.forEmail(email));
+ accountManager.link(id, authRequestFactory.createForEmail(email));
}
accountsUpdate
.get()
diff --git a/proto/cache.proto b/proto/cache.proto
index 4f4c838..16e5e95 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -268,13 +268,14 @@
// com.google.gerrit.server.account.externalids.AllExternalIds.
// Next ID: 2
message AllExternalIdsProto {
- // Next ID: 6
+ // Next ID: 7
message ExternalIdProto {
string key = 1;
int32 accountId = 2;
string email = 3;
string password = 4;
bytes blobId = 5;
+ bool isCaseInsensitive = 6;
}
repeated ExternalIdProto external_id = 1;
}