Migrate external IDs to NoteDb (part 3)
This is the third part of migrating external IDs from ReviewDb to NoteDb. This change: * changes the code to always read external IDs from NoteDb (the user.readExternalIdsFromGit configuration parameter is removed) * bumps the database schema version * deletes the database table for external IDs Pushing to the refs/meta/external-ids branch is still prevented by a commit validator. Since all external IDs are now in NoteDb only we could allow pushing to refs/meta/external-ids. However we would still like to do validation of the branch content and reject invalid content (e.g. invalid Git config files, usage of non-existing account IDs etc.) and such a validator is not implemented yet (but can be implemented in a follow-up change). Change-Id: Id9e5574a1d8d82f4f48fbb0b6dadc0e27d138a28 Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
		@@ -109,7 +109,7 @@ public class AccountCreator {
 | 
				
			|||||||
      if (email != null) {
 | 
					      if (email != null) {
 | 
				
			||||||
        extIds.add(ExternalId.createEmail(id, email));
 | 
					        extIds.add(ExternalId.createEmail(id, email));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      externalIdsUpdate.create().insert(db, extIds);
 | 
					      externalIdsUpdate.create().insert(extIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Account a = new Account(id, TimeUtil.nowTs());
 | 
					      Account a = new Account(id, TimeUtil.nowTs());
 | 
				
			||||||
      a.setFullName(fullName);
 | 
					      a.setFullName(fullName);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,9 +168,9 @@ public class AccountIT extends AbstractDaemonTest {
 | 
				
			|||||||
      // savedExternalIds is null when we don't run SSH tests and the assume in
 | 
					      // savedExternalIds is null when we don't run SSH tests and the assume in
 | 
				
			||||||
      // @Before in AbstractDaemonTest prevents this class' @Before method from
 | 
					      // @Before in AbstractDaemonTest prevents this class' @Before method from
 | 
				
			||||||
      // being executed.
 | 
					      // being executed.
 | 
				
			||||||
      externalIdsUpdate.delete(db, getExternalIds(admin));
 | 
					      externalIdsUpdate.delete(getExternalIds(admin));
 | 
				
			||||||
      externalIdsUpdate.delete(db, getExternalIds(user));
 | 
					      externalIdsUpdate.delete(getExternalIds(user));
 | 
				
			||||||
      externalIdsUpdate.insert(db, savedExternalIds);
 | 
					      externalIdsUpdate.insert(savedExternalIds);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    accountCache.evict(admin.getId());
 | 
					    accountCache.evict(admin.getId());
 | 
				
			||||||
    accountCache.evict(user.getId());
 | 
					    accountCache.evict(user.getId());
 | 
				
			||||||
@@ -533,7 +533,7 @@ public class AccountIT extends AbstractDaemonTest {
 | 
				
			|||||||
        ImmutableList.of(
 | 
					        ImmutableList.of(
 | 
				
			||||||
            ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id, email),
 | 
					            ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id, email),
 | 
				
			||||||
            ExternalId.createWithEmail(ExternalId.Key.parse(extId2), admin.id, email));
 | 
					            ExternalId.createWithEmail(ExternalId.Key.parse(extId2), admin.id, email));
 | 
				
			||||||
    externalIdsUpdateFactory.create().insert(db, extIds);
 | 
					    externalIdsUpdateFactory.create().insert(extIds);
 | 
				
			||||||
    accountCache.evict(admin.id);
 | 
					    accountCache.evict(admin.id);
 | 
				
			||||||
    accountIndexedCounter.assertReindexOf(admin);
 | 
					    accountIndexedCounter.assertReindexOf(admin);
 | 
				
			||||||
    assertThat(
 | 
					    assertThat(
 | 
				
			||||||
@@ -588,7 +588,7 @@ public class AccountIT extends AbstractDaemonTest {
 | 
				
			|||||||
    String email = "foo.bar@example.com";
 | 
					    String email = "foo.bar@example.com";
 | 
				
			||||||
    externalIdsUpdateFactory
 | 
					    externalIdsUpdateFactory
 | 
				
			||||||
        .create()
 | 
					        .create()
 | 
				
			||||||
        .insert(db, ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email));
 | 
					        .insert(ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email));
 | 
				
			||||||
    accountCache.evict(admin.id);
 | 
					    accountCache.evict(admin.id);
 | 
				
			||||||
    assertEmail(byEmailCache.get(email), admin);
 | 
					    assertEmail(byEmailCache.get(email), admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -830,7 +830,7 @@ public class AccountIT extends AbstractDaemonTest {
 | 
				
			|||||||
  public void addOtherUsersGpgKey_Conflict() throws Exception {
 | 
					  public void addOtherUsersGpgKey_Conflict() throws Exception {
 | 
				
			||||||
    // Both users have a matching external ID for this key.
 | 
					    // Both users have a matching external ID for this key.
 | 
				
			||||||
    addExternalIdEmail(admin, "test5@example.com");
 | 
					    addExternalIdEmail(admin, "test5@example.com");
 | 
				
			||||||
    externalIdsUpdate.insert(db, ExternalId.create("foo", "myId", user.getId()));
 | 
					    externalIdsUpdate.insert(ExternalId.create("foo", "myId", user.getId()));
 | 
				
			||||||
    accountCache.evict(user.getId());
 | 
					    accountCache.evict(user.getId());
 | 
				
			||||||
    accountIndexedCounter.assertReindexOf(user);
 | 
					    accountIndexedCounter.assertReindexOf(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1043,7 +1043,7 @@ public class AccountIT extends AbstractDaemonTest {
 | 
				
			|||||||
        expected.transform(k -> BaseEncoding.base16().encode(k.getPublicKey().getFingerprint()));
 | 
					        expected.transform(k -> BaseEncoding.base16().encode(k.getPublicKey().getFingerprint()));
 | 
				
			||||||
    Iterable<String> actualFps =
 | 
					    Iterable<String> actualFps =
 | 
				
			||||||
        externalIds
 | 
					        externalIds
 | 
				
			||||||
            .byAccount(db, currAccountId, SCHEME_GPGKEY)
 | 
					            .byAccount(currAccountId, SCHEME_GPGKEY)
 | 
				
			||||||
            .stream()
 | 
					            .stream()
 | 
				
			||||||
            .map(e -> e.key().id())
 | 
					            .map(e -> e.key().id())
 | 
				
			||||||
            .collect(toSet());
 | 
					            .collect(toSet());
 | 
				
			||||||
@@ -1072,7 +1072,7 @@ public class AccountIT extends AbstractDaemonTest {
 | 
				
			|||||||
  private void addExternalIdEmail(TestAccount account, String email) throws Exception {
 | 
					  private void addExternalIdEmail(TestAccount account, String email) throws Exception {
 | 
				
			||||||
    checkNotNull(email);
 | 
					    checkNotNull(email);
 | 
				
			||||||
    externalIdsUpdate.insert(
 | 
					    externalIdsUpdate.insert(
 | 
				
			||||||
        db, ExternalId.createWithEmail(name("test"), email, account.getId(), email));
 | 
					        ExternalId.createWithEmail(name("test"), email, account.getId(), email));
 | 
				
			||||||
    // Clear saved AccountState and ExternalIds.
 | 
					    // Clear saved AccountState and ExternalIds.
 | 
				
			||||||
    accountCache.evict(account.getId());
 | 
					    accountCache.evict(account.getId());
 | 
				
			||||||
    accountIndexedCounter.assertReindexOf(account);
 | 
					    accountIndexedCounter.assertReindexOf(account);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,6 @@ import com.github.rholder.retry.RetryerBuilder;
 | 
				
			|||||||
import com.github.rholder.retry.StopStrategies;
 | 
					import com.github.rholder.retry.StopStrategies;
 | 
				
			||||||
import com.google.common.collect.ImmutableList;
 | 
					import com.google.common.collect.ImmutableList;
 | 
				
			||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
 | 
					import com.google.gerrit.acceptance.AbstractDaemonTest;
 | 
				
			||||||
import com.google.gerrit.acceptance.GerritConfig;
 | 
					 | 
				
			||||||
import com.google.gerrit.acceptance.PushOneCommit;
 | 
					import com.google.gerrit.acceptance.PushOneCommit;
 | 
				
			||||||
import com.google.gerrit.acceptance.RestResponse;
 | 
					import com.google.gerrit.acceptance.RestResponse;
 | 
				
			||||||
import com.google.gerrit.acceptance.Sandboxed;
 | 
					import com.google.gerrit.acceptance.Sandboxed;
 | 
				
			||||||
@@ -209,7 +208,6 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @GerritConfig(name = "user.readExternalIdsFromGit", value = "true")
 | 
					 | 
				
			||||||
  public void readExternalIdsWhenInvalidExternalIdsExist() throws Exception {
 | 
					  public void readExternalIdsWhenInvalidExternalIdsExist() throws Exception {
 | 
				
			||||||
    allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
 | 
					    allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
 | 
				
			||||||
    resetCurrentApiUser();
 | 
					    resetCurrentApiUser();
 | 
				
			||||||
@@ -217,15 +215,15 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
    insertValidExternalIds();
 | 
					    insertValidExternalIds();
 | 
				
			||||||
    insertInvalidButParsableExternalIds();
 | 
					    insertInvalidButParsableExternalIds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Set<ExternalId> parseableExtIds = externalIds.all(db);
 | 
					    Set<ExternalId> parseableExtIds = externalIds.all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    insertNonParsableExternalIds();
 | 
					    insertNonParsableExternalIds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Set<ExternalId> extIds = externalIds.all(db);
 | 
					    Set<ExternalId> extIds = externalIds.all();
 | 
				
			||||||
    assertThat(extIds).containsExactlyElementsIn(parseableExtIds);
 | 
					    assertThat(extIds).containsExactlyElementsIn(parseableExtIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (ExternalId parseableExtId : parseableExtIds) {
 | 
					    for (ExternalId parseableExtId : parseableExtIds) {
 | 
				
			||||||
      ExternalId extId = externalIds.get(db, parseableExtId.key());
 | 
					      ExternalId extId = externalIds.get(parseableExtId.key());
 | 
				
			||||||
      assertThat(extId).isEqualTo(parseableExtId);
 | 
					      assertThat(extId).isEqualTo(parseableExtId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -270,13 +268,12 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // create valid external IDs
 | 
					    // create valid external IDs
 | 
				
			||||||
    u.insert(
 | 
					    u.insert(
 | 
				
			||||||
        db,
 | 
					 | 
				
			||||||
        ExternalId.createWithPassword(
 | 
					        ExternalId.createWithPassword(
 | 
				
			||||||
            ExternalId.Key.parse(nextId(scheme, i)),
 | 
					            ExternalId.Key.parse(nextId(scheme, i)),
 | 
				
			||||||
            admin.id,
 | 
					            admin.id,
 | 
				
			||||||
            "admin.other@example.com",
 | 
					            "admin.other@example.com",
 | 
				
			||||||
            "secret-password"));
 | 
					            "secret-password"));
 | 
				
			||||||
    u.insert(db, createExternalIdWithOtherCaseEmail(nextId(scheme, i)));
 | 
					    u.insert(createExternalIdWithOtherCaseEmail(nextId(scheme, i)));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private Set<ConsistencyProblemInfo> insertInvalidButParsableExternalIds()
 | 
					  private Set<ConsistencyProblemInfo> insertInvalidButParsableExternalIds()
 | 
				
			||||||
@@ -288,7 +285,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
    Set<ConsistencyProblemInfo> expectedProblems = new HashSet<>();
 | 
					    Set<ConsistencyProblemInfo> expectedProblems = new HashSet<>();
 | 
				
			||||||
    ExternalId extIdForNonExistingAccount =
 | 
					    ExternalId extIdForNonExistingAccount =
 | 
				
			||||||
        createExternalIdForNonExistingAccount(nextId(scheme, i));
 | 
					        createExternalIdForNonExistingAccount(nextId(scheme, i));
 | 
				
			||||||
    u.insert(db, extIdForNonExistingAccount);
 | 
					    u.insert(extIdForNonExistingAccount);
 | 
				
			||||||
    expectedProblems.add(
 | 
					    expectedProblems.add(
 | 
				
			||||||
        consistencyError(
 | 
					        consistencyError(
 | 
				
			||||||
            "External ID '"
 | 
					            "External ID '"
 | 
				
			||||||
@@ -297,7 +294,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
                + extIdForNonExistingAccount.accountId().get()));
 | 
					                + extIdForNonExistingAccount.accountId().get()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ExternalId extIdWithInvalidEmail = createExternalIdWithInvalidEmail(nextId(scheme, i));
 | 
					    ExternalId extIdWithInvalidEmail = createExternalIdWithInvalidEmail(nextId(scheme, i));
 | 
				
			||||||
    u.insert(db, extIdWithInvalidEmail);
 | 
					    u.insert(extIdWithInvalidEmail);
 | 
				
			||||||
    expectedProblems.add(
 | 
					    expectedProblems.add(
 | 
				
			||||||
        consistencyError(
 | 
					        consistencyError(
 | 
				
			||||||
            "External ID '"
 | 
					            "External ID '"
 | 
				
			||||||
@@ -306,7 +303,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
                + extIdWithInvalidEmail.email()));
 | 
					                + extIdWithInvalidEmail.email()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ExternalId extIdWithDuplicateEmail = createExternalIdWithDuplicateEmail(nextId(scheme, i));
 | 
					    ExternalId extIdWithDuplicateEmail = createExternalIdWithDuplicateEmail(nextId(scheme, i));
 | 
				
			||||||
    u.insert(db, extIdWithDuplicateEmail);
 | 
					    u.insert(extIdWithDuplicateEmail);
 | 
				
			||||||
    expectedProblems.add(
 | 
					    expectedProblems.add(
 | 
				
			||||||
        consistencyError(
 | 
					        consistencyError(
 | 
				
			||||||
            "Email '"
 | 
					            "Email '"
 | 
				
			||||||
@@ -318,7 +315,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
                + "'"));
 | 
					                + "'"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ExternalId extIdWithBadPassword = createExternalIdWithBadPassword("admin-username");
 | 
					    ExternalId extIdWithBadPassword = createExternalIdWithBadPassword("admin-username");
 | 
				
			||||||
    u.insert(db, extIdWithBadPassword);
 | 
					    u.insert(extIdWithBadPassword);
 | 
				
			||||||
    expectedProblems.add(
 | 
					    expectedProblems.add(
 | 
				
			||||||
        consistencyError(
 | 
					        consistencyError(
 | 
				
			||||||
            "External ID '"
 | 
					            "External ID '"
 | 
				
			||||||
@@ -508,7 +505,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
            () -> {
 | 
					            () -> {
 | 
				
			||||||
              if (!doneBgUpdate.getAndSet(true)) {
 | 
					              if (!doneBgUpdate.getAndSet(true)) {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                  extIdsUpdate.create().insert(db, ExternalId.create(barId, admin.id));
 | 
					                  extIdsUpdate.create().insert(ExternalId.create(barId, admin.id));
 | 
				
			||||||
                } catch (IOException | ConfigInvalidException | OrmException e) {
 | 
					                } catch (IOException | ConfigInvalidException | OrmException e) {
 | 
				
			||||||
                  // Ignore, the successful insertion of the external ID is asserted later
 | 
					                  // Ignore, the successful insertion of the external ID is asserted later
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -516,11 +513,11 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            retryer);
 | 
					            retryer);
 | 
				
			||||||
    assertThat(doneBgUpdate.get()).isFalse();
 | 
					    assertThat(doneBgUpdate.get()).isFalse();
 | 
				
			||||||
    update.insert(db, ExternalId.create(fooId, admin.id));
 | 
					    update.insert(ExternalId.create(fooId, admin.id));
 | 
				
			||||||
    assertThat(doneBgUpdate.get()).isTrue();
 | 
					    assertThat(doneBgUpdate.get()).isTrue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assertThat(externalIds.get(db, fooId)).isNotNull();
 | 
					    assertThat(externalIds.get(fooId)).isNotNull();
 | 
				
			||||||
    assertThat(externalIds.get(db, barId)).isNotNull();
 | 
					    assertThat(externalIds.get(barId)).isNotNull();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
@@ -544,7 +541,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
              try {
 | 
					              try {
 | 
				
			||||||
                extIdsUpdate
 | 
					                extIdsUpdate
 | 
				
			||||||
                    .create()
 | 
					                    .create()
 | 
				
			||||||
                    .insert(db, ExternalId.create(extIdsKeys[bgCounter.getAndAdd(1)], admin.id));
 | 
					                    .insert(ExternalId.create(extIdsKeys[bgCounter.getAndAdd(1)], admin.id));
 | 
				
			||||||
              } catch (IOException | ConfigInvalidException | OrmException e) {
 | 
					              } catch (IOException | ConfigInvalidException | OrmException e) {
 | 
				
			||||||
                // Ignore, the successful insertion of the external ID is asserted later
 | 
					                // Ignore, the successful insertion of the external ID is asserted later
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
@@ -555,14 +552,14 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
                .build());
 | 
					                .build());
 | 
				
			||||||
    assertThat(bgCounter.get()).isEqualTo(0);
 | 
					    assertThat(bgCounter.get()).isEqualTo(0);
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      update.insert(db, ExternalId.create(ExternalId.Key.create("abc", "abc"), admin.id));
 | 
					      update.insert(ExternalId.create(ExternalId.Key.create("abc", "abc"), admin.id));
 | 
				
			||||||
      fail("expected LockFailureException");
 | 
					      fail("expected LockFailureException");
 | 
				
			||||||
    } catch (LockFailureException e) {
 | 
					    } catch (LockFailureException e) {
 | 
				
			||||||
      // Ignore, expected
 | 
					      // Ignore, expected
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    assertThat(bgCounter.get()).isEqualTo(extIdsKeys.length);
 | 
					    assertThat(bgCounter.get()).isEqualTo(extIdsKeys.length);
 | 
				
			||||||
    for (ExternalId.Key extIdKey : extIdsKeys) {
 | 
					    for (ExternalId.Key extIdKey : extIdsKeys) {
 | 
				
			||||||
      assertThat(externalIds.get(db, extIdKey)).isNotNull();
 | 
					      assertThat(externalIds.get(extIdKey)).isNotNull();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -570,38 +567,36 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
  public void readExternalIdWithAccountIdThatCanBeExpressedInKiB() throws Exception {
 | 
					  public void readExternalIdWithAccountIdThatCanBeExpressedInKiB() throws Exception {
 | 
				
			||||||
    ExternalId.Key extIdKey = ExternalId.Key.parse("foo:bar");
 | 
					    ExternalId.Key extIdKey = ExternalId.Key.parse("foo:bar");
 | 
				
			||||||
    Account.Id accountId = new Account.Id(1024 * 100);
 | 
					    Account.Id accountId = new Account.Id(1024 * 100);
 | 
				
			||||||
    extIdsUpdate.create().insert(db, ExternalId.create(extIdKey, accountId));
 | 
					    extIdsUpdate.create().insert(ExternalId.create(extIdKey, accountId));
 | 
				
			||||||
    ExternalId extId = externalIds.get(db, extIdKey);
 | 
					    ExternalId extId = externalIds.get(extIdKey);
 | 
				
			||||||
    assertThat(extId.accountId()).isEqualTo(accountId);
 | 
					    assertThat(extId.accountId()).isEqualTo(accountId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @GerritConfig(name = "user.readExternalIdsFromGit", value = "true")
 | 
					 | 
				
			||||||
  public void checkNoReloadAfterUpdate() throws Exception {
 | 
					  public void checkNoReloadAfterUpdate() throws Exception {
 | 
				
			||||||
    Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(db, admin.id));
 | 
					    Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id));
 | 
				
			||||||
    externalIdReader.setFailOnLoad(true);
 | 
					    externalIdReader.setFailOnLoad(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // insert external ID
 | 
					    // insert external ID
 | 
				
			||||||
    ExternalId extId = ExternalId.create("foo", "bar", admin.id);
 | 
					    ExternalId extId = ExternalId.create("foo", "bar", admin.id);
 | 
				
			||||||
    extIdsUpdate.create().insert(db, extId);
 | 
					    extIdsUpdate.create().insert(extId);
 | 
				
			||||||
    expectedExtIds.add(extId);
 | 
					    expectedExtIds.add(extId);
 | 
				
			||||||
    assertThat(externalIds.byAccount(db, admin.id)).containsExactlyElementsIn(expectedExtIds);
 | 
					    assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // update external ID
 | 
					    // update external ID
 | 
				
			||||||
    expectedExtIds.remove(extId);
 | 
					    expectedExtIds.remove(extId);
 | 
				
			||||||
    extId = ExternalId.createWithEmail("foo", "bar", admin.id, "foo.bar@example.com");
 | 
					    extId = ExternalId.createWithEmail("foo", "bar", admin.id, "foo.bar@example.com");
 | 
				
			||||||
    extIdsUpdate.create().upsert(db, extId);
 | 
					    extIdsUpdate.create().upsert(extId);
 | 
				
			||||||
    expectedExtIds.add(extId);
 | 
					    expectedExtIds.add(extId);
 | 
				
			||||||
    assertThat(externalIds.byAccount(db, admin.id)).containsExactlyElementsIn(expectedExtIds);
 | 
					    assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // delete external ID
 | 
					    // delete external ID
 | 
				
			||||||
    extIdsUpdate.create().delete(db, extId);
 | 
					    extIdsUpdate.create().delete(extId);
 | 
				
			||||||
    expectedExtIds.remove(extId);
 | 
					    expectedExtIds.remove(extId);
 | 
				
			||||||
    assertThat(externalIds.byAccount(db, admin.id)).containsExactlyElementsIn(expectedExtIds);
 | 
					    assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @GerritConfig(name = "user.readExternalIdsFromGit", value = "true")
 | 
					 | 
				
			||||||
  public void byAccountFailIfReadingExternalIdsFails() throws Exception {
 | 
					  public void byAccountFailIfReadingExternalIdsFails() throws Exception {
 | 
				
			||||||
    externalIdReader.setFailOnLoad(true);
 | 
					    externalIdReader.setFailOnLoad(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -609,11 +604,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
    insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
 | 
					    insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    exception.expect(IOException.class);
 | 
					    exception.expect(IOException.class);
 | 
				
			||||||
    externalIds.byAccount(db, admin.id);
 | 
					    externalIds.byAccount(admin.id);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @GerritConfig(name = "user.readExternalIdsFromGit", value = "true")
 | 
					 | 
				
			||||||
  public void byEmailFailIfReadingExternalIdsFails() throws Exception {
 | 
					  public void byEmailFailIfReadingExternalIdsFails() throws Exception {
 | 
				
			||||||
    externalIdReader.setFailOnLoad(true);
 | 
					    externalIdReader.setFailOnLoad(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -621,17 +615,16 @@ public class ExternalIdIT extends AbstractDaemonTest {
 | 
				
			|||||||
    insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
 | 
					    insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    exception.expect(IOException.class);
 | 
					    exception.expect(IOException.class);
 | 
				
			||||||
    externalIds.byEmail(db, admin.email);
 | 
					    externalIds.byEmail(admin.email);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @GerritConfig(name = "user.readExternalIdsFromGit", value = "true")
 | 
					 | 
				
			||||||
  public void byAccountUpdateExternalIdsBehindGerritsBack() throws Exception {
 | 
					  public void byAccountUpdateExternalIdsBehindGerritsBack() throws Exception {
 | 
				
			||||||
    Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(db, admin.id));
 | 
					    Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id));
 | 
				
			||||||
    ExternalId newExtId = ExternalId.create("foo", "bar", admin.id);
 | 
					    ExternalId newExtId = ExternalId.create("foo", "bar", admin.id);
 | 
				
			||||||
    insertExtIdBehindGerritsBack(newExtId);
 | 
					    insertExtIdBehindGerritsBack(newExtId);
 | 
				
			||||||
    expectedExternalIds.add(newExtId);
 | 
					    expectedExternalIds.add(newExtId);
 | 
				
			||||||
    assertThat(externalIds.byAccount(db, admin.id)).containsExactlyElementsIn(expectedExternalIds);
 | 
					    assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExternalIds);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void insertExtIdBehindGerritsBack(ExternalId extId) throws Exception {
 | 
					  private void insertExtIdBehindGerritsBack(ExternalId extId) throws Exception {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ import com.google.gerrit.extensions.restapi.Response;
 | 
				
			|||||||
import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
					import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
				
			||||||
import com.google.gerrit.gpg.PublicKeyStore;
 | 
					import com.google.gerrit.gpg.PublicKeyStore;
 | 
				
			||||||
import com.google.gerrit.gpg.server.DeleteGpgKey.Input;
 | 
					import com.google.gerrit.gpg.server.DeleteGpgKey.Input;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
import com.google.gerrit.server.account.AccountCache;
 | 
					import com.google.gerrit.server.account.AccountCache;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
@@ -43,7 +42,6 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
 | 
				
			|||||||
  public static class Input {}
 | 
					  public static class Input {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final Provider<PersonIdent> serverIdent;
 | 
					  private final Provider<PersonIdent> serverIdent;
 | 
				
			||||||
  private final Provider<ReviewDb> db;
 | 
					 | 
				
			||||||
  private final Provider<PublicKeyStore> storeProvider;
 | 
					  private final Provider<PublicKeyStore> storeProvider;
 | 
				
			||||||
  private final AccountCache accountCache;
 | 
					  private final AccountCache accountCache;
 | 
				
			||||||
  private final ExternalIdsUpdate.User externalIdsUpdateFactory;
 | 
					  private final ExternalIdsUpdate.User externalIdsUpdateFactory;
 | 
				
			||||||
@@ -51,12 +49,10 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
 | 
				
			|||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  DeleteGpgKey(
 | 
					  DeleteGpgKey(
 | 
				
			||||||
      @GerritPersonIdent Provider<PersonIdent> serverIdent,
 | 
					      @GerritPersonIdent Provider<PersonIdent> serverIdent,
 | 
				
			||||||
      Provider<ReviewDb> db,
 | 
					 | 
				
			||||||
      Provider<PublicKeyStore> storeProvider,
 | 
					      Provider<PublicKeyStore> storeProvider,
 | 
				
			||||||
      AccountCache accountCache,
 | 
					      AccountCache accountCache,
 | 
				
			||||||
      ExternalIdsUpdate.User externalIdsUpdateFactory) {
 | 
					      ExternalIdsUpdate.User externalIdsUpdateFactory) {
 | 
				
			||||||
    this.serverIdent = serverIdent;
 | 
					    this.serverIdent = serverIdent;
 | 
				
			||||||
    this.db = db;
 | 
					 | 
				
			||||||
    this.storeProvider = storeProvider;
 | 
					    this.storeProvider = storeProvider;
 | 
				
			||||||
    this.accountCache = accountCache;
 | 
					    this.accountCache = accountCache;
 | 
				
			||||||
    this.externalIdsUpdateFactory = externalIdsUpdateFactory;
 | 
					    this.externalIdsUpdateFactory = externalIdsUpdateFactory;
 | 
				
			||||||
@@ -70,7 +66,6 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
 | 
				
			|||||||
    externalIdsUpdateFactory
 | 
					    externalIdsUpdateFactory
 | 
				
			||||||
        .create()
 | 
					        .create()
 | 
				
			||||||
        .delete(
 | 
					        .delete(
 | 
				
			||||||
            db.get(),
 | 
					 | 
				
			||||||
            rsrc.getUser().getAccountId(),
 | 
					            rsrc.getUser().getAccountId(),
 | 
				
			||||||
            ExternalId.Key.create(
 | 
					            ExternalId.Key.create(
 | 
				
			||||||
                SCHEME_GPGKEY, BaseEncoding.base16().encode(key.getFingerprint())));
 | 
					                SCHEME_GPGKEY, BaseEncoding.base16().encode(key.getFingerprint())));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,6 @@ import com.google.gerrit.gpg.Fingerprint;
 | 
				
			|||||||
import com.google.gerrit.gpg.GerritPublicKeyChecker;
 | 
					import com.google.gerrit.gpg.GerritPublicKeyChecker;
 | 
				
			||||||
import com.google.gerrit.gpg.PublicKeyChecker;
 | 
					import com.google.gerrit.gpg.PublicKeyChecker;
 | 
				
			||||||
import com.google.gerrit.gpg.PublicKeyStore;
 | 
					import com.google.gerrit.gpg.PublicKeyStore;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.account.AccountResource;
 | 
					import com.google.gerrit.server.account.AccountResource;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
@@ -64,7 +63,6 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
 | 
				
			|||||||
  public static final String MIME_TYPE = "application/pgp-keys";
 | 
					  public static final String MIME_TYPE = "application/pgp-keys";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final DynamicMap<RestView<GpgKey>> views;
 | 
					  private final DynamicMap<RestView<GpgKey>> views;
 | 
				
			||||||
  private final Provider<ReviewDb> db;
 | 
					 | 
				
			||||||
  private final Provider<CurrentUser> self;
 | 
					  private final Provider<CurrentUser> self;
 | 
				
			||||||
  private final Provider<PublicKeyStore> storeProvider;
 | 
					  private final Provider<PublicKeyStore> storeProvider;
 | 
				
			||||||
  private final GerritPublicKeyChecker.Factory checkerFactory;
 | 
					  private final GerritPublicKeyChecker.Factory checkerFactory;
 | 
				
			||||||
@@ -73,13 +71,11 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
 | 
				
			|||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  GpgKeys(
 | 
					  GpgKeys(
 | 
				
			||||||
      DynamicMap<RestView<GpgKey>> views,
 | 
					      DynamicMap<RestView<GpgKey>> views,
 | 
				
			||||||
      Provider<ReviewDb> db,
 | 
					 | 
				
			||||||
      Provider<CurrentUser> self,
 | 
					      Provider<CurrentUser> self,
 | 
				
			||||||
      Provider<PublicKeyStore> storeProvider,
 | 
					      Provider<PublicKeyStore> storeProvider,
 | 
				
			||||||
      GerritPublicKeyChecker.Factory checkerFactory,
 | 
					      GerritPublicKeyChecker.Factory checkerFactory,
 | 
				
			||||||
      ExternalIds externalIds) {
 | 
					      ExternalIds externalIds) {
 | 
				
			||||||
    this.views = views;
 | 
					    this.views = views;
 | 
				
			||||||
    this.db = db;
 | 
					 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.storeProvider = storeProvider;
 | 
					    this.storeProvider = storeProvider;
 | 
				
			||||||
    this.checkerFactory = checkerFactory;
 | 
					    this.checkerFactory = checkerFactory;
 | 
				
			||||||
@@ -199,8 +195,8 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private Iterable<ExternalId> getGpgExtIds(AccountResource rsrc) throws IOException, OrmException {
 | 
					  private Iterable<ExternalId> getGpgExtIds(AccountResource rsrc) throws IOException {
 | 
				
			||||||
    return externalIds.byAccount(db.get(), rsrc.getUser().getAccountId(), SCHEME_GPGKEY);
 | 
					    return externalIds.byAccount(rsrc.getUser().getAccountId(), SCHEME_GPGKEY);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static long keyId(byte[] fp) {
 | 
					  private static long keyId(byte[] fp) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,6 @@ import com.google.gerrit.gpg.PublicKeyChecker;
 | 
				
			|||||||
import com.google.gerrit.gpg.PublicKeyStore;
 | 
					import com.google.gerrit.gpg.PublicKeyStore;
 | 
				
			||||||
import com.google.gerrit.gpg.server.PostGpgKeys.Input;
 | 
					import com.google.gerrit.gpg.server.PostGpgKeys.Input;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
@@ -85,7 +84,6 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private final Logger log = LoggerFactory.getLogger(getClass());
 | 
					  private final Logger log = LoggerFactory.getLogger(getClass());
 | 
				
			||||||
  private final Provider<PersonIdent> serverIdent;
 | 
					  private final Provider<PersonIdent> serverIdent;
 | 
				
			||||||
  private final Provider<ReviewDb> db;
 | 
					 | 
				
			||||||
  private final Provider<CurrentUser> self;
 | 
					  private final Provider<CurrentUser> self;
 | 
				
			||||||
  private final Provider<PublicKeyStore> storeProvider;
 | 
					  private final Provider<PublicKeyStore> storeProvider;
 | 
				
			||||||
  private final GerritPublicKeyChecker.Factory checkerFactory;
 | 
					  private final GerritPublicKeyChecker.Factory checkerFactory;
 | 
				
			||||||
@@ -98,7 +96,6 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  PostGpgKeys(
 | 
					  PostGpgKeys(
 | 
				
			||||||
      @GerritPersonIdent Provider<PersonIdent> serverIdent,
 | 
					      @GerritPersonIdent Provider<PersonIdent> serverIdent,
 | 
				
			||||||
      Provider<ReviewDb> db,
 | 
					 | 
				
			||||||
      Provider<CurrentUser> self,
 | 
					      Provider<CurrentUser> self,
 | 
				
			||||||
      Provider<PublicKeyStore> storeProvider,
 | 
					      Provider<PublicKeyStore> storeProvider,
 | 
				
			||||||
      GerritPublicKeyChecker.Factory checkerFactory,
 | 
					      GerritPublicKeyChecker.Factory checkerFactory,
 | 
				
			||||||
@@ -108,7 +105,6 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
      ExternalIds externalIds,
 | 
					      ExternalIds externalIds,
 | 
				
			||||||
      ExternalIdsUpdate.User externalIdsUpdateFactory) {
 | 
					      ExternalIdsUpdate.User externalIdsUpdateFactory) {
 | 
				
			||||||
    this.serverIdent = serverIdent;
 | 
					    this.serverIdent = serverIdent;
 | 
				
			||||||
    this.db = db;
 | 
					 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.storeProvider = storeProvider;
 | 
					    this.storeProvider = storeProvider;
 | 
				
			||||||
    this.checkerFactory = checkerFactory;
 | 
					    this.checkerFactory = checkerFactory;
 | 
				
			||||||
@@ -126,7 +122,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
    GpgKeys.checkVisible(self, rsrc);
 | 
					    GpgKeys.checkVisible(self, rsrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Collection<ExternalId> existingExtIds =
 | 
					    Collection<ExternalId> existingExtIds =
 | 
				
			||||||
        externalIds.byAccount(db.get(), rsrc.getUser().getAccountId(), SCHEME_GPGKEY);
 | 
					        externalIds.byAccount(rsrc.getUser().getAccountId(), SCHEME_GPGKEY);
 | 
				
			||||||
    try (PublicKeyStore store = storeProvider.get()) {
 | 
					    try (PublicKeyStore store = storeProvider.get()) {
 | 
				
			||||||
      Set<Fingerprint> toRemove = readKeysToRemove(input, existingExtIds);
 | 
					      Set<Fingerprint> toRemove = readKeysToRemove(input, existingExtIds);
 | 
				
			||||||
      List<PGPPublicKeyRing> newKeys = readKeysToAdd(input, toRemove);
 | 
					      List<PGPPublicKeyRing> newKeys = readKeysToAdd(input, toRemove);
 | 
				
			||||||
@@ -151,7 +147,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
          toRemove.stream().map(fp -> toExtIdKey(fp.get())).collect(toList());
 | 
					          toRemove.stream().map(fp -> toExtIdKey(fp.get())).collect(toList());
 | 
				
			||||||
      externalIdsUpdateFactory
 | 
					      externalIdsUpdateFactory
 | 
				
			||||||
          .create()
 | 
					          .create()
 | 
				
			||||||
          .replace(db.get(), rsrc.getUser().getAccountId(), extIdKeysToRemove, newExtIds);
 | 
					          .replace(rsrc.getUser().getAccountId(), extIdKeysToRemove, newExtIds);
 | 
				
			||||||
      accountCache.evict(rsrc.getUser().getAccountId());
 | 
					      accountCache.evict(rsrc.getUser().getAccountId());
 | 
				
			||||||
      return toJson(newKeys, toRemove, store, rsrc.getUser());
 | 
					      return toJson(newKeys, toRemove, store, rsrc.getUser());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -223,7 +223,7 @@ public class GerritPublicKeyCheckerTest {
 | 
				
			|||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  public void noExternalIds() throws Exception {
 | 
					  public void noExternalIds() throws Exception {
 | 
				
			||||||
    ExternalIdsUpdate externalIdsUpdate = externalIdsUpdateFactory.create();
 | 
					    ExternalIdsUpdate externalIdsUpdate = externalIdsUpdateFactory.create();
 | 
				
			||||||
    externalIdsUpdate.deleteAll(db, user.getAccountId());
 | 
					    externalIdsUpdate.deleteAll(user.getAccountId());
 | 
				
			||||||
    reloadUser();
 | 
					    reloadUser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TestKey key = validKeyWithSecondUserId();
 | 
					    TestKey key = validKeyWithSecondUserId();
 | 
				
			||||||
@@ -237,7 +237,7 @@ public class GerritPublicKeyCheckerTest {
 | 
				
			|||||||
    assertProblems(
 | 
					    assertProblems(
 | 
				
			||||||
        checker.check(key.getPublicKey()), Status.BAD, "Key is not associated with any users");
 | 
					        checker.check(key.getPublicKey()), Status.BAD, "Key is not associated with any users");
 | 
				
			||||||
    externalIdsUpdate.insert(
 | 
					    externalIdsUpdate.insert(
 | 
				
			||||||
        db, ExternalId.create(toExtIdKey(key.getPublicKey()), user.getAccountId()));
 | 
					        ExternalId.create(toExtIdKey(key.getPublicKey()), user.getAccountId()));
 | 
				
			||||||
    reloadUser();
 | 
					    reloadUser();
 | 
				
			||||||
    assertProblems(checker.check(key.getPublicKey()), Status.BAD, "No identities found for user");
 | 
					    assertProblems(checker.check(key.getPublicKey()), Status.BAD, "No identities found for user");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -406,7 +406,7 @@ public class GerritPublicKeyCheckerTest {
 | 
				
			|||||||
    cb.setCommitter(ident);
 | 
					    cb.setCommitter(ident);
 | 
				
			||||||
    assertThat(store.save(cb)).isAnyOf(NEW, FAST_FORWARD, FORCED);
 | 
					    assertThat(store.save(cb)).isAnyOf(NEW, FAST_FORWARD, FORCED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    externalIdsUpdateFactory.create().insert(db, newExtIds);
 | 
					    externalIdsUpdateFactory.create().insert(newExtIds);
 | 
				
			||||||
    accountCache.evict(user.getAccountId());
 | 
					    accountCache.evict(user.getAccountId());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -432,7 +432,7 @@ public class GerritPublicKeyCheckerTest {
 | 
				
			|||||||
  private void addExternalId(String scheme, String id, String email) throws Exception {
 | 
					  private void addExternalId(String scheme, String id, String email) throws Exception {
 | 
				
			||||||
    externalIdsUpdateFactory
 | 
					    externalIdsUpdateFactory
 | 
				
			||||||
        .create()
 | 
					        .create()
 | 
				
			||||||
        .insert(db, ExternalId.createWithEmail(scheme, id, user.getAccountId(), email));
 | 
					        .insert(ExternalId.createWithEmail(scheme, id, user.getAccountId(), email));
 | 
				
			||||||
    reloadUser();
 | 
					    reloadUser();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,13 +19,11 @@ import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_U
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.lifecycle.LifecycleManager;
 | 
					import com.google.gerrit.lifecycle.LifecycleManager;
 | 
				
			||||||
import com.google.gerrit.pgm.util.SiteProgram;
 | 
					import com.google.gerrit.pgm.util.SiteProgram;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.account.externalids.DisabledExternalIdCache;
 | 
					import com.google.gerrit.server.account.externalids.DisabledExternalIdCache;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIdsBatchUpdate;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIdsBatchUpdate;
 | 
				
			||||||
import com.google.gerrit.server.schema.SchemaVersionCheck;
 | 
					import com.google.gerrit.server.schema.SchemaVersionCheck;
 | 
				
			||||||
import com.google.gwtorm.server.SchemaFactory;
 | 
					 | 
				
			||||||
import com.google.inject.AbstractModule;
 | 
					import com.google.inject.AbstractModule;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Injector;
 | 
					import com.google.inject.Injector;
 | 
				
			||||||
@@ -38,8 +36,6 @@ public class LocalUsernamesToLowerCase extends SiteProgram {
 | 
				
			|||||||
  private final LifecycleManager manager = new LifecycleManager();
 | 
					  private final LifecycleManager manager = new LifecycleManager();
 | 
				
			||||||
  private final TextProgressMonitor monitor = new TextProgressMonitor();
 | 
					  private final TextProgressMonitor monitor = new TextProgressMonitor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject private SchemaFactory<ReviewDb> database;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Inject private ExternalIds externalIds;
 | 
					  @Inject private ExternalIds externalIds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject private ExternalIdsBatchUpdate externalIdsBatchUpdate;
 | 
					  @Inject private ExternalIdsBatchUpdate externalIdsBatchUpdate;
 | 
				
			||||||
@@ -63,19 +59,17 @@ public class LocalUsernamesToLowerCase extends SiteProgram {
 | 
				
			|||||||
            })
 | 
					            })
 | 
				
			||||||
        .injectMembers(this);
 | 
					        .injectMembers(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try (ReviewDb db = database.open()) {
 | 
					    Collection<ExternalId> todo = externalIds.all();
 | 
				
			||||||
      Collection<ExternalId> todo = externalIds.all(db);
 | 
					    monitor.beginTask("Converting local usernames", todo.size());
 | 
				
			||||||
      monitor.beginTask("Converting local usernames", todo.size());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      for (ExternalId extId : todo) {
 | 
					    for (ExternalId extId : todo) {
 | 
				
			||||||
        convertLocalUserToLowerCase(extId);
 | 
					      convertLocalUserToLowerCase(extId);
 | 
				
			||||||
        monitor.update(1);
 | 
					      monitor.update(1);
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      externalIdsBatchUpdate.commit(db, "Convert local usernames to lower case");
 | 
					 | 
				
			||||||
      monitor.endTask();
 | 
					 | 
				
			||||||
      manager.stop();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    externalIdsBatchUpdate.commit("Convert local usernames to lower case");
 | 
				
			||||||
 | 
					    monitor.endTask();
 | 
				
			||||||
 | 
					    manager.stop();
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,10 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.pgm.init;
 | 
					package com.google.gerrit.pgm.init;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalId.toAccountExternalIds;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gerrit.pgm.init.api.InitFlags;
 | 
					import com.google.gerrit.pgm.init.api.InitFlags;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdentProvider;
 | 
					import com.google.gerrit.server.GerritPersonIdentProvider;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIdReader;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIdReader;
 | 
				
			||||||
@@ -52,9 +49,8 @@ public class ExternalIdsOnInit {
 | 
				
			|||||||
    this.allUsers = allUsers.get();
 | 
					    this.allUsers = allUsers.get();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public synchronized void insert(ReviewDb db, String commitMessage, Collection<ExternalId> extIds)
 | 
					  public synchronized void insert(String commitMessage, Collection<ExternalId> extIds)
 | 
				
			||||||
      throws OrmException, IOException, ConfigInvalidException {
 | 
					      throws OrmException, IOException, ConfigInvalidException {
 | 
				
			||||||
    db.accountExternalIds().insert(toAccountExternalIds(extIds));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    File path = getPath();
 | 
					    File path = getPath();
 | 
				
			||||||
    if (path != null) {
 | 
					    if (path != null) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -104,7 +104,7 @@ public class InitAdminUser implements InitStep {
 | 
				
			|||||||
          if (email != null) {
 | 
					          if (email != null) {
 | 
				
			||||||
            extIds.add(ExternalId.createEmail(id, email));
 | 
					            extIds.add(ExternalId.createEmail(id, email));
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          externalIds.insert(db, "Add external IDs for initial admin user", extIds);
 | 
					          externalIds.insert("Add external IDs for initial admin user", extIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          Account a = new Account(id, TimeUtil.nowTs());
 | 
					          Account a = new Account(id, TimeUtil.nowTs());
 | 
				
			||||||
          a.setFullName(name);
 | 
					          a.setFullName(name);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,186 +0,0 @@
 | 
				
			|||||||
// Copyright (C) 2008 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.reviewdb.client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gerrit.extensions.client.AuthType;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.Column;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.StringKey;
 | 
					 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Association of an external account identifier to a local {@link Account}. */
 | 
					 | 
				
			||||||
public final class AccountExternalId {
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Scheme used for {@link AuthType#LDAP}, {@link AuthType#CLIENT_SSL_CERT_LDAP}, {@link
 | 
					 | 
				
			||||||
   * AuthType#HTTP_LDAP}, and {@link AuthType#LDAP_BIND} usernames.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * <p>The name {@code gerrit:} was a very poor choice.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public static final String SCHEME_GERRIT = "gerrit:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Scheme used for randomly created identities constructed by a UUID. */
 | 
					 | 
				
			||||||
  public static final String SCHEME_UUID = "uuid:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** 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. */
 | 
					 | 
				
			||||||
  public static final String SCHEME_USERNAME = "username:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Scheme used for GPG public keys. */
 | 
					 | 
				
			||||||
  public static final String SCHEME_GPGKEY = "gpgkey:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Scheme for external auth used during authentication, e.g. OAuth Token */
 | 
					 | 
				
			||||||
  public static final String SCHEME_EXTERNAL = "external:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Column(id = 1)
 | 
					 | 
				
			||||||
    protected String externalId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected Key() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Key(String scheme, final String identity) {
 | 
					 | 
				
			||||||
      if (!scheme.endsWith(":")) {
 | 
					 | 
				
			||||||
        scheme += ":";
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      externalId = scheme + identity;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Key(final String e) {
 | 
					 | 
				
			||||||
      externalId = e;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String get() {
 | 
					 | 
				
			||||||
      return externalId;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void set(String newValue) {
 | 
					 | 
				
			||||||
      externalId = newValue;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public String getScheme() {
 | 
					 | 
				
			||||||
      int c = externalId.indexOf(':');
 | 
					 | 
				
			||||||
      return 0 < c ? externalId.substring(0, c) : null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Column(id = 1, name = Column.NONE)
 | 
					 | 
				
			||||||
  protected Key key;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Column(id = 2)
 | 
					 | 
				
			||||||
  protected Account.Id accountId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Column(id = 3, notNull = false)
 | 
					 | 
				
			||||||
  protected String emailAddress;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Encoded version of the hashed and salted password, to be interpreted by the
 | 
					 | 
				
			||||||
  // {@link HashedPassword} class.
 | 
					 | 
				
			||||||
  @Column(id = 4, notNull = false)
 | 
					 | 
				
			||||||
  protected String password;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** <i>computed value</i> is this identity trusted by the site administrator? */
 | 
					 | 
				
			||||||
  protected boolean trusted;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** <i>computed value</i> can this identity be removed from the account? */
 | 
					 | 
				
			||||||
  protected boolean canDelete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  protected AccountExternalId() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Create a new binding to an external identity.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param who the account this binds to.
 | 
					 | 
				
			||||||
   * @param k the binding key.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public AccountExternalId(final Account.Id who, final AccountExternalId.Key k) {
 | 
					 | 
				
			||||||
    accountId = who;
 | 
					 | 
				
			||||||
    key = k;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public AccountExternalId.Key getKey() {
 | 
					 | 
				
			||||||
    return key;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Get local id of this account, to link with in other entities */
 | 
					 | 
				
			||||||
  public Account.Id getAccountId() {
 | 
					 | 
				
			||||||
    return accountId;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getExternalId() {
 | 
					 | 
				
			||||||
    return key.externalId;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getEmailAddress() {
 | 
					 | 
				
			||||||
    return emailAddress;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setEmailAddress(final String e) {
 | 
					 | 
				
			||||||
    emailAddress = e;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean isScheme(final String scheme) {
 | 
					 | 
				
			||||||
    final String id = getExternalId();
 | 
					 | 
				
			||||||
    return id != null && id.startsWith(scheme);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getSchemeRest() {
 | 
					 | 
				
			||||||
    String scheme = key.getScheme();
 | 
					 | 
				
			||||||
    return null != scheme ? getExternalId().substring(scheme.length() + 1) : null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setPassword(String hashed) {
 | 
					 | 
				
			||||||
    password = hashed;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getPassword() {
 | 
					 | 
				
			||||||
    return password;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean isTrusted() {
 | 
					 | 
				
			||||||
    return trusted;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setTrusted(final boolean t) {
 | 
					 | 
				
			||||||
    trusted = t;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean canDelete() {
 | 
					 | 
				
			||||||
    return canDelete;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setCanDelete(final boolean t) {
 | 
					 | 
				
			||||||
    canDelete = t;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public boolean equals(Object o) {
 | 
					 | 
				
			||||||
    if (o instanceof AccountExternalId) {
 | 
					 | 
				
			||||||
      AccountExternalId extId = (AccountExternalId) o;
 | 
					 | 
				
			||||||
      return Objects.equals(key, extId.key)
 | 
					 | 
				
			||||||
          && Objects.equals(accountId, extId.accountId)
 | 
					 | 
				
			||||||
          && Objects.equals(emailAddress, extId.emailAddress)
 | 
					 | 
				
			||||||
          && Objects.equals(password, extId.password);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public int hashCode() {
 | 
					 | 
				
			||||||
    return Objects.hash(key, accountId, emailAddress, password);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,38 +0,0 @@
 | 
				
			|||||||
// Copyright (C) 2008 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.reviewdb.server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
 | 
					 | 
				
			||||||
import com.google.gwtorm.server.Access;
 | 
					 | 
				
			||||||
import com.google.gwtorm.server.OrmException;
 | 
					 | 
				
			||||||
import com.google.gwtorm.server.PrimaryKey;
 | 
					 | 
				
			||||||
import com.google.gwtorm.server.Query;
 | 
					 | 
				
			||||||
import com.google.gwtorm.server.ResultSet;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public interface AccountExternalIdAccess extends Access<AccountExternalId, AccountExternalId.Key> {
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  @PrimaryKey("key")
 | 
					 | 
				
			||||||
  AccountExternalId get(AccountExternalId.Key key) throws OrmException;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Query("WHERE accountId = ?")
 | 
					 | 
				
			||||||
  ResultSet<AccountExternalId> byAccount(Account.Id id) throws OrmException;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Query("WHERE emailAddress = ?")
 | 
					 | 
				
			||||||
  ResultSet<AccountExternalId> byEmailAddress(String email) throws OrmException;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Query
 | 
					 | 
				
			||||||
  ResultSet<AccountExternalId> all() throws OrmException;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -50,8 +50,7 @@ public interface ReviewDb extends Schema {
 | 
				
			|||||||
  @Relation(id = 6)
 | 
					  @Relation(id = 6)
 | 
				
			||||||
  AccountAccess accounts();
 | 
					  AccountAccess accounts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Relation(id = 7)
 | 
					  // Deleted @Relation(id = 7)
 | 
				
			||||||
  AccountExternalIdAccess accountExternalIds();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Deleted @Relation(id = 8)
 | 
					  // Deleted @Relation(id = 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,11 +82,6 @@ public class ReviewDbWrapper implements ReviewDb {
 | 
				
			|||||||
    return delegate.accounts();
 | 
					    return delegate.accounts();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public AccountExternalIdAccess accountExternalIds() {
 | 
					 | 
				
			||||||
    return delegate.accountExternalIds();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public AccountGroupAccess accountGroups() {
 | 
					  public AccountGroupAccess accountGroups() {
 | 
				
			||||||
    return delegate.accountGroups();
 | 
					    return delegate.accountGroups();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,16 +15,6 @@ CREATE INDEX accounts_byFullName
 | 
				
			|||||||
ON accounts (full_name);
 | 
					ON accounts (full_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- *********************************************************************
 | 
					 | 
				
			||||||
-- AccountExternalIdAccess
 | 
					 | 
				
			||||||
--    covers:             byAccount
 | 
					 | 
				
			||||||
CREATE INDEX account_external_ids_byAccount
 | 
					 | 
				
			||||||
ON account_external_ids (account_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--    covers:             byEmailAddress
 | 
					 | 
				
			||||||
CREATE INDEX account_external_ids_byEmail
 | 
					 | 
				
			||||||
ON account_external_ids (email_address);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- *********************************************************************
 | 
					-- *********************************************************************
 | 
				
			||||||
-- AccountGroupMemberAccess
 | 
					-- AccountGroupMemberAccess
 | 
				
			||||||
--    @PrimaryKey covers: byAccount
 | 
					--    @PrimaryKey covers: byAccount
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,18 +18,6 @@ ON accounts (full_name)
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- *********************************************************************
 | 
					 | 
				
			||||||
-- AccountExternalIdAccess
 | 
					 | 
				
			||||||
--    covers:             byAccount
 | 
					 | 
				
			||||||
CREATE INDEX account_external_ids_byAccount
 | 
					 | 
				
			||||||
ON account_external_ids (account_id)
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--    covers:             byEmailAddress
 | 
					 | 
				
			||||||
CREATE INDEX account_external_ids_byEmail
 | 
					 | 
				
			||||||
ON account_external_ids (email_address)
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- *********************************************************************
 | 
					-- *********************************************************************
 | 
				
			||||||
-- AccountGroupMemberAccess
 | 
					-- AccountGroupMemberAccess
 | 
				
			||||||
--    @PrimaryKey covers: byAccount
 | 
					--    @PrimaryKey covers: byAccount
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,17 +62,6 @@ CREATE INDEX accounts_byFullName
 | 
				
			|||||||
ON accounts (full_name);
 | 
					ON accounts (full_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- *********************************************************************
 | 
					 | 
				
			||||||
-- AccountExternalIdAccess
 | 
					 | 
				
			||||||
--    covers:             byAccount
 | 
					 | 
				
			||||||
CREATE INDEX account_external_ids_byAccount
 | 
					 | 
				
			||||||
ON account_external_ids (account_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--    covers:             byEmailAddress
 | 
					 | 
				
			||||||
CREATE INDEX account_external_ids_byEmail
 | 
					 | 
				
			||||||
ON account_external_ids (email_address);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- *********************************************************************
 | 
					-- *********************************************************************
 | 
				
			||||||
-- AccountGroupMemberAccess
 | 
					-- AccountGroupMemberAccess
 | 
				
			||||||
--    @PrimaryKey covers: byAccount
 | 
					--    @PrimaryKey covers: byAccount
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,7 @@ public class AccountByEmailCacheImpl implements AccountByEmailCache {
 | 
				
			|||||||
      try (ReviewDb db = schema.open()) {
 | 
					      try (ReviewDb db = schema.open()) {
 | 
				
			||||||
        return Streams.concat(
 | 
					        return Streams.concat(
 | 
				
			||||||
                Streams.stream(db.accounts().byPreferredEmail(email)).map(a -> a.getId()),
 | 
					                Streams.stream(db.accounts().byPreferredEmail(email)).map(a -> a.getId()),
 | 
				
			||||||
                externalIds.get().byEmail(db, email).stream().map(e -> e.accountId()))
 | 
					                externalIds.get().byEmail(email).stream().map(e -> e.accountId()))
 | 
				
			||||||
            .collect(toImmutableSet());
 | 
					            .collect(toImmutableSet());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,7 +208,7 @@ public class AccountCacheImpl implements AccountCache {
 | 
				
			|||||||
      return new AccountState(
 | 
					      return new AccountState(
 | 
				
			||||||
          account,
 | 
					          account,
 | 
				
			||||||
          internalGroups,
 | 
					          internalGroups,
 | 
				
			||||||
          externalIds.byAccount(db, who),
 | 
					          externalIds.byAccount(who),
 | 
				
			||||||
          watchConfig.get().getProjectWatches(who));
 | 
					          watchConfig.get().getProjectWatches(who));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,9 +171,7 @@ public class AccountManager {
 | 
				
			|||||||
      externalIdsUpdateFactory
 | 
					      externalIdsUpdateFactory
 | 
				
			||||||
          .create()
 | 
					          .create()
 | 
				
			||||||
          .replace(
 | 
					          .replace(
 | 
				
			||||||
              db,
 | 
					              extId, ExternalId.create(extId.key(), extId.accountId(), newEmail, extId.password()));
 | 
				
			||||||
              extId,
 | 
					 | 
				
			||||||
              ExternalId.create(extId.key(), extId.accountId(), newEmail, extId.password()));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!realm.allowsEdit(AccountFieldName.FULL_NAME)
 | 
					    if (!realm.allowsEdit(AccountFieldName.FULL_NAME)
 | 
				
			||||||
@@ -235,7 +233,7 @@ public class AccountManager {
 | 
				
			|||||||
      AccountsUpdate accountsUpdate = accountsUpdateFactory.create();
 | 
					      AccountsUpdate accountsUpdate = accountsUpdateFactory.create();
 | 
				
			||||||
      accountsUpdate.upsert(db, account);
 | 
					      accountsUpdate.upsert(db, account);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ExternalId existingExtId = externalIds.get(db, extId.key());
 | 
					      ExternalId existingExtId = externalIds.get(extId.key());
 | 
				
			||||||
      if (existingExtId != null && !existingExtId.accountId().equals(extId.accountId())) {
 | 
					      if (existingExtId != null && !existingExtId.accountId().equals(extId.accountId())) {
 | 
				
			||||||
        // external ID is assigned to another account, do not overwrite
 | 
					        // external ID is assigned to another account, do not overwrite
 | 
				
			||||||
        accountsUpdate.delete(db, account);
 | 
					        accountsUpdate.delete(db, account);
 | 
				
			||||||
@@ -246,7 +244,7 @@ public class AccountManager {
 | 
				
			|||||||
                + newId
 | 
					                + newId
 | 
				
			||||||
                + "; external ID already in use.");
 | 
					                + "; external ID already in use.");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      externalIdsUpdateFactory.create().upsert(db, extId);
 | 
					      externalIdsUpdateFactory.create().upsert(extId);
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      // If adding the account failed, it may be that it actually was the
 | 
					      // If adding the account failed, it may be that it actually was the
 | 
				
			||||||
      // first account. So we reset the 'check for first account'-guard, as
 | 
					      // first account. So we reset the 'check for first account'-guard, as
 | 
				
			||||||
@@ -279,7 +277,7 @@ public class AccountManager {
 | 
				
			|||||||
      //
 | 
					      //
 | 
				
			||||||
      IdentifiedUser user = userFactory.create(newId);
 | 
					      IdentifiedUser user = userFactory.create(newId);
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        changeUserNameFactory.create(db, user, who.getUserName()).call();
 | 
					        changeUserNameFactory.create(user, who.getUserName()).call();
 | 
				
			||||||
      } catch (NameAlreadyUsedException e) {
 | 
					      } catch (NameAlreadyUsedException e) {
 | 
				
			||||||
        String message =
 | 
					        String message =
 | 
				
			||||||
            "Cannot assign user name \""
 | 
					            "Cannot assign user name \""
 | 
				
			||||||
@@ -347,7 +345,7 @@ public class AccountManager {
 | 
				
			|||||||
      // this is why the best we can do here is to fail early and cleanup
 | 
					      // this is why the best we can do here is to fail early and cleanup
 | 
				
			||||||
      // the database
 | 
					      // the database
 | 
				
			||||||
      accountsUpdateFactory.create().delete(db, account);
 | 
					      accountsUpdateFactory.create().delete(db, account);
 | 
				
			||||||
      externalIdsUpdateFactory.create().delete(db, extId);
 | 
					      externalIdsUpdateFactory.create().delete(extId);
 | 
				
			||||||
      throw new AccountUserNameException(errorMessage, e);
 | 
					      throw new AccountUserNameException(errorMessage, e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -373,8 +371,7 @@ public class AccountManager {
 | 
				
			|||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        externalIdsUpdateFactory
 | 
					        externalIdsUpdateFactory
 | 
				
			||||||
            .create()
 | 
					            .create()
 | 
				
			||||||
            .insert(
 | 
					            .insert(ExternalId.createWithEmail(who.getExternalIdKey(), to, who.getEmailAddress()));
 | 
				
			||||||
                db, ExternalId.createWithEmail(who.getExternalIdKey(), to, who.getEmailAddress()));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (who.getEmailAddress() != null) {
 | 
					        if (who.getEmailAddress() != null) {
 | 
				
			||||||
          Account a = db.accounts().get(to);
 | 
					          Account a = db.accounts().get(to);
 | 
				
			||||||
@@ -409,22 +406,20 @@ public class AccountManager {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  public AuthResult updateLink(Account.Id to, AuthRequest who)
 | 
					  public AuthResult updateLink(Account.Id to, AuthRequest who)
 | 
				
			||||||
      throws OrmException, AccountException, IOException, ConfigInvalidException {
 | 
					      throws OrmException, AccountException, IOException, ConfigInvalidException {
 | 
				
			||||||
    try (ReviewDb db = schema.open()) {
 | 
					    Collection<ExternalId> filteredExtIdsByScheme =
 | 
				
			||||||
      Collection<ExternalId> filteredExtIdsByScheme =
 | 
					        externalIds.byAccount(to, who.getExternalIdKey().scheme());
 | 
				
			||||||
          externalIds.byAccount(db, to, who.getExternalIdKey().scheme());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!filteredExtIdsByScheme.isEmpty()
 | 
					    if (!filteredExtIdsByScheme.isEmpty()
 | 
				
			||||||
          && (filteredExtIdsByScheme.size() > 1
 | 
					        && (filteredExtIdsByScheme.size() > 1
 | 
				
			||||||
              || !filteredExtIdsByScheme
 | 
					            || !filteredExtIdsByScheme
 | 
				
			||||||
                  .stream()
 | 
					                .stream()
 | 
				
			||||||
                  .filter(e -> e.key().equals(who.getExternalIdKey()))
 | 
					                .filter(e -> e.key().equals(who.getExternalIdKey()))
 | 
				
			||||||
                  .findAny()
 | 
					                .findAny()
 | 
				
			||||||
                  .isPresent())) {
 | 
					                .isPresent())) {
 | 
				
			||||||
        externalIdsUpdateFactory.create().delete(db, filteredExtIdsByScheme);
 | 
					      externalIdsUpdateFactory.create().delete(filteredExtIdsByScheme);
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      byIdCache.evict(to);
 | 
					 | 
				
			||||||
      return link(to, who);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    byIdCache.evict(to);
 | 
				
			||||||
 | 
					    return link(to, who);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -445,7 +440,7 @@ public class AccountManager {
 | 
				
			|||||||
          throw new AccountException(
 | 
					          throw new AccountException(
 | 
				
			||||||
              "Identity '" + who.getExternalIdKey().get() + "' in use by another account");
 | 
					              "Identity '" + who.getExternalIdKey().get() + "' in use by another account");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        externalIdsUpdateFactory.create().delete(db, extId);
 | 
					        externalIdsUpdateFactory.create().delete(extId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (who.getEmailAddress() != null) {
 | 
					        if (who.getEmailAddress() != null) {
 | 
				
			||||||
          Account a = db.accounts().get(from);
 | 
					          Account a = db.accounts().get(from);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USE
 | 
				
			|||||||
import com.google.gerrit.common.Nullable;
 | 
					import com.google.gerrit.common.Nullable;
 | 
				
			||||||
import com.google.gerrit.common.errors.NameAlreadyUsedException;
 | 
					import com.google.gerrit.common.errors.NameAlreadyUsedException;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
				
			||||||
@@ -44,7 +43,7 @@ public class ChangeUserName implements Callable<VoidResult> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Generic factory to change any user's username. */
 | 
					  /** Generic factory to change any user's username. */
 | 
				
			||||||
  public interface Factory {
 | 
					  public interface Factory {
 | 
				
			||||||
    ChangeUserName create(ReviewDb db, IdentifiedUser user, String newUsername);
 | 
					    ChangeUserName create(IdentifiedUser user, String newUsername);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final AccountCache accountCache;
 | 
					  private final AccountCache accountCache;
 | 
				
			||||||
@@ -52,7 +51,6 @@ public class ChangeUserName implements Callable<VoidResult> {
 | 
				
			|||||||
  private final ExternalIds externalIds;
 | 
					  private final ExternalIds externalIds;
 | 
				
			||||||
  private final ExternalIdsUpdate.Server externalIdsUpdateFactory;
 | 
					  private final ExternalIdsUpdate.Server externalIdsUpdateFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final ReviewDb db;
 | 
					 | 
				
			||||||
  private final IdentifiedUser user;
 | 
					  private final IdentifiedUser user;
 | 
				
			||||||
  private final String newUsername;
 | 
					  private final String newUsername;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,14 +60,12 @@ public class ChangeUserName implements Callable<VoidResult> {
 | 
				
			|||||||
      SshKeyCache sshKeyCache,
 | 
					      SshKeyCache sshKeyCache,
 | 
				
			||||||
      ExternalIds externalIds,
 | 
					      ExternalIds externalIds,
 | 
				
			||||||
      ExternalIdsUpdate.Server externalIdsUpdateFactory,
 | 
					      ExternalIdsUpdate.Server externalIdsUpdateFactory,
 | 
				
			||||||
      @Assisted ReviewDb db,
 | 
					 | 
				
			||||||
      @Assisted IdentifiedUser user,
 | 
					      @Assisted IdentifiedUser user,
 | 
				
			||||||
      @Nullable @Assisted String newUsername) {
 | 
					      @Nullable @Assisted String newUsername) {
 | 
				
			||||||
    this.accountCache = accountCache;
 | 
					    this.accountCache = accountCache;
 | 
				
			||||||
    this.sshKeyCache = sshKeyCache;
 | 
					    this.sshKeyCache = sshKeyCache;
 | 
				
			||||||
    this.externalIds = externalIds;
 | 
					    this.externalIds = externalIds;
 | 
				
			||||||
    this.externalIdsUpdateFactory = externalIdsUpdateFactory;
 | 
					    this.externalIdsUpdateFactory = externalIdsUpdateFactory;
 | 
				
			||||||
    this.db = db;
 | 
					 | 
				
			||||||
    this.user = user;
 | 
					    this.user = user;
 | 
				
			||||||
    this.newUsername = newUsername;
 | 
					    this.newUsername = newUsername;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -78,7 +74,7 @@ public class ChangeUserName implements Callable<VoidResult> {
 | 
				
			|||||||
  public VoidResult call()
 | 
					  public VoidResult call()
 | 
				
			||||||
      throws OrmException, NameAlreadyUsedException, InvalidUserNameException, IOException,
 | 
					      throws OrmException, NameAlreadyUsedException, InvalidUserNameException, IOException,
 | 
				
			||||||
          ConfigInvalidException {
 | 
					          ConfigInvalidException {
 | 
				
			||||||
    Collection<ExternalId> old = externalIds.byAccount(db, user.getAccountId(), SCHEME_USERNAME);
 | 
					    Collection<ExternalId> old = externalIds.byAccount(user.getAccountId(), SCHEME_USERNAME);
 | 
				
			||||||
    if (!old.isEmpty()) {
 | 
					    if (!old.isEmpty()) {
 | 
				
			||||||
      throw new IllegalStateException(USERNAME_CANNOT_BE_CHANGED);
 | 
					      throw new IllegalStateException(USERNAME_CANNOT_BE_CHANGED);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -97,11 +93,11 @@ public class ChangeUserName implements Callable<VoidResult> {
 | 
				
			|||||||
            password = i.password();
 | 
					            password = i.password();
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        externalIdsUpdate.insert(db, ExternalId.create(key, user.getAccountId(), null, password));
 | 
					        externalIdsUpdate.insert(ExternalId.create(key, user.getAccountId(), null, password));
 | 
				
			||||||
      } catch (OrmDuplicateKeyException dupeErr) {
 | 
					      } catch (OrmDuplicateKeyException dupeErr) {
 | 
				
			||||||
        // If we are using this identity, don't report the exception.
 | 
					        // If we are using this identity, don't report the exception.
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
        ExternalId other = externalIds.get(db, key);
 | 
					        ExternalId other = externalIds.get(key);
 | 
				
			||||||
        if (other != null && other.accountId().equals(user.getAccountId())) {
 | 
					        if (other != null && other.accountId().equals(user.getAccountId())) {
 | 
				
			||||||
          return VoidResult.INSTANCE;
 | 
					          return VoidResult.INSTANCE;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -114,7 +110,7 @@ public class ChangeUserName implements Callable<VoidResult> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // If we have any older user names, remove them.
 | 
					    // If we have any older user names, remove them.
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    externalIdsUpdate.delete(db, old);
 | 
					    externalIdsUpdate.delete(old);
 | 
				
			||||||
    for (ExternalId extId : old) {
 | 
					    for (ExternalId extId : old) {
 | 
				
			||||||
      sshKeyCache.evict(extId.key().id());
 | 
					      sshKeyCache.evict(extId.key().id());
 | 
				
			||||||
      accountCache.evictByUsername(extId.key().id());
 | 
					      accountCache.evictByUsername(extId.key().id());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,11 +137,11 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
 | 
				
			|||||||
    Account.Id id = new Account.Id(db.nextAccountId());
 | 
					    Account.Id id = new Account.Id(db.nextAccountId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ExternalId extUser = ExternalId.createUsername(username, id, input.httpPassword);
 | 
					    ExternalId extUser = ExternalId.createUsername(username, id, input.httpPassword);
 | 
				
			||||||
    if (externalIds.get(db, extUser.key()) != null) {
 | 
					    if (externalIds.get(extUser.key()) != null) {
 | 
				
			||||||
      throw new ResourceConflictException("username '" + username + "' already exists");
 | 
					      throw new ResourceConflictException("username '" + username + "' already exists");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (input.email != null) {
 | 
					    if (input.email != null) {
 | 
				
			||||||
      if (externalIds.get(db, ExternalId.Key.create(SCHEME_MAILTO, input.email)) != null) {
 | 
					      if (externalIds.get(ExternalId.Key.create(SCHEME_MAILTO, input.email)) != null) {
 | 
				
			||||||
        throw new UnprocessableEntityException("email '" + input.email + "' already exists");
 | 
					        throw new UnprocessableEntityException("email '" + input.email + "' already exists");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (!validator.isValid(input.email)) {
 | 
					      if (!validator.isValid(input.email)) {
 | 
				
			||||||
@@ -157,17 +157,17 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    ExternalIdsUpdate externalIdsUpdate = externalIdsUpdateFactory.create();
 | 
					    ExternalIdsUpdate externalIdsUpdate = externalIdsUpdateFactory.create();
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      externalIdsUpdate.insert(db, extIds);
 | 
					      externalIdsUpdate.insert(extIds);
 | 
				
			||||||
    } catch (OrmDuplicateKeyException duplicateKey) {
 | 
					    } catch (OrmDuplicateKeyException duplicateKey) {
 | 
				
			||||||
      throw new ResourceConflictException("username '" + username + "' already exists");
 | 
					      throw new ResourceConflictException("username '" + username + "' already exists");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (input.email != null) {
 | 
					    if (input.email != null) {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        externalIdsUpdate.insert(db, ExternalId.createEmail(id, input.email));
 | 
					        externalIdsUpdate.insert(ExternalId.createEmail(id, input.email));
 | 
				
			||||||
      } catch (OrmDuplicateKeyException duplicateKey) {
 | 
					      } catch (OrmDuplicateKeyException duplicateKey) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          externalIdsUpdate.delete(db, extUser);
 | 
					          externalIdsUpdate.delete(extUser);
 | 
				
			||||||
        } catch (IOException | ConfigInvalidException cleanupError) {
 | 
					        } catch (IOException | ConfigInvalidException cleanupError) {
 | 
				
			||||||
          // Ignored
 | 
					          // Ignored
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
 | 
				
			|||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 | 
					import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.Response;
 | 
					import com.google.gerrit.extensions.restapi.Response;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
					import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
import com.google.gerrit.server.account.DeleteEmail.Input;
 | 
					import com.google.gerrit.server.account.DeleteEmail.Input;
 | 
				
			||||||
@@ -47,7 +46,6 @@ public class DeleteEmail implements RestModifyView<AccountResource.Email, Input>
 | 
				
			|||||||
  private final Provider<CurrentUser> self;
 | 
					  private final Provider<CurrentUser> self;
 | 
				
			||||||
  private final Realm realm;
 | 
					  private final Realm realm;
 | 
				
			||||||
  private final PermissionBackend permissionBackend;
 | 
					  private final PermissionBackend permissionBackend;
 | 
				
			||||||
  private final Provider<ReviewDb> dbProvider;
 | 
					 | 
				
			||||||
  private final AccountManager accountManager;
 | 
					  private final AccountManager accountManager;
 | 
				
			||||||
  private final ExternalIds externalIds;
 | 
					  private final ExternalIds externalIds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -56,13 +54,11 @@ public class DeleteEmail implements RestModifyView<AccountResource.Email, Input>
 | 
				
			|||||||
      Provider<CurrentUser> self,
 | 
					      Provider<CurrentUser> self,
 | 
				
			||||||
      Realm realm,
 | 
					      Realm realm,
 | 
				
			||||||
      PermissionBackend permissionBackend,
 | 
					      PermissionBackend permissionBackend,
 | 
				
			||||||
      Provider<ReviewDb> dbProvider,
 | 
					 | 
				
			||||||
      AccountManager accountManager,
 | 
					      AccountManager accountManager,
 | 
				
			||||||
      ExternalIds externalIds) {
 | 
					      ExternalIds externalIds) {
 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.realm = realm;
 | 
					    this.realm = realm;
 | 
				
			||||||
    this.permissionBackend = permissionBackend;
 | 
					    this.permissionBackend = permissionBackend;
 | 
				
			||||||
    this.dbProvider = dbProvider;
 | 
					 | 
				
			||||||
    this.accountManager = accountManager;
 | 
					    this.accountManager = accountManager;
 | 
				
			||||||
    this.externalIds = externalIds;
 | 
					    this.externalIds = externalIds;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -87,7 +83,7 @@ public class DeleteEmail implements RestModifyView<AccountResource.Email, Input>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Set<ExternalId> extIds =
 | 
					    Set<ExternalId> extIds =
 | 
				
			||||||
        externalIds
 | 
					        externalIds
 | 
				
			||||||
            .byAccount(dbProvider.get(), user.getAccountId())
 | 
					            .byAccount(user.getAccountId())
 | 
				
			||||||
            .stream()
 | 
					            .stream()
 | 
				
			||||||
            .filter(e -> email.equals(e.email()))
 | 
					            .filter(e -> email.equals(e.email()))
 | 
				
			||||||
            .collect(toSet());
 | 
					            .collect(toSet());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,6 @@ import com.google.gerrit.extensions.restapi.Response;
 | 
				
			|||||||
import com.google.gerrit.extensions.restapi.RestApiException;
 | 
					import com.google.gerrit.extensions.restapi.RestApiException;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
					import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 | 
					import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
				
			||||||
@@ -41,18 +40,13 @@ public class DeleteExternalIds implements RestModifyView<AccountResource, List<S
 | 
				
			|||||||
  private final AccountManager accountManager;
 | 
					  private final AccountManager accountManager;
 | 
				
			||||||
  private final ExternalIds externalIds;
 | 
					  private final ExternalIds externalIds;
 | 
				
			||||||
  private final Provider<CurrentUser> self;
 | 
					  private final Provider<CurrentUser> self;
 | 
				
			||||||
  private final Provider<ReviewDb> dbProvider;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  DeleteExternalIds(
 | 
					  DeleteExternalIds(
 | 
				
			||||||
      AccountManager accountManager,
 | 
					      AccountManager accountManager, ExternalIds externalIds, Provider<CurrentUser> self) {
 | 
				
			||||||
      ExternalIds externalIds,
 | 
					 | 
				
			||||||
      Provider<CurrentUser> self,
 | 
					 | 
				
			||||||
      Provider<ReviewDb> dbProvider) {
 | 
					 | 
				
			||||||
    this.accountManager = accountManager;
 | 
					    this.accountManager = accountManager;
 | 
				
			||||||
    this.externalIds = externalIds;
 | 
					    this.externalIds = externalIds;
 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.dbProvider = dbProvider;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
@@ -68,7 +62,7 @@ public class DeleteExternalIds implements RestModifyView<AccountResource, List<S
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Map<ExternalId.Key, ExternalId> externalIdMap =
 | 
					    Map<ExternalId.Key, ExternalId> externalIdMap =
 | 
				
			||||||
        externalIds
 | 
					        externalIds
 | 
				
			||||||
            .byAccount(dbProvider.get(), resource.getUser().getAccountId())
 | 
					            .byAccount(resource.getUser().getAccountId())
 | 
				
			||||||
            .stream()
 | 
					            .stream()
 | 
				
			||||||
            .collect(toMap(i -> i.key(), i -> i));
 | 
					            .collect(toMap(i -> i.key(), i -> i));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.common.AccountExternalIdInfo;
 | 
				
			|||||||
import com.google.gerrit.extensions.restapi.AuthException;
 | 
					import com.google.gerrit.extensions.restapi.AuthException;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.RestApiException;
 | 
					import com.google.gerrit.extensions.restapi.RestApiException;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.RestReadView;
 | 
					import com.google.gerrit.extensions.restapi.RestReadView;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
				
			||||||
@@ -38,18 +37,12 @@ import java.util.List;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@Singleton
 | 
					@Singleton
 | 
				
			||||||
public class GetExternalIds implements RestReadView<AccountResource> {
 | 
					public class GetExternalIds implements RestReadView<AccountResource> {
 | 
				
			||||||
  private final Provider<ReviewDb> db;
 | 
					 | 
				
			||||||
  private final ExternalIds externalIds;
 | 
					  private final ExternalIds externalIds;
 | 
				
			||||||
  private final Provider<CurrentUser> self;
 | 
					  private final Provider<CurrentUser> self;
 | 
				
			||||||
  private final AuthConfig authConfig;
 | 
					  private final AuthConfig authConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  GetExternalIds(
 | 
					  GetExternalIds(ExternalIds externalIds, Provider<CurrentUser> self, AuthConfig authConfig) {
 | 
				
			||||||
      Provider<ReviewDb> db,
 | 
					 | 
				
			||||||
      ExternalIds externalIds,
 | 
					 | 
				
			||||||
      Provider<CurrentUser> self,
 | 
					 | 
				
			||||||
      AuthConfig authConfig) {
 | 
					 | 
				
			||||||
    this.db = db;
 | 
					 | 
				
			||||||
    this.externalIds = externalIds;
 | 
					    this.externalIds = externalIds;
 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.authConfig = authConfig;
 | 
					    this.authConfig = authConfig;
 | 
				
			||||||
@@ -62,7 +55,7 @@ public class GetExternalIds implements RestReadView<AccountResource> {
 | 
				
			|||||||
      throw new AuthException("not allowed to get external IDs");
 | 
					      throw new AuthException("not allowed to get external IDs");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Collection<ExternalId> ids = externalIds.byAccount(db.get(), resource.getUser().getAccountId());
 | 
					    Collection<ExternalId> ids = externalIds.byAccount(resource.getUser().getAccountId());
 | 
				
			||||||
    if (ids.isEmpty()) {
 | 
					    if (ids.isEmpty()) {
 | 
				
			||||||
      return ImmutableList.of();
 | 
					      return ImmutableList.of();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
 | 
				
			|||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 | 
					import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.Response;
 | 
					import com.google.gerrit.extensions.restapi.Response;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
					import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
import com.google.gerrit.server.account.PutHttpPassword.Input;
 | 
					import com.google.gerrit.server.account.PutHttpPassword.Input;
 | 
				
			||||||
@@ -59,7 +58,6 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final Provider<CurrentUser> self;
 | 
					  private final Provider<CurrentUser> self;
 | 
				
			||||||
  private final Provider<ReviewDb> dbProvider;
 | 
					 | 
				
			||||||
  private final PermissionBackend permissionBackend;
 | 
					  private final PermissionBackend permissionBackend;
 | 
				
			||||||
  private final AccountCache accountCache;
 | 
					  private final AccountCache accountCache;
 | 
				
			||||||
  private final ExternalIds externalIds;
 | 
					  private final ExternalIds externalIds;
 | 
				
			||||||
@@ -68,13 +66,11 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  PutHttpPassword(
 | 
					  PutHttpPassword(
 | 
				
			||||||
      Provider<CurrentUser> self,
 | 
					      Provider<CurrentUser> self,
 | 
				
			||||||
      Provider<ReviewDb> dbProvider,
 | 
					 | 
				
			||||||
      PermissionBackend permissionBackend,
 | 
					      PermissionBackend permissionBackend,
 | 
				
			||||||
      AccountCache accountCache,
 | 
					      AccountCache accountCache,
 | 
				
			||||||
      ExternalIds externalIds,
 | 
					      ExternalIds externalIds,
 | 
				
			||||||
      ExternalIdsUpdate.User externalIdsUpdate) {
 | 
					      ExternalIdsUpdate.User externalIdsUpdate) {
 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.dbProvider = dbProvider;
 | 
					 | 
				
			||||||
    this.permissionBackend = permissionBackend;
 | 
					    this.permissionBackend = permissionBackend;
 | 
				
			||||||
    this.accountCache = accountCache;
 | 
					    this.accountCache = accountCache;
 | 
				
			||||||
    this.externalIds = externalIds;
 | 
					    this.externalIds = externalIds;
 | 
				
			||||||
@@ -114,15 +110,13 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
      throw new ResourceConflictException("username must be set");
 | 
					      throw new ResourceConflictException("username must be set");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ExternalId extId =
 | 
					    ExternalId extId = externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, user.getUserName()));
 | 
				
			||||||
        externalIds.get(
 | 
					 | 
				
			||||||
            dbProvider.get(), ExternalId.Key.create(SCHEME_USERNAME, user.getUserName()));
 | 
					 | 
				
			||||||
    if (extId == null) {
 | 
					    if (extId == null) {
 | 
				
			||||||
      throw new ResourceNotFoundException();
 | 
					      throw new ResourceNotFoundException();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ExternalId newExtId =
 | 
					    ExternalId newExtId =
 | 
				
			||||||
        ExternalId.createWithPassword(extId.key(), extId.accountId(), extId.email(), newPassword);
 | 
					        ExternalId.createWithPassword(extId.key(), extId.accountId(), extId.email(), newPassword);
 | 
				
			||||||
    externalIdsUpdate.create().upsert(dbProvider.get(), newExtId);
 | 
					    externalIdsUpdate.create().upsert(newExtId);
 | 
				
			||||||
    accountCache.evict(user.getAccountId());
 | 
					    accountCache.evict(user.getAccountId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Strings.isNullOrEmpty(newPassword) ? Response.<String>none() : Response.ok(newPassword);
 | 
					    return Strings.isNullOrEmpty(newPassword) ? Response.<String>none() : Response.ok(newPassword);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 | 
				
			|||||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
 | 
					import com.google.gerrit.extensions.restapi.ResourceConflictException;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
					import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
				
			||||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 | 
					import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.account.PutUsername.Input;
 | 
					import com.google.gerrit.server.account.PutUsername.Input;
 | 
				
			||||||
import com.google.gerrit.server.permissions.GlobalPermission;
 | 
					import com.google.gerrit.server.permissions.GlobalPermission;
 | 
				
			||||||
@@ -45,20 +44,17 @@ public class PutUsername implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
  private final ChangeUserName.Factory changeUserNameFactory;
 | 
					  private final ChangeUserName.Factory changeUserNameFactory;
 | 
				
			||||||
  private final PermissionBackend permissionBackend;
 | 
					  private final PermissionBackend permissionBackend;
 | 
				
			||||||
  private final Realm realm;
 | 
					  private final Realm realm;
 | 
				
			||||||
  private final Provider<ReviewDb> db;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  PutUsername(
 | 
					  PutUsername(
 | 
				
			||||||
      Provider<CurrentUser> self,
 | 
					      Provider<CurrentUser> self,
 | 
				
			||||||
      ChangeUserName.Factory changeUserNameFactory,
 | 
					      ChangeUserName.Factory changeUserNameFactory,
 | 
				
			||||||
      PermissionBackend permissionBackend,
 | 
					      PermissionBackend permissionBackend,
 | 
				
			||||||
      Realm realm,
 | 
					      Realm realm) {
 | 
				
			||||||
      Provider<ReviewDb> db) {
 | 
					 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.changeUserNameFactory = changeUserNameFactory;
 | 
					    this.changeUserNameFactory = changeUserNameFactory;
 | 
				
			||||||
    this.permissionBackend = permissionBackend;
 | 
					    this.permissionBackend = permissionBackend;
 | 
				
			||||||
    this.realm = realm;
 | 
					    this.realm = realm;
 | 
				
			||||||
    this.db = db;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
@@ -79,7 +75,7 @@ public class PutUsername implements RestModifyView<AccountResource, Input> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      changeUserNameFactory.create(db.get(), rsrc.getUser(), input.username).call();
 | 
					      changeUserNameFactory.create(rsrc.getUser(), input.username).call();
 | 
				
			||||||
    } catch (IllegalStateException e) {
 | 
					    } catch (IllegalStateException e) {
 | 
				
			||||||
      if (ChangeUserName.USERNAME_CANNOT_BE_CHANGED.equals(e.getMessage())) {
 | 
					      if (ChangeUserName.USERNAME_CANNOT_BE_CHANGED.equals(e.getMessage())) {
 | 
				
			||||||
        throw new MethodNotAllowedException(e.getMessage());
 | 
					        throw new MethodNotAllowedException(e.getMessage());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,20 +16,16 @@ package com.google.gerrit.server.account.externalids;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import static com.google.common.base.Preconditions.checkNotNull;
 | 
					import static com.google.common.base.Preconditions.checkNotNull;
 | 
				
			||||||
import static java.nio.charset.StandardCharsets.UTF_8;
 | 
					import static java.nio.charset.StandardCharsets.UTF_8;
 | 
				
			||||||
import static java.util.stream.Collectors.toSet;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.auto.value.AutoValue;
 | 
					import com.google.auto.value.AutoValue;
 | 
				
			||||||
import com.google.common.base.Strings;
 | 
					import com.google.common.base.Strings;
 | 
				
			||||||
import com.google.common.collect.ImmutableSet;
 | 
					 | 
				
			||||||
import com.google.common.collect.Iterables;
 | 
					import com.google.common.collect.Iterables;
 | 
				
			||||||
import com.google.common.hash.Hashing;
 | 
					import com.google.common.hash.Hashing;
 | 
				
			||||||
import com.google.gerrit.common.Nullable;
 | 
					import com.google.gerrit.common.Nullable;
 | 
				
			||||||
import com.google.gerrit.extensions.client.AuthType;
 | 
					import com.google.gerrit.extensions.client.AuthType;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.account.HashedPassword;
 | 
					import com.google.gerrit.server.account.HashedPassword;
 | 
				
			||||||
import java.io.Serializable;
 | 
					import java.io.Serializable;
 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
					import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
				
			||||||
import org.eclipse.jgit.lib.Config;
 | 
					import org.eclipse.jgit.lib.Config;
 | 
				
			||||||
@@ -75,10 +71,6 @@ public abstract class ExternalId implements Serializable {
 | 
				
			|||||||
      return new AutoValue_ExternalId_Key(Strings.emptyToNull(scheme), id);
 | 
					      return new AutoValue_ExternalId_Key(Strings.emptyToNull(scheme), id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static ExternalId.Key from(AccountExternalId.Key externalIdKey) {
 | 
					 | 
				
			||||||
      return parse(externalIdKey.get());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Parses an external ID key from a string in the format "scheme:id" or "id".
 | 
					     * Parses an external ID key from a string in the format "scheme:id" or "id".
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -92,11 +84,6 @@ public abstract class ExternalId implements Serializable {
 | 
				
			|||||||
      return create(externalId.substring(0, c), externalId.substring(c + 1));
 | 
					      return create(externalId.substring(0, c), externalId.substring(c + 1));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Set<AccountExternalId.Key> toAccountExternalIdKeys(
 | 
					 | 
				
			||||||
        Collection<ExternalId.Key> extIdKeys) {
 | 
					 | 
				
			||||||
      return extIdKeys.stream().map(k -> k.asAccountExternalIdKey()).collect(toSet());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public abstract @Nullable String scheme();
 | 
					    public abstract @Nullable String scheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract String id();
 | 
					    public abstract String id();
 | 
				
			||||||
@@ -105,13 +92,6 @@ public abstract class ExternalId implements Serializable {
 | 
				
			|||||||
      return scheme.equals(scheme());
 | 
					      return scheme.equals(scheme());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public AccountExternalId.Key asAccountExternalIdKey() {
 | 
					 | 
				
			||||||
      if (scheme() != null) {
 | 
					 | 
				
			||||||
        return new AccountExternalId.Key(scheme(), id());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return new AccountExternalId.Key(id());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the SHA1 of the external ID that is used as note ID in the refs/meta/external-ids
 | 
					     * Returns the SHA1 of the external ID that is used as note ID in the refs/meta/external-ids
 | 
				
			||||||
     * notes branch.
 | 
					     * notes branch.
 | 
				
			||||||
@@ -281,29 +261,6 @@ public abstract class ExternalId implements Serializable {
 | 
				
			|||||||
        String.format("Invalid external ID config for note '%s': %s", noteId, message));
 | 
					        String.format("Invalid external ID config for note '%s': %s", noteId, message));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static ExternalId from(AccountExternalId externalId) {
 | 
					 | 
				
			||||||
    if (externalId == null) {
 | 
					 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return new AutoValue_ExternalId(
 | 
					 | 
				
			||||||
        ExternalId.Key.parse(externalId.getExternalId()),
 | 
					 | 
				
			||||||
        externalId.getAccountId(),
 | 
					 | 
				
			||||||
        Strings.emptyToNull(externalId.getEmailAddress()),
 | 
					 | 
				
			||||||
        Strings.emptyToNull(externalId.getPassword()));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static Set<ExternalId> from(Collection<AccountExternalId> externalIds) {
 | 
					 | 
				
			||||||
    if (externalIds == null) {
 | 
					 | 
				
			||||||
      return ImmutableSet.of();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return externalIds.stream().map(ExternalId::from).collect(toSet());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static Set<AccountExternalId> toAccountExternalIds(Collection<ExternalId> extIds) {
 | 
					 | 
				
			||||||
    return extIds.stream().map(e -> e.asAccountExternalId()).collect(toSet());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public abstract Key key();
 | 
					  public abstract Key key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public abstract Account.Id accountId();
 | 
					  public abstract Account.Id accountId();
 | 
				
			||||||
@@ -316,13 +273,6 @@ public abstract class ExternalId implements Serializable {
 | 
				
			|||||||
    return key().isScheme(scheme);
 | 
					    return key().isScheme(scheme);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public AccountExternalId asAccountExternalId() {
 | 
					 | 
				
			||||||
    AccountExternalId extId = new AccountExternalId(accountId(), key().asAccountExternalIdKey());
 | 
					 | 
				
			||||||
    extId.setEmailAddress(email());
 | 
					 | 
				
			||||||
    extId.setPassword(password());
 | 
					 | 
				
			||||||
    return extId;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Exports this external ID as Git config file text.
 | 
					   * Exports this external ID as Git config file text.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,18 +24,14 @@ import com.google.gerrit.metrics.Description.Units;
 | 
				
			|||||||
import com.google.gerrit.metrics.MetricMaker;
 | 
					import com.google.gerrit.metrics.MetricMaker;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer0;
 | 
					import com.google.gerrit.metrics.Timer0;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.RefNames;
 | 
					import com.google.gerrit.reviewdb.client.RefNames;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.config.AllUsersName;
 | 
					import com.google.gerrit.server.config.AllUsersName;
 | 
				
			||||||
import com.google.gerrit.server.config.GerritServerConfig;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
					import com.google.gerrit.server.git.GitRepositoryManager;
 | 
				
			||||||
import com.google.gwtorm.server.OrmException;
 | 
					 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Singleton;
 | 
					import com.google.inject.Singleton;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
					import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
				
			||||||
import org.eclipse.jgit.lib.Config;
 | 
					 | 
				
			||||||
import org.eclipse.jgit.lib.ObjectId;
 | 
					import org.eclipse.jgit.lib.ObjectId;
 | 
				
			||||||
import org.eclipse.jgit.lib.Ref;
 | 
					import org.eclipse.jgit.lib.Ref;
 | 
				
			||||||
import org.eclipse.jgit.lib.Repository;
 | 
					import org.eclipse.jgit.lib.Repository;
 | 
				
			||||||
@@ -46,7 +42,7 @@ import org.slf4j.Logger;
 | 
				
			|||||||
import org.slf4j.LoggerFactory;
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Class to read external IDs from ReviewDb or NoteDb.
 | 
					 * Class to read external IDs from NoteDb.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>In NoteDb external IDs are stored in the All-Users repository in a Git Notes branch called
 | 
					 * <p>In NoteDb external IDs are stored in the All-Users repository in a Git Notes branch called
 | 
				
			||||||
 * refs/meta/external-ids where the sha1 of the external ID is used as note name. Each note content
 | 
					 * refs/meta/external-ids where the sha1 of the external ID is used as note name. Each note content
 | 
				
			||||||
@@ -78,7 +74,6 @@ public class ExternalIdReader {
 | 
				
			|||||||
    return NoteMap.newEmptyMap();
 | 
					    return NoteMap.newEmptyMap();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final boolean readFromGit;
 | 
					 | 
				
			||||||
  private final GitRepositoryManager repoManager;
 | 
					  private final GitRepositoryManager repoManager;
 | 
				
			||||||
  private final AllUsersName allUsersName;
 | 
					  private final AllUsersName allUsersName;
 | 
				
			||||||
  private boolean failOnLoad = false;
 | 
					  private boolean failOnLoad = false;
 | 
				
			||||||
@@ -86,11 +81,7 @@ public class ExternalIdReader {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  ExternalIdReader(
 | 
					  ExternalIdReader(
 | 
				
			||||||
      @GerritServerConfig Config cfg,
 | 
					      GitRepositoryManager repoManager, AllUsersName allUsersName, MetricMaker metricMaker) {
 | 
				
			||||||
      GitRepositoryManager repoManager,
 | 
					 | 
				
			||||||
      AllUsersName allUsersName,
 | 
					 | 
				
			||||||
      MetricMaker metricMaker) {
 | 
					 | 
				
			||||||
    this.readFromGit = cfg.getBoolean("user", null, "readExternalIdsFromGit", false);
 | 
					 | 
				
			||||||
    this.repoManager = repoManager;
 | 
					    this.repoManager = repoManager;
 | 
				
			||||||
    this.allUsersName = allUsersName;
 | 
					    this.allUsersName = allUsersName;
 | 
				
			||||||
    this.readAllLatency =
 | 
					    this.readAllLatency =
 | 
				
			||||||
@@ -106,10 +97,6 @@ public class ExternalIdReader {
 | 
				
			|||||||
    this.failOnLoad = failOnLoad;
 | 
					    this.failOnLoad = failOnLoad;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  boolean readFromGit() {
 | 
					 | 
				
			||||||
    return readFromGit;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ObjectId readRevision() throws IOException {
 | 
					  ObjectId readRevision() throws IOException {
 | 
				
			||||||
    try (Repository repo = repoManager.openRepository(allUsersName)) {
 | 
					    try (Repository repo = repoManager.openRepository(allUsersName)) {
 | 
				
			||||||
      return readRevision(repo);
 | 
					      return readRevision(repo);
 | 
				
			||||||
@@ -117,16 +104,12 @@ public class ExternalIdReader {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Reads and returns all external IDs. */
 | 
					  /** Reads and returns all external IDs. */
 | 
				
			||||||
  Set<ExternalId> all(ReviewDb db) throws IOException, OrmException {
 | 
					  Set<ExternalId> all() throws IOException {
 | 
				
			||||||
    checkReadEnabled();
 | 
					    checkReadEnabled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (readFromGit) {
 | 
					    try (Repository repo = repoManager.openRepository(allUsersName)) {
 | 
				
			||||||
      try (Repository repo = repoManager.openRepository(allUsersName)) {
 | 
					      return all(repo, readRevision(repo));
 | 
				
			||||||
        return all(repo, readRevision(repo));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ExternalId.from(db.accountExternalIds().all().toList());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -166,22 +149,18 @@ public class ExternalIdReader {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Reads and returns the specified external ID. */
 | 
					  /** Reads and returns the specified external ID. */
 | 
				
			||||||
  @Nullable
 | 
					  @Nullable
 | 
				
			||||||
  ExternalId get(ReviewDb db, ExternalId.Key key)
 | 
					  ExternalId get(ExternalId.Key key) throws IOException, ConfigInvalidException {
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					 | 
				
			||||||
    checkReadEnabled();
 | 
					    checkReadEnabled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (readFromGit) {
 | 
					    try (Repository repo = repoManager.openRepository(allUsersName);
 | 
				
			||||||
      try (Repository repo = repoManager.openRepository(allUsersName);
 | 
					        RevWalk rw = new RevWalk(repo)) {
 | 
				
			||||||
          RevWalk rw = new RevWalk(repo)) {
 | 
					      ObjectId rev = readRevision(repo);
 | 
				
			||||||
        ObjectId rev = readRevision(repo);
 | 
					      if (rev.equals(ObjectId.zeroId())) {
 | 
				
			||||||
        if (rev.equals(ObjectId.zeroId())) {
 | 
					        return null;
 | 
				
			||||||
          return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return parse(key, rw, rev);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return parse(key, rw, rev);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return ExternalId.from(db.accountExternalIds().get(key.asAccountExternalIdKey()));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Reads and returns the specified external ID from the given revision. */
 | 
					  /** Reads and returns the specified external ID from the given revision. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,8 +18,6 @@ import static java.util.stream.Collectors.toSet;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.Nullable;
 | 
					import com.google.gerrit.common.Nullable;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gwtorm.server.OrmException;
 | 
					 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Singleton;
 | 
					import com.google.inject.Singleton;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
@@ -44,8 +42,8 @@ public class ExternalIds {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns all external IDs. */
 | 
					  /** Returns all external IDs. */
 | 
				
			||||||
  public Set<ExternalId> all(ReviewDb db) throws IOException, OrmException {
 | 
					  public Set<ExternalId> all() throws IOException {
 | 
				
			||||||
    return externalIdReader.all(db);
 | 
					    return externalIdReader.all();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns all external IDs from the specified revision of the refs/meta/external-ids branch. */
 | 
					  /** Returns all external IDs from the specified revision of the refs/meta/external-ids branch. */
 | 
				
			||||||
@@ -55,9 +53,8 @@ public class ExternalIds {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Returns the specified external ID. */
 | 
					  /** Returns the specified external ID. */
 | 
				
			||||||
  @Nullable
 | 
					  @Nullable
 | 
				
			||||||
  public ExternalId get(ReviewDb db, ExternalId.Key key)
 | 
					  public ExternalId get(ExternalId.Key key) throws IOException, ConfigInvalidException {
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					    return externalIdReader.get(key);
 | 
				
			||||||
    return externalIdReader.get(db, key);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns the specified external ID from the given revision. */
 | 
					  /** Returns the specified external ID from the given revision. */
 | 
				
			||||||
@@ -68,26 +65,16 @@ public class ExternalIds {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns the external IDs of the specified account. */
 | 
					  /** Returns the external IDs of the specified account. */
 | 
				
			||||||
  public Set<ExternalId> byAccount(ReviewDb db, Account.Id accountId)
 | 
					  public Set<ExternalId> byAccount(Account.Id accountId) throws IOException {
 | 
				
			||||||
      throws IOException, OrmException {
 | 
					    return externalIdCache.byAccount(accountId);
 | 
				
			||||||
    if (externalIdReader.readFromGit()) {
 | 
					 | 
				
			||||||
      return externalIdCache.byAccount(accountId);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ExternalId.from(db.accountExternalIds().byAccount(accountId).toList());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns the external IDs of the specified account that have the given scheme. */
 | 
					  /** Returns the external IDs of the specified account that have the given scheme. */
 | 
				
			||||||
  public Set<ExternalId> byAccount(ReviewDb db, Account.Id accountId, String scheme)
 | 
					  public Set<ExternalId> byAccount(Account.Id accountId, String scheme) throws IOException {
 | 
				
			||||||
      throws IOException, OrmException {
 | 
					    return byAccount(accountId).stream().filter(e -> e.key().isScheme(scheme)).collect(toSet());
 | 
				
			||||||
    return byAccount(db, accountId).stream().filter(e -> e.key().isScheme(scheme)).collect(toSet());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Set<ExternalId> byEmail(ReviewDb db, String email) throws IOException, OrmException {
 | 
					  public Set<ExternalId> byEmail(String email) throws IOException {
 | 
				
			||||||
    if (externalIdReader.readFromGit()) {
 | 
					    return externalIdCache.byEmail(email);
 | 
				
			||||||
      return externalIdCache.byEmail(email);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ExternalId.from(db.accountExternalIds().byEmailAddress(email).toList());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,10 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.account.externalids;
 | 
					package com.google.gerrit.server.account.externalids;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalId.toAccountExternalIds;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.common.collect.ImmutableSet;
 | 
					import com.google.common.collect.ImmutableSet;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
import com.google.gerrit.server.config.AllUsersName;
 | 
					import com.google.gerrit.server.config.AllUsersName;
 | 
				
			||||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
					import com.google.gerrit.server.git.GitRepositoryManager;
 | 
				
			||||||
@@ -39,8 +36,8 @@ import org.eclipse.jgit.revwalk.RevWalk;
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>For NoteDb all updates will result in a single commit to the refs/meta/external-ids branch.
 | 
					 * <p>For NoteDb all updates will result in a single commit to the refs/meta/external-ids branch.
 | 
				
			||||||
 * This means callers can prepare many updates by invoking {@link #replace(ExternalId, ExternalId)}
 | 
					 * This means callers can prepare many updates by invoking {@link #replace(ExternalId, ExternalId)}
 | 
				
			||||||
 * multiple times and when {@link ExternalIdsBatchUpdate#commit(ReviewDb, String)} is invoked a
 | 
					 * multiple times and when {@link ExternalIdsBatchUpdate#commit(String)} is invoked a single NoteDb
 | 
				
			||||||
 * single NoteDb commit is created that contains all the prepared updates.
 | 
					 * commit is created that contains all the prepared updates.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ExternalIdsBatchUpdate {
 | 
					public class ExternalIdsBatchUpdate {
 | 
				
			||||||
  private final GitRepositoryManager repoManager;
 | 
					  private final GitRepositoryManager repoManager;
 | 
				
			||||||
@@ -65,7 +62,7 @@ public class ExternalIdsBatchUpdate {
 | 
				
			|||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Adds an external ID replacement to the batch.
 | 
					   * Adds an external ID replacement to the batch.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>The actual replacement is only done when {@link #commit(ReviewDb, String)} is invoked.
 | 
					   * <p>The actual replacement is only done when {@link #commit(String)} is invoked.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void replace(ExternalId extIdToDelete, ExternalId extIdToAdd) {
 | 
					  public void replace(ExternalId extIdToDelete, ExternalId extIdToAdd) {
 | 
				
			||||||
    ExternalIdsUpdate.checkSameAccount(ImmutableSet.of(extIdToDelete, extIdToAdd));
 | 
					    ExternalIdsUpdate.checkSameAccount(ImmutableSet.of(extIdToDelete, extIdToAdd));
 | 
				
			||||||
@@ -85,15 +82,12 @@ public class ExternalIdsBatchUpdate {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>For NoteDb a single commit is created that contains all the external ID updates.
 | 
					   * <p>For NoteDb a single commit is created that contains all the external ID updates.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void commit(ReviewDb db, String commitMessage)
 | 
					  public void commit(String commitMessage)
 | 
				
			||||||
      throws IOException, OrmException, ConfigInvalidException {
 | 
					      throws IOException, OrmException, ConfigInvalidException {
 | 
				
			||||||
    if (toDelete.isEmpty() && toAdd.isEmpty()) {
 | 
					    if (toDelete.isEmpty() && toAdd.isEmpty()) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    db.accountExternalIds().delete(toAccountExternalIds(toDelete));
 | 
					 | 
				
			||||||
    db.accountExternalIds().insert(toAccountExternalIds(toAdd));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try (Repository repo = repoManager.openRepository(allUsersName);
 | 
					    try (Repository repo = repoManager.openRepository(allUsersName);
 | 
				
			||||||
        RevWalk rw = new RevWalk(repo);
 | 
					        RevWalk rw = new RevWalk(repo);
 | 
				
			||||||
        ObjectInserter ins = repo.newObjectInserter()) {
 | 
					        ObjectInserter ins = repo.newObjectInserter()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,8 +16,6 @@ package com.google.gerrit.server.account.externalids;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import static com.google.common.base.Preconditions.checkNotNull;
 | 
					import static com.google.common.base.Preconditions.checkNotNull;
 | 
				
			||||||
import static com.google.common.base.Preconditions.checkState;
 | 
					import static com.google.common.base.Preconditions.checkState;
 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalId.Key.toAccountExternalIdKeys;
 | 
					 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalId.toAccountExternalIds;
 | 
					 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalIdReader.MAX_NOTE_SZ;
 | 
					import static com.google.gerrit.server.account.externalids.ExternalIdReader.MAX_NOTE_SZ;
 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalIdReader.readNoteMap;
 | 
					import static com.google.gerrit.server.account.externalids.ExternalIdReader.readNoteMap;
 | 
				
			||||||
import static com.google.gerrit.server.account.externalids.ExternalIdReader.readRevision;
 | 
					import static com.google.gerrit.server.account.externalids.ExternalIdReader.readRevision;
 | 
				
			||||||
@@ -42,7 +40,6 @@ import com.google.gerrit.metrics.Description;
 | 
				
			|||||||
import com.google.gerrit.metrics.MetricMaker;
 | 
					import com.google.gerrit.metrics.MetricMaker;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.RefNames;
 | 
					import com.google.gerrit.reviewdb.client.RefNames;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
import com.google.gerrit.server.config.AllUsersName;
 | 
					import com.google.gerrit.server.config.AllUsersName;
 | 
				
			||||||
@@ -252,9 +249,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>If the external ID already exists, the insert fails with {@link OrmDuplicateKeyException}.
 | 
					   * <p>If the external ID already exists, the insert fails with {@link OrmDuplicateKeyException}.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void insert(ReviewDb db, ExternalId extId)
 | 
					  public void insert(ExternalId extId) throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					    insert(Collections.singleton(extId));
 | 
				
			||||||
    insert(db, Collections.singleton(extId));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -263,10 +259,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   * <p>If any of the external ID already exists, the insert fails with {@link
 | 
					   * <p>If any of the external ID already exists, the insert fails with {@link
 | 
				
			||||||
   * OrmDuplicateKeyException}.
 | 
					   * OrmDuplicateKeyException}.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void insert(ReviewDb db, Collection<ExternalId> extIds)
 | 
					  public void insert(Collection<ExternalId> extIds)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    db.accountExternalIds().insert(toAccountExternalIds(extIds));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -282,9 +276,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>If the external ID already exists, it is overwritten, otherwise it is inserted.
 | 
					   * <p>If the external ID already exists, it is overwritten, otherwise it is inserted.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void upsert(ReviewDb db, ExternalId extId)
 | 
					  public void upsert(ExternalId extId) throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					    upsert(Collections.singleton(extId));
 | 
				
			||||||
    upsert(db, Collections.singleton(extId));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -292,10 +285,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>If any of the external IDs already exists, it is overwritten. New external IDs are inserted.
 | 
					   * <p>If any of the external IDs already exists, it is overwritten. New external IDs are inserted.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void upsert(ReviewDb db, Collection<ExternalId> extIds)
 | 
					  public void upsert(Collection<ExternalId> extIds)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    db.accountExternalIds().upsert(toAccountExternalIds(extIds));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -312,9 +303,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   * @throws IllegalStateException is thrown if there is an existing external ID that has the same
 | 
					   * @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.
 | 
					   *     key, but otherwise doesn't match the specified external ID.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void delete(ReviewDb db, ExternalId extId)
 | 
					  public void delete(ExternalId extId) throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					    delete(Collections.singleton(extId));
 | 
				
			||||||
    delete(db, Collections.singleton(extId));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -324,10 +314,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *     key as any of the external IDs that should be deleted, but otherwise doesn't match the that
 | 
					   *     key as any of the external IDs that should be deleted, but otherwise doesn't match the that
 | 
				
			||||||
   *     external ID.
 | 
					   *     external ID.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void delete(ReviewDb db, Collection<ExternalId> extIds)
 | 
					  public void delete(Collection<ExternalId> extIds)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    db.accountExternalIds().delete(toAccountExternalIds(extIds));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -344,9 +332,9 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   * @throws IllegalStateException is thrown if the external ID does not belong to the specified
 | 
					   * @throws IllegalStateException is thrown if the external ID does not belong to the specified
 | 
				
			||||||
   *     account.
 | 
					   *     account.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void delete(ReviewDb db, Account.Id accountId, ExternalId.Key extIdKey)
 | 
					  public void delete(Account.Id accountId, ExternalId.Key extIdKey)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    delete(db, accountId, Collections.singleton(extIdKey));
 | 
					    delete(accountId, Collections.singleton(extIdKey));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -355,10 +343,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   * @throws IllegalStateException is thrown if any of the external IDs does not belong to the
 | 
					   * @throws IllegalStateException is thrown if any of the external IDs does not belong to the
 | 
				
			||||||
   *     specified account.
 | 
					   *     specified account.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void delete(ReviewDb db, Account.Id accountId, Collection<ExternalId.Key> extIdKeys)
 | 
					  public void delete(Account.Id accountId, Collection<ExternalId.Key> extIdKeys)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    db.accountExternalIds().deleteKeys(toAccountExternalIdKeys(extIdKeys));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -374,10 +360,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>The external IDs are deleted regardless of which account they belong to.
 | 
					   * <p>The external IDs are deleted regardless of which account they belong to.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void deleteByKeys(ReviewDb db, Collection<ExternalId.Key> extIdKeys)
 | 
					  public void deleteByKeys(Collection<ExternalId.Key> extIdKeys)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    db.accountExternalIds().deleteKeys(toAccountExternalIdKeys(extIdKeys));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -389,9 +373,9 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Deletes all external IDs of the specified account. */
 | 
					  /** Deletes all external IDs of the specified account. */
 | 
				
			||||||
  public void deleteAll(ReviewDb db, Account.Id accountId)
 | 
					  public void deleteAll(Account.Id accountId)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    delete(db, externalIds.byAccount(db, accountId));
 | 
					    delete(externalIds.byAccount(accountId));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -406,16 +390,10 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *     the specified account.
 | 
					   *     the specified account.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void replace(
 | 
					  public void replace(
 | 
				
			||||||
      ReviewDb db,
 | 
					      Account.Id accountId, Collection<ExternalId.Key> toDelete, Collection<ExternalId> toAdd)
 | 
				
			||||||
      Account.Id accountId,
 | 
					 | 
				
			||||||
      Collection<ExternalId.Key> toDelete,
 | 
					 | 
				
			||||||
      Collection<ExternalId> toAdd)
 | 
					 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    checkSameAccount(toAdd, accountId);
 | 
					    checkSameAccount(toAdd, accountId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    db.accountExternalIds().deleteKeys(toAccountExternalIdKeys(toDelete));
 | 
					 | 
				
			||||||
    db.accountExternalIds().insert(toAccountExternalIds(toAdd));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -440,12 +418,8 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * <p>The external IDs are replaced regardless of which account they belong to.
 | 
					   * <p>The external IDs are replaced regardless of which account they belong to.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void replaceByKeys(
 | 
					  public void replaceByKeys(Collection<ExternalId.Key> toDelete, Collection<ExternalId> toAdd)
 | 
				
			||||||
      ReviewDb db, Collection<ExternalId.Key> toDelete, Collection<ExternalId> toAdd)
 | 
					 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    db.accountExternalIds().deleteKeys(toAccountExternalIdKeys(toDelete));
 | 
					 | 
				
			||||||
    db.accountExternalIds().insert(toAccountExternalIds(toAdd));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefsMetaExternalIdsUpdate u =
 | 
					    RefsMetaExternalIdsUpdate u =
 | 
				
			||||||
        updateNoteMap(
 | 
					        updateNoteMap(
 | 
				
			||||||
            o -> {
 | 
					            o -> {
 | 
				
			||||||
@@ -466,9 +440,9 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   * @throws IllegalStateException is thrown if the specified external IDs belong to different
 | 
					   * @throws IllegalStateException is thrown if the specified external IDs belong to different
 | 
				
			||||||
   *     accounts.
 | 
					   *     accounts.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void replace(ReviewDb db, ExternalId toDelete, ExternalId toAdd)
 | 
					  public void replace(ExternalId toDelete, ExternalId toAdd)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    replace(db, Collections.singleton(toDelete), Collections.singleton(toAdd));
 | 
					    replace(Collections.singleton(toDelete), Collections.singleton(toAdd));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -482,7 +456,7 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
   * @throws IllegalStateException is thrown if the specified external IDs belong to different
 | 
					   * @throws IllegalStateException is thrown if the specified external IDs belong to different
 | 
				
			||||||
   *     accounts.
 | 
					   *     accounts.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void replace(ReviewDb db, Collection<ExternalId> toDelete, Collection<ExternalId> toAdd)
 | 
					  public void replace(Collection<ExternalId> toDelete, Collection<ExternalId> toAdd)
 | 
				
			||||||
      throws IOException, ConfigInvalidException, OrmException {
 | 
					      throws IOException, ConfigInvalidException, OrmException {
 | 
				
			||||||
    Account.Id accountId = checkSameAccount(Iterables.concat(toDelete, toAdd));
 | 
					    Account.Id accountId = checkSameAccount(Iterables.concat(toDelete, toAdd));
 | 
				
			||||||
    if (accountId == null) {
 | 
					    if (accountId == null) {
 | 
				
			||||||
@@ -490,7 +464,7 @@ public class ExternalIdsUpdate {
 | 
				
			|||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    replace(db, accountId, toDelete.stream().map(e -> e.key()).collect(toSet()), toAdd);
 | 
					    replace(accountId, toDelete.stream().map(e -> e.key()).collect(toSet()), toAdd);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,6 @@ import com.google.gerrit.extensions.client.AccountFieldName;
 | 
				
			|||||||
import com.google.gerrit.extensions.client.AuthType;
 | 
					import com.google.gerrit.extensions.client.AuthType;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.Account;
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
 | 
					import com.google.gerrit.reviewdb.client.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.account.AbstractRealm;
 | 
					import com.google.gerrit.server.account.AbstractRealm;
 | 
				
			||||||
import com.google.gerrit.server.account.AccountException;
 | 
					import com.google.gerrit.server.account.AccountException;
 | 
				
			||||||
import com.google.gerrit.server.account.AuthRequest;
 | 
					import com.google.gerrit.server.account.AuthRequest;
 | 
				
			||||||
@@ -36,7 +35,6 @@ import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
				
			|||||||
import com.google.gerrit.server.auth.AuthenticationUnavailableException;
 | 
					import com.google.gerrit.server.auth.AuthenticationUnavailableException;
 | 
				
			||||||
import com.google.gerrit.server.config.AuthConfig;
 | 
					import com.google.gerrit.server.config.AuthConfig;
 | 
				
			||||||
import com.google.gerrit.server.config.GerritServerConfig;
 | 
					import com.google.gerrit.server.config.GerritServerConfig;
 | 
				
			||||||
import com.google.gwtorm.server.SchemaFactory;
 | 
					 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Singleton;
 | 
					import com.google.inject.Singleton;
 | 
				
			||||||
import com.google.inject.name.Named;
 | 
					import com.google.inject.name.Named;
 | 
				
			||||||
@@ -319,22 +317,17 @@ class LdapRealm extends AbstractRealm {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static class UserLoader extends CacheLoader<String, Optional<Account.Id>> {
 | 
					  static class UserLoader extends CacheLoader<String, Optional<Account.Id>> {
 | 
				
			||||||
    private final SchemaFactory<ReviewDb> schema;
 | 
					 | 
				
			||||||
    private final ExternalIds externalIds;
 | 
					    private final ExternalIds externalIds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Inject
 | 
					    @Inject
 | 
				
			||||||
    UserLoader(SchemaFactory<ReviewDb> schema, ExternalIds externalIds) {
 | 
					    UserLoader(ExternalIds externalIds) {
 | 
				
			||||||
      this.schema = schema;
 | 
					 | 
				
			||||||
      this.externalIds = externalIds;
 | 
					      this.externalIds = externalIds;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Optional<Account.Id> load(String username) throws Exception {
 | 
					    public Optional<Account.Id> load(String username) throws Exception {
 | 
				
			||||||
      try (ReviewDb db = schema.open()) {
 | 
					      return Optional.ofNullable(externalIds.get(ExternalId.Key.create(SCHEME_GERRIT, username)))
 | 
				
			||||||
        return Optional.ofNullable(
 | 
					          .map(ExternalId::accountId);
 | 
				
			||||||
                externalIds.get(db, ExternalId.Key.create(SCHEME_GERRIT, username)))
 | 
					 | 
				
			||||||
            .map(ExternalId::accountId);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit;
 | 
				
			|||||||
/** A version of the database schema. */
 | 
					/** A version of the database schema. */
 | 
				
			||||||
public abstract class SchemaVersion {
 | 
					public abstract class SchemaVersion {
 | 
				
			||||||
  /** The current schema version. */
 | 
					  /** The current schema version. */
 | 
				
			||||||
  public static final Class<Schema_149> C = Schema_149.class;
 | 
					  public static final Class<Schema_150> C = Schema_150.class;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static int getBinaryVersion() {
 | 
					  public static int getBinaryVersion() {
 | 
				
			||||||
    return guessVersion(C);
 | 
					    return guessVersion(C);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,16 +14,24 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.schema;
 | 
					package com.google.gerrit.server.schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
 | 
					import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Strings;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.server.account.HashedPassword;
 | 
					import com.google.gerrit.server.account.HashedPassword;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
 | 
					import com.google.gwtorm.jdbc.JdbcSchema;
 | 
				
			||||||
import com.google.gwtorm.server.OrmException;
 | 
					import com.google.gwtorm.server.OrmException;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Provider;
 | 
					import com.google.inject.Provider;
 | 
				
			||||||
 | 
					import java.sql.PreparedStatement;
 | 
				
			||||||
 | 
					import java.sql.ResultSet;
 | 
				
			||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
import java.util.List;
 | 
					import java.sql.Statement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class Schema_142 extends SchemaVersion {
 | 
					public class Schema_142 extends SchemaVersion {
 | 
				
			||||||
 | 
					  private static final int MAX_BATCH_SIZE = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  Schema_142(Provider<Schema_141> prior) {
 | 
					  Schema_142(Provider<Schema_141> prior) {
 | 
				
			||||||
    super(prior);
 | 
					    super(prior);
 | 
				
			||||||
@@ -31,19 +39,39 @@ public class Schema_142 extends SchemaVersion {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
 | 
					  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
 | 
				
			||||||
    List<AccountExternalId> newIds = db.accountExternalIds().all().toList();
 | 
					    try (PreparedStatement updateStmt =
 | 
				
			||||||
    for (AccountExternalId id : newIds) {
 | 
					        ((JdbcSchema) db)
 | 
				
			||||||
      if (!id.isScheme(AccountExternalId.SCHEME_USERNAME)) {
 | 
					            .getConnection()
 | 
				
			||||||
        continue;
 | 
					            .prepareStatement(
 | 
				
			||||||
 | 
					                "UPDATE account_external_ids " + "SET password = ? " + "WHERE external_id = ?")) {
 | 
				
			||||||
 | 
					      int batchCount = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      try (Statement stmt = newStatement(db);
 | 
				
			||||||
 | 
					          ResultSet rs =
 | 
				
			||||||
 | 
					              stmt.executeQuery("SELECT external_id, password FROM account_external_ids")) {
 | 
				
			||||||
 | 
					        while (rs.next()) {
 | 
				
			||||||
 | 
					          String externalId = rs.getString("external_id");
 | 
				
			||||||
 | 
					          String password = rs.getString("password");
 | 
				
			||||||
 | 
					          if (!ExternalId.Key.parse(externalId).isScheme(SCHEME_USERNAME)
 | 
				
			||||||
 | 
					              || Strings.isNullOrEmpty(password)) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          HashedPassword hashed = HashedPassword.fromPassword(password);
 | 
				
			||||||
 | 
					          updateStmt.setString(1, hashed.encode());
 | 
				
			||||||
 | 
					          updateStmt.setString(2, externalId);
 | 
				
			||||||
 | 
					          updateStmt.addBatch();
 | 
				
			||||||
 | 
					          batchCount++;
 | 
				
			||||||
 | 
					          if (batchCount >= MAX_BATCH_SIZE) {
 | 
				
			||||||
 | 
					            updateStmt.executeBatch();
 | 
				
			||||||
 | 
					            batchCount = 0;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      String password = id.getPassword();
 | 
					      if (batchCount > 0) {
 | 
				
			||||||
      if (password != null) {
 | 
					        updateStmt.executeBatch();
 | 
				
			||||||
        HashedPassword hashed = HashedPassword.fromPassword(password);
 | 
					 | 
				
			||||||
        id.setPassword(hashed.encode());
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    db.accountExternalIds().upsert(newIds);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.schema;
 | 
					package com.google.gerrit.server.schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.client.Account;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
@@ -21,11 +22,15 @@ import com.google.gerrit.server.account.externalids.ExternalIdReader;
 | 
				
			|||||||
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
 | 
				
			||||||
import com.google.gerrit.server.config.AllUsersName;
 | 
					import com.google.gerrit.server.config.AllUsersName;
 | 
				
			||||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
					import com.google.gerrit.server.git.GitRepositoryManager;
 | 
				
			||||||
 | 
					import com.google.gwtorm.jdbc.JdbcSchema;
 | 
				
			||||||
import com.google.gwtorm.server.OrmException;
 | 
					import com.google.gwtorm.server.OrmException;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Provider;
 | 
					import com.google.inject.Provider;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.sql.ResultSet;
 | 
				
			||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
 | 
					import java.sql.Statement;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
					import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
				
			||||||
import org.eclipse.jgit.lib.ObjectId;
 | 
					import org.eclipse.jgit.lib.ObjectId;
 | 
				
			||||||
@@ -56,7 +61,26 @@ public class Schema_144 extends SchemaVersion {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
 | 
					  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
 | 
				
			||||||
    Set<ExternalId> toAdd = ExternalId.from(db.accountExternalIds().all().toList());
 | 
					    Set<ExternalId> toAdd = new HashSet<>();
 | 
				
			||||||
 | 
					    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
 | 
				
			||||||
 | 
					        ResultSet rs =
 | 
				
			||||||
 | 
					            stmt.executeQuery(
 | 
				
			||||||
 | 
					                "SELECT "
 | 
				
			||||||
 | 
					                    + "account_id, "
 | 
				
			||||||
 | 
					                    + "email_address, "
 | 
				
			||||||
 | 
					                    + "password, "
 | 
				
			||||||
 | 
					                    + "external_id "
 | 
				
			||||||
 | 
					                    + "FROM account_external_ids")) {
 | 
				
			||||||
 | 
					      while (rs.next()) {
 | 
				
			||||||
 | 
					        Account.Id accountId = new Account.Id(rs.getInt(1));
 | 
				
			||||||
 | 
					        String email = rs.getString(2);
 | 
				
			||||||
 | 
					        String password = rs.getString(3);
 | 
				
			||||||
 | 
					        String externalId = rs.getString(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        toAdd.add(ExternalId.create(ExternalId.Key.parse(externalId), accountId, email, password));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      try (Repository repo = repoManager.openRepository(allUsersName);
 | 
					      try (Repository repo = repoManager.openRepository(allUsersName);
 | 
				
			||||||
          RevWalk rw = new RevWalk(repo);
 | 
					          RevWalk rw = new RevWalk(repo);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					// 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.schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
 | 
					import com.google.inject.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Drop ACCOUNT_EXTERNAL_IDS table. */
 | 
				
			||||||
 | 
					public class Schema_150 extends SchemaVersion {
 | 
				
			||||||
 | 
					  @Inject
 | 
				
			||||||
 | 
					  Schema_150(Provider<Schema_149> prior) {
 | 
				
			||||||
 | 
					    super(prior);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,7 +15,6 @@
 | 
				
			|||||||
package com.google.gerrit.testutil;
 | 
					package com.google.gerrit.testutil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.AccountAccess;
 | 
					import com.google.gerrit.reviewdb.server.AccountAccess;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.AccountExternalIdAccess;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.server.AccountGroupAccess;
 | 
					import com.google.gerrit.reviewdb.server.AccountGroupAccess;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.AccountGroupByIdAccess;
 | 
					import com.google.gerrit.reviewdb.server.AccountGroupByIdAccess;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.AccountGroupByIdAudAccess;
 | 
					import com.google.gerrit.reviewdb.server.AccountGroupByIdAudAccess;
 | 
				
			||||||
@@ -88,11 +87,6 @@ public class DisabledReviewDb implements ReviewDb {
 | 
				
			|||||||
    throw new Disabled();
 | 
					    throw new Disabled();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public AccountExternalIdAccess accountExternalIds() {
 | 
					 | 
				
			||||||
    throw new Disabled();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public AccountGroupAccess accountGroups() {
 | 
					  public AccountGroupAccess accountGroups() {
 | 
				
			||||||
    throw new Disabled();
 | 
					    throw new Disabled();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,14 +19,12 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USE
 | 
				
			|||||||
import com.google.common.cache.CacheLoader;
 | 
					import com.google.common.cache.CacheLoader;
 | 
				
			||||||
import com.google.common.cache.LoadingCache;
 | 
					import com.google.common.cache.LoadingCache;
 | 
				
			||||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
 | 
					import com.google.gerrit.reviewdb.client.AccountSshKey;
 | 
				
			||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 | 
					import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalId;
 | 
					import com.google.gerrit.server.account.externalids.ExternalId;
 | 
				
			||||||
import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
					import com.google.gerrit.server.account.externalids.ExternalIds;
 | 
				
			||||||
import com.google.gerrit.server.cache.CacheModule;
 | 
					import com.google.gerrit.server.cache.CacheModule;
 | 
				
			||||||
import com.google.gerrit.server.ssh.SshKeyCache;
 | 
					import com.google.gerrit.server.ssh.SshKeyCache;
 | 
				
			||||||
import com.google.gerrit.server.ssh.SshKeyCreator;
 | 
					import com.google.gerrit.server.ssh.SshKeyCreator;
 | 
				
			||||||
import com.google.gwtorm.server.SchemaFactory;
 | 
					 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Module;
 | 
					import com.google.inject.Module;
 | 
				
			||||||
import com.google.inject.Singleton;
 | 
					import com.google.inject.Singleton;
 | 
				
			||||||
@@ -92,40 +90,33 @@ public class SshKeyCacheImpl implements SshKeyCache {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static class Loader extends CacheLoader<String, Iterable<SshKeyCacheEntry>> {
 | 
					  static class Loader extends CacheLoader<String, Iterable<SshKeyCacheEntry>> {
 | 
				
			||||||
    private final SchemaFactory<ReviewDb> schema;
 | 
					 | 
				
			||||||
    private final ExternalIds externalIds;
 | 
					    private final ExternalIds externalIds;
 | 
				
			||||||
    private final VersionedAuthorizedKeys.Accessor authorizedKeys;
 | 
					    private final VersionedAuthorizedKeys.Accessor authorizedKeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Inject
 | 
					    @Inject
 | 
				
			||||||
    Loader(
 | 
					    Loader(ExternalIds externalIds, VersionedAuthorizedKeys.Accessor authorizedKeys) {
 | 
				
			||||||
        SchemaFactory<ReviewDb> schema,
 | 
					 | 
				
			||||||
        ExternalIds externalIds,
 | 
					 | 
				
			||||||
        VersionedAuthorizedKeys.Accessor authorizedKeys) {
 | 
					 | 
				
			||||||
      this.schema = schema;
 | 
					 | 
				
			||||||
      this.externalIds = externalIds;
 | 
					      this.externalIds = externalIds;
 | 
				
			||||||
      this.authorizedKeys = authorizedKeys;
 | 
					      this.authorizedKeys = authorizedKeys;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Iterable<SshKeyCacheEntry> load(String username) throws Exception {
 | 
					    public Iterable<SshKeyCacheEntry> load(String username) throws Exception {
 | 
				
			||||||
      try (ReviewDb db = schema.open()) {
 | 
					      ExternalId user = externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, username));
 | 
				
			||||||
        ExternalId user = externalIds.get(db, ExternalId.Key.create(SCHEME_USERNAME, username));
 | 
					      if (user == null) {
 | 
				
			||||||
        if (user == null) {
 | 
					        return NO_SUCH_USER;
 | 
				
			||||||
          return NO_SUCH_USER;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        List<SshKeyCacheEntry> kl = new ArrayList<>(4);
 | 
					 | 
				
			||||||
        for (AccountSshKey k : authorizedKeys.getKeys(user.accountId())) {
 | 
					 | 
				
			||||||
          if (k.isValid()) {
 | 
					 | 
				
			||||||
            add(kl, k);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (kl.isEmpty()) {
 | 
					 | 
				
			||||||
          return NO_KEYS;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return Collections.unmodifiableList(kl);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      List<SshKeyCacheEntry> kl = new ArrayList<>(4);
 | 
				
			||||||
 | 
					      for (AccountSshKey k : authorizedKeys.getKeys(user.accountId())) {
 | 
				
			||||||
 | 
					        if (k.isValid()) {
 | 
				
			||||||
 | 
					          add(kl, k);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (kl.isEmpty()) {
 | 
				
			||||||
 | 
					        return NO_KEYS;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return Collections.unmodifiableList(kl);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void add(List<SshKeyCacheEntry> kl, AccountSshKey k) {
 | 
					    private void add(List<SshKeyCacheEntry> kl, AccountSshKey k) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user