From 96750907d6b294ce9a28a9c16ed334a1c2cbb418 Mon Sep 17 00:00:00 2001 From: Edwin Kempin Date: Wed, 29 Nov 2017 15:46:01 +0100 Subject: [PATCH] Account index: Add new field that contains name parts without secondary emails In a follow-up change we want to hide secondary emails from users that don't have the 'Modify Account' capability. For this we must also disable querying accounts by secondary email. Otherwise one could search for 'foo.com' to find all accounts that have a '*@foo.com' email address. Currently the NAME_PART field also contains the name parts of the secondary email addresses. This means we must not use this field if the calling user doesn't have the 'Modify Account' capability. However since the NAME_PART field is needed for serving default queries and queries with the 'name' operator we need a replacement for this field that contains the name parts without secondary emails. This change adds this field and create a new schema version. This change is separate from the follow-up change that requires the 'Modify Account' capability to see secondary email addresses so that at Google we can first upgrade the account index and only then rollout the change that needs to new index field. The follow-up change also works with old index versions, but can't support name queries by prefix without the new field, which affect reviewer suggestion. Having this as 2 changes allows us to do the rollout without affecting users. Change-Id: If0ac6daa87403d94025a31a37ddf0b118cd5b579 Signed-off-by: Edwin Kempin --- .../server/index/account/AccountField.java | 56 ++++++++++++++----- .../account/AccountSchemaDefinitions.java | 4 +- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/java/com/google/gerrit/server/index/account/AccountField.java b/java/com/google/gerrit/server/index/account/AccountField.java index 5e12c120da..bbfe160ea2 100644 --- a/java/com/google/gerrit/server/index/account/AccountField.java +++ b/java/com/google/gerrit/server/index/account/AccountField.java @@ -33,6 +33,7 @@ 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.Arrays; import java.util.Collections; import java.util.Locale; import java.util.Set; @@ -43,27 +44,38 @@ public class AccountField { public static final FieldDef ID = integer("id").stored().build(a -> a.getAccount().getId().get()); + /** + * External IDs. + * + *

This field includes secondary emails. Use this field only if the current user is allowed to + * see secondary emails (requires the {@link GlobalCapability.MODIFY_ACCOUNT} capability). + */ public static final FieldDef> EXTERNAL_ID = exact("external_id") .buildRepeatable(a -> Iterables.transform(a.getExternalIds(), id -> id.key().get())); - /** Fuzzy prefix match on name and email parts. */ + /** + * Fuzzy prefix match on name and email parts. + * + *

This field includes parts from the secondary emails. Use this field only if the current user + * is allowed to see secondary emails (requires the {@link GlobalCapability.MODIFY_ACCOUNT} + * capability). + * + *

Use the {@link AccountField#NAME_PART_NO_SECONDARY_EMAIL} if the current user can't see + * secondary emails. + */ public static final FieldDef> NAME_PART = prefix("name") .buildRepeatable( - a -> { - String fullName = a.getAccount().getFullName(); - Set parts = - SchemaUtil.getNameParts( - fullName, Iterables.transform(a.getExternalIds(), ExternalId::email)); + a -> getNameParts(a, Iterables.transform(a.getExternalIds(), ExternalId::email))); - // Additional values not currently added by getPersonParts. - // TODO(dborowitz): Move to getPersonParts and remove this hack. - if (fullName != null) { - parts.add(fullName.toLowerCase(Locale.US)); - } - return parts; - }); + /** + * Fuzzy prefix match on name and preferred email parts. Parts of secondary emails are not + * included. + */ + public static final FieldDef> NAME_PART_NO_SECONDARY_EMAIL = + prefix("name2") + .buildRepeatable(a -> getNameParts(a, Arrays.asList(a.getAccount().getPreferredEmail()))); public static final FieldDef FULL_NAME = exact("full_name").build(a -> a.getAccount().getFullName()); @@ -71,6 +83,12 @@ public class AccountField { public static final FieldDef ACTIVE = exact("inactive").build(a -> a.getAccount().isActive() ? "1" : "0"); + /** + * All emails (preferred email + secondary emails). Use this field only if the current user is + * allowed to see secondary emails (requires the 'Modify Account' capability). + * + *

Use the {@link AccountField#PREFERRED_EMAIL} if the current user can't see secondary emails. + */ public static final FieldDef> EMAIL = prefix("email") .buildRepeatable( @@ -145,5 +163,17 @@ public class AccountField { .map(e -> e.toByteArray()) .collect(toSet())); + private static final Set getNameParts(AccountState a, Iterable emails) { + String fullName = a.getAccount().getFullName(); + Set parts = SchemaUtil.getNameParts(fullName, emails); + + // Additional values not currently added by getPersonParts. + // TODO(dborowitz): Move to getPersonParts and remove this hack. + if (fullName != null) { + parts.add(fullName.toLowerCase(Locale.US)); + } + return parts; + } + private AccountField() {} } diff --git a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java index dcdf9e3222..3e702f2bfd 100644 --- a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java +++ b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java @@ -40,7 +40,9 @@ public class AccountSchemaDefinitions extends SchemaDefinitions { static final Schema V6 = schema(V5, AccountField.REF_STATE, AccountField.EXTERNAL_ID_STATE); - static final Schema V7 = schema(V6, AccountField.PREFERRED_EMAIL_EXACT); + @Deprecated static final Schema V7 = schema(V6, AccountField.PREFERRED_EMAIL_EXACT); + + static final Schema V8 = schema(V7, AccountField.NAME_PART_NO_SECONDARY_EMAIL); public static final String NAME = "accounts"; public static final AccountSchemaDefinitions INSTANCE = new AccountSchemaDefinitions();