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:
Edwin Kempin 2017-03-22 14:48:18 +01:00
parent 7cd6a74556
commit 276b8a897f
39 changed files with 256 additions and 645 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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())));

View File

@ -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) {

View File

@ -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());
} }

View File

@ -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();
} }
} }

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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());
} }
} }

View File

@ -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));
} }
} }

View File

@ -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);

View File

@ -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());

View File

@ -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
} }

View File

@ -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());

View File

@ -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));

View File

@ -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();
} }

View File

@ -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);

View File

@ -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());

View File

@ -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.
* *

View File

@ -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. */

View File

@ -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());
} }
} }

View File

@ -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()) {

View File

@ -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);
} }
/** /**

View File

@ -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);
}
} }
} }

View File

@ -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);

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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) {