Merge changes I9b9967ae,I90c2cd62

* changes:
  AccountResolver: Remove callerMayAssumeCandidatesAreVisible() method
  AccountResolver: Fix account visibility checking
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index d2417d7..703693d 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -230,10 +230,6 @@
       return true;
     }
 
-    default boolean callerMayAssumeCandidatesAreVisible() {
-      return false;
-    }
-
     /**
      * Searches can be done on behalf of either the current user or another provided user. The
      * results of some searchers, such as BySelf, are affected by the context user.
@@ -312,11 +308,6 @@
     }
 
     @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return true;
-    }
-
-    @Override
     public boolean requiresContextUser() {
       return true;
     }
@@ -457,33 +448,19 @@
     }
   }
 
-  private class ByFullName implements Searcher<AccountState> {
-    boolean allowSkippingVisibilityCheck = true;
-
+  private class ByFullName extends StringSearcher {
     ByFullName() {
       super();
     }
 
-    ByFullName(boolean allowSkippingVisibilityCheck) {
-      this();
-      this.allowSkippingVisibilityCheck = allowSkippingVisibilityCheck;
+    @Override
+    protected boolean matches(String input) {
+      return true;
     }
 
     @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return allowSkippingVisibilityCheck;
-    }
-
-    @Override
-    public Optional<AccountState> tryParse(String input) {
-      List<AccountState> results =
-          accountQueryProvider.get().enforceVisibility(true).byFullName(input);
-      return results.size() == 1 ? Optional.of(results.get(0)) : Optional.empty();
-    }
-
-    @Override
-    public Stream<AccountState> search(AccountState input) {
-      return Stream.of(input);
+    public Stream<AccountState> search(String input) {
+      return accountQueryProvider.get().byFullName(input).stream();
     }
 
     @Override
@@ -493,22 +470,10 @@
   }
 
   private class ByDefaultSearch extends StringSearcher {
-    boolean allowSkippingVisibilityCheck = true;
-
     ByDefaultSearch() {
       super();
     }
 
-    ByDefaultSearch(boolean allowSkippingVisibilityCheck) {
-      this();
-      this.allowSkippingVisibilityCheck = allowSkippingVisibilityCheck;
-    }
-
-    @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return allowSkippingVisibilityCheck;
-    }
-
     @Override
     public boolean requiresContextUser() {
       return true;
@@ -533,8 +498,7 @@
       } catch (PermissionBackendException e) {
         // remains false
       }
-      return accountQueryProvider.get().enforceVisibility(true)
-          .byDefault(input, canSeeSecondaryEmails).stream();
+      return accountQueryProvider.get().byDefault(input, canSeeSecondaryEmails).stream();
     }
 
     @Override
@@ -562,18 +526,6 @@
           .addAll(nameOrEmailSearchers)
           .build();
 
-  private final ImmutableList<Searcher<?>> forcedVisibilitySearchers =
-      ImmutableList.of(
-          new ByNameAndEmail(),
-          new ByEmail(),
-          new FromRealm(),
-          new ByFullName(false),
-          new ByDefaultSearch(false),
-          new BySelf(),
-          new ByExactAccountId(),
-          new ByParenthesizedAccountId(),
-          new ByUsername());
-
   private final AccountCache accountCache;
   private final AccountControl.Factory accountControlFactory;
   private final Emails emails;
@@ -670,25 +622,21 @@
    *
    * @param asUser user to resolve the users by.
    * @param input input string.
-   * @param forceVisibilityCheck whether to force all searchers to check for visibility.
    * @return a result describing matching accounts. Never null even if the result set is empty.
    * @throws ConfigInvalidException if an error occurs.
    * @throws IOException if an error occurs.
    */
-  public Result resolveAsUser(CurrentUser asUser, String input, boolean forceVisibilityCheck)
+  public Result resolveAsUser(CurrentUser asUser, String input)
       throws ConfigInvalidException, IOException {
-    return resolveAsUser(asUser, input, AccountResolver::isActive, forceVisibilityCheck);
+    return resolveAsUser(asUser, input, AccountResolver::isActive);
   }
 
   public Result resolveAsUser(
-      CurrentUser asUser,
-      String input,
-      Predicate<AccountState> accountActivityPredicate,
-      boolean forceVisibilityCheck)
+      CurrentUser asUser, String input, Predicate<AccountState> accountActivityPredicate)
       throws ConfigInvalidException, IOException {
     return searchImpl(
         input,
-        forceVisibilityCheck ? forcedVisibilitySearchers : searchers,
+        searchers,
         asUser,
         new ProvidedUserCanSeePredicate(asUser),
         accountActivityPredicate);
@@ -826,9 +774,9 @@
       }
       Stream<AccountState> results = maybeResults.get();
 
-      if (!searcher.callerMayAssumeCandidatesAreVisible()) {
-        results = results.filter(visibilitySupplier.get());
-      }
+      // Filter out non-visible results, except if it's the BySelf searcher. Since users can always
+      // see themselves checking the visibility is not needed for the BySelf searcher.
+      results = searcher instanceof BySelf ? results : results.filter(visibilitySupplier.get());
 
       List<AccountState> list;
       if (searcher.callerShouldFilterOutInactiveCandidates()) {
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index cbf47c5..f4c211d 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -253,7 +253,6 @@
       qb = WatcherChangeQueryBuilder.asUser(args.queryBuilder.get(), user);
       p = qb.isVisible();
     }
-    qb.forceAccountVisibilityCheck();
 
     if (filter != null) {
       Predicate<ChangeData> filterPredicate = qb.parse(filter);
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index b83b010..622c4bf 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -488,7 +488,6 @@
   private final Arguments args;
   protected Map<String, String> hasOperandAliases = Collections.emptyMap();
   private Map<Account.Id, DestinationList> destinationListByAccount = new HashMap<>();
-  private boolean forceAccountVisibilityCheck = false;
 
   private static final Splitter RULE_SPLITTER = Splitter.on("=");
   private static final Splitter PLUGIN_SPLITTER = Splitter.on("_");
@@ -519,11 +518,6 @@
     return args;
   }
 
-  /** Whether to force account visibility check when searching for changes by account(s). */
-  public void forceAccountVisibilityCheck() {
-    forceAccountVisibilityCheck = true;
-  }
-
   @Operator
   public Predicate<ChangeData> age(String value) {
     return new AgePredicate(value);
@@ -1710,9 +1704,7 @@
   private Set<Account.Id> parseAccount(String who)
       throws QueryParseException, IOException, ConfigInvalidException {
     try {
-      return args.accountResolver
-          .resolveAsUser(args.getUser(), who, forceAccountVisibilityCheck)
-          .asNonEmptyIdSet();
+      return args.accountResolver.resolveAsUser(args.getUser(), who).asNonEmptyIdSet();
     } catch (UnresolvableAccountException e) {
       if (e.isSelf()) {
         throw new QueryRequiresAuthException(e.getMessage(), e);
@@ -1726,7 +1718,7 @@
       throws QueryParseException, IOException, ConfigInvalidException {
     try {
       return args.accountResolver
-          .resolveAsUser(args.getUser(), who, activityFilter, forceAccountVisibilityCheck)
+          .resolveAsUser(args.getUser(), who, activityFilter)
           .asNonEmptyIdSet();
     } catch (UnresolvableAccountException e) {
       if (e.isSelf()) {
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
index 0cfa0f8..6f3aa15 100644
--- a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -26,11 +26,13 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
 import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.AccountResolver.Result;
 import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
@@ -56,7 +58,9 @@
   @Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdateProvider;
   @Inject private AccountOperations accountOperations;
   @Inject private AccountResolver accountResolver;
+  @Inject private AccountControl.Factory accountControlFactory;
   @Inject private Provider<CurrentUser> self;
+  @Inject private GroupOperations groupOperations;
   @Inject private RequestScopeOperations requestScopeOperations;
   @Inject private Sequences sequences;
 
@@ -365,6 +369,37 @@
     assertThat(resolve("doe")).containsExactly(id2);
   }
 
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
+  public void resolveAsUser_byFullName_accountThatIsNotVisibleToCurrentUserIsFound()
+      throws Exception {
+    Account.Id currentUser = accountOperations.newAccount().create();
+    Account.Id resolveAsUser = accountOperations.newAccount().create();
+    Account.Id userToBeFound = accountOperations.newAccount().fullname("Somebodys Name").create();
+
+    // Create a group that contains resolveAsUser and userToBeFound, so that resolveAsUser can see
+    // userToBeFound.
+    groupOperations.newGroup().addMember(resolveAsUser).addMember(userToBeFound).create();
+
+    // Verify that resolveAsUser can see userToBeFound.
+    assertThat(canSee(resolveAsUser, userToBeFound)).isTrue();
+
+    // Verify that currentUser cannot see userToBeFound
+    assertThat(canSee(currentUser, userToBeFound)).isFalse();
+
+    // Resolving userToBeFound as resolveAsUser should work even if the currentUser cannot see
+    // userToBeFound.
+    requestScopeOperations.setApiUser(currentUser);
+    String input = accountOperations.account(userToBeFound).get().fullname().get();
+    assertThat(resolveAsUser(resolveAsUser, input)).containsExactly(userToBeFound);
+  }
+
+  private boolean canSee(Account.Id currentUser, Account.Id userToBeSeen) {
+    return accountControlFactory
+        .get(identifiedUserFactory.create(currentUser))
+        .canSee(userToBeSeen);
+  }
+
   private ImmutableSet<Account.Id> resolve(Object input) throws Exception {
     return resolveAsResult(input).asIdSet();
   }
@@ -373,6 +408,16 @@
     return accountResolver.resolve(input.toString());
   }
 
+  private ImmutableSet<Account.Id> resolveAsUser(Account.Id resolveAsUser, Object input)
+      throws Exception {
+    return resolveAsUserAsResult(resolveAsUser, input).asIdSet();
+  }
+
+  private Result resolveAsUserAsResult(Account.Id resolveAsUser, Object input) throws Exception {
+    return accountResolver.resolveAsUser(
+        identifiedUserFactory.create(resolveAsUser), input.toString());
+  }
+
   @SuppressWarnings("deprecation")
   private ImmutableSet<Account.Id> resolveByNameOrEmail(Object input) throws Exception {
     return accountResolver.resolveByNameOrEmail(input.toString()).asIdSet();
diff --git a/javatests/com/google/gerrit/server/account/AccountResolverTest.java b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
index 37728f7..34f746a 100644
--- a/javatests/com/google/gerrit/server/account/AccountResolverTest.java
+++ b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
@@ -43,7 +43,6 @@
     private final String pattern;
     private final boolean shortCircuit;
     private final ImmutableList<AccountState> accounts;
-    private boolean assumeVisible;
     private boolean filterInactive;
 
     private TestSearcher(String pattern, boolean shortCircuit, AccountState... accounts) {
@@ -68,15 +67,6 @@
     }
 
     @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return assumeVisible;
-    }
-
-    void setCallerMayAssumeCandidatesAreVisible() {
-      this.assumeVisible = true;
-    }
-
-    @Override
     public boolean callerShouldFilterOutInactiveCandidates() {
       return filterInactive;
     }
@@ -139,17 +129,6 @@
   }
 
   @Test
-  public void skipVisibilityCheck() throws Exception {
-    TestSearcher searcher = new TestSearcher("foo", false, newAccount(1), newAccount(2));
-    ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher);
-
-    assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(2));
-
-    searcher.setCallerMayAssumeCandidatesAreVisible();
-    assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(1, 2));
-  }
-
-  @Test
   public void dontFilterInactive() throws Exception {
     ImmutableList<Searcher<?>> searchers =
         ImmutableList.of(