diff options
Diffstat (limited to 'gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java')
-rw-r--r-- | gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java | 2106 |
1 files changed, 0 insertions, 2106 deletions
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java deleted file mode 100644 index e66257e33b..0000000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java +++ /dev/null @@ -1,2106 +0,0 @@ -// Copyright (C) 2014 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.api.accounts; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; -import static com.google.gerrit.acceptance.GitUtil.deleteRef; -import static com.google.gerrit.acceptance.GitUtil.fetch; -import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS; -import static com.google.gerrit.gpg.PublicKeyStore.keyToString; -import static com.google.gerrit.gpg.testutil.TestKeys.allValidKeys; -import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithExpiration; -import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithSecondUserId; -import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithoutExpiration; -import static com.google.gerrit.server.StarredChangesUtil.DEFAULT_LABEL; -import static com.google.gerrit.server.StarredChangesUtil.IGNORE_LABEL; -import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY; -import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; -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 java.util.stream.Collectors.toSet; -import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; -import static org.junit.Assert.fail; - -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Iterables; -import com.google.common.io.BaseEncoding; -import com.google.common.util.concurrent.AtomicLongMap; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.AccountCreator; -import com.google.gerrit.acceptance.GerritConfig; -import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.Sandboxed; -import com.google.gerrit.acceptance.TestAccount; -import com.google.gerrit.acceptance.UseSsh; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.common.TimeUtil; -import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.common.data.Permission; -import com.google.gerrit.extensions.api.accounts.AccountInput; -import com.google.gerrit.extensions.api.accounts.EmailInput; -import com.google.gerrit.extensions.api.changes.AddReviewerInput; -import com.google.gerrit.extensions.api.changes.ReviewInput; -import com.google.gerrit.extensions.api.changes.StarsInput; -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.CheckAccountsInput; -import com.google.gerrit.extensions.common.AccountInfo; -import com.google.gerrit.extensions.common.ChangeInfo; -import com.google.gerrit.extensions.common.GpgKeyInfo; -import com.google.gerrit.extensions.common.SshKeyInfo; -import com.google.gerrit.extensions.events.AccountIndexedListener; -import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; -import com.google.gerrit.extensions.registration.DynamicSet; -import com.google.gerrit.extensions.registration.RegistrationHandle; -import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.BadRequestException; -import com.google.gerrit.extensions.restapi.ResourceConflictException; -import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import com.google.gerrit.extensions.restapi.RestApiException; -import com.google.gerrit.gpg.Fingerprint; -import com.google.gerrit.gpg.PublicKeyStore; -import com.google.gerrit.gpg.testutil.TestKey; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RefNames; -import com.google.gerrit.server.Sequences; -import com.google.gerrit.server.account.AccountConfig; -import com.google.gerrit.server.account.AccountManager; -import com.google.gerrit.server.account.AccountsUpdate; -import com.google.gerrit.server.account.AuthRequest; -import com.google.gerrit.server.account.AuthResult; -import com.google.gerrit.server.account.Emails; -import com.google.gerrit.server.account.WatchConfig; -import com.google.gerrit.server.account.WatchConfig.NotifyType; -import com.google.gerrit.server.account.externalids.ExternalId; -import com.google.gerrit.server.account.externalids.ExternalIds; -import com.google.gerrit.server.account.externalids.ExternalIdsUpdate; -import com.google.gerrit.server.config.AllUsersName; -import com.google.gerrit.server.git.ProjectConfig; -import com.google.gerrit.server.group.InternalGroup; -import com.google.gerrit.server.mail.Address; -import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl; -import com.google.gerrit.server.project.RefPattern; -import com.google.gerrit.server.query.account.InternalAccountQuery; -import com.google.gerrit.server.util.MagicBranch; -import com.google.gerrit.testutil.ConfigSuite; -import com.google.gerrit.testutil.FakeEmailSender.Message; -import com.google.inject.Inject; -import com.google.inject.Provider; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import org.bouncycastle.bcpg.ArmoredOutputStream; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.eclipse.jgit.api.errors.TransportException; -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.ObjectReader; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.PushCertificateIdent; -import org.eclipse.jgit.transport.PushResult; -import org.eclipse.jgit.transport.RemoteRefUpdate; -import org.eclipse.jgit.treewalk.TreeWalk; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class AccountIT extends AbstractDaemonTest { - @ConfigSuite.Default - public static Config enableSignedPushConfig() { - Config cfg = new Config(); - cfg.setBoolean("receive", null, "enableSignedPush", true); - return cfg; - } - - @Inject private Provider<PublicKeyStore> publicKeyStoreProvider; - - @Inject private AllUsersName allUsers; - - @Inject private AccountsUpdate.Server accountsUpdate; - - @Inject private ExternalIds externalIds; - - @Inject private ExternalIdsUpdate.User externalIdsUpdateFactory; - - @Inject private DynamicSet<AccountIndexedListener> accountIndexedListeners; - - @Inject private DynamicSet<GitReferenceUpdatedListener> refUpdateListeners; - - @Inject private Sequences seq; - - @Inject private Provider<InternalAccountQuery> accountQueryProvider; - - @Inject protected Emails emails; - - @Inject private AccountManager accountManager; - - private AccountIndexedCounter accountIndexedCounter; - private RegistrationHandle accountIndexEventCounterHandle; - private RefUpdateCounter refUpdateCounter; - private RegistrationHandle refUpdateCounterHandle; - private ExternalIdsUpdate externalIdsUpdate; - private List<ExternalId> savedExternalIds; - - @Before - public void addAccountIndexEventCounter() { - accountIndexedCounter = new AccountIndexedCounter(); - accountIndexEventCounterHandle = accountIndexedListeners.add(accountIndexedCounter); - } - - @After - public void removeAccountIndexEventCounter() { - if (accountIndexEventCounterHandle != null) { - accountIndexEventCounterHandle.remove(); - } - } - - @Before - public void addRefUpdateCounter() { - refUpdateCounter = new RefUpdateCounter(); - refUpdateCounterHandle = refUpdateListeners.add(refUpdateCounter); - } - - @After - public void removeRefUpdateCounter() { - if (refUpdateCounterHandle != null) { - refUpdateCounterHandle.remove(); - } - } - - @Before - public void saveExternalIds() throws Exception { - externalIdsUpdate = externalIdsUpdateFactory.create(); - - savedExternalIds = new ArrayList<>(); - savedExternalIds.addAll(externalIds.byAccount(admin.id)); - savedExternalIds.addAll(externalIds.byAccount(user.id)); - } - - @After - public void restoreExternalIds() throws Exception { - if (savedExternalIds != null) { - // savedExternalIds is null when we don't run SSH tests and the assume in - // @Before in AbstractDaemonTest prevents this class' @Before method from - // being executed. - externalIdsUpdate.delete(externalIds.byAccount(admin.id)); - externalIdsUpdate.delete(externalIds.byAccount(user.id)); - externalIdsUpdate.insert(savedExternalIds); - } - } - - @After - public void clearPublicKeyStore() throws Exception { - try (Repository repo = repoManager.openRepository(allUsers)) { - Ref ref = repo.exactRef(REFS_GPG_KEYS); - if (ref != null) { - RefUpdate ru = repo.updateRef(REFS_GPG_KEYS); - ru.setForceUpdate(true); - assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED); - } - } - } - - @After - public void deleteGpgKeys() throws Exception { - String ref = REFS_GPG_KEYS; - try (Repository repo = repoManager.openRepository(allUsers)) { - if (repo.getRefDatabase().exactRef(ref) != null) { - RefUpdate ru = repo.updateRef(ref); - ru.setForceUpdate(true); - assertWithMessage("Failed to delete " + ref) - .that(ru.delete()) - .isEqualTo(RefUpdate.Result.FORCED); - } - } - } - - @Test - public void create() throws Exception { - Account.Id accountId = create(2); // account creation + external ID creation - refUpdateCounter.assertRefUpdateFor( - RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)), - RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS), - RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS)); - } - - @Test - @UseSsh - public void createWithSshKeys() throws Exception { - Account.Id accountId = create(3); // account creation + external ID creation + adding SSH keys - refUpdateCounter.assertRefUpdateFor( - ImmutableMap.of( - RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)), - 2, - RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS), - 1, - RefUpdateCounter.projectRef( - allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS), - 1)); - } - - private Account.Id create(int expectedAccountReindexCalls) throws Exception { - String name = "foo"; - TestAccount foo = accountCreator.create(name); - AccountInfo info = gApi.accounts().id(foo.id.get()).get(); - assertThat(info.username).isEqualTo(name); - assertThat(info.name).isEqualTo(name); - accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls); - assertUserBranch(foo.getId(), name, null); - return foo.getId(); - } - - @Test - public void createAnonymousCoward() throws Exception { - TestAccount anonymousCoward = accountCreator.create(); - accountIndexedCounter.assertReindexOf(anonymousCoward); - assertUserBranchWithoutAccountConfig(anonymousCoward.getId()); - } - - @Test - public void updateNonExistingAccount() throws Exception { - Account.Id nonExistingAccountId = new Account.Id(999999); - AtomicBoolean consumerCalled = new AtomicBoolean(); - Account account = - accountsUpdate.create().update(nonExistingAccountId, a -> consumerCalled.set(true)); - assertThat(account).isNull(); - assertThat(consumerCalled.get()).isFalse(); - } - - @Test - public void updateAccountWithoutAccountConfigNoteDb() throws Exception { - TestAccount anonymousCoward = accountCreator.create(); - assertUserBranchWithoutAccountConfig(anonymousCoward.getId()); - - String status = "OOO"; - Account account = - accountsUpdate.create().update(anonymousCoward.getId(), a -> a.setStatus(status)); - assertThat(account).isNotNull(); - assertThat(account.getFullName()).isNull(); - assertThat(account.getStatus()).isEqualTo(status); - assertUserBranch(anonymousCoward.getId(), null, status); - } - - private void assertUserBranchWithoutAccountConfig(Account.Id accountId) throws Exception { - assertUserBranch(accountId, null, null); - } - - private void assertUserBranch( - Account.Id accountId, @Nullable String name, @Nullable String status) throws Exception { - try (Repository repo = repoManager.openRepository(allUsers); - RevWalk rw = new RevWalk(repo); - ObjectReader or = repo.newObjectReader()) { - Ref ref = repo.exactRef(RefNames.refsUsers(accountId)); - assertThat(ref).isNotNull(); - RevCommit c = rw.parseCommit(ref.getObjectId()); - long timestampDiffMs = - Math.abs( - c.getCommitTime() * 1000L - - accountCache.get(accountId).getAccount().getRegisteredOn().getTime()); - assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS); - - // Check the 'account.config' file. - try (TreeWalk tw = TreeWalk.forPath(or, AccountConfig.ACCOUNT_CONFIG, c.getTree())) { - if (name != null || status != null) { - assertThat(tw).isNotNull(); - Config cfg = new Config(); - cfg.fromText(new String(or.open(tw.getObjectId(0), OBJ_BLOB).getBytes(), UTF_8)); - assertThat(cfg.getString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_FULL_NAME)) - .isEqualTo(name); - assertThat(cfg.getString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_STATUS)) - .isEqualTo(status); - } else { - // No account properties were set, hence an 'account.config' file was not created. - assertThat(tw).isNull(); - } - } - } - } - - @Test - public void get() throws Exception { - AccountInfo info = gApi.accounts().id("admin").get(); - assertThat(info.name).isEqualTo("Administrator"); - assertThat(info.email).isEqualTo("admin@example.com"); - assertThat(info.username).isEqualTo("admin"); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void getByIntId() throws Exception { - AccountInfo info = gApi.accounts().id("admin").get(); - AccountInfo infoByIntId = gApi.accounts().id(info._accountId).get(); - assertThat(info.name).isEqualTo(infoByIntId.name); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void self() throws Exception { - AccountInfo info = gApi.accounts().self().get(); - assertUser(info, admin); - - info = gApi.accounts().id("self").get(); - assertUser(info, admin); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void active() throws Exception { - assertThat(gApi.accounts().id("user").getActive()).isTrue(); - gApi.accounts().id("user").setActive(false); - assertThat(gApi.accounts().id("user").getActive()).isFalse(); - accountIndexedCounter.assertReindexOf(user); - - gApi.accounts().id("user").setActive(true); - assertThat(gApi.accounts().id("user").getActive()).isTrue(); - accountIndexedCounter.assertReindexOf(user); - } - - @Test - public void deactivateSelf() throws Exception { - exception.expect(ResourceConflictException.class); - exception.expectMessage("cannot deactivate own account"); - gApi.accounts().self().setActive(false); - } - - @Test - public void deactivateNotActive() throws Exception { - assertThat(gApi.accounts().id("user").getActive()).isTrue(); - gApi.accounts().id("user").setActive(false); - assertThat(gApi.accounts().id("user").getActive()).isFalse(); - try { - gApi.accounts().id("user").setActive(false); - fail("Expected exception"); - } catch (ResourceConflictException e) { - assertThat(e.getMessage()).isEqualTo("account not active"); - } - gApi.accounts().id("user").setActive(true); - } - - @Test - public void starUnstarChange() throws Exception { - PushOneCommit.Result r = createChange(); - String triplet = project.get() + "~master~" + r.getChangeId(); - refUpdateCounter.clear(); - - gApi.accounts().self().starChange(triplet); - ChangeInfo change = info(triplet); - assertThat(change.starred).isTrue(); - assertThat(change.stars).contains(DEFAULT_LABEL); - refUpdateCounter.assertRefUpdateFor( - RefUpdateCounter.projectRef( - allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id))); - - gApi.accounts().self().unstarChange(triplet); - change = info(triplet); - assertThat(change.starred).isNull(); - assertThat(change.stars).isNull(); - refUpdateCounter.assertRefUpdateFor( - RefUpdateCounter.projectRef( - allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id))); - - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void starUnstarChangeWithLabels() throws Exception { - PushOneCommit.Result r = createChange(); - String triplet = project.get() + "~master~" + r.getChangeId(); - refUpdateCounter.clear(); - - assertThat(gApi.accounts().self().getStars(triplet)).isEmpty(); - assertThat(gApi.accounts().self().getStarredChanges()).isEmpty(); - - gApi.accounts() - .self() - .setStars(triplet, new StarsInput(ImmutableSet.of(DEFAULT_LABEL, "red", "blue"))); - ChangeInfo change = info(triplet); - assertThat(change.starred).isTrue(); - assertThat(change.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder(); - assertThat(gApi.accounts().self().getStars(triplet)) - .containsExactly("blue", "red", DEFAULT_LABEL) - .inOrder(); - List<ChangeInfo> starredChanges = gApi.accounts().self().getStarredChanges(); - assertThat(starredChanges).hasSize(1); - ChangeInfo starredChange = starredChanges.get(0); - assertThat(starredChange._number).isEqualTo(r.getChange().getId().get()); - assertThat(starredChange.starred).isTrue(); - assertThat(starredChange.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder(); - refUpdateCounter.assertRefUpdateFor( - RefUpdateCounter.projectRef( - allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id))); - - gApi.accounts() - .self() - .setStars( - triplet, - new StarsInput(ImmutableSet.of("yellow"), ImmutableSet.of(DEFAULT_LABEL, "blue"))); - change = info(triplet); - assertThat(change.starred).isNull(); - assertThat(change.stars).containsExactly("red", "yellow").inOrder(); - assertThat(gApi.accounts().self().getStars(triplet)).containsExactly("red", "yellow").inOrder(); - starredChanges = gApi.accounts().self().getStarredChanges(); - assertThat(starredChanges).hasSize(1); - starredChange = starredChanges.get(0); - assertThat(starredChange._number).isEqualTo(r.getChange().getId().get()); - assertThat(starredChange.starred).isNull(); - assertThat(starredChange.stars).containsExactly("red", "yellow").inOrder(); - refUpdateCounter.assertRefUpdateFor( - RefUpdateCounter.projectRef( - allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id))); - - accountIndexedCounter.assertNoReindex(); - - setApiUser(user); - exception.expect(AuthException.class); - exception.expectMessage("not allowed to get stars of another account"); - gApi.accounts().id(Integer.toString((admin.id.get()))).getStars(triplet); - } - - @Test - public void starWithInvalidLabels() throws Exception { - PushOneCommit.Result r = createChange(); - String triplet = project.get() + "~master~" + r.getChangeId(); - exception.expect(BadRequestException.class); - exception.expectMessage("invalid labels: another invalid label, invalid label"); - gApi.accounts() - .self() - .setStars( - triplet, - new StarsInput( - ImmutableSet.of(DEFAULT_LABEL, "invalid label", "blue", "another invalid label"))); - } - - @Test - public void deleteStarLabelsFromChangeWithoutStarLabels() throws Exception { - PushOneCommit.Result r = createChange(); - String triplet = project.get() + "~master~" + r.getChangeId(); - assertThat(gApi.accounts().self().getStars(triplet)).isEmpty(); - - gApi.accounts().self().setStars(triplet, new StarsInput()); - - assertThat(gApi.accounts().self().getStars(triplet)).isEmpty(); - } - - @Test - public void starWithDefaultAndIgnoreLabel() throws Exception { - PushOneCommit.Result r = createChange(); - String triplet = project.get() + "~master~" + r.getChangeId(); - exception.expect(BadRequestException.class); - exception.expectMessage( - "The labels " - + DEFAULT_LABEL - + " and " - + IGNORE_LABEL - + " are mutually exclusive." - + " Only one of them can be set."); - gApi.accounts() - .self() - .setStars(triplet, new StarsInput(ImmutableSet.of(DEFAULT_LABEL, "blue", IGNORE_LABEL))); - } - - @Test - public void ignoreChangeBySetStars() throws Exception { - TestAccount user2 = accountCreator.user2(); - accountIndexedCounter.clear(); - - PushOneCommit.Result r = createChange(); - - AddReviewerInput in = new AddReviewerInput(); - in.reviewer = user.email; - gApi.changes().id(r.getChangeId()).addReviewer(in); - - in = new AddReviewerInput(); - in.reviewer = user2.email; - gApi.changes().id(r.getChangeId()).addReviewer(in); - - setApiUser(user); - gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL))); - - sender.clear(); - setApiUser(admin); - gApi.changes().id(r.getChangeId()).abandon(); - List<Message> messages = sender.getMessages(); - assertThat(messages).hasSize(1); - assertThat(messages.get(0).rcpt()).containsExactly(user2.emailAddress); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void addReviewerToIgnoredChange() throws Exception { - PushOneCommit.Result r = createChange(); - - setApiUser(user); - gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL))); - - sender.clear(); - setApiUser(admin); - - AddReviewerInput in = new AddReviewerInput(); - in.reviewer = user.email; - gApi.changes().id(r.getChangeId()).addReviewer(in); - List<Message> messages = sender.getMessages(); - assertThat(messages).hasSize(1); - Message message = messages.get(0); - assertThat(message.rcpt()).containsExactly(user.emailAddress); - assertMailReplyTo(message, admin.email); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void suggestAccounts() throws Exception { - String adminUsername = "admin"; - List<AccountInfo> result = gApi.accounts().suggestAccounts().withQuery(adminUsername).get(); - assertThat(result).hasSize(1); - assertThat(result.get(0).username).isEqualTo(adminUsername); - - List<AccountInfo> resultShortcutApi = gApi.accounts().suggestAccounts(adminUsername).get(); - assertThat(resultShortcutApi).hasSize(result.size()); - - List<AccountInfo> emptyResult = gApi.accounts().suggestAccounts("unknown").get(); - assertThat(emptyResult).isEmpty(); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void addEmail() throws Exception { - List<String> emails = ImmutableList.of("new.email@example.com", "new.email@example.systems"); - Set<String> currentEmails = getEmails(); - for (String email : emails) { - assertThat(currentEmails).doesNotContain(email); - EmailInput input = newEmailInput(email); - gApi.accounts().self().addEmail(input); - accountIndexedCounter.assertReindexOf(admin); - } - - resetCurrentApiUser(); - assertThat(getEmails()).containsAllIn(emails); - } - - @Test - public void addInvalidEmail() throws Exception { - List<String> emails = - ImmutableList.of( - // Missing domain part - "new.email", - - // Missing domain part - "new.email@", - - // Missing user part - "@example.com", - - // Non-supported TLD (see tlds-alpha-by-domain.txt) - "new.email@example.africa"); - for (String email : emails) { - EmailInput input = newEmailInput(email); - try { - gApi.accounts().self().addEmail(input); - fail("Expected BadRequestException for invalid email address: " + email); - } catch (BadRequestException e) { - assertThat(e).hasMessageThat().isEqualTo("invalid email address"); - } - } - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void cannotAddNonConfirmedEmailWithoutModifyAccountPermission() throws Exception { - TestAccount account = accountCreator.create(name("user")); - EmailInput input = newEmailInput("test@test.com"); - setApiUser(user); - exception.expect(AuthException.class); - gApi.accounts().id(account.username).addEmail(input); - } - - @Test - public void cannotAddEmailAddressUsedByAnotherAccount() throws Exception { - String email = "new.email@example.com"; - EmailInput input = newEmailInput(email); - gApi.accounts().self().addEmail(input); - exception.expect(ResourceConflictException.class); - exception.expectMessage("Identity 'mailto:" + email + "' in use by another account"); - gApi.accounts().id(user.username).addEmail(input); - } - - @Test - @GerritConfig( - name = "auth.registerEmailPrivateKey", - value = "HsOc6l+2lhS9G7sE/RsnS7Z6GJjdRDX14co=") - public void addEmailSendsConfirmationEmail() throws Exception { - String email = "new.email@example.com"; - EmailInput input = newEmailInput(email, false); - gApi.accounts().self().addEmail(input); - - assertThat(sender.getMessages()).hasSize(1); - Message m = sender.getMessages().get(0); - assertThat(m.rcpt()).containsExactly(new Address(email)); - } - - @Test - public void deleteEmail() throws Exception { - String email = "foo.bar@example.com"; - EmailInput input = newEmailInput(email); - gApi.accounts().self().addEmail(input); - - resetCurrentApiUser(); - assertThat(getEmails()).contains(email); - - accountIndexedCounter.clear(); - gApi.accounts().self().deleteEmail(input.email); - accountIndexedCounter.assertReindexOf(admin); - - resetCurrentApiUser(); - assertThat(getEmails()).doesNotContain(email); - } - - @Test - public void deleteEmailFromCustomExternalIdSchemes() throws Exception { - String email = "foo.bar@example.com"; - String extId1 = "foo:bar"; - String extId2 = "foo:baz"; - List<ExternalId> extIds = - ImmutableList.of( - ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id, email), - ExternalId.createWithEmail(ExternalId.Key.parse(extId2), admin.id, email)); - externalIdsUpdateFactory.create().insert(extIds); - accountIndexedCounter.assertReindexOf(admin); - assertThat( - gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet())) - .containsAllOf(extId1, extId2); - - resetCurrentApiUser(); - assertThat(getEmails()).contains(email); - - gApi.accounts().self().deleteEmail(email); - accountIndexedCounter.assertReindexOf(admin); - - resetCurrentApiUser(); - assertThat(getEmails()).doesNotContain(email); - assertThat( - gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet())) - .containsNoneOf(extId1, extId2); - } - - @Test - public void deleteEmailOfOtherUser() throws Exception { - String email = "foo.bar@example.com"; - EmailInput input = new EmailInput(); - input.email = email; - input.noConfirmation = true; - gApi.accounts().id(user.id.get()).addEmail(input); - accountIndexedCounter.assertReindexOf(user); - - setApiUser(user); - assertThat(getEmails()).contains(email); - - // admin can delete email of user - setApiUser(admin); - gApi.accounts().id(user.id.get()).deleteEmail(email); - accountIndexedCounter.assertReindexOf(user); - - setApiUser(user); - assertThat(getEmails()).doesNotContain(email); - - // user cannot delete email of admin - exception.expect(AuthException.class); - exception.expectMessage("modify account not permitted"); - gApi.accounts().id(admin.id.get()).deleteEmail(admin.email); - } - - @Test - public void lookUpByEmail() throws Exception { - // exact match with scheme "mailto:" - assertEmail(emails.getAccountFor(admin.email), admin); - - // exact match with other scheme - String email = "foo.bar@example.com"; - externalIdsUpdateFactory - .create() - .insert(ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email)); - assertEmail(emails.getAccountFor(email), admin); - - // wrong case doesn't match - assertThat(emails.getAccountFor(admin.email.toUpperCase(Locale.US))).isEmpty(); - - // prefix doesn't match - assertThat(emails.getAccountFor(admin.email.substring(0, admin.email.indexOf('@')))).isEmpty(); - - // non-existing doesn't match - assertThat(emails.getAccountFor("non-existing@example.com")).isEmpty(); - - // lookup several accounts by email at once - ImmutableSetMultimap<String, Account.Id> byEmails = - emails.getAccountsFor(admin.email, user.email); - assertEmail(byEmails.get(admin.email), admin); - assertEmail(byEmails.get(user.email), user); - } - - @Test - public void lookUpByPreferredEmail() throws Exception { - // create an inconsistent account that has a preferred email without external ID - String prefix = "foo.preferred"; - String prefEmail = prefix + "@example.com"; - TestAccount foo = accountCreator.create(name("foo")); - accountsUpdate.create().update(foo.id, a -> a.setPreferredEmail(prefEmail)); - - // verify that the account is still found when using the preferred email to lookup the account - 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 - public void putStatus() throws Exception { - List<String> statuses = ImmutableList.of("OOO", "Busy"); - AccountInfo info; - for (String status : statuses) { - gApi.accounts().self().setStatus(status); - admin.status = status; - info = gApi.accounts().self().get(); - assertUser(info, admin); - accountIndexedCounter.assertReindexOf(admin); - } - } - - @Test - public void setName() throws Exception { - gApi.accounts().self().setName("Admin McAdminface"); - assertThat(gApi.accounts().self().get().name).isEqualTo("Admin McAdminface"); - } - - @Test - public void adminCanSetNameOfOtherUser() throws Exception { - gApi.accounts().id(user.username).setName("User McUserface"); - assertThat(gApi.accounts().id(user.username).get().name).isEqualTo("User McUserface"); - } - - @Test - public void userCannotSetNameOfOtherUser() throws Exception { - setApiUser(user); - exception.expect(AuthException.class); - gApi.accounts().id(admin.username).setName("Admin McAdminface"); - } - - @Test - @Sandboxed - public void userCanSetNameOfOtherUserWithModifyAccountPermission() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.MODIFY_ACCOUNT); - gApi.accounts().id(admin.username).setName("Admin McAdminface"); - assertThat(gApi.accounts().id(admin.username).get().name).isEqualTo("Admin McAdminface"); - } - - @Test - @Sandboxed - public void fetchUserBranch() throws Exception { - setApiUser(user); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user); - String userRefName = RefNames.refsUsers(user.id); - - // remove default READ permissions - ProjectConfig cfg = projectCache.checkedGet(allUsers).getConfig(); - cfg.getAccessSection(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true) - .remove(new Permission(Permission.READ)); - saveProjectConfig(allUsers, cfg); - - // deny READ permission that is inherited from All-Projects - deny(allUsers, RefNames.REFS + "*", Permission.READ, ANONYMOUS_USERS); - - // fetching user branch without READ permission fails - try { - fetch(allUsersRepo, userRefName + ":userRef"); - Assert.fail("user branch is visible although no READ permission is granted"); - } catch (TransportException e) { - // expected because no READ granted on user branch - } - - // allow each user to read its own user branch - grant( - allUsers, - RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", - Permission.READ, - false, - REGISTERED_USERS); - - // fetch user branch using refs/users/YY/XXXXXXX - fetch(allUsersRepo, userRefName + ":userRef"); - Ref userRef = allUsersRepo.getRepository().exactRef("userRef"); - assertThat(userRef).isNotNull(); - - // fetch user branch using refs/users/self - fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userSelfRef"); - Ref userSelfRef = allUsersRepo.getRepository().getRefDatabase().exactRef("userSelfRef"); - assertThat(userSelfRef).isNotNull(); - assertThat(userSelfRef.getObjectId()).isEqualTo(userRef.getObjectId()); - - accountIndexedCounter.assertNoReindex(); - - // fetching user branch of another user fails - String otherUserRefName = RefNames.refsUsers(admin.id); - exception.expect(TransportException.class); - exception.expectMessage("Remote does not have " + otherUserRefName + " available for fetch."); - fetch(allUsersRepo, otherUserRefName + ":otherUserRef"); - } - - @Test - public void pushToUserBranch() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef"); - allUsersRepo.reset("userRef"); - PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo); - push.to(RefNames.refsUsers(admin.id)).assertOkStatus(); - accountIndexedCounter.assertReindexOf(admin); - - push = pushFactory.create(db, admin.getIdent(), allUsersRepo); - push.to(RefNames.REFS_USERS_SELF).assertOkStatus(); - accountIndexedCounter.assertReindexOf(admin); - } - - @Test - public void pushToUserBranchForReview() throws Exception { - String userRefName = RefNames.refsUsers(admin.id); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRefName + ":userRef"); - allUsersRepo.reset("userRef"); - PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo); - PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName); - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - gApi.changes().id(r.getChangeId()).current().submit(); - accountIndexedCounter.assertReindexOf(admin); - - push = pushFactory.create(db, admin.getIdent(), allUsersRepo); - r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName); - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - gApi.changes().id(r.getChangeId()).current().submit(); - accountIndexedCounter.assertReindexOf(admin); - } - - @Test - public void pushAccountConfigToUserBranchForReviewAndSubmit() throws Exception { - String userRef = RefNames.refsUsers(admin.id); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_STATUS, "out-of-office"); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(MagicBranch.NEW_CHANGE + userRef); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef); - - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - gApi.changes().id(r.getChangeId()).current().submit(); - accountIndexedCounter.assertReindexOf(admin); - - AccountInfo info = gApi.accounts().self().get(); - assertThat(info.email).isEqualTo(admin.email); - assertThat(info.name).isEqualTo(admin.fullName); - assertThat(info.status).isEqualTo("out-of-office"); - } - - @Test - public void pushAccountConfigWithPrefEmailThatDoesNotExistAsExtIdToUserBranchForReviewAndSubmit() - throws Exception { - TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo"); - String userRef = RefNames.refsUsers(foo.id); - accountIndexedCounter.clear(); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - String email = "some.email@example.com"; - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_PREFERRED_EMAIL, email); - - PushOneCommit.Result r = - pushFactory - .create( - db, - foo.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(MagicBranch.NEW_CHANGE + userRef); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef); - - setApiUser(foo); - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - gApi.changes().id(r.getChangeId()).current().submit(); - - accountIndexedCounter.assertReindexOf(foo); - - AccountInfo info = gApi.accounts().self().get(); - assertThat(info.email).isEqualTo(email); - assertThat(info.name).isEqualTo(foo.fullName); - } - - @Test - public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfConfigIsInvalid() - throws Exception { - String userRef = RefNames.refsUsers(admin.id); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - "invalid config") - .to(MagicBranch.NEW_CHANGE + userRef); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef); - - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - exception.expect(ResourceConflictException.class); - exception.expectMessage( - String.format( - "invalid account configuration: commit '%s' has an invalid '%s' file for account '%s':" - + " Invalid config file %s in commit %s", - r.getCommit().name(), - AccountConfig.ACCOUNT_CONFIG, - admin.id, - AccountConfig.ACCOUNT_CONFIG, - r.getCommit().name())); - gApi.changes().id(r.getChangeId()).current().submit(); - } - - @Test - public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfPreferredEmailIsInvalid() - throws Exception { - String userRef = RefNames.refsUsers(admin.id); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - String noEmail = "no.email"; - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_PREFERRED_EMAIL, noEmail); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(MagicBranch.NEW_CHANGE + userRef); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef); - - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - exception.expect(ResourceConflictException.class); - exception.expectMessage( - String.format( - "invalid account configuration: invalid preferred email '%s' for account '%s'", - noEmail, admin.id)); - gApi.changes().id(r.getChangeId()).current().submit(); - } - - @Test - public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfOwnAccountIsDeactivated() - throws Exception { - String userRef = RefNames.refsUsers(admin.id); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - Config ac = getAccountConfig(allUsersRepo); - ac.setBoolean(AccountConfig.ACCOUNT, null, AccountConfig.KEY_ACTIVE, false); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(MagicBranch.NEW_CHANGE + userRef); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef); - - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - exception.expect(ResourceConflictException.class); - exception.expectMessage("invalid account configuration: cannot deactivate own account"); - gApi.changes().id(r.getChangeId()).current().submit(); - } - - @Test - @Sandboxed - public void pushAccountConfigToUserBranchForReviewDeactivateOtherAccount() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestAccount foo = accountCreator.create(name("foo")); - assertThat(gApi.accounts().id(foo.id.get()).getActive()).isTrue(); - String userRef = RefNames.refsUsers(foo.id); - accountIndexedCounter.clear(); - - InternalGroup adminGroup = - groupCache.get(new AccountGroup.NameKey("Administrators")).orElse(null); - grant(allUsers, userRef, Permission.PUSH, false, adminGroup.getGroupUUID()); - grantLabel("Code-Review", -2, 2, allUsers, userRef, false, adminGroup.getGroupUUID(), false); - grant(allUsers, userRef, Permission.SUBMIT, false, adminGroup.getGroupUUID()); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - Config ac = getAccountConfig(allUsersRepo); - ac.setBoolean(AccountConfig.ACCOUNT, null, AccountConfig.KEY_ACTIVE, false); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(MagicBranch.NEW_CHANGE + userRef); - r.assertOkStatus(); - accountIndexedCounter.assertNoReindex(); - assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef); - - gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); - gApi.changes().id(r.getChangeId()).current().submit(); - accountIndexedCounter.assertReindexOf(foo); - - assertThat(gApi.accounts().id(foo.id.get()).getActive()).isFalse(); - } - - @Test - public void pushWatchConfigToUserBranch() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef"); - allUsersRepo.reset("userRef"); - - Config wc = new Config(); - wc.setString( - WatchConfig.PROJECT, - project.get(), - WatchConfig.KEY_NOTIFY, - WatchConfig.NotifyValue.create(null, EnumSet.of(NotifyType.ALL_COMMENTS)).toString()); - PushOneCommit push = - pushFactory.create( - db, - admin.getIdent(), - allUsersRepo, - "Add project watch", - WatchConfig.WATCH_CONFIG, - wc.toText()); - push.to(RefNames.REFS_USERS_SELF).assertOkStatus(); - accountIndexedCounter.assertReindexOf(admin); - - String invalidNotifyValue = "]invalid["; - wc.setString(WatchConfig.PROJECT, project.get(), WatchConfig.KEY_NOTIFY, invalidNotifyValue); - push = - pushFactory.create( - db, - admin.getIdent(), - allUsersRepo, - "Add invalid project watch", - WatchConfig.WATCH_CONFIG, - wc.toText()); - PushOneCommit.Result r = push.to(RefNames.REFS_USERS_SELF); - r.assertErrorStatus("invalid watch configuration"); - r.assertMessage( - String.format( - "%s: Invalid project watch of account %d for project %s: %s", - WatchConfig.WATCH_CONFIG, admin.getId().get(), project.get(), invalidNotifyValue)); - } - - @Test - public void pushAccountConfigToUserBranch() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef"); - allUsersRepo.reset("userRef"); - - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_STATUS, "out-of-office"); - - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(RefNames.REFS_USERS_SELF) - .assertOkStatus(); - accountIndexedCounter.assertReindexOf(admin); - - AccountInfo info = gApi.accounts().self().get(); - assertThat(info.email).isEqualTo(admin.email); - assertThat(info.name).isEqualTo(admin.fullName); - assertThat(info.status).isEqualTo("out-of-office"); - } - - @Test - public void pushAccountConfigToUserBranchIsRejectedIfConfigIsInvalid() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef"); - allUsersRepo.reset("userRef"); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - "invalid config") - .to(RefNames.REFS_USERS_SELF); - r.assertErrorStatus("invalid account configuration"); - r.assertMessage( - String.format( - "commit '%s' has an invalid '%s' file for account '%s':" - + " Invalid config file %s in commit %s", - r.getCommit().name(), - AccountConfig.ACCOUNT_CONFIG, - admin.id, - AccountConfig.ACCOUNT_CONFIG, - r.getCommit().name())); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void pushAccountConfigToUserBranchIsRejectedIfPreferredEmailIsInvalid() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef"); - allUsersRepo.reset("userRef"); - - String noEmail = "no.email"; - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_PREFERRED_EMAIL, noEmail); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(RefNames.REFS_USERS_SELF); - r.assertErrorStatus("invalid account configuration"); - r.assertMessage( - String.format("invalid preferred email '%s' for account '%s'", noEmail, admin.id)); - accountIndexedCounter.assertNoReindex(); - } - - @Test - public void pushAccountConfigToUserBranchInvalidPreferredEmailButNotChanged() throws Exception { - TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo"); - String userRef = RefNames.refsUsers(foo.id); - - String noEmail = "no.email"; - accountsUpdate.create().update(foo.id, a -> a.setPreferredEmail(noEmail)); - accountIndexedCounter.clear(); - - grant(allUsers, userRef, Permission.PUSH, false, REGISTERED_USERS); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - String status = "in vacation"; - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_STATUS, status); - - pushFactory - .create( - db, - foo.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(userRef) - .assertOkStatus(); - accountIndexedCounter.assertReindexOf(foo); - - AccountInfo info = gApi.accounts().id(foo.id.get()).get(); - assertThat(info.email).isEqualTo(noEmail); - assertThat(info.name).isEqualTo(foo.fullName); - assertThat(info.status).isEqualTo(status); - } - - @Test - public void pushAccountConfigToUserBranchIfPreferredEmailDoesNotExistAsExtId() throws Exception { - TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo"); - String userRef = RefNames.refsUsers(foo.id); - accountIndexedCounter.clear(); - - InternalGroup adminGroup = - groupCache.get(new AccountGroup.NameKey("Administrators")).orElse(null); - grant(allUsers, userRef, Permission.PUSH, false, adminGroup.getGroupUUID()); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - String email = "some.email@example.com"; - Config ac = getAccountConfig(allUsersRepo); - ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_PREFERRED_EMAIL, email); - - pushFactory - .create( - db, - foo.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(userRef) - .assertOkStatus(); - accountIndexedCounter.assertReindexOf(foo); - - AccountInfo info = gApi.accounts().id(foo.id.get()).get(); - assertThat(info.email).isEqualTo(email); - assertThat(info.name).isEqualTo(foo.fullName); - } - - @Test - public void pushAccountConfigToUserBranchIsRejectedIfOwnAccountIsDeactivated() throws Exception { - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef"); - allUsersRepo.reset("userRef"); - - Config ac = getAccountConfig(allUsersRepo); - ac.setBoolean(AccountConfig.ACCOUNT, null, AccountConfig.KEY_ACTIVE, false); - - PushOneCommit.Result r = - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(RefNames.REFS_USERS_SELF); - r.assertErrorStatus("invalid account configuration"); - r.assertMessage("cannot deactivate own account"); - accountIndexedCounter.assertNoReindex(); - } - - @Test - @Sandboxed - public void pushAccountConfigToUserBranchDeactivateOtherAccount() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - - TestAccount foo = accountCreator.create(name("foo")); - assertThat(gApi.accounts().id(foo.id.get()).getActive()).isTrue(); - String userRef = RefNames.refsUsers(foo.id); - accountIndexedCounter.clear(); - - InternalGroup adminGroup = - groupCache.get(new AccountGroup.NameKey("Administrators")).orElse(null); - grant(allUsers, userRef, Permission.PUSH, false, adminGroup.getGroupUUID()); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - fetch(allUsersRepo, userRef + ":userRef"); - allUsersRepo.reset("userRef"); - - Config ac = getAccountConfig(allUsersRepo); - ac.setBoolean(AccountConfig.ACCOUNT, null, AccountConfig.KEY_ACTIVE, false); - - pushFactory - .create( - db, - admin.getIdent(), - allUsersRepo, - "Update account config", - AccountConfig.ACCOUNT_CONFIG, - ac.toText()) - .to(userRef) - .assertOkStatus(); - accountIndexedCounter.assertReindexOf(foo); - - assertThat(gApi.accounts().id(foo.id.get()).getActive()).isFalse(); - } - - @Test - @Sandboxed - public void cannotCreateUserBranch() throws Exception { - grant(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE); - grant(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH); - - String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId())); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), allUsersRepo).to(userRef); - r.assertErrorStatus(); - assertThat(r.getMessage()).contains("Not allowed to create user branch."); - - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(userRef)).isNull(); - } - } - - @Test - @Sandboxed - public void createUserBranchWithAccessDatabaseCapability() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - grant(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE); - grant(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH); - - String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId())); - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - pushFactory.create(db, admin.getIdent(), allUsersRepo).to(userRef).assertOkStatus(); - - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(userRef)).isNotNull(); - } - } - - @Test - @Sandboxed - public void cannotCreateNonUserBranchUnderRefsUsersWithAccessDatabaseCapability() - throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - grant(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE); - grant(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH); - - String userRef = RefNames.REFS_USERS + "foo"; - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), allUsersRepo).to(userRef); - r.assertErrorStatus(); - assertThat(r.getMessage()).contains("Not allowed to create non-user branch under refs/users/."); - - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(userRef)).isNull(); - } - } - - @Test - @Sandboxed - public void createDefaultUserBranch() throws Exception { - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNull(); - } - - grant(allUsers, RefNames.REFS_USERS_DEFAULT, Permission.CREATE); - grant(allUsers, RefNames.REFS_USERS_DEFAULT, Permission.PUSH); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - pushFactory - .create(db, admin.getIdent(), allUsersRepo) - .to(RefNames.REFS_USERS_DEFAULT) - .assertOkStatus(); - - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNotNull(); - } - } - - @Test - @Sandboxed - public void cannotDeleteUserBranch() throws Exception { - grant( - allUsers, - RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", - Permission.DELETE, - true, - REGISTERED_USERS); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - String userRef = RefNames.refsUsers(admin.id); - PushResult r = deleteRef(allUsersRepo, userRef); - RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef); - assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON); - assertThat(refUpdate.getMessage()).contains("Not allowed to delete user branch."); - - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(userRef)).isNotNull(); - } - } - - @Test - @Sandboxed - public void deleteUserBranchWithAccessDatabaseCapability() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - grant( - allUsers, - RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", - Permission.DELETE, - true, - REGISTERED_USERS); - - TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers); - String userRef = RefNames.refsUsers(admin.id); - PushResult r = deleteRef(allUsersRepo, userRef); - RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef); - assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK); - - try (Repository repo = repoManager.openRepository(allUsers)) { - assertThat(repo.exactRef(userRef)).isNull(); - } - - assertThat(accountCache.getOrNull(admin.id)).isNull(); - assertThat(accountQueryProvider.get().byDefault(admin.id.toString())).isEmpty(); - } - - @Test - public void addGpgKey() throws Exception { - TestKey key = validKeyWithoutExpiration(); - String id = key.getKeyIdString(); - addExternalIdEmail(admin, "test1@example.com"); - - assertKeyMapContains(key, addGpgKey(key.getPublicKeyArmored())); - assertKeys(key); - - setApiUser(user); - exception.expect(ResourceNotFoundException.class); - exception.expectMessage(id); - gApi.accounts().self().gpgKey(id).get(); - } - - @Test - public void reAddExistingGpgKey() throws Exception { - addExternalIdEmail(admin, "test5@example.com"); - TestKey key = validKeyWithSecondUserId(); - String id = key.getKeyIdString(); - PGPPublicKey pk = key.getPublicKey(); - - GpgKeyInfo info = addGpgKey(armor(pk)).get(id); - assertThat(info.userIds).hasSize(2); - assertIteratorSize(2, getOnlyKeyFromStore(key).getUserIDs()); - - pk = PGPPublicKey.removeCertification(pk, "foo:myId"); - info = addGpgKey(armor(pk)).get(id); - assertThat(info.userIds).hasSize(1); - assertIteratorSize(1, getOnlyKeyFromStore(key).getUserIDs()); - } - - @Test - public void addOtherUsersGpgKey_Conflict() throws Exception { - // Both users have a matching external ID for this key. - addExternalIdEmail(admin, "test5@example.com"); - externalIdsUpdate.insert(ExternalId.create("foo", "myId", user.getId())); - accountIndexedCounter.assertReindexOf(user); - - TestKey key = validKeyWithSecondUserId(); - addGpgKey(key.getPublicKeyArmored()); - setApiUser(user); - - exception.expect(ResourceConflictException.class); - exception.expectMessage("GPG key already associated with another account"); - addGpgKey(key.getPublicKeyArmored()); - } - - @Test - public void listGpgKeys() throws Exception { - List<TestKey> keys = allValidKeys(); - List<String> toAdd = new ArrayList<>(keys.size()); - for (TestKey key : keys) { - addExternalIdEmail(admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress()); - toAdd.add(key.getPublicKeyArmored()); - } - gApi.accounts().self().putGpgKeys(toAdd, ImmutableList.<String>of()); - assertKeys(keys); - accountIndexedCounter.assertReindexOf(admin); - } - - @Test - public void deleteGpgKey() throws Exception { - TestKey key = validKeyWithoutExpiration(); - String id = key.getKeyIdString(); - addExternalIdEmail(admin, "test1@example.com"); - addGpgKey(key.getPublicKeyArmored()); - assertKeys(key); - - gApi.accounts().self().gpgKey(id).delete(); - accountIndexedCounter.assertReindexOf(admin); - assertKeys(); - - exception.expect(ResourceNotFoundException.class); - exception.expectMessage(id); - gApi.accounts().self().gpgKey(id).get(); - } - - @Test - public void addAndRemoveGpgKeys() throws Exception { - for (TestKey key : allValidKeys()) { - addExternalIdEmail(admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress()); - } - TestKey key1 = validKeyWithoutExpiration(); - TestKey key2 = validKeyWithExpiration(); - TestKey key5 = validKeyWithSecondUserId(); - - Map<String, GpgKeyInfo> infos = - gApi.accounts() - .self() - .putGpgKeys( - ImmutableList.of(key1.getPublicKeyArmored(), key2.getPublicKeyArmored()), - ImmutableList.of(key5.getKeyIdString())); - assertThat(infos.keySet()).containsExactly(key1.getKeyIdString(), key2.getKeyIdString()); - assertKeys(key1, key2); - accountIndexedCounter.assertReindexOf(admin); - - infos = - gApi.accounts() - .self() - .putGpgKeys( - ImmutableList.of(key5.getPublicKeyArmored()), - ImmutableList.of(key1.getKeyIdString())); - assertThat(infos.keySet()).containsExactly(key1.getKeyIdString(), key5.getKeyIdString()); - assertKeyMapContains(key5, infos); - assertThat(infos.get(key1.getKeyIdString()).key).isNull(); - assertKeys(key2, key5); - accountIndexedCounter.assertReindexOf(admin); - - exception.expect(BadRequestException.class); - exception.expectMessage("Cannot both add and delete key: " + keyToString(key2.getPublicKey())); - infos = - gApi.accounts() - .self() - .putGpgKeys( - ImmutableList.of(key2.getPublicKeyArmored()), - ImmutableList.of(key2.getKeyIdString())); - } - - @Test - public void addMalformedGpgKey() throws Exception { - String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\ntest\n-----END PGP PUBLIC KEY BLOCK-----"; - exception.expect(BadRequestException.class); - exception.expectMessage("Failed to parse GPG keys"); - addGpgKey(key); - } - - @Test - @UseSsh - public void sshKeys() throws Exception { - // - // The test account should initially have exactly one ssh key - List<SshKeyInfo> info = gApi.accounts().self().listSshKeys(); - assertThat(info).hasSize(1); - assertSequenceNumbers(info); - SshKeyInfo key = info.get(0); - String inital = AccountCreator.publicKey(admin.sshKey, admin.email); - assertThat(key.sshPublicKey).isEqualTo(inital); - accountIndexedCounter.assertNoReindex(); - - // Add a new key - String newKey = AccountCreator.publicKey(AccountCreator.genSshKey(), admin.email); - gApi.accounts().self().addSshKey(newKey); - info = gApi.accounts().self().listSshKeys(); - assertThat(info).hasSize(2); - assertSequenceNumbers(info); - accountIndexedCounter.assertReindexOf(admin); - - // Add an existing key (the request succeeds, but the key isn't added again) - gApi.accounts().self().addSshKey(inital); - info = gApi.accounts().self().listSshKeys(); - assertThat(info).hasSize(2); - assertSequenceNumbers(info); - accountIndexedCounter.assertNoReindex(); - - // Add another new key - String newKey2 = AccountCreator.publicKey(AccountCreator.genSshKey(), admin.email); - gApi.accounts().self().addSshKey(newKey2); - info = gApi.accounts().self().listSshKeys(); - assertThat(info).hasSize(3); - assertSequenceNumbers(info); - accountIndexedCounter.assertReindexOf(admin); - - // Delete second key - gApi.accounts().self().deleteSshKey(2); - info = gApi.accounts().self().listSshKeys(); - assertThat(info).hasSize(2); - assertThat(info.get(0).seq).isEqualTo(1); - assertThat(info.get(1).seq).isEqualTo(3); - accountIndexedCounter.assertReindexOf(admin); - } - - // reindex is tested by {@link AbstractQueryAccountsTest#reindex} - @Test - public void reindexPermissions() throws Exception { - // admin can reindex any account - setApiUser(admin); - gApi.accounts().id(user.username).index(); - accountIndexedCounter.assertReindexOf(user); - - // user can reindex own account - setApiUser(user); - gApi.accounts().self().index(); - accountIndexedCounter.assertReindexOf(user); - - // user cannot reindex any account - exception.expect(AuthException.class); - exception.expectMessage("modify account not permitted"); - gApi.accounts().id(admin.username).index(); - } - - @Test - @Sandboxed - public void checkConsistency() throws Exception { - allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); - resetCurrentApiUser(); - - // Create an account with a preferred email. - String username = name("foo"); - String email = username + "@example.com"; - TestAccount account = accountCreator.create(username, email, "Foo Bar"); - - ConsistencyCheckInput input = new ConsistencyCheckInput(); - input.checkAccounts = new CheckAccountsInput(); - ConsistencyCheckInfo checkInfo = gApi.config().server().checkConsistency(input); - assertThat(checkInfo.checkAccountsResult.problems).isEmpty(); - - Set<ConsistencyProblemInfo> expectedProblems = new HashSet<>(); - - // Delete the external ID for the preferred email. This makes the account inconsistent since it - // now doesn't have an external ID for its preferred email. - externalIdsUpdate.delete(ExternalId.createEmail(account.getId(), email)); - expectedProblems.add( - new ConsistencyProblemInfo( - ConsistencyProblemInfo.Status.ERROR, - "Account '" - + account.getId().get() - + "' has no external ID for its preferred email '" - + email - + "'")); - - checkInfo = gApi.config().server().checkConsistency(input); - assertThat(checkInfo.checkAccountsResult.problems).hasSize(expectedProblems.size()); - assertThat(checkInfo.checkAccountsResult.problems).containsExactlyElementsIn(expectedProblems); - } - - @Test - public void internalQueryFindActiveAndInactiveAccounts() throws Exception { - String name = name("foo"); - assertThat(accountQueryProvider.get().byDefault(name)).isEmpty(); - - TestAccount foo1 = accountCreator.create(name + "-1"); - assertThat(gApi.accounts().id(foo1.username).getActive()).isTrue(); - - TestAccount foo2 = accountCreator.create(name + "-2"); - gApi.accounts().id(foo2.username).setActive(false); - assertThat(gApi.accounts().id(foo2.username).getActive()).isFalse(); - - assertThat(accountQueryProvider.get().byDefault(name)).hasSize(2); - } - - @Test - public void checkMetaId() throws Exception { - // metaId is set when account is loaded - assertThat(accounts.get(admin.getId()).getMetaId()).isEqualTo(getMetaId(admin.getId())); - - // metaId is set when account is created - AccountsUpdate au = accountsUpdate.create(); - Account.Id accountId = new Account.Id(seq.nextAccountId()); - Account account = au.insert(accountId, a -> {}); - assertThat(account.getMetaId()).isEqualTo(getMetaId(accountId)); - - // metaId is set when account is updated - Account updatedAccount = au.update(accountId, a -> a.setFullName("foo")); - assertThat(account.getMetaId()).isNotEqualTo(updatedAccount.getMetaId()); - assertThat(updatedAccount.getMetaId()).isEqualTo(getMetaId(accountId)); - - // metaId is set when account is replaced - Account newAccount = new Account(accountId, TimeUtil.nowTs()); - au.replace(newAccount); - assertThat(updatedAccount.getMetaId()).isNotEqualTo(newAccount.getMetaId()); - assertThat(newAccount.getMetaId()).isEqualTo(getMetaId(accountId)); - } - - private EmailInput newEmailInput(String email, boolean noConfirmation) { - EmailInput input = new EmailInput(); - input.email = email; - input.noConfirmation = noConfirmation; - return input; - } - - private EmailInput newEmailInput(String email) { - return newEmailInput(email, true); - } - - private String getMetaId(Account.Id accountId) throws IOException { - try (Repository repo = repoManager.openRepository(allUsers); - RevWalk rw = new RevWalk(repo); - ObjectReader or = repo.newObjectReader()) { - Ref ref = repo.exactRef(RefNames.refsUsers(accountId)); - return ref != null ? ref.getObjectId().name() : null; - } - } - - @Test - public void createUserWithValidUsername() throws Exception { - ImmutableList<String> names = - ImmutableList.of( - "user@domain", - "user-name", - "user_name", - "1234", - "user1234", - "1234@domain", - "user!+alias{*}#$%&’^=~|@domain"); - for (String name : names) { - gApi.accounts().create(name); - } - } - - @Test - public void createUserWithInvalidUsername() throws Exception { - ImmutableList<String> invalidNames = - ImmutableList.of( - "@", "@foo", "-", "-foo", "_", "_foo", "!", "+", "{", "}", "*", "%", "#", "$", "&", "’", - "^", "=", "~"); - for (String name : invalidNames) { - try { - gApi.accounts().create(name); - fail(String.format("Expected BadRequestException for username [%s]", name)); - } catch (BadRequestException e) { - assertThat(e).hasMessageThat().isEqualTo(String.format("Invalid username '%s'", name)); - } - } - } - - @Test - public void groups() throws Exception { - assertGroups( - admin.username, ImmutableList.of("Anonymous Users", "Registered Users", "Administrators")); - - // TODO: update when test user is fixed to be included in "Anonymous Users" and - // "Registered Users" groups - assertGroups(user.username, ImmutableList.of()); - - String group = createGroup("group"); - String newUser = createAccount("user1", group); - assertGroups(newUser, ImmutableList.of(group)); - } - - @Test - public void updateDisplayName() throws Exception { - AccountInput input = new AccountInput(); - input.username = name("test"); - input.email = "user@gerrit.com"; - gApi.accounts().create(input); - AuthRequest who = AuthRequest.forEmail(input.email); - AuthResult authResult = accountManager.authenticate(who); - assertThat(authResult.isNew()).isFalse(); - AccountInfo info = gApi.accounts().id(input.email).get(); - assertThat(info.username).isEqualTo(input.username); - assertThat(info.email).isEqualTo(input.email); - assertThat(info.name).isEqualTo(input.username); - who.setDisplayName("Something Else"); - AuthResult authResult2 = accountManager.authenticate(who); - assertThat(authResult2.isNew()).isFalse(); - assertThat(authResult2.getAccountId()).isEqualTo(authResult.getAccountId()); - info = gApi.accounts().id(input.email).get(); - assertThat(info.username).isEqualTo(input.username); - assertThat(info.email).isEqualTo(input.email); - assertThat(info.name).isEqualTo("Something Else"); - } - - private void assertGroups(String user, List<String> expected) throws Exception { - List<String> actual = - gApi.accounts().id(user).getGroups().stream().map(g -> g.name).collect(toList()); - assertThat(actual).containsExactlyElementsIn(expected); - } - - private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) { - int seq = 1; - for (SshKeyInfo key : sshKeys) { - assertThat(key.seq).isEqualTo(seq++); - } - } - - private PGPPublicKey getOnlyKeyFromStore(TestKey key) throws Exception { - try (PublicKeyStore store = publicKeyStoreProvider.get()) { - Iterable<PGPPublicKeyRing> keys = store.get(key.getKeyId()); - assertThat(keys).hasSize(1); - return keys.iterator().next().getPublicKey(); - } - } - - private static String armor(PGPPublicKey key) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(4096); - try (ArmoredOutputStream aout = new ArmoredOutputStream(out)) { - key.encode(aout); - } - return new String(out.toByteArray(), UTF_8); - } - - private static void assertIteratorSize(int size, Iterator<?> it) { - List<?> lst = ImmutableList.copyOf(it); - assertThat(lst).hasSize(size); - } - - private static void assertKeyMapContains(TestKey expected, Map<String, GpgKeyInfo> actualMap) { - GpgKeyInfo actual = actualMap.get(expected.getKeyIdString()); - assertThat(actual).isNotNull(); - assertThat(actual.id).isNull(); - actual.id = expected.getKeyIdString(); - assertKeyEquals(expected, actual); - } - - private void assertKeys(TestKey... expectedKeys) throws Exception { - assertKeys(Arrays.asList(expectedKeys)); - } - - private void assertKeys(Iterable<TestKey> expectedKeys) throws Exception { - // Check via API. - FluentIterable<TestKey> expected = FluentIterable.from(expectedKeys); - Map<String, GpgKeyInfo> keyMap = gApi.accounts().self().listGpgKeys(); - assertThat(keyMap.keySet()) - .named("keys returned by listGpgKeys()") - .containsExactlyElementsIn(expected.transform(TestKey::getKeyIdString)); - - for (TestKey key : expected) { - assertKeyEquals(key, gApi.accounts().self().gpgKey(key.getKeyIdString()).get()); - assertKeyEquals( - key, - gApi.accounts() - .self() - .gpgKey(Fingerprint.toString(key.getPublicKey().getFingerprint())) - .get()); - assertKeyMapContains(key, keyMap); - } - - // Check raw external IDs. - Account.Id currAccountId = atrScope.get().getUser().getAccountId(); - Iterable<String> expectedFps = - expected.transform(k -> BaseEncoding.base16().encode(k.getPublicKey().getFingerprint())); - Iterable<String> actualFps = - externalIds.byAccount(currAccountId, SCHEME_GPGKEY).stream() - .map(e -> e.key().id()) - .collect(toSet()); - assertThat(actualFps).named("external IDs in database").containsExactlyElementsIn(expectedFps); - - // Check raw stored keys. - for (TestKey key : expected) { - getOnlyKeyFromStore(key); - } - } - - private static void assertKeyEquals(TestKey expected, GpgKeyInfo actual) { - String id = expected.getKeyIdString(); - assertThat(actual.id).named(id).isEqualTo(id); - assertThat(actual.fingerprint) - .named(id) - .isEqualTo(Fingerprint.toString(expected.getPublicKey().getFingerprint())); - List<String> userIds = ImmutableList.copyOf(expected.getPublicKey().getUserIDs()); - assertThat(actual.userIds).named(id).containsExactlyElementsIn(userIds); - String key = actual.key; - assertThat(key).named(id).startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"); - assertThat(key).named(id).endsWith("-----END PGP PUBLIC KEY BLOCK-----\n"); - assertThat(actual.status).isEqualTo(GpgKeyInfo.Status.TRUSTED); - assertThat(actual.problems).isEmpty(); - } - - private void addExternalIdEmail(TestAccount account, String email) throws Exception { - checkNotNull(email); - externalIdsUpdate.insert( - ExternalId.createWithEmail(name("test"), email, account.getId(), email)); - accountIndexedCounter.assertReindexOf(account); - setApiUser(account); - } - - private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception { - Map<String, GpgKeyInfo> gpgKeys = - gApi.accounts().self().putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of()); - accountIndexedCounter.assertReindexOf(gApi.accounts().self().get()); - return gpgKeys; - } - - private void assertUser(AccountInfo info, TestAccount account) throws Exception { - assertThat(info.name).isEqualTo(account.fullName); - assertThat(info.email).isEqualTo(account.email); - assertThat(info.username).isEqualTo(account.username); - assertThat(info.status).isEqualTo(account.status); - } - - private Set<String> getEmails() throws RestApiException { - return gApi.accounts().self().getEmails().stream().map(e -> e.email).collect(toSet()); - } - - private void assertEmail(Set<Account.Id> accounts, TestAccount expectedAccount) { - assertThat(accounts).hasSize(1); - assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.getId()); - } - - private Config getAccountConfig(TestRepository<?> allUsersRepo) throws Exception { - Config ac = new Config(); - try (TreeWalk tw = - TreeWalk.forPath( - allUsersRepo.getRepository(), - AccountConfig.ACCOUNT_CONFIG, - getHead(allUsersRepo.getRepository()).getTree())) { - assertThat(tw).isNotNull(); - ac.fromText( - new String( - allUsersRepo - .getRevWalk() - .getObjectReader() - .open(tw.getObjectId(0), OBJ_BLOB) - .getBytes(), - UTF_8)); - } - return ac; - } - - private static class AccountIndexedCounter implements AccountIndexedListener { - private final AtomicLongMap<Integer> countsByAccount = AtomicLongMap.create(); - - @Override - public void onAccountIndexed(int id) { - countsByAccount.incrementAndGet(id); - } - - void clear() { - countsByAccount.clear(); - } - - long getCount(Account.Id accountId) { - return countsByAccount.get(accountId.get()); - } - - void assertReindexOf(TestAccount testAccount) { - assertReindexOf(testAccount, 1); - } - - void assertReindexOf(AccountInfo accountInfo) { - assertReindexOf(new Account.Id(accountInfo._accountId), 1); - } - - void assertReindexOf(TestAccount testAccount, int expectedCount) { - assertThat(getCount(testAccount.id)).isEqualTo(expectedCount); - assertThat(countsByAccount).hasSize(1); - clear(); - } - - void assertReindexOf(Account.Id accountId, int expectedCount) { - assertThat(getCount(accountId)).isEqualTo(expectedCount); - countsByAccount.remove(accountId.get()); - } - - void assertNoReindex() { - assertThat(countsByAccount).isEmpty(); - } - } - - private static class RefUpdateCounter implements GitReferenceUpdatedListener { - private final AtomicLongMap<String> countsByProjectRefs = AtomicLongMap.create(); - - static String projectRef(Project.NameKey project, String ref) { - return projectRef(project.get(), ref); - } - - static String projectRef(String project, String ref) { - return project + ":" + ref; - } - - @Override - public void onGitReferenceUpdated(Event event) { - countsByProjectRefs.incrementAndGet(projectRef(event.getProjectName(), event.getRefName())); - } - - void clear() { - countsByProjectRefs.clear(); - } - - long getCount(String projectRef) { - return countsByProjectRefs.get(projectRef); - } - - void assertRefUpdateFor(String... projectRefs) { - Map<String, Integer> expectedRefUpdateCounts = new HashMap<>(); - for (String projectRef : projectRefs) { - expectedRefUpdateCounts.put(projectRef, 1); - } - assertRefUpdateFor(expectedRefUpdateCounts); - } - - void assertRefUpdateFor(Map<String, Integer> expectedProjectRefUpdateCounts) { - for (Map.Entry<String, Integer> e : expectedProjectRefUpdateCounts.entrySet()) { - assertThat(getCount(e.getKey())).isEqualTo(e.getValue()); - } - assertThat(countsByProjectRefs).hasSize(expectedProjectRefUpdateCounts.size()); - clear(); - } - } -} |