Merge changes I1dcdfb1b,If893d1bd,I7f0383cb
* changes: AccountIndex: Add field for exact preferred email Return only exact matches from InternalAccountQuery#byPreferredEmail AccountIndex: Add fields that allow to detect an account document as stale
This commit is contained in:
@@ -709,7 +709,8 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
@Test
|
||||
public void lookUpByPreferredEmail() throws Exception {
|
||||
// create an inconsistent account that has a preferred email without external ID
|
||||
String prefEmail = "foo.preferred@example.com";
|
||||
String prefix = "foo.preferred";
|
||||
String prefEmail = prefix + "@example.com";
|
||||
TestAccount foo = accountCreator.create(name("foo"));
|
||||
accountsUpdate.create().update(db, foo.id, a -> a.setPreferredEmail(prefEmail));
|
||||
|
||||
@@ -717,6 +718,14 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
ImmutableSet<Account.Id> accountsByPrefEmail = emails.getAccountFor(prefEmail);
|
||||
assertThat(accountsByPrefEmail).hasSize(1);
|
||||
assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id);
|
||||
|
||||
// look up by email prefix doesn't find the account
|
||||
accountsByPrefEmail = emails.getAccountFor(prefix);
|
||||
assertThat(accountsByPrefEmail).isEmpty();
|
||||
|
||||
// look up by other case doesn't find the account
|
||||
accountsByPrefEmail = emails.getAccountFor(prefEmail.toUpperCase(Locale.US));
|
||||
assertThat(accountsByPrefEmail).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -223,8 +223,6 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
|
||||
.get()
|
||||
.byPreferredEmail(email)
|
||||
.stream()
|
||||
// the index query also matches prefixes, filter those out
|
||||
.filter(a -> email.equalsIgnoreCase(a.getAccount().getPreferredEmail()))
|
||||
.map(AccountState::getAccount)
|
||||
.findFirst();
|
||||
return match.isPresent() ? auth(match.get()) : null;
|
||||
|
||||
@@ -19,6 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.client.AuthType;
|
||||
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
|
||||
import com.google.gerrit.pgm.init.api.ConsoleUI;
|
||||
import com.google.gerrit.pgm.init.api.InitFlags;
|
||||
import com.google.gerrit.pgm.init.api.InitStep;
|
||||
@@ -29,6 +30,7 @@ import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.index.account.AccountIndex;
|
||||
import com.google.gerrit.server.index.account.AccountIndexCollection;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
@@ -44,8 +46,9 @@ import java.util.List;
|
||||
import org.apache.commons.validator.routines.EmailValidator;
|
||||
|
||||
public class InitAdminUser implements InitStep {
|
||||
private final ConsoleUI ui;
|
||||
private final InitFlags flags;
|
||||
private final ConsoleUI ui;
|
||||
private final AllUsersNameOnInitProvider allUsers;
|
||||
private final AccountsOnInit accounts;
|
||||
private final VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory;
|
||||
private final ExternalIdsOnInit externalIds;
|
||||
@@ -58,6 +61,7 @@ public class InitAdminUser implements InitStep {
|
||||
InitAdminUser(
|
||||
InitFlags flags,
|
||||
ConsoleUI ui,
|
||||
AllUsersNameOnInitProvider allUsers,
|
||||
AccountsOnInit accounts,
|
||||
VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory,
|
||||
ExternalIdsOnInit externalIds,
|
||||
@@ -65,6 +69,7 @@ public class InitAdminUser implements InitStep {
|
||||
GroupsOnInit groupsOnInit) {
|
||||
this.flags = flags;
|
||||
this.ui = ui;
|
||||
this.allUsers = allUsers;
|
||||
this.accounts = accounts;
|
||||
this.authorizedKeysFactory = authorizedKeysFactory;
|
||||
this.externalIds = externalIds;
|
||||
@@ -128,7 +133,11 @@ public class InitAdminUser implements InitStep {
|
||||
|
||||
AccountState as =
|
||||
new AccountState(
|
||||
a, Collections.singleton(adminGroup.getGroupUUID()), extIds, new HashMap<>());
|
||||
new AllUsersName(allUsers.get()),
|
||||
a,
|
||||
Collections.singleton(adminGroup.getGroupUUID()),
|
||||
extIds,
|
||||
new HashMap<>());
|
||||
for (AccountIndex accountIndex : indexCollection.getWriteIndexes()) {
|
||||
accountIndex.replace(as);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.group.Groups;
|
||||
import com.google.gerrit.server.index.account.AccountIndexer;
|
||||
import com.google.gerrit.server.query.account.InternalAccountQuery;
|
||||
@@ -76,15 +77,18 @@ public class AccountCacheImpl implements AccountCache {
|
||||
};
|
||||
}
|
||||
|
||||
private final AllUsersName allUsersName;
|
||||
private final LoadingCache<Account.Id, Optional<AccountState>> byId;
|
||||
private final LoadingCache<String, Optional<Account.Id>> byName;
|
||||
private final Provider<AccountIndexer> indexer;
|
||||
|
||||
@Inject
|
||||
AccountCacheImpl(
|
||||
AllUsersName allUsersName,
|
||||
@Named(BYID_NAME) LoadingCache<Account.Id, Optional<AccountState>> byId,
|
||||
@Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername,
|
||||
Provider<AccountIndexer> indexer) {
|
||||
this.allUsersName = allUsersName;
|
||||
this.byId = byId;
|
||||
this.byName = byUsername;
|
||||
this.indexer = indexer;
|
||||
@@ -142,16 +146,21 @@ public class AccountCacheImpl implements AccountCache {
|
||||
}
|
||||
}
|
||||
|
||||
private static AccountState missing(Account.Id accountId) {
|
||||
private AccountState missing(Account.Id accountId) {
|
||||
Account account = new Account(accountId, TimeUtil.nowTs());
|
||||
account.setActive(false);
|
||||
Set<AccountGroup.UUID> anon = ImmutableSet.of();
|
||||
return new AccountState(
|
||||
account, anon, Collections.emptySet(), new HashMap<ProjectWatchKey, Set<NotifyType>>());
|
||||
allUsersName,
|
||||
account,
|
||||
anon,
|
||||
Collections.emptySet(),
|
||||
new HashMap<ProjectWatchKey, Set<NotifyType>>());
|
||||
}
|
||||
|
||||
static class ByIdLoader extends CacheLoader<Account.Id, Optional<AccountState>> {
|
||||
private final SchemaFactory<ReviewDb> schema;
|
||||
private final AllUsersName allUsersName;
|
||||
private final Accounts accounts;
|
||||
private final GroupCache groupCache;
|
||||
private final Groups groups;
|
||||
@@ -163,6 +172,7 @@ public class AccountCacheImpl implements AccountCache {
|
||||
@Inject
|
||||
ByIdLoader(
|
||||
SchemaFactory<ReviewDb> sf,
|
||||
AllUsersName allUsersName,
|
||||
Accounts accounts,
|
||||
GroupCache groupCache,
|
||||
Groups groups,
|
||||
@@ -170,8 +180,9 @@ public class AccountCacheImpl implements AccountCache {
|
||||
@Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername,
|
||||
Provider<WatchConfig.Accessor> watchConfig,
|
||||
ExternalIds externalIds) {
|
||||
this.accounts = accounts;
|
||||
this.schema = sf;
|
||||
this.allUsersName = allUsersName;
|
||||
this.accounts = accounts;
|
||||
this.groupCache = groupCache;
|
||||
this.groups = groups;
|
||||
this.loader = loader;
|
||||
@@ -219,6 +230,7 @@ public class AccountCacheImpl implements AccountCache {
|
||||
|
||||
return Optional.of(
|
||||
new AccountState(
|
||||
allUsersName,
|
||||
account,
|
||||
internalGroups,
|
||||
externalIds.byAccount(who),
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -43,6 +44,7 @@ public class AccountState {
|
||||
public static final Function<AccountState, Account.Id> ACCOUNT_ID_FUNCTION =
|
||||
a -> a.getAccount().getId();
|
||||
|
||||
private final AllUsersName allUsersName;
|
||||
private final Account account;
|
||||
private final Set<AccountGroup.UUID> internalGroups;
|
||||
private final Collection<ExternalId> externalIds;
|
||||
@@ -50,10 +52,12 @@ public class AccountState {
|
||||
private Cache<IdentifiedUser.PropertyKey<Object>, Object> properties;
|
||||
|
||||
public AccountState(
|
||||
AllUsersName allUsersName,
|
||||
Account account,
|
||||
Set<AccountGroup.UUID> actualGroups,
|
||||
Collection<ExternalId> externalIds,
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
|
||||
this.allUsersName = allUsersName;
|
||||
this.account = account;
|
||||
this.internalGroups = actualGroups;
|
||||
this.externalIds = externalIds;
|
||||
@@ -61,6 +65,10 @@ public class AccountState {
|
||||
this.account.setUserName(getUserName(externalIds));
|
||||
}
|
||||
|
||||
public AllUsersName getAllUsersNameForIndexing() {
|
||||
return allUsersName;
|
||||
}
|
||||
|
||||
/** Get the cached account metadata. */
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** Class to access accounts by email. */
|
||||
@Singleton
|
||||
@@ -61,15 +60,9 @@ public class Emails {
|
||||
* @see #getAccountsFor(String...)
|
||||
*/
|
||||
public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException {
|
||||
List<AccountState> byPreferredEmail = queryProvider.get().byPreferredEmail(email);
|
||||
return Streams.concat(
|
||||
externalIds.byEmail(email).stream().map(e -> e.accountId()),
|
||||
byPreferredEmail
|
||||
.stream()
|
||||
// the index query also matches prefixes and emails with other case,
|
||||
// filter those out
|
||||
.filter(a -> email.equals(a.getAccount().getPreferredEmail()))
|
||||
.map(a -> a.getAccount().getId()))
|
||||
queryProvider.get().byPreferredEmail(email).stream().map(a -> a.getAccount().getId()))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@@ -91,9 +84,6 @@ public class Emails {
|
||||
.byPreferredEmail(emails)
|
||||
.entries()
|
||||
.stream()
|
||||
// the index query also matches prefixes and emails with other case,
|
||||
// filter those out
|
||||
.filter(e -> e.getKey().equals(e.getValue().getAccount().getPreferredEmail()))
|
||||
.forEach(e -> builder.put(e.getKey(), e.getValue().getAccount().getId()));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.hash.Hashing;
|
||||
@@ -31,6 +32,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
@AutoValue
|
||||
@@ -176,7 +178,8 @@ public abstract class ExternalId implements Serializable {
|
||||
extId.key(), extId.accountId(), extId.email(), extId.password(), blobId);
|
||||
}
|
||||
|
||||
static ExternalId create(
|
||||
@VisibleForTesting
|
||||
public static ExternalId create(
|
||||
Key key,
|
||||
Account.Id accountId,
|
||||
@Nullable String email,
|
||||
@@ -305,6 +308,15 @@ public abstract class ExternalId implements Serializable {
|
||||
return key().isScheme(scheme);
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
checkState(blobId() != null, "Missing blobId in external ID %s", key().get());
|
||||
byte[] b = new byte[2 * Constants.OBJECT_ID_STRING_LENGTH + 1];
|
||||
key().sha1().copyTo(b, 0);
|
||||
b[Constants.OBJECT_ID_STRING_LENGTH] = ':';
|
||||
blobId().copyTo(b, Constants.OBJECT_ID_STRING_LENGTH + 1);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* For checking if two external IDs are equals the blobId is excluded and external IDs that have
|
||||
* different blob IDs but identical other fields are considered equal. This way an external ID
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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.index;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@AutoValue
|
||||
public abstract class RefState {
|
||||
public static RefState create(String ref, String sha) {
|
||||
return new AutoValue_RefState(ref, ObjectId.fromString(sha));
|
||||
}
|
||||
|
||||
public static RefState create(String ref, @Nullable ObjectId id) {
|
||||
return new AutoValue_RefState(ref, firstNonNull(id, ObjectId.zeroId()));
|
||||
}
|
||||
|
||||
public static RefState of(Ref ref) {
|
||||
return new AutoValue_RefState(ref.getName(), ref.getObjectId());
|
||||
}
|
||||
|
||||
public byte[] toByteArray(Project.NameKey project) {
|
||||
byte[] a = (project.toString() + ':' + ref() + ':').getBytes(UTF_8);
|
||||
byte[] b = new byte[a.length + Constants.OBJECT_ID_STRING_LENGTH];
|
||||
System.arraycopy(a, 0, b, 0, a.length);
|
||||
id().copyTo(b, a.length);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static void check(boolean condition, String str) {
|
||||
checkArgument(condition, "invalid RefState: %s", str);
|
||||
}
|
||||
|
||||
public abstract String ref();
|
||||
|
||||
public abstract ObjectId id();
|
||||
|
||||
public boolean match(Repository repo) throws IOException {
|
||||
Ref ref = repo.exactRef(ref());
|
||||
ObjectId expected = ref != null ? ref.getObjectId() : ObjectId.zeroId();
|
||||
return id().equals(expected);
|
||||
}
|
||||
}
|
||||
@@ -17,20 +17,26 @@ package com.google.gerrit.server.index.account;
|
||||
import static com.google.gerrit.index.FieldDef.exact;
|
||||
import static com.google.gerrit.index.FieldDef.integer;
|
||||
import static com.google.gerrit.index.FieldDef.prefix;
|
||||
import static com.google.gerrit.index.FieldDef.storedOnly;
|
||||
import static com.google.gerrit.index.FieldDef.timestamp;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.index.FieldDef;
|
||||
import com.google.gerrit.index.SchemaUtil;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.index.RefState;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
/** Secondary index schemas for accounts. */
|
||||
public class AccountField {
|
||||
@@ -84,6 +90,9 @@ public class AccountField {
|
||||
return preferredEmail != null ? preferredEmail.toLowerCase() : null;
|
||||
});
|
||||
|
||||
public static final FieldDef<AccountState, String> PREFERRED_EMAIL_EXACT =
|
||||
exact("preferredemail_exact").build(a -> a.getAccount().getPreferredEmail());
|
||||
|
||||
public static final FieldDef<AccountState, Timestamp> REGISTERED =
|
||||
timestamp("registered").build(a -> a.getAccount().getRegisteredOn());
|
||||
|
||||
@@ -98,5 +107,43 @@ public class AccountField {
|
||||
.transform(k -> k.project().get())
|
||||
.toSet());
|
||||
|
||||
/**
|
||||
* All values of all refs that were used in the course of indexing this document, except the
|
||||
* refs/meta/external-ids notes branch which is handled specially (see {@link
|
||||
* #EXTERNAL_ID_STATE}).
|
||||
*
|
||||
* <p>Emitted as UTF-8 encoded strings of the form {@code project:ref/name:[hex sha]}.
|
||||
*/
|
||||
public static final FieldDef<AccountState, Iterable<byte[]>> REF_STATE =
|
||||
storedOnly("ref_state")
|
||||
.buildRepeatable(
|
||||
a -> {
|
||||
if (a.getAccount().getMetaId() == null) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
return ImmutableList.of(
|
||||
RefState.create(
|
||||
RefNames.refsUsers(a.getAccount().getId()),
|
||||
ObjectId.fromString(a.getAccount().getMetaId()))
|
||||
.toByteArray(a.getAllUsersNameForIndexing()));
|
||||
});
|
||||
|
||||
/**
|
||||
* All note values of all external IDs that were used in the course of indexing this document.
|
||||
*
|
||||
* <p>Emitted as UTF-8 encoded strings of the form {@code [hex sha of external ID]:[hex sha of
|
||||
* note blob]}, or with other words {@code [note ID]:[note data ID]}.
|
||||
*/
|
||||
public static final FieldDef<AccountState, Iterable<byte[]>> EXTERNAL_ID_STATE =
|
||||
storedOnly("external_id_state")
|
||||
.buildRepeatable(
|
||||
a ->
|
||||
a.getExternalIds()
|
||||
.stream()
|
||||
.filter(e -> e.blobId() != null)
|
||||
.map(e -> e.toByteArray())
|
||||
.collect(toSet()));
|
||||
|
||||
private AccountField() {}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,13 @@ public class AccountSchemaDefinitions extends SchemaDefinitions<AccountState> {
|
||||
AccountField.USERNAME,
|
||||
AccountField.WATCHED_PROJECT);
|
||||
|
||||
static final Schema<AccountState> V5 = schema(V4, AccountField.PREFERRED_EMAIL);
|
||||
@Deprecated static final Schema<AccountState> V5 = schema(V4, AccountField.PREFERRED_EMAIL);
|
||||
|
||||
@Deprecated
|
||||
static final Schema<AccountState> V6 =
|
||||
schema(V5, AccountField.REF_STATE, AccountField.EXTERNAL_ID_STATE);
|
||||
|
||||
static final Schema<AccountState> V7 = schema(V6, AccountField.PREFERRED_EMAIL_EXACT);
|
||||
|
||||
public static final String NAME = "accounts";
|
||||
public static final AccountSchemaDefinitions INSTANCE = new AccountSchemaDefinitions();
|
||||
|
||||
@@ -50,7 +50,7 @@ import com.google.gerrit.server.ReviewerByEmailSet;
|
||||
import com.google.gerrit.server.ReviewerSet;
|
||||
import com.google.gerrit.server.StarredChangesUtil;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.index.change.StalenessChecker.RefState;
|
||||
import com.google.gerrit.server.index.RefState;
|
||||
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
|
||||
import com.google.gerrit.server.mail.Address;
|
||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.index.change;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -35,6 +34,7 @@ import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.index.RefState;
|
||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
@@ -47,8 +47,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.slf4j.Logger;
|
||||
@@ -221,43 +219,6 @@ public class StalenessChecker {
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class RefState {
|
||||
static RefState create(String ref, String sha) {
|
||||
return new AutoValue_StalenessChecker_RefState(ref, ObjectId.fromString(sha));
|
||||
}
|
||||
|
||||
static RefState create(String ref, @Nullable ObjectId id) {
|
||||
return new AutoValue_StalenessChecker_RefState(ref, firstNonNull(id, ObjectId.zeroId()));
|
||||
}
|
||||
|
||||
static RefState of(Ref ref) {
|
||||
return new AutoValue_StalenessChecker_RefState(ref.getName(), ref.getObjectId());
|
||||
}
|
||||
|
||||
byte[] toByteArray(Project.NameKey project) {
|
||||
byte[] a = (project.toString() + ':' + ref() + ':').getBytes(UTF_8);
|
||||
byte[] b = new byte[a.length + Constants.OBJECT_ID_STRING_LENGTH];
|
||||
System.arraycopy(a, 0, b, 0, a.length);
|
||||
id().copyTo(b, a.length);
|
||||
return b;
|
||||
}
|
||||
|
||||
private static void check(boolean condition, String str) {
|
||||
checkArgument(condition, "invalid RefState: %s", str);
|
||||
}
|
||||
|
||||
abstract String ref();
|
||||
|
||||
abstract ObjectId id();
|
||||
|
||||
private boolean match(Repository repo) throws IOException {
|
||||
Ref ref = repo.exactRef(ref());
|
||||
ObjectId expected = ref != null ? ref.getObjectId() : ObjectId.zeroId();
|
||||
return id().equals(expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern for matching refs.
|
||||
*
|
||||
|
||||
@@ -66,6 +66,11 @@ public class AccountPredicates {
|
||||
email.toLowerCase());
|
||||
}
|
||||
|
||||
public static Predicate<AccountState> preferredEmailExact(String email) {
|
||||
return new AccountPredicate(
|
||||
AccountField.PREFERRED_EMAIL_EXACT, AccountQueryBuilder.FIELD_PREFERRED_EMAIL_EXACT, email);
|
||||
}
|
||||
|
||||
public static Predicate<AccountState> equalsName(String name) {
|
||||
return new AccountPredicate(
|
||||
AccountField.NAME_PART, AccountQueryBuilder.FIELD_NAME, name.toLowerCase());
|
||||
|
||||
@@ -37,6 +37,7 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState> {
|
||||
public static final String FIELD_LIMIT = "limit";
|
||||
public static final String FIELD_NAME = "name";
|
||||
public static final String FIELD_PREFERRED_EMAIL = "preferredemail";
|
||||
public static final String FIELD_PREFERRED_EMAIL_EXACT = "preferredemail_exact";
|
||||
public static final String FIELD_USERNAME = "username";
|
||||
public static final String FIELD_VISIBLETO = "visibleto";
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.query.account;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
@@ -25,6 +26,7 @@ import com.google.gerrit.index.query.InternalQuery;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.index.account.AccountField;
|
||||
import com.google.gerrit.server.index.account.AccountIndexCollection;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -113,17 +115,60 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
|
||||
return query(AccountPredicates.fullName(fullName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries for accounts that have a preferred email that exactly matches the given email.
|
||||
*
|
||||
* @param email preferred email by which accounts should be found
|
||||
* @return list of accounts that have a preferred email that exactly matches the given email
|
||||
* @throws OrmException if query cannot be parsed
|
||||
*/
|
||||
public List<AccountState> byPreferredEmail(String email) throws OrmException {
|
||||
return query(AccountPredicates.preferredEmail(email));
|
||||
if (schema().hasField(AccountField.PREFERRED_EMAIL_EXACT)) {
|
||||
return query(AccountPredicates.preferredEmailExact(email));
|
||||
}
|
||||
|
||||
return query(AccountPredicates.preferredEmail(email))
|
||||
.stream()
|
||||
.filter(a -> a.getAccount().getPreferredEmail().equals(email))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes multiple queries for accounts by preferred email (exact match).
|
||||
*
|
||||
* @param emails preferred emails by which accounts should be found
|
||||
* @return multimap of the given emails to accounts that have a preferred email that exactly
|
||||
* matches this email
|
||||
* @throws OrmException if query cannot be parsed
|
||||
*/
|
||||
public Multimap<String, AccountState> byPreferredEmail(String... emails) throws OrmException {
|
||||
List<String> emailList = Arrays.asList(emails);
|
||||
|
||||
if (schema().hasField(AccountField.PREFERRED_EMAIL_EXACT)) {
|
||||
List<List<AccountState>> r =
|
||||
query(
|
||||
emailList
|
||||
.stream()
|
||||
.map(e -> AccountPredicates.preferredEmailExact(e))
|
||||
.collect(toList()));
|
||||
Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
|
||||
for (int i = 0; i < emailList.size(); i++) {
|
||||
accountsByEmail.putAll(emailList.get(i), r.get(i));
|
||||
}
|
||||
return accountsByEmail;
|
||||
}
|
||||
|
||||
List<List<AccountState>> r =
|
||||
query(emailList.stream().map(e -> AccountPredicates.preferredEmail(e)).collect(toList()));
|
||||
Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
|
||||
for (int i = 0; i < emailList.size(); i++) {
|
||||
accountsByEmail.putAll(emailList.get(i), r.get(i));
|
||||
String email = emailList.get(i);
|
||||
Set<AccountState> matchingAccounts =
|
||||
r.get(i)
|
||||
.stream()
|
||||
.filter(a -> a.getAccount().getPreferredEmail().equals(email))
|
||||
.collect(toSet());
|
||||
accountsByEmail.putAll(email, matchingAccounts);
|
||||
}
|
||||
return accountsByEmail;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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.index.account;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||
import com.google.gerrit.testutil.GerritBaseTests;
|
||||
import java.util.List;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AccountFieldTest extends GerritBaseTests {
|
||||
@Test
|
||||
public void refStateFieldValues() throws Exception {
|
||||
AllUsersName allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
|
||||
Account account = new Account(new Account.Id(1), TimeUtil.nowTs());
|
||||
String metaId = "0e39795bb25dc914118224995c53c5c36923a461";
|
||||
account.setMetaId(metaId);
|
||||
List<String> values =
|
||||
toStrings(
|
||||
AccountField.REF_STATE.get(
|
||||
new AccountState(
|
||||
allUsersName,
|
||||
account,
|
||||
ImmutableSet.of(),
|
||||
ImmutableSet.of(),
|
||||
ImmutableMap.of())));
|
||||
assertThat(values).hasSize(1);
|
||||
String expectedValue =
|
||||
allUsersName.get() + ":" + RefNames.refsUsers(account.getId()) + ":" + metaId;
|
||||
assertThat(Iterables.getOnlyElement(values)).isEqualTo(expectedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void externalIdStateFieldValues() throws Exception {
|
||||
Account.Id id = new Account.Id(1);
|
||||
Account account = new Account(id, TimeUtil.nowTs());
|
||||
ExternalId extId1 =
|
||||
ExternalId.create(
|
||||
ExternalId.Key.create(ExternalId.SCHEME_MAILTO, "foo.bar@example.com"),
|
||||
id,
|
||||
"foo.bar@example.com",
|
||||
null,
|
||||
ObjectId.fromString("1b9a0cf038ea38a0ab08617c39aa8e28413a27ca"));
|
||||
ExternalId extId2 =
|
||||
ExternalId.create(
|
||||
ExternalId.Key.create(ExternalId.SCHEME_USERNAME, "foo"),
|
||||
id,
|
||||
null,
|
||||
"secret",
|
||||
ObjectId.fromString("5b3a73dc9a668a5b89b5f049225261e3e3291d1a"));
|
||||
List<String> values =
|
||||
toStrings(
|
||||
AccountField.EXTERNAL_ID_STATE.get(
|
||||
new AccountState(
|
||||
null,
|
||||
account,
|
||||
ImmutableSet.of(),
|
||||
ImmutableSet.of(extId1, extId2),
|
||||
ImmutableMap.of())));
|
||||
String expectedValue1 = extId1.key().sha1().name() + ":" + extId1.blobId().name();
|
||||
String expectedValue2 = extId2.key().sha1().name() + ":" + extId2.blobId().name();
|
||||
assertThat(values).containsExactly(expectedValue1, expectedValue2);
|
||||
}
|
||||
|
||||
private List<String> toStrings(Iterable<byte[]> values) {
|
||||
return Streams.stream(values).map(v -> new String(v, UTF_8)).collect(toList());
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.index.change.StalenessChecker.RefState;
|
||||
import com.google.gerrit.server.index.RefState;
|
||||
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
|
||||
import com.google.gerrit.server.notedb.NoteDbChangeState;
|
||||
import com.google.gerrit.testutil.GerritBaseTests;
|
||||
|
||||
@@ -25,6 +25,8 @@ import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||
import com.google.gerrit.server.mail.Address;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -383,6 +385,10 @@ public class FromAddressGeneratorProviderTest {
|
||||
account.setFullName(name);
|
||||
account.setPreferredEmail(email);
|
||||
return new AccountState(
|
||||
account, Collections.emptySet(), Collections.emptySet(), new HashMap<>());
|
||||
new AllUsersName(AllUsersNameProvider.DEFAULT),
|
||||
account,
|
||||
Collections.emptySet(),
|
||||
Collections.emptySet(),
|
||||
new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -78,6 +80,11 @@ public class FakeAccountCache implements AccountCache {
|
||||
}
|
||||
|
||||
private static AccountState newState(Account account) {
|
||||
return new AccountState(account, ImmutableSet.of(), ImmutableSet.of(), new HashMap<>());
|
||||
return new AccountState(
|
||||
new AllUsersName(AllUsersNameProvider.DEFAULT),
|
||||
account,
|
||||
ImmutableSet.of(),
|
||||
ImmutableSet.of(),
|
||||
new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user