Add cache for external ids
Introducing a cache for external ids is a preparation step for moving the external ids from ReviewDb into NoteDb. For NoteDb it is planned to store the external ids in a git note branch, where the note keys are the sha1's of the external ids and the note values contain the external id, the account id and optionally email and password. With this format we can easily lookup external ids by the external id, but listing all external ids of an account requires parsing all external ids. Looking up the external ids of an account is possible from the account index, however for reindexing an account we would still need to lookup all external ids of the account from git. Having a cache for the external ids ensures that the external ids are only loaded once from git. If there is any update to external ids, we take care to update the cache as well. For this change it means that all code that modifies external ids must do an extra call to update the external id cache. This is not optimal since updating the cache can be easily forgotten. This is why the follow-up change cleans this up by introducing a dedicated class that handles all external id updates and then this is the only class that must take care to update the external id cache. Change-Id: I9ea979c646cddb9b39e723de5c061a70a2ce6fd6 Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.account.AccountByEmailCache;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
|
||||
import com.google.gerrit.server.index.account.AccountIndexer;
|
||||
@@ -56,6 +57,7 @@ public class AccountCreator {
|
||||
private final AccountCache accountCache;
|
||||
private final AccountByEmailCache byEmailCache;
|
||||
private final AccountIndexer indexer;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
AccountCreator(SchemaFactory<ReviewDb> schema,
|
||||
@@ -64,7 +66,8 @@ public class AccountCreator {
|
||||
SshKeyCache sshKeyCache,
|
||||
AccountCache accountCache,
|
||||
AccountByEmailCache byEmailCache,
|
||||
AccountIndexer indexer) {
|
||||
AccountIndexer indexer,
|
||||
ExternalIdCache externalIdCache) {
|
||||
accounts = new HashMap<>();
|
||||
reviewDbProvider = schema;
|
||||
this.authorizedKeys = authorizedKeys;
|
||||
@@ -73,6 +76,7 @@ public class AccountCreator {
|
||||
this.accountCache = accountCache;
|
||||
this.byEmailCache = byEmailCache;
|
||||
this.indexer = indexer;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
public synchronized TestAccount create(String username, String email,
|
||||
@@ -90,11 +94,13 @@ public class AccountCreator {
|
||||
String httpPass = "http-pass";
|
||||
extUser.setPassword(httpPass);
|
||||
db.accountExternalIds().insert(Collections.singleton(extUser));
|
||||
externalIdCache.onCreate(extUser);
|
||||
|
||||
if (email != null) {
|
||||
AccountExternalId extMailto = new AccountExternalId(id, getEmailKey(email));
|
||||
extMailto.setEmailAddress(email);
|
||||
db.accountExternalIds().insert(Collections.singleton(extMailto));
|
||||
externalIdCache.onCreate(extMailto);
|
||||
}
|
||||
|
||||
Account a = new Account(id, TimeUtil.nowTs());
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.google.gerrit.gpg.testutil.TestKey;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
@@ -112,6 +113,9 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
@Inject
|
||||
private AllUsersName allUsers;
|
||||
|
||||
@Inject
|
||||
private ExternalIdCache externalIdCache;
|
||||
|
||||
private List<AccountExternalId> savedExternalIds;
|
||||
|
||||
@Before
|
||||
@@ -127,10 +131,18 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
// savedExternalIds is null when we don't run SSH tests and the assume in
|
||||
// @Before in AbstractDaemonTest prevents this class' @Before method from
|
||||
// being executed.
|
||||
db.accountExternalIds().delete(getExternalIds(admin));
|
||||
db.accountExternalIds().delete(getExternalIds(user));
|
||||
Collection<AccountExternalId> adminExtIds = getExternalIds(admin);
|
||||
db.accountExternalIds().delete(adminExtIds);
|
||||
externalIdCache.onRemove(adminExtIds);
|
||||
|
||||
Collection<AccountExternalId> userExtIds = getExternalIds(user);
|
||||
db.accountExternalIds().delete(userExtIds);
|
||||
externalIdCache.onRemove(userExtIds);
|
||||
|
||||
db.accountExternalIds().insert(savedExternalIds);
|
||||
externalIdCache.onCreate(savedExternalIds);
|
||||
}
|
||||
|
||||
accountCache.evict(admin.getId());
|
||||
accountCache.evict(user.getId());
|
||||
}
|
||||
@@ -595,6 +607,7 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
user.getId(), new AccountExternalId.Key("foo:myId"));
|
||||
|
||||
db.accountExternalIds().insert(Collections.singleton(extId));
|
||||
externalIdCache.onCreate(extId);
|
||||
accountCache.evict(user.getId());
|
||||
|
||||
TestKey key = validKeyWithSecondUserId();
|
||||
@@ -791,8 +804,9 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
Account.Id currAccountId = atrScope.get().getUser().getAccountId();
|
||||
Iterable<String> expectedFps = expected.transform(
|
||||
k -> BaseEncoding.base16().encode(k.getPublicKey().getFingerprint()));
|
||||
Iterable<String> actualFps = GpgKeys.getGpgExtIds(db, currAccountId)
|
||||
.transform(AccountExternalId::getSchemeRest);
|
||||
Iterable<String> actualFps =
|
||||
GpgKeys.getGpgExtIds(externalIdCache, currAccountId)
|
||||
.transform(AccountExternalId::getSchemeRest);
|
||||
assertThat(actualFps)
|
||||
.named("external IDs in database")
|
||||
.containsExactlyElementsIn(expectedFps);
|
||||
@@ -825,6 +839,7 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
account.getId(), new AccountExternalId.Key(name("test"), email));
|
||||
extId.setEmailAddress(email);
|
||||
db.accountExternalIds().insert(Collections.singleton(extId));
|
||||
externalIdCache.onCreate(extId);
|
||||
// Clear saved AccountState and AccountExternalIds.
|
||||
accountCache.evict(account.getId());
|
||||
setApiUser(account);
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -47,16 +48,19 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
|
||||
private final Provider<ReviewDb> db;
|
||||
private final Provider<PublicKeyStore> storeProvider;
|
||||
private final AccountCache accountCache;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
DeleteGpgKey(@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||
Provider<ReviewDb> db,
|
||||
Provider<PublicKeyStore> storeProvider,
|
||||
AccountCache accountCache) {
|
||||
AccountCache accountCache,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.serverIdent = serverIdent;
|
||||
this.db = db;
|
||||
this.storeProvider = storeProvider;
|
||||
this.accountCache = accountCache;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,6 +72,7 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
|
||||
AccountExternalId.SCHEME_GPGKEY,
|
||||
BaseEncoding.base16().encode(key.getFingerprint()));
|
||||
db.get().accountExternalIds().deleteKeys(Collections.singleton(extIdKey));
|
||||
externalIdCache.onRemove(rsrc.getUser().getAccountId(), extIdKey);
|
||||
accountCache.evict(rsrc.getUser().getAccountId());
|
||||
|
||||
try (PublicKeyStore store = storeProvider.get()) {
|
||||
|
||||
@@ -38,9 +38,9 @@ import com.google.gerrit.gpg.PublicKeyChecker;
|
||||
import com.google.gerrit.gpg.PublicKeyStore;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -69,22 +69,22 @@ public class GpgKeys implements
|
||||
public static String MIME_TYPE = "application/pgp-keys";
|
||||
|
||||
private final DynamicMap<RestView<GpgKey>> views;
|
||||
private final Provider<ReviewDb> db;
|
||||
private final Provider<CurrentUser> self;
|
||||
private final Provider<PublicKeyStore> storeProvider;
|
||||
private final GerritPublicKeyChecker.Factory checkerFactory;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
GpgKeys(DynamicMap<RestView<GpgKey>> views,
|
||||
Provider<ReviewDb> db,
|
||||
Provider<CurrentUser> self,
|
||||
Provider<PublicKeyStore> storeProvider,
|
||||
GerritPublicKeyChecker.Factory checkerFactory) {
|
||||
GerritPublicKeyChecker.Factory checkerFactory,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.views = views;
|
||||
this.db = db;
|
||||
this.self = self;
|
||||
this.storeProvider = storeProvider;
|
||||
this.checkerFactory = checkerFactory;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,15 +208,14 @@ public class GpgKeys implements
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static FluentIterable<AccountExternalId> getGpgExtIds(ReviewDb db,
|
||||
Account.Id accountId) throws OrmException {
|
||||
return FluentIterable.from(db.accountExternalIds().byAccount(accountId))
|
||||
public static FluentIterable<AccountExternalId> getGpgExtIds(
|
||||
ExternalIdCache externalIdCache, Account.Id accountId) {
|
||||
return FluentIterable.from(externalIdCache.byAccount(accountId))
|
||||
.filter(in -> in.isScheme(SCHEME_GPGKEY));
|
||||
}
|
||||
|
||||
private Iterable<AccountExternalId> getGpgExtIds(AccountResource rsrc)
|
||||
throws OrmException {
|
||||
return getGpgExtIds(db.get(), rsrc.getUser().getAccountId());
|
||||
private Iterable<AccountExternalId> getGpgExtIds(AccountResource rsrc) {
|
||||
return getGpgExtIds(externalIdCache, rsrc.getUser().getAccountId());
|
||||
}
|
||||
|
||||
private static long keyId(byte[] fp) {
|
||||
|
||||
@@ -17,11 +17,11 @@ package com.google.gerrit.gpg.server;
|
||||
import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
|
||||
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -47,6 +47,7 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gerrit.server.mail.send.AddKeySender;
|
||||
import com.google.gerrit.server.query.account.InternalAccountQuery;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
@@ -90,6 +91,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
||||
private final AddKeySender.Factory addKeyFactory;
|
||||
private final AccountCache accountCache;
|
||||
private final Provider<InternalAccountQuery> accountQueryProvider;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
PostGpgKeys(@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||
@@ -99,7 +101,8 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
||||
GerritPublicKeyChecker.Factory checkerFactory,
|
||||
AddKeySender.Factory addKeyFactory,
|
||||
AccountCache accountCache,
|
||||
Provider<InternalAccountQuery> accountQueryProvider) {
|
||||
Provider<InternalAccountQuery> accountQueryProvider,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.serverIdent = serverIdent;
|
||||
this.db = db;
|
||||
this.self = self;
|
||||
@@ -108,6 +111,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
||||
this.addKeyFactory = addKeyFactory;
|
||||
this.accountCache = accountCache;
|
||||
this.accountQueryProvider = accountQueryProvider;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,7 +121,8 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
||||
GpgKeys.checkVisible(self, rsrc);
|
||||
|
||||
List<AccountExternalId> existingExtIds =
|
||||
GpgKeys.getGpgExtIds(db.get(), rsrc.getUser().getAccountId()).toList();
|
||||
GpgKeys.getGpgExtIds(externalIdCache,
|
||||
rsrc.getUser().getAccountId()).toList();
|
||||
|
||||
try (PublicKeyStore store = storeProvider.get()) {
|
||||
Set<Fingerprint> toRemove = readKeysToRemove(input, existingExtIds);
|
||||
@@ -142,9 +147,13 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
||||
storeKeys(rsrc, newKeys, toRemove);
|
||||
if (!newExtIds.isEmpty()) {
|
||||
db.get().accountExternalIds().insert(newExtIds);
|
||||
externalIdCache.onCreate(newExtIds);
|
||||
}
|
||||
db.get().accountExternalIds().deleteKeys(
|
||||
Iterables.transform(toRemove, fp -> toExtIdKey(fp.get())));
|
||||
|
||||
List<AccountExternalId.Key> extIdKeysToRemove =
|
||||
toRemove.stream().map(fp -> toExtIdKey(fp.get())).collect(toList());
|
||||
db.get().accountExternalIds().deleteKeys(extIdKeysToRemove);
|
||||
externalIdCache.onRemove(rsrc.getUser().getAccountId(), extIdKeysToRemove);
|
||||
accountCache.evict(rsrc.getUser().getAccountId());
|
||||
return toJson(newKeys, toRemove, store, rsrc.getUser());
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountManager;
|
||||
import com.google.gerrit.server.account.AuthRequest;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gerrit.server.schema.SchemaCreator;
|
||||
import com.google.gerrit.server.util.RequestContext;
|
||||
import com.google.gerrit.server.util.ThreadLocalRequestContext;
|
||||
@@ -69,6 +70,7 @@ import org.junit.Test;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -95,6 +97,9 @@ public class GerritPublicKeyCheckerTest {
|
||||
@Inject
|
||||
private ThreadLocalRequestContext requestContext;
|
||||
|
||||
@Inject
|
||||
private ExternalIdCache externalIdCache;
|
||||
|
||||
private LifecycleManager lifecycle;
|
||||
private ReviewDb db;
|
||||
private Account.Id userId;
|
||||
@@ -229,8 +234,10 @@ public class GerritPublicKeyCheckerTest {
|
||||
|
||||
@Test
|
||||
public void noExternalIds() throws Exception {
|
||||
db.accountExternalIds().delete(
|
||||
db.accountExternalIds().byAccount(user.getAccountId()));
|
||||
Collection<AccountExternalId> extIds =
|
||||
externalIdCache.byAccount(user.getAccountId());
|
||||
db.accountExternalIds().delete(extIds);
|
||||
externalIdCache.onRemove(extIds);
|
||||
reloadUser();
|
||||
|
||||
TestKey key = validKeyWithSecondUserId();
|
||||
@@ -248,9 +255,10 @@ public class GerritPublicKeyCheckerTest {
|
||||
checker.check(key.getPublicKey()), Status.BAD,
|
||||
"Key is not associated with any users");
|
||||
|
||||
db.accountExternalIds().insert(Collections.singleton(
|
||||
new AccountExternalId(
|
||||
user.getAccountId(), toExtIdKey(key.getPublicKey()))));
|
||||
AccountExternalId extId = new AccountExternalId(user.getAccountId(),
|
||||
toExtIdKey(key.getPublicKey()));
|
||||
db.accountExternalIds().insert(Collections.singleton(extId));
|
||||
externalIdCache.onCreate(extId);
|
||||
reloadUser();
|
||||
assertProblems(
|
||||
checker.check(key.getPublicKey()), Status.BAD,
|
||||
@@ -427,6 +435,7 @@ public class GerritPublicKeyCheckerTest {
|
||||
assertThat(store.save(cb)).isAnyOf(NEW, FAST_FORWARD, FORCED);
|
||||
|
||||
db.accountExternalIds().insert(newExtIds);
|
||||
externalIdCache.onCreate(newExtIds);
|
||||
accountCache.evict(user.getAccountId());
|
||||
}
|
||||
|
||||
@@ -459,6 +468,7 @@ public class GerritPublicKeyCheckerTest {
|
||||
extId.setEmailAddress(email);
|
||||
}
|
||||
db.accountExternalIds().insert(Collections.singleton(extId));
|
||||
externalIdCache.onCreate(extId);
|
||||
reloadUser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountByEmailCache;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
@@ -42,21 +43,24 @@ class DeleteExternalIds extends Handler<Set<AccountExternalId.Key>> {
|
||||
private final ExternalIdDetailFactory detailFactory;
|
||||
private final AccountByEmailCache byEmailCache;
|
||||
private final AccountCache accountCache;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
private final Set<AccountExternalId.Key> keys;
|
||||
|
||||
@Inject
|
||||
DeleteExternalIds(final ReviewDb db, final IdentifiedUser user,
|
||||
final ExternalIdDetailFactory detailFactory,
|
||||
final AccountByEmailCache byEmailCache, final AccountCache accountCache,
|
||||
|
||||
@Assisted final Set<AccountExternalId.Key> keys) {
|
||||
DeleteExternalIds(ReviewDb db,
|
||||
IdentifiedUser user,
|
||||
ExternalIdDetailFactory detailFactory,
|
||||
AccountByEmailCache byEmailCache,
|
||||
AccountCache accountCache,
|
||||
ExternalIdCache externalIdCache,
|
||||
@Assisted Set<AccountExternalId.Key> keys) {
|
||||
this.db = db;
|
||||
this.user = user;
|
||||
this.detailFactory = detailFactory;
|
||||
this.byEmailCache = byEmailCache;
|
||||
this.accountCache = accountCache;
|
||||
|
||||
this.externalIdCache = externalIdCache;
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
@@ -74,6 +78,7 @@ class DeleteExternalIds extends Handler<Set<AccountExternalId.Key>> {
|
||||
|
||||
if (!toDelete.isEmpty()) {
|
||||
db.accountExternalIds().delete(toDelete);
|
||||
externalIdCache.onRemove(toDelete);
|
||||
accountCache.evict(user.getAccountId());
|
||||
for (AccountExternalId e : toDelete) {
|
||||
byEmailCache.evict(e.getEmailAddress());
|
||||
|
||||
@@ -20,12 +20,13 @@ import com.google.gerrit.extensions.registration.DynamicItem;
|
||||
import com.google.gerrit.httpd.WebSession;
|
||||
import com.google.gerrit.httpd.rpc.Handler;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.ExternalIdCache;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,25 +35,27 @@ class ExternalIdDetailFactory extends Handler<List<AccountExternalId>> {
|
||||
ExternalIdDetailFactory create();
|
||||
}
|
||||
|
||||
private final ReviewDb db;
|
||||
private final IdentifiedUser user;
|
||||
private final AuthConfig authConfig;
|
||||
private final DynamicItem<WebSession> session;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
ExternalIdDetailFactory(final ReviewDb db, final IdentifiedUser user,
|
||||
final AuthConfig authConfig, final DynamicItem<WebSession> session) {
|
||||
this.db = db;
|
||||
ExternalIdDetailFactory(IdentifiedUser user,
|
||||
AuthConfig authConfig,
|
||||
DynamicItem<WebSession> session,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.user = user;
|
||||
this.authConfig = authConfig;
|
||||
this.session = session;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccountExternalId> call() throws OrmException {
|
||||
final AccountExternalId.Key last = session.get().getLastLoginExternalId();
|
||||
final List<AccountExternalId> ids =
|
||||
db.accountExternalIds().byAccount(user.getAccountId()).toList();
|
||||
AccountExternalId.Key last = session.get().getLastLoginExternalId();
|
||||
List<AccountExternalId> ids =
|
||||
new ArrayList<>(externalIdCache.byAccount(user.getAccountId()));
|
||||
|
||||
for (final AccountExternalId e : ids) {
|
||||
e.setTrusted(authConfig.isIdentityTrustable(Collections.singleton(e)));
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.gerrit.server.account.AccountVisibility;
|
||||
import com.google.gerrit.server.account.AccountVisibilityProvider;
|
||||
import com.google.gerrit.server.account.CapabilityCollection;
|
||||
import com.google.gerrit.server.account.CapabilityControl;
|
||||
import com.google.gerrit.server.account.ExternalIdCacheImpl;
|
||||
import com.google.gerrit.server.account.FakeRealm;
|
||||
import com.google.gerrit.server.account.GroupCacheImpl;
|
||||
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
|
||||
@@ -153,6 +154,7 @@ public class BatchProgramModule extends FactoryModule {
|
||||
install(new PrologModule());
|
||||
install(AccountByEmailCacheImpl.module());
|
||||
install(AccountCacheImpl.module());
|
||||
install(ExternalIdCacheImpl.module());
|
||||
install(GroupCacheImpl.module());
|
||||
install(GroupIncludeCacheImpl.module());
|
||||
install(ProjectCacheImpl.module());
|
||||
|
||||
@@ -18,6 +18,8 @@ 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 {
|
||||
/**
|
||||
@@ -167,4 +169,21 @@ public final class AccountExternalId {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
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;
|
||||
@@ -28,9 +27,6 @@ public interface AccountExternalIdAccess extends
|
||||
@PrimaryKey("key")
|
||||
AccountExternalId get(AccountExternalId.Key key) throws OrmException;
|
||||
|
||||
@Query("WHERE accountId = ?")
|
||||
ResultSet<AccountExternalId> byAccount(Account.Id id) throws OrmException;
|
||||
|
||||
@Query
|
||||
ResultSet<AccountExternalId> all() throws OrmException;
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ public class AccountCacheImpl implements AccountCache {
|
||||
private final GeneralPreferencesLoader loader;
|
||||
private final LoadingCache<String, Optional<Account.Id>> byName;
|
||||
private final Provider<WatchConfig.Accessor> watchConfig;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
ByIdLoader(SchemaFactory<ReviewDb> sf,
|
||||
@@ -162,12 +163,14 @@ public class AccountCacheImpl implements AccountCache {
|
||||
GeneralPreferencesLoader loader,
|
||||
@Named(BYUSER_NAME) LoadingCache<String,
|
||||
Optional<Account.Id>> byUsername,
|
||||
Provider<WatchConfig.Accessor> watchConfig) {
|
||||
Provider<WatchConfig.Accessor> watchConfig,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.schema = sf;
|
||||
this.groupCache = groupCache;
|
||||
this.loader = loader;
|
||||
this.byName = byUsername;
|
||||
this.watchConfig = watchConfig;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,8 +194,7 @@ public class AccountCacheImpl implements AccountCache {
|
||||
}
|
||||
|
||||
Collection<AccountExternalId> externalIds =
|
||||
Collections.unmodifiableCollection(
|
||||
db.accountExternalIds().byAccount(who).toList());
|
||||
externalIdCache.byAccount(who);
|
||||
|
||||
Set<AccountGroup.UUID> internalGroups = new HashSet<>();
|
||||
for (AccountGroupMember g : db.accountGroupMembers().byAccount(who)) {
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.query.account.InternalAccountQuery;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -42,6 +41,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -63,6 +63,7 @@ public class AccountManager {
|
||||
private final AtomicBoolean awaitsFirstAccountCheck;
|
||||
private final AuditService auditService;
|
||||
private final Provider<InternalAccountQuery> accountQueryProvider;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
AccountManager(SchemaFactory<ReviewDb> schema,
|
||||
@@ -73,7 +74,8 @@ public class AccountManager {
|
||||
ChangeUserName.Factory changeUserNameFactory,
|
||||
ProjectCache projectCache,
|
||||
AuditService auditService,
|
||||
Provider<InternalAccountQuery> accountQueryProvider) {
|
||||
Provider<InternalAccountQuery> accountQueryProvider,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.schema = schema;
|
||||
this.byIdCache = byIdCache;
|
||||
this.byEmailCache = byEmailCache;
|
||||
@@ -84,6 +86,7 @@ public class AccountManager {
|
||||
this.awaitsFirstAccountCheck = new AtomicBoolean(true);
|
||||
this.auditService = auditService;
|
||||
this.accountQueryProvider = accountQueryProvider;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,6 +174,7 @@ public class AccountManager {
|
||||
|
||||
extId.setEmailAddress(newEmail);
|
||||
db.accountExternalIds().update(Collections.singleton(extId));
|
||||
externalIdCache.onUpdate(extId);
|
||||
}
|
||||
|
||||
if (!realm.allowsEdit(AccountFieldName.FULL_NAME)
|
||||
@@ -242,6 +246,7 @@ public class AccountManager {
|
||||
+ "\" to account " + newId + "; external ID already in use.");
|
||||
}
|
||||
db.accountExternalIds().upsert(Collections.singleton(extId));
|
||||
externalIdCache.onUpdate(extId);
|
||||
} finally {
|
||||
// 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
|
||||
@@ -335,6 +340,7 @@ public class AccountManager {
|
||||
// the database
|
||||
db.accounts().delete(Collections.singleton(account));
|
||||
db.accountExternalIds().delete(Collections.singleton(extId));
|
||||
externalIdCache.onRemove(extId);
|
||||
throw new AccountUserNameException(errorMessage, e);
|
||||
}
|
||||
}
|
||||
@@ -367,6 +373,7 @@ public class AccountManager {
|
||||
extId = createId(to, who);
|
||||
extId.setEmailAddress(who.getEmailAddress());
|
||||
db.accountExternalIds().insert(Collections.singleton(extId));
|
||||
externalIdCache.onCreate(extId);
|
||||
|
||||
if (who.getEmailAddress() != null) {
|
||||
Account a = db.accounts().get(to);
|
||||
@@ -405,12 +412,12 @@ public class AccountManager {
|
||||
try (ReviewDb db = schema.open()) {
|
||||
AccountExternalId.Key key = id(who);
|
||||
List<AccountExternalId.Key> filteredKeysByScheme =
|
||||
filterKeysByScheme(key.getScheme(), db.accountExternalIds()
|
||||
.byAccount(to));
|
||||
filterKeysByScheme(key.getScheme(), externalIdCache.byAccount(to));
|
||||
if (!filteredKeysByScheme.isEmpty()
|
||||
&& (filteredKeysByScheme.size() > 1 || !filteredKeysByScheme
|
||||
.contains(key))) {
|
||||
db.accountExternalIds().deleteKeys(filteredKeysByScheme);
|
||||
externalIdCache.onRemove(to, filteredKeysByScheme);
|
||||
}
|
||||
byIdCache.evict(to);
|
||||
return link(to, who);
|
||||
@@ -418,7 +425,7 @@ public class AccountManager {
|
||||
}
|
||||
|
||||
private List<AccountExternalId.Key> filterKeysByScheme(
|
||||
String keyScheme, ResultSet<AccountExternalId> externalIds) {
|
||||
String keyScheme, Collection<AccountExternalId> externalIds) {
|
||||
List<AccountExternalId.Key> filteredExternalIds = new ArrayList<>();
|
||||
for (AccountExternalId accountExternalId : externalIds) {
|
||||
if (accountExternalId.isScheme(keyScheme)) {
|
||||
@@ -448,6 +455,7 @@ public class AccountManager {
|
||||
"Identity '" + key.get() + "' in use by another account");
|
||||
}
|
||||
db.accountExternalIds().delete(Collections.singleton(extId));
|
||||
externalIdCache.onRemove(extId);
|
||||
|
||||
if (who.getEmailAddress() != null) {
|
||||
Account a = db.accounts().get(from);
|
||||
|
||||
@@ -51,19 +51,22 @@ public class ChangeUserName implements Callable<VoidResult> {
|
||||
|
||||
private final AccountCache accountCache;
|
||||
private final SshKeyCache sshKeyCache;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
private final ReviewDb db;
|
||||
private final IdentifiedUser user;
|
||||
private final String newUsername;
|
||||
|
||||
@Inject
|
||||
ChangeUserName(final AccountCache accountCache,
|
||||
final SshKeyCache sshKeyCache,
|
||||
|
||||
@Assisted final ReviewDb db, @Assisted final IdentifiedUser user,
|
||||
@Nullable @Assisted final String newUsername) {
|
||||
ChangeUserName(AccountCache accountCache,
|
||||
SshKeyCache sshKeyCache,
|
||||
ExternalIdCache externalIdCache,
|
||||
@Assisted ReviewDb db,
|
||||
@Assisted IdentifiedUser user,
|
||||
@Nullable @Assisted String newUsername) {
|
||||
this.accountCache = accountCache;
|
||||
this.sshKeyCache = sshKeyCache;
|
||||
this.externalIdCache = externalIdCache;
|
||||
|
||||
this.db = db;
|
||||
this.user = user;
|
||||
@@ -96,6 +99,7 @@ public class ChangeUserName implements Callable<VoidResult> {
|
||||
}
|
||||
|
||||
db.accountExternalIds().insert(Collections.singleton(id));
|
||||
externalIdCache.onCreate(id);
|
||||
} catch (OrmDuplicateKeyException dupeErr) {
|
||||
// If we are using this identity, don't report the exception.
|
||||
//
|
||||
@@ -113,6 +117,7 @@ public class ChangeUserName implements Callable<VoidResult> {
|
||||
// If we have any older user names, remove them.
|
||||
//
|
||||
db.accountExternalIds().delete(old);
|
||||
externalIdCache.onRemove(old);
|
||||
for (AccountExternalId i : old) {
|
||||
sshKeyCache.evict(i.getSchemeRest());
|
||||
accountCache.evictByUsername(i.getSchemeRest());
|
||||
@@ -124,9 +129,9 @@ public class ChangeUserName implements Callable<VoidResult> {
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
|
||||
private Collection<AccountExternalId> old() throws OrmException {
|
||||
private Collection<AccountExternalId> old() {
|
||||
final Collection<AccountExternalId> r = new ArrayList<>(1);
|
||||
for (AccountExternalId i : db.accountExternalIds().byAccount(
|
||||
for (AccountExternalId i : externalIdCache.byAccount(
|
||||
user.getAccountId())) {
|
||||
if (i.isScheme(SCHEME_USERNAME)) {
|
||||
r.add(i);
|
||||
|
||||
@@ -73,6 +73,7 @@ public class CreateAccount
|
||||
private final AccountLoader.Factory infoLoader;
|
||||
private final DynamicSet<AccountExternalIdCreator> externalIdCreators;
|
||||
private final AuditService auditService;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
private final String username;
|
||||
|
||||
@Inject
|
||||
@@ -87,6 +88,7 @@ public class CreateAccount
|
||||
AccountLoader.Factory infoLoader,
|
||||
DynamicSet<AccountExternalIdCreator> externalIdCreators,
|
||||
AuditService auditService,
|
||||
ExternalIdCache externalIdCache,
|
||||
@Assisted String username) {
|
||||
this.db = db;
|
||||
this.currentUser = currentUser;
|
||||
@@ -99,6 +101,7 @@ public class CreateAccount
|
||||
this.infoLoader = infoLoader;
|
||||
this.externalIdCreators = externalIdCreators;
|
||||
this.auditService = auditService;
|
||||
this.externalIdCache = externalIdCache;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@@ -153,6 +156,7 @@ public class CreateAccount
|
||||
|
||||
try {
|
||||
db.accountExternalIds().insert(externalIds);
|
||||
externalIdCache.onCreate(externalIds);
|
||||
} catch (OrmDuplicateKeyException duplicateKey) {
|
||||
throw new ResourceConflictException(
|
||||
"username '" + username + "' already exists");
|
||||
@@ -164,9 +168,11 @@ public class CreateAccount
|
||||
extMailto.setEmailAddress(input.email);
|
||||
try {
|
||||
db.accountExternalIds().insert(Collections.singleton(extMailto));
|
||||
externalIdCache.onCreate(extMailto);
|
||||
} catch (OrmDuplicateKeyException duplicateKey) {
|
||||
try {
|
||||
db.accountExternalIds().delete(Collections.singleton(extUser));
|
||||
externalIdCache.onRemove(extUser);
|
||||
} catch (OrmException cleanupError) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/** Caches external ids of all accounts */
|
||||
public interface ExternalIdCache {
|
||||
void onCreate(Iterable<AccountExternalId> extId);
|
||||
void onRemove(Iterable<AccountExternalId> extId);
|
||||
void onRemove(Account.Id accountId,
|
||||
Iterable<AccountExternalId.Key> extIdKeys);
|
||||
void onUpdate(AccountExternalId extId);
|
||||
Collection<AccountExternalId> byAccount(Account.Id accountId);
|
||||
|
||||
default void onCreate(AccountExternalId extId) {
|
||||
onCreate(Collections.singleton(extId));
|
||||
}
|
||||
|
||||
default void onRemove(AccountExternalId extId) {
|
||||
onRemove(Collections.singleton(extId));
|
||||
}
|
||||
|
||||
default void onRemove(Account.Id accountId, AccountExternalId.Key extIdKey) {
|
||||
onRemove(accountId, Collections.singleton(extIdKey));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Singleton
|
||||
public class ExternalIdCacheImpl implements ExternalIdCache {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(ExternalIdCacheImpl.class);
|
||||
|
||||
private static final String CACHE_NAME = "external_ids_map";
|
||||
|
||||
public static Module module() {
|
||||
return new CacheModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
cache(CACHE_NAME, AllKey.class,
|
||||
new TypeLiteral<ImmutableSetMultimap<Account.Id, AccountExternalId>>() {})
|
||||
.maximumWeight(1).loader(Loader.class);
|
||||
|
||||
bind(ExternalIdCacheImpl.class);
|
||||
bind(ExternalIdCache.class).to(ExternalIdCacheImpl.class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final LoadingCache<AllKey,
|
||||
ImmutableSetMultimap<Account.Id, AccountExternalId>> extIdsByAccount;
|
||||
private final Lock lock;
|
||||
|
||||
@Inject
|
||||
ExternalIdCacheImpl(
|
||||
@Named(CACHE_NAME) LoadingCache<AllKey,
|
||||
ImmutableSetMultimap<Account.Id, AccountExternalId>> extIdsByAccount) {
|
||||
this.extIdsByAccount = extIdsByAccount;
|
||||
this.lock = new ReentrantLock(true /* fair */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Iterable<AccountExternalId> extIds) {
|
||||
lock.lock();
|
||||
try {
|
||||
Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
|
||||
.arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
|
||||
for (AccountExternalId extId : extIds) {
|
||||
n.put(extId.getAccountId(), extId);
|
||||
}
|
||||
extIdsByAccount.put(AllKey.ALL, ImmutableSetMultimap.copyOf(n));
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Cannot list external IDs", e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(Iterable<AccountExternalId> extIds) {
|
||||
lock.lock();
|
||||
try {
|
||||
Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
|
||||
.arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
|
||||
for (AccountExternalId extId : extIds) {
|
||||
n.remove(extId.getAccountId(), extId);
|
||||
}
|
||||
extIdsByAccount.put(AllKey.ALL, ImmutableSetMultimap.copyOf(n));
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Cannot list external IDs", e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(Account.Id accountId,
|
||||
Iterable<AccountExternalId.Key> extIdKeys) {
|
||||
lock.lock();
|
||||
try {
|
||||
Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
|
||||
.arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
|
||||
for (AccountExternalId extId : byAccount(accountId)) {
|
||||
for (AccountExternalId.Key extIdKey : extIdKeys) {
|
||||
if (extIdKey.equals(extId.getKey())) {
|
||||
n.remove(accountId, extId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
extIdsByAccount.put(AllKey.ALL, ImmutableSetMultimap.copyOf(n));
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Cannot list external IDs", e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(AccountExternalId updatedExtId) {
|
||||
lock.lock();
|
||||
try {
|
||||
Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
|
||||
.arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
|
||||
for (AccountExternalId extId : byAccount(updatedExtId.getAccountId())) {
|
||||
if (updatedExtId.getKey().equals(extId.getKey())) {
|
||||
n.remove(updatedExtId.getAccountId(), extId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
n.put(updatedExtId.getAccountId(), updatedExtId);
|
||||
extIdsByAccount.put(AllKey.ALL, ImmutableSetMultimap.copyOf(n));
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Cannot list external IDs", e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AccountExternalId> byAccount(Account.Id accountId) {
|
||||
try {
|
||||
return extIdsByAccount.get(AllKey.ALL).get(accountId);
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Cannot list external ids", e);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
static class AllKey {
|
||||
static final AllKey ALL = new AllKey();
|
||||
|
||||
private AllKey() {
|
||||
}
|
||||
}
|
||||
|
||||
static class Loader
|
||||
extends CacheLoader<AllKey,
|
||||
ImmutableSetMultimap<Account.Id, AccountExternalId>> {
|
||||
private final SchemaFactory<ReviewDb> schema;
|
||||
|
||||
@Inject
|
||||
Loader(SchemaFactory<ReviewDb> schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSetMultimap<Account.Id, AccountExternalId> load(AllKey key)
|
||||
throws Exception {
|
||||
try (ReviewDb db = schema.open()) {
|
||||
Multimap<Account.Id, AccountExternalId> extIdsByAccount =
|
||||
MultimapBuilder.hashKeys().arrayListValues().build();
|
||||
for (AccountExternalId extId : db.accountExternalIds().all()) {
|
||||
extIdsByAccount.put(extId.getAccountId(), extId);
|
||||
}
|
||||
return ImmutableSetMultimap.copyOf(extIdsByAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,13 +60,17 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
private final Provider<CurrentUser> self;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final AccountCache accountCache;
|
||||
private final ExternalIdCache externalIdCache;
|
||||
|
||||
@Inject
|
||||
PutHttpPassword(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
|
||||
AccountCache accountCache) {
|
||||
PutHttpPassword(Provider<CurrentUser> self,
|
||||
Provider<ReviewDb> dbProvider,
|
||||
AccountCache accountCache,
|
||||
ExternalIdCache externalIdCache) {
|
||||
this.self = self;
|
||||
this.dbProvider = dbProvider;
|
||||
this.accountCache = accountCache;
|
||||
this.externalIdCache = externalIdCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,6 +121,7 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
}
|
||||
id.setPassword(newPassword);
|
||||
dbProvider.get().accountExternalIds().update(Collections.singleton(id));
|
||||
externalIdCache.onUpdate(id);
|
||||
accountCache.evict(user.getAccountId());
|
||||
|
||||
return Strings.isNullOrEmpty(newPassword)
|
||||
|
||||
@@ -86,6 +86,7 @@ import com.google.gerrit.server.account.CapabilityCollection;
|
||||
import com.google.gerrit.server.account.CapabilityControl;
|
||||
import com.google.gerrit.server.account.ChangeUserName;
|
||||
import com.google.gerrit.server.account.EmailExpander;
|
||||
import com.google.gerrit.server.account.ExternalIdCacheImpl;
|
||||
import com.google.gerrit.server.account.GroupCacheImpl;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.gerrit.server.account.GroupDetailFactory;
|
||||
@@ -217,6 +218,7 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
install(AccountCacheImpl.module());
|
||||
install(ChangeKindCacheImpl.module());
|
||||
install(ConflictsCacheImpl.module());
|
||||
install(ExternalIdCacheImpl.module());
|
||||
install(GroupCacheImpl.module());
|
||||
install(GroupIncludeCacheImpl.module());
|
||||
install(MergeabilityCacheImpl.module());
|
||||
|
||||
Reference in New Issue
Block a user