diff options
Diffstat (limited to 'gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java')
-rw-r--r-- | gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java | 973 |
1 files changed, 0 insertions, 973 deletions
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java deleted file mode 100644 index 33313d13e4..0000000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java +++ /dev/null @@ -1,973 +0,0 @@ -// 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.acceptance.rest.account; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.fetch; -import static com.google.gerrit.acceptance.GitUtil.pushHead; -import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO; -import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME; -import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID; -import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.stream.Collectors.toList; -import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; -import static org.junit.Assert.fail; - -import com.github.rholder.retry.BlockStrategy; -import com.github.rholder.retry.Retryer; -import com.github.rholder.retry.RetryerBuilder; -import com.github.rholder.retry.StopStrategies; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.Sandboxed; -import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.common.data.Permission; -import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo; -import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo; -import com.google.gerrit.extensions.api.config.ConsistencyCheckInput; -import com.google.gerrit.extensions.api.config.ConsistencyCheckInput.CheckAccountExternalIdsInput; -import com.google.gerrit.extensions.common.AccountExternalIdInfo; -import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.UnprocessableEntityException; -import com.google.gerrit.metrics.MetricMaker; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.RefNames; -import com.google.gerrit.server.account.externalids.DisabledExternalIdCache; -import com.google.gerrit.server.account.externalids.ExternalId; -import com.google.gerrit.server.account.externalids.ExternalIdReader; -import com.google.gerrit.server.account.externalids.ExternalIds; -import com.google.gerrit.server.account.externalids.ExternalIdsUpdate; -import com.google.gerrit.server.account.externalids.ExternalIdsUpdate.RefsMetaExternalIdsUpdate; -import com.google.gerrit.server.config.AllUsersName; -import com.google.gerrit.server.extensions.events.GitReferenceUpdated; -import com.google.gerrit.server.git.LockFailureException; -import com.google.gson.reflect.TypeToken; -import com.google.gwtorm.server.OrmDuplicateKeyException; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import org.eclipse.jgit.api.errors.TransportException; -import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; -import org.eclipse.jgit.junit.TestRepository; -import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.notes.NoteMap; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.PushResult; -import org.eclipse.jgit.transport.RemoteRefUpdate; -import org.eclipse.jgit.transport.RemoteRefUpdate.Status; -import org.eclipse.jgit.util.MutableInteger; -import org.junit.Test; - -@Sandboxed -public class ExternalIdIT extends AbstractDaemonTest { - @Inject private AllUsersName allUsers; - @Inject private ExternalIdsUpdate.Server extIdsUpdate; - @Inject private ExternalIds externalIds; - @Inject private ExternalIdReader externalIdReader; - @Inject private MetricMaker metricMaker; - - @Test - public void getExternalIds() throws Exception { - Collection<ExternalId> expectedIds = accountCache.get(user.getId()).getExternalIds(); - List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds); - - RestResponse response = userRestSession.get("/accounts/self/external.ids"); - response.assertOK(); - - List<AccountExternalIdInfo> results = - newGson() - .fromJson( - response.getReader(), new TypeToken<List<AccountExternalIdInfo>>() {}.getType()); - - Collections.sort(expectedIdInfos); - Collections.sort(results); - assertThat(results).containsExactlyElementsIn(expectedIdInfos); - } - - @Test - public void getExternalIdsOfOtherUserNotAllowed() throws Exception { - setApiUser(user); - exception.expect(AuthException.class); - exception.expectMessage("access database not permitted"); - gApi.accounts().id(admin.id.get()).getExternalIds(); - } - - @Test - public void getExternalIdsOfOtherUserWithAccessDatabase() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - Collection<ExternalId> expectedIds = accountCache.get(admin.getId()).getExternalIds(); - List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds); - - RestResponse response = userRestSession.get("/accounts/" + admin.id + "/external.ids"); - response.assertOK(); - - List<AccountExternalIdInfo> results = - newGson() - .fromJson( - response.getReader(), new TypeToken<List<AccountExternalIdInfo>>() {}.getType()); - - Collections.sort(expectedIdInfos); - Collections.sort(results); - assertThat(results).containsExactlyElementsIn(expectedIdInfos); - } - - @Test - public void deleteExternalIds() throws Exception { - setApiUser(user); - List<AccountExternalIdInfo> externalIds = gApi.accounts().self().getExternalIds(); - - List<String> toDelete = new ArrayList<>(); - List<AccountExternalIdInfo> expectedIds = new ArrayList<>(); - for (AccountExternalIdInfo id : externalIds) { - if (id.canDelete != null && id.canDelete) { - toDelete.add(id.identity); - continue; - } - expectedIds.add(id); - } - - assertThat(toDelete).hasSize(1); - - RestResponse response = userRestSession.post("/accounts/self/external.ids:delete", toDelete); - response.assertNoContent(); - List<AccountExternalIdInfo> results = gApi.accounts().self().getExternalIds(); - // The external ID in WebSession will not be set for tests, resulting that - // "mailto:user@example.com" can be deleted while "username:user" can't. - assertThat(results).hasSize(1); - assertThat(results).containsExactlyElementsIn(expectedIds); - } - - @Test - public void deleteExternalIdsOfOtherUserNotAllowed() throws Exception { - List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds(); - setApiUser(user); - exception.expect(AuthException.class); - exception.expectMessage("access database not permitted"); - gApi.accounts() - .id(admin.id.get()) - .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList())); - } - - @Test - public void deleteExternalIdOfOtherUserUnderOwnAccount_UnprocessableEntity() throws Exception { - List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds(); - setApiUser(user); - exception.expect(UnprocessableEntityException.class); - exception.expectMessage(String.format("External id %s does not exist", extIds.get(0).identity)); - gApi.accounts() - .self() - .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList())); - } - - @Test - public void deleteExternalIdsOfOtherUserWithAccessDatabase() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - List<AccountExternalIdInfo> externalIds = gApi.accounts().self().getExternalIds(); - - List<String> toDelete = new ArrayList<>(); - List<AccountExternalIdInfo> expectedIds = new ArrayList<>(); - for (AccountExternalIdInfo id : externalIds) { - if (id.canDelete != null && id.canDelete) { - toDelete.add(id.identity); - continue; - } - expectedIds.add(id); - } - - assertThat(toDelete).hasSize(1); - - setApiUser(user); - RestResponse response = - userRestSession.post("/accounts/" + admin.id + "/external.ids:delete", toDelete); - response.assertNoContent(); - List<AccountExternalIdInfo> results = gApi.accounts().id(admin.id.get()).getExternalIds(); - // The external ID in WebSession will not be set for tests, resulting that - // "mailto:user@example.com" can be deleted while "username:user" can't. - assertThat(results).hasSize(1); - assertThat(results).containsExactlyElementsIn(expectedIds); - } - - @Test - public void deleteExternalIdOfPreferredEmail() throws Exception { - String preferredEmail = gApi.accounts().self().get().email; - assertThat(preferredEmail).isNotNull(); - - gApi.accounts() - .self() - .deleteExternalIds( - ImmutableList.of(ExternalId.Key.create(SCHEME_MAILTO, preferredEmail).get())); - assertThat(gApi.accounts().self().get().email).isNull(); - } - - @Test - public void deleteExternalIds_Conflict() throws Exception { - List<String> toDelete = new ArrayList<>(); - String externalIdStr = "username:" + user.username; - toDelete.add(externalIdStr); - RestResponse response = userRestSession.post("/accounts/self/external.ids:delete", toDelete); - response.assertConflict(); - assertThat(response.getEntityContent()) - .isEqualTo(String.format("External id %s cannot be deleted", externalIdStr)); - } - - @Test - public void deleteExternalIds_UnprocessableEntity() throws Exception { - List<String> toDelete = new ArrayList<>(); - String externalIdStr = "mailto:user@domain.com"; - toDelete.add(externalIdStr); - RestResponse response = userRestSession.post("/accounts/self/external.ids:delete", toDelete); - response.assertUnprocessableEntity(); - assertThat(response.getEntityContent()) - .isEqualTo(String.format("External id %s does not exist", externalIdStr)); - } - - @Test - public void fetchExternalIdsBranch() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user); - - // refs/meta/external-ids is only visible to users with the 'Access Database' capability - try { - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - fail("expected TransportException"); - } catch (TransportException e) { - assertThat(e.getMessage()) - .isEqualTo( - "Remote does not have " + RefNames.REFS_EXTERNAL_IDS + " available for fetch."); - } - - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - // re-clone to get new request context, otherwise the old global capabilities are still cached - // in the IdentifiedUser object - allUsersRepo = cloneProject(allUsers, user); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - } - - @Test - public void pushToExternalIdsBranch() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - // different case email is allowed - ExternalId newExtId = createExternalIdWithOtherCaseEmail("foo:bar"); - addExtId(allUsersRepo, newExtId); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - List<AccountExternalIdInfo> extIdsBefore = gApi.accounts().self().getExternalIds(); - - allowPushOfExternalIds(); - PushResult r = pushHead(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - assertThat(r.getRemoteUpdate(RefNames.REFS_EXTERNAL_IDS).getStatus()).isEqualTo(Status.OK); - - List<AccountExternalIdInfo> extIdsAfter = gApi.accounts().self().getExternalIds(); - assertThat(extIdsAfter) - .containsExactlyElementsIn( - Iterables.concat(extIdsBefore, ImmutableSet.of(toExternalIdInfo(newExtId)))); - } - - @Test - public void pushToExternalIdsBranchRejectsExternalIdWithoutAccountId() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - insertExternalIdWithoutAccountId( - allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar"); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - allowPushOfExternalIds(); - PushResult r = pushHead(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - assertRefUpdateFailure(r.getRemoteUpdate(RefNames.REFS_EXTERNAL_IDS), "invalid external IDs"); - } - - @Test - public void pushToExternalIdsBranchRejectsExternalIdWithKeyThatDoesntMatchTheNoteId() - throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - insertExternalIdWithKeyThatDoesntMatchNoteId( - allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar"); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - allowPushOfExternalIds(); - PushResult r = pushHead(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - assertRefUpdateFailure(r.getRemoteUpdate(RefNames.REFS_EXTERNAL_IDS), "invalid external IDs"); - } - - @Test - public void pushToExternalIdsBranchRejectsExternalIdWithInvalidConfig() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - insertExternalIdWithInvalidConfig( - allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar"); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - allowPushOfExternalIds(); - PushResult r = pushHead(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - assertRefUpdateFailure(r.getRemoteUpdate(RefNames.REFS_EXTERNAL_IDS), "invalid external IDs"); - } - - @Test - public void pushToExternalIdsBranchRejectsExternalIdWithEmptyNote() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - insertExternalIdWithEmptyNote( - allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar"); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - allowPushOfExternalIds(); - PushResult r = pushHead(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - assertRefUpdateFailure(r.getRemoteUpdate(RefNames.REFS_EXTERNAL_IDS), "invalid external IDs"); - } - - @Test - public void pushToExternalIdsBranchRejectsExternalIdForNonExistingAccount() throws Exception { - testPushToExternalIdsBranchRejectsInvalidExternalId( - createExternalIdForNonExistingAccount("foo:bar")); - } - - @Test - public void pushToExternalIdsBranchRejectsExternalIdWithInvalidEmail() throws Exception { - testPushToExternalIdsBranchRejectsInvalidExternalId( - createExternalIdWithInvalidEmail("foo:bar")); - } - - @Test - public void pushToExternalIdsBranchRejectsDuplicateEmails() throws Exception { - testPushToExternalIdsBranchRejectsInvalidExternalId( - createExternalIdWithDuplicateEmail("foo:bar")); - } - - @Test - public void pushToExternalIdsBranchRejectsBadPassword() throws Exception { - testPushToExternalIdsBranchRejectsInvalidExternalId(createExternalIdWithBadPassword("foo")); - } - - private void testPushToExternalIdsBranchRejectsInvalidExternalId(ExternalId invalidExtId) - throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - addExtId(allUsersRepo, invalidExtId); - allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS); - - allowPushOfExternalIds(); - PushResult r = pushHead(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); - assertRefUpdateFailure(r.getRemoteUpdate(RefNames.REFS_EXTERNAL_IDS), "invalid external IDs"); - } - - @Test - public void readExternalIdsWhenInvalidExternalIdsExist() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - resetCurrentApiUser(); - - insertValidExternalIds(); - insertInvalidButParsableExternalIds(); - - Set<ExternalId> parseableExtIds = externalIds.all(); - - insertNonParsableExternalIds(); - - Set<ExternalId> extIds = externalIds.all(); - assertThat(extIds).containsExactlyElementsIn(parseableExtIds); - - for (ExternalId parseableExtId : parseableExtIds) { - ExternalId extId = externalIds.get(parseableExtId.key()); - assertThat(extId).isEqualTo(parseableExtId); - } - } - - @Test - public void checkConsistency() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - resetCurrentApiUser(); - - insertValidExternalIds(); - - ConsistencyCheckInput input = new ConsistencyCheckInput(); - input.checkAccountExternalIds = new CheckAccountExternalIdsInput(); - ConsistencyCheckInfo checkInfo = gApi.config().server().checkConsistency(input); - assertThat(checkInfo.checkAccountExternalIdsResult.problems).isEmpty(); - - Set<ConsistencyProblemInfo> expectedProblems = new HashSet<>(); - expectedProblems.addAll(insertInvalidButParsableExternalIds()); - expectedProblems.addAll(insertNonParsableExternalIds()); - - checkInfo = gApi.config().server().checkConsistency(input); - assertThat(checkInfo.checkAccountExternalIdsResult.problems).hasSize(expectedProblems.size()); - assertThat(checkInfo.checkAccountExternalIdsResult.problems) - .containsExactlyElementsIn(expectedProblems); - } - - @Test - public void checkConsistencyNotAllowed() throws Exception { - exception.expect(AuthException.class); - exception.expectMessage("access database not permitted"); - gApi.config().server().checkConsistency(new ConsistencyCheckInput()); - } - - private ConsistencyProblemInfo consistencyError(String message) { - return new ConsistencyProblemInfo(ConsistencyProblemInfo.Status.ERROR, message); - } - - private void insertValidExternalIds() throws IOException, ConfigInvalidException, OrmException { - MutableInteger i = new MutableInteger(); - String scheme = "valid"; - ExternalIdsUpdate u = extIdsUpdate.create(); - - // create valid external IDs - u.insert( - ExternalId.createWithPassword( - ExternalId.Key.parse(nextId(scheme, i)), - admin.id, - "admin.other@example.com", - "secret-password")); - u.insert(createExternalIdWithOtherCaseEmail(nextId(scheme, i))); - } - - private Set<ConsistencyProblemInfo> insertInvalidButParsableExternalIds() - throws IOException, ConfigInvalidException, OrmException { - MutableInteger i = new MutableInteger(); - String scheme = "invalid"; - ExternalIdsUpdate u = extIdsUpdate.create(); - - Set<ConsistencyProblemInfo> expectedProblems = new HashSet<>(); - ExternalId extIdForNonExistingAccount = - createExternalIdForNonExistingAccount(nextId(scheme, i)); - u.insert(extIdForNonExistingAccount); - expectedProblems.add( - consistencyError( - "External ID '" - + extIdForNonExistingAccount.key().get() - + "' belongs to account that doesn't exist: " - + extIdForNonExistingAccount.accountId().get())); - - ExternalId extIdWithInvalidEmail = createExternalIdWithInvalidEmail(nextId(scheme, i)); - u.insert(extIdWithInvalidEmail); - expectedProblems.add( - consistencyError( - "External ID '" - + extIdWithInvalidEmail.key().get() - + "' has an invalid email: " - + extIdWithInvalidEmail.email())); - - ExternalId extIdWithDuplicateEmail = createExternalIdWithDuplicateEmail(nextId(scheme, i)); - u.insert(extIdWithDuplicateEmail); - expectedProblems.add( - consistencyError( - "Email '" - + extIdWithDuplicateEmail.email() - + "' is not unique, it's used by the following external IDs: '" - + extIdWithDuplicateEmail.key().get() - + "', 'mailto:" - + extIdWithDuplicateEmail.email() - + "'")); - - ExternalId extIdWithBadPassword = createExternalIdWithBadPassword("admin-username"); - u.insert(extIdWithBadPassword); - expectedProblems.add( - consistencyError( - "External ID '" - + extIdWithBadPassword.key().get() - + "' has an invalid password: unrecognized algorithm")); - - return expectedProblems; - } - - private Set<ConsistencyProblemInfo> insertNonParsableExternalIds() throws IOException { - MutableInteger i = new MutableInteger(); - String scheme = "corrupt"; - - Set<ConsistencyProblemInfo> expectedProblems = new HashSet<>(); - try (Repository repo = repoManager.openRepository(allUsers); - RevWalk rw = new RevWalk(repo)) { - String externalId = nextId(scheme, i); - String noteId = insertExternalIdWithoutAccountId(repo, rw, externalId); - expectedProblems.add( - consistencyError( - "Invalid external ID config for note '" - + noteId - + "': Value for 'externalId." - + externalId - + ".accountId' is missing, expected account ID")); - - externalId = nextId(scheme, i); - noteId = insertExternalIdWithKeyThatDoesntMatchNoteId(repo, rw, externalId); - expectedProblems.add( - consistencyError( - "Invalid external ID config for note '" - + noteId - + "': SHA1 of external ID '" - + externalId - + "' does not match note ID '" - + noteId - + "'")); - - noteId = insertExternalIdWithInvalidConfig(repo, rw, nextId(scheme, i)); - expectedProblems.add( - consistencyError( - "Invalid external ID config for note '" + noteId + "': Invalid line in config file")); - - noteId = insertExternalIdWithEmptyNote(repo, rw, nextId(scheme, i)); - expectedProblems.add( - consistencyError( - "Invalid external ID config for note '" - + noteId - + "': Expected exactly 1 'externalId' section, found 0")); - } - - return expectedProblems; - } - - private ExternalId createExternalIdWithOtherCaseEmail(String externalId) { - return ExternalId.createWithPassword( - ExternalId.Key.parse(externalId), admin.id, admin.email.toUpperCase(Locale.US), "password"); - } - - private String insertExternalIdWithoutAccountId(Repository repo, RevWalk rw, String externalId) - throws IOException { - ObjectId rev = ExternalIdReader.readRevision(repo); - NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev); - - ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id); - - try (ObjectInserter ins = repo.newObjectInserter()) { - ObjectId noteId = extId.key().sha1(); - Config c = new Config(); - extId.writeToConfig(c); - c.unset("externalId", extId.key().get(), "accountId"); - byte[] raw = c.toText().getBytes(UTF_8); - ObjectId dataBlob = ins.insert(OBJ_BLOB, raw); - noteMap.set(noteId, dataBlob); - - ExternalIdsUpdate.commit( - allUsers, - repo, - rw, - ins, - rev, - noteMap, - "Add external ID", - admin.getIdent(), - admin.getIdent(), - null, - GitReferenceUpdated.DISABLED); - return noteId.getName(); - } - } - - private String insertExternalIdWithKeyThatDoesntMatchNoteId( - Repository repo, RevWalk rw, String externalId) throws IOException { - ObjectId rev = ExternalIdReader.readRevision(repo); - NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev); - - ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id); - - try (ObjectInserter ins = repo.newObjectInserter()) { - ObjectId noteId = ExternalId.Key.parse(externalId + "x").sha1(); - Config c = new Config(); - extId.writeToConfig(c); - byte[] raw = c.toText().getBytes(UTF_8); - ObjectId dataBlob = ins.insert(OBJ_BLOB, raw); - noteMap.set(noteId, dataBlob); - - ExternalIdsUpdate.commit( - allUsers, - repo, - rw, - ins, - rev, - noteMap, - "Add external ID", - admin.getIdent(), - admin.getIdent(), - null, - GitReferenceUpdated.DISABLED); - return noteId.getName(); - } - } - - private String insertExternalIdWithInvalidConfig(Repository repo, RevWalk rw, String externalId) - throws IOException { - ObjectId rev = ExternalIdReader.readRevision(repo); - NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev); - - try (ObjectInserter ins = repo.newObjectInserter()) { - ObjectId noteId = ExternalId.Key.parse(externalId).sha1(); - byte[] raw = "bad-config".getBytes(UTF_8); - ObjectId dataBlob = ins.insert(OBJ_BLOB, raw); - noteMap.set(noteId, dataBlob); - - ExternalIdsUpdate.commit( - allUsers, - repo, - rw, - ins, - rev, - noteMap, - "Add external ID", - admin.getIdent(), - admin.getIdent(), - null, - GitReferenceUpdated.DISABLED); - return noteId.getName(); - } - } - - private String insertExternalIdWithEmptyNote(Repository repo, RevWalk rw, String externalId) - throws IOException { - ObjectId rev = ExternalIdReader.readRevision(repo); - NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev); - - try (ObjectInserter ins = repo.newObjectInserter()) { - ObjectId noteId = ExternalId.Key.parse(externalId).sha1(); - byte[] raw = "".getBytes(UTF_8); - ObjectId dataBlob = ins.insert(OBJ_BLOB, raw); - noteMap.set(noteId, dataBlob); - - ExternalIdsUpdate.commit( - allUsers, - repo, - rw, - ins, - rev, - noteMap, - "Add external ID", - admin.getIdent(), - admin.getIdent(), - null, - GitReferenceUpdated.DISABLED); - return noteId.getName(); - } - } - - private ExternalId createExternalIdForNonExistingAccount(String externalId) { - return ExternalId.create(ExternalId.Key.parse(externalId), new Account.Id(1)); - } - - private ExternalId createExternalIdWithInvalidEmail(String externalId) { - return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), admin.id, "invalid-email"); - } - - private ExternalId createExternalIdWithDuplicateEmail(String externalId) { - return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), admin.id, admin.email); - } - - private ExternalId createExternalIdWithBadPassword(String username) { - return ExternalId.create( - ExternalId.Key.create(SCHEME_USERNAME, username), - admin.id, - null, - "non-hashed-password-is-not-allowed"); - } - - private static String nextId(String scheme, MutableInteger i) { - return scheme + ":foo" + ++i.value; - } - - @Test - public void retryOnLockFailure() throws Exception { - Retryer<RefsMetaExternalIdsUpdate> retryer = - ExternalIdsUpdate.retryerBuilder() - .withBlockStrategy( - new BlockStrategy() { - @Override - public void block(long sleepTime) { - // Don't sleep in tests. - } - }) - .build(); - - ExternalId.Key fooId = ExternalId.Key.create("foo", "foo"); - ExternalId.Key barId = ExternalId.Key.create("bar", "bar"); - - final AtomicBoolean doneBgUpdate = new AtomicBoolean(false); - ExternalIdsUpdate update = - new ExternalIdsUpdate( - repoManager, - accountCache, - allUsers, - metricMaker, - externalIds, - new DisabledExternalIdCache(), - serverIdent.get(), - serverIdent.get(), - null, - GitReferenceUpdated.DISABLED, - () -> { - if (!doneBgUpdate.getAndSet(true)) { - try { - extIdsUpdate.create().insert(ExternalId.create(barId, admin.id)); - } catch (IOException | ConfigInvalidException | OrmException e) { - // Ignore, the successful insertion of the external ID is asserted later - } - } - }, - retryer); - assertThat(doneBgUpdate.get()).isFalse(); - update.insert(ExternalId.create(fooId, admin.id)); - assertThat(doneBgUpdate.get()).isTrue(); - - assertThat(externalIds.get(fooId)).isNotNull(); - assertThat(externalIds.get(barId)).isNotNull(); - } - - @Test - public void failAfterRetryerGivesUp() throws Exception { - ExternalId.Key[] extIdsKeys = { - ExternalId.Key.create("foo", "foo"), - ExternalId.Key.create("bar", "bar"), - ExternalId.Key.create("baz", "baz") - }; - final AtomicInteger bgCounter = new AtomicInteger(0); - ExternalIdsUpdate update = - new ExternalIdsUpdate( - repoManager, - accountCache, - allUsers, - metricMaker, - externalIds, - new DisabledExternalIdCache(), - serverIdent.get(), - serverIdent.get(), - null, - GitReferenceUpdated.DISABLED, - () -> { - try { - extIdsUpdate - .create() - .insert(ExternalId.create(extIdsKeys[bgCounter.getAndAdd(1)], admin.id)); - } catch (IOException | ConfigInvalidException | OrmException e) { - // Ignore, the successful insertion of the external ID is asserted later - } - }, - RetryerBuilder.<RefsMetaExternalIdsUpdate>newBuilder() - .retryIfException(e -> e instanceof LockFailureException) - .withStopStrategy(StopStrategies.stopAfterAttempt(extIdsKeys.length)) - .build()); - assertThat(bgCounter.get()).isEqualTo(0); - try { - update.insert(ExternalId.create(ExternalId.Key.create("abc", "abc"), admin.id)); - fail("expected LockFailureException"); - } catch (LockFailureException e) { - // Ignore, expected - } - assertThat(bgCounter.get()).isEqualTo(extIdsKeys.length); - for (ExternalId.Key extIdKey : extIdsKeys) { - assertThat(externalIds.get(extIdKey)).isNotNull(); - } - } - - @Test - public void readExternalIdWithAccountIdThatCanBeExpressedInKiB() throws Exception { - ExternalId.Key extIdKey = ExternalId.Key.parse("foo:bar"); - Account.Id accountId = new Account.Id(1024 * 100); - extIdsUpdate.create().insert(ExternalId.create(extIdKey, accountId)); - ExternalId extId = externalIds.get(extIdKey); - assertThat(extId.accountId()).isEqualTo(accountId); - } - - @Test - public void checkNoReloadAfterUpdate() throws Exception { - Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id)); - externalIdReader.setFailOnLoad(true); - - // insert external ID - ExternalId extId = ExternalId.create("foo", "bar", admin.id); - extIdsUpdate.create().insert(extId); - expectedExtIds.add(extId); - assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds); - - // update external ID - expectedExtIds.remove(extId); - extId = ExternalId.createWithEmail("foo", "bar", admin.id, "foo.bar@example.com"); - extIdsUpdate.create().upsert(extId); - expectedExtIds.add(extId); - assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds); - - // delete external ID - extIdsUpdate.create().delete(extId); - expectedExtIds.remove(extId); - assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds); - } - - @Test - public void byAccountFailIfReadingExternalIdsFails() throws Exception { - externalIdReader.setFailOnLoad(true); - - // update external ID branch so that external IDs need to be reloaded - insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id)); - - exception.expect(IOException.class); - externalIds.byAccount(admin.id); - } - - @Test - public void byEmailFailIfReadingExternalIdsFails() throws Exception { - externalIdReader.setFailOnLoad(true); - - // update external ID branch so that external IDs need to be reloaded - insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id)); - - exception.expect(IOException.class); - externalIds.byEmail(admin.email); - } - - @Test - public void byAccountUpdateExternalIdsBehindGerritsBack() throws Exception { - Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id)); - ExternalId newExtId = ExternalId.create("foo", "bar", admin.id); - insertExtIdBehindGerritsBack(newExtId); - expectedExternalIds.add(newExtId); - assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExternalIds); - } - - @Test - public void unsetEmail() throws Exception { - ExternalId extId = ExternalId.createWithEmail("x", "1", user.id, "x@example.com"); - extIdsUpdate.create().insert(extId); - - ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id); - extIdsUpdate.create().upsert(extIdWithoutEmail); - - assertThat(externalIds.get(extId.key())).isEqualTo(extIdWithoutEmail); - } - - @Test - public void unsetHttpPassword() throws Exception { - ExternalId extId = - ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id, null, "secret"); - extIdsUpdate.create().insert(extId); - - ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id); - extIdsUpdate.create().upsert(extIdWithoutPassword); - - assertThat(externalIds.get(extId.key())).isEqualTo(extIdWithoutPassword); - } - - private void insertExtIdBehindGerritsBack(ExternalId extId) throws Exception { - try (Repository repo = repoManager.openRepository(allUsers); - RevWalk rw = new RevWalk(repo); - ObjectInserter ins = repo.newObjectInserter()) { - ObjectId rev = ExternalIdReader.readRevision(repo); - NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev); - ExternalIdsUpdate.insert(rw, ins, noteMap, extId); - ExternalIdsUpdate.commit( - allUsers, - repo, - rw, - ins, - rev, - noteMap, - "insert new ID", - serverIdent.get(), - serverIdent.get(), - null, - GitReferenceUpdated.DISABLED); - } - } - - private void addExtId(TestRepository<?> testRepo, ExternalId... extIds) - throws IOException, OrmDuplicateKeyException, ConfigInvalidException { - ObjectId rev = ExternalIdReader.readRevision(testRepo.getRepository()); - - try (ObjectInserter ins = testRepo.getRepository().newObjectInserter()) { - NoteMap noteMap = ExternalIdReader.readNoteMap(testRepo.getRevWalk(), rev); - for (ExternalId extId : extIds) { - ExternalIdsUpdate.insert(testRepo.getRevWalk(), ins, noteMap, extId); - } - - ExternalIdsUpdate.commit( - allUsers, - testRepo.getRepository(), - testRepo.getRevWalk(), - ins, - rev, - noteMap, - "Add external ID", - admin.getIdent(), - admin.getIdent(), - null, - GitReferenceUpdated.DISABLED); - } - } - - private List<AccountExternalIdInfo> toExternalIdInfos(Collection<ExternalId> extIds) { - return extIds.stream().map(this::toExternalIdInfo).collect(toList()); - } - - private AccountExternalIdInfo toExternalIdInfo(ExternalId extId) { - AccountExternalIdInfo info = new AccountExternalIdInfo(); - info.identity = extId.key().get(); - info.emailAddress = extId.email(); - info.canDelete = !extId.isScheme(SCHEME_USERNAME) ? true : null; - info.trusted = - extId.isScheme(SCHEME_MAILTO) - || extId.isScheme(SCHEME_UUID) - || extId.isScheme(SCHEME_USERNAME) - ? true - : null; - return info; - } - - private void allowPushOfExternalIds() throws IOException, ConfigInvalidException { - grant(allUsers, RefNames.REFS_EXTERNAL_IDS, Permission.READ); - grant(allUsers, RefNames.REFS_EXTERNAL_IDS, Permission.PUSH); - } - - private void assertRefUpdateFailure(RemoteRefUpdate update, String msg) { - assertThat(update.getStatus()).isEqualTo(Status.REJECTED_OTHER_REASON); - assertThat(update.getMessage()).contains(msg); - } -} |