AccountResolver: Fix StringIndexOutOfBoundsException
If a change query is done for an account with the 'Full Name <email>' format, Gerrit first finds accounts that match the email. If there are multiple accounts that match the email, Gerrit only returns the matches where also the full name matches. This logic to match on the full name run into a StringIndexOutOfBoundsException when the input string was an email surrounded by '<' and '>', e.g. "<foo.bar@example.com>". Such an input string is deteced as 'Full Name <email>' format but then parsing the full name in front of '<' failed because it is not present in the input string. Stacktrace: java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1969) at com.google.gerrit.server.account.AccountResolver$ByNameAndEmail.search(AccountResolver.java:349) at com.google.gerrit.server.account.AccountResolver$ByNameAndEmail.search(AccountResolver.java:328) at com.google.gerrit.server.account.AccountResolver$Searcher.trySearch(AccountResolver.java:227) at com.google.gerrit.server.account.AccountResolver.searchImpl(AccountResolver.java:595) at com.google.gerrit.server.account.AccountResolver.resolve(AccountResolver.java:519) at com.google.gerrit.server.query.change.ChangeQueryBuilder.parseAccount(ChangeQueryBuilder.java:1394) at com.google.gerrit.server.query.change.ChangeQueryBuilder.ownerDefaultField(ChangeQueryBuilder.java:1034) at com.google.gerrit.server.query.change.ChangeQueryBuilder.defaultField(ChangeQueryBuilder.java:1320) at com.google.gerrit.index.query.QueryBuilder.toPredicate(QueryBuilder.java:258) at com.google.gerrit.index.query.QueryBuilder.children(QueryBuilder.java:331) at com.google.gerrit.index.query.QueryBuilder.toPredicate(QueryBuilder.java:251) at com.google.gerrit.index.query.QueryBuilder.parse(QueryBuilder.java:224) at com.google.gerrit.index.query.QueryBuilder.parse(QueryBuilder.java:243) at com.google.gerrit.server.restapi.change.QueryChanges.query(QueryChanges.java:166) at com.google.gerrit.server.restapi.change.QueryChanges.apply(QueryChanges.java:130) at com.google.gerrit.server.restapi.change.QueryChanges.apply(QueryChanges.java:45) ... Signed-off-by: Edwin Kempin <ekempin@google.com> Change-Id: Id03b16fedd6b31ac2666d165a0627a46c163f3c7
This commit is contained in:
@@ -95,5 +95,25 @@ public interface AccountOperations {
|
||||
* @return a builder to update the account
|
||||
*/
|
||||
TestAccountUpdate.Builder forUpdate();
|
||||
|
||||
/**
|
||||
* Starts the fluent chain to invalidate an account. The returned builder can be used to specify
|
||||
* how the account should be made invalid. To invalidate the account for real, {@link
|
||||
* TestAccountInvalidation.Builder#invalidate()} must be called.
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre>
|
||||
* accountOperations.forInvalidation()
|
||||
* .preferredEmailWithoutExternalId("foo.bar@example.com")
|
||||
* .invalidate();
|
||||
* </pre>
|
||||
*
|
||||
* <p><strong>Note:</strong> The invalidation will fail with an exception if the account to
|
||||
* invalidate doesn't exist.
|
||||
*
|
||||
* @return a builder to invalidate the account
|
||||
*/
|
||||
TestAccountInvalidation.Builder forInvalidation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,5 +168,23 @@ public class AccountOperationsImpl implements AccountOperations {
|
||||
accountUpdate.status().ifPresent(builder::setStatus);
|
||||
accountUpdate.active().ifPresent(builder::setActive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestAccountInvalidation.Builder forInvalidation() {
|
||||
return TestAccountInvalidation.builder(this::invalidateAccount);
|
||||
}
|
||||
|
||||
private void invalidateAccount(TestAccountInvalidation testAccountInvalidation)
|
||||
throws Exception {
|
||||
Optional<AccountState> accountState = getAccountState(accountId);
|
||||
checkState(accountState.isPresent(), "Tried to invalidate a non-existing test account");
|
||||
|
||||
if (testAccountInvalidation.preferredEmailWithoutExternalId().isPresent()) {
|
||||
updateAccount(
|
||||
(account, updateBuilder) ->
|
||||
updateBuilder.setPreferredEmail(
|
||||
testAccountInvalidation.preferredEmailWithoutExternalId().get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2020 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.acceptance.testsuite.account;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.gerrit.acceptance.testsuite.ThrowingConsumer;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* API to invalidate accounts in tests.
|
||||
*
|
||||
* <p>This allows to test Gerrit behavior when there is invalid account data in NoteDb (e.g.
|
||||
* accounts with duplicate emails).
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class TestAccountInvalidation {
|
||||
/**
|
||||
* Sets a preferred email for the account for which the account doesn't have an external ID.
|
||||
*
|
||||
* <p>This allows to set the same preferred email for multiple accounts so that the email becomes
|
||||
* ambiguous.
|
||||
*/
|
||||
public abstract Optional<String> preferredEmailWithoutExternalId();
|
||||
|
||||
abstract ThrowingConsumer<TestAccountInvalidation> accountInvalidator();
|
||||
|
||||
public static Builder builder(ThrowingConsumer<TestAccountInvalidation> accountInvalidator) {
|
||||
return new AutoValue_TestAccountInvalidation.Builder().accountInvalidator(accountInvalidator);
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
public abstract Builder preferredEmailWithoutExternalId(String preferredEmail);
|
||||
|
||||
abstract Builder accountInvalidator(
|
||||
ThrowingConsumer<TestAccountInvalidation> accountInvalidator);
|
||||
|
||||
abstract TestAccountInvalidation autoBuild();
|
||||
|
||||
/** Executes the account invalidation as specified. */
|
||||
public void invalidate() {
|
||||
TestAccountInvalidation accountInvalidation = autoBuild();
|
||||
accountInvalidation.accountInvalidator().acceptAndThrowSilently(accountInvalidation);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user