Java tutorial
// 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.assert_; import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS; import static com.google.gerrit.gpg.PublicKeyStore.keyToString; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Function; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.extensions.api.accounts.EmailInput; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.GpgKeyInfo; 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.gpg.Fingerprint; import com.google.gerrit.gpg.PublicKeyStore; import com.google.gerrit.gpg.server.GpgKeys; import com.google.gerrit.gpg.testutil.TestKey; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountExternalId; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.testutil.ConfigSuite; import com.google.inject.Inject; import com.google.inject.Provider; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PushCertificateIdent; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; 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; private List<AccountExternalId> savedExternalIds; @Before public void saveExternalIds() throws Exception { savedExternalIds = new ArrayList<>(); savedExternalIds.addAll(getExternalIds(admin)); savedExternalIds.addAll(getExternalIds(user)); } @After public void restoreExternalIds() throws Exception { db.accountExternalIds().delete(getExternalIds(admin)); db.accountExternalIds().delete(getExternalIds(user)); db.accountExternalIds().insert(savedExternalIds); } @After public void clearPublicKeyStore() throws Exception { try (Repository repo = repoManager.openRepository(allUsers)) { Ref ref = repo.getRef(REFS_GPG_KEYS); if (ref != null) { RefUpdate ru = repo.updateRef(REFS_GPG_KEYS); ru.setForceUpdate(true); assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED); } } } private List<AccountExternalId> getExternalIds(TestAccount account) throws Exception { return db.accountExternalIds().byAccount(account.getId()).toList(); } @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); assert_().withFailureMessage("Failed to delete " + ref).that(ru.delete()) .isEqualTo(RefUpdate.Result.FORCED); } } } @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"); } @Test public void self() throws Exception { AccountInfo info = gApi.accounts().self().get(); assertThat(info.name).isEqualTo("Administrator"); assertThat(info.email).isEqualTo("admin@example.com"); assertThat(info.username).isEqualTo("admin"); } @Test public void starUnstarChange() throws Exception { PushOneCommit.Result r = createChange(); String triplet = project.get() + "~master~" + r.getChangeId(); gApi.accounts().self().starChange(triplet); assertThat(info(triplet).starred).isTrue(); gApi.accounts().self().unstarChange(triplet); assertThat(info(triplet).starred).isNull(); } @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(); } @Test public void addEmail() throws Exception { List<String> emails = ImmutableList.of("new.email@example.com", "new.email@example.systems"); for (String email : emails) { EmailInput input = new EmailInput(); input.email = email; input.noConfirmation = true; gApi.accounts().self().addEmail(input); } } @Test public void addInvalidEmail() throws Exception { EmailInput input = new EmailInput(); input.email = "invalid@"; input.noConfirmation = true; exception.expect(BadRequestException.class); exception.expectMessage("invalid email address"); gApi.accounts().self().addEmail(input); } @Test public void addGpgKey() throws Exception { TestKey key = TestKey.key1(); 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 = TestKey.key5(); 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"); AccountExternalId extId = new AccountExternalId(user.getId(), new AccountExternalId.Key("foo:myId")); db.accountExternalIds().insert(Collections.singleton(extId)); TestKey key = TestKey.key5(); 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 = TestKey.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); } @Test public void deleteGpgKey() throws Exception { TestKey key = TestKey.key1(); String id = key.getKeyIdString(); addExternalIdEmail(admin, "test1@example.com"); addGpgKey(key.getPublicKeyArmored()); assertKeys(key); gApi.accounts().self().gpgKey(id).delete(); assertKeys(); exception.expect(ResourceNotFoundException.class); exception.expectMessage(id); gApi.accounts().self().gpgKey(id).get(); } @Test public void addAndRemoveGpgKeys() throws Exception { for (TestKey key : TestKey.allValidKeys()) { addExternalIdEmail(admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress()); } TestKey key1 = TestKey.key1(); TestKey key2 = TestKey.key2(); TestKey key5 = TestKey.key5(); 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); 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); 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())); } 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); } @SuppressWarnings({ "unchecked", "rawtypes" }) private static void assertIteratorSize(int size, Iterator it) { assertThat(ImmutableList.copyOf(it)).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(new Function<TestKey, String>() { @Override public String apply(TestKey in) { return in.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 = ((IdentifiedUser) atrScope.get().getCurrentUser()).getAccountId(); assertThat(GpgKeys.getGpgExtIds(db, currAccountId).transform(new Function<AccountExternalId, String>() { @Override public String apply(AccountExternalId in) { return in.getSchemeRest(); } })).named("external IDs in database") .containsExactlyElementsIn(expected.transform(new Function<TestKey, String>() { @Override public String apply(TestKey in) { return BaseEncoding.base16().encode(in.getPublicKey().getFingerprint()); } })); // 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())); @SuppressWarnings("unchecked") List<String> userIds = ImmutableList.copyOf(expected.getPublicKey().getUserIDs()); assertThat(actual.userIds).named(id).containsExactlyElementsIn(userIds); assertThat(actual.key).named(id).startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"); } private void addExternalIdEmail(TestAccount account, String email) throws Exception { checkNotNull(email); AccountExternalId extId = new AccountExternalId(account.getId(), new AccountExternalId.Key(name("test"), email)); extId.setEmailAddress(email); db.accountExternalIds().insert(Collections.singleton(extId)); // Clear saved AccountState and AccountExternalIds. accountCache.evict(account.getId()); setApiUser(account); } private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception { return gApi.accounts().self().putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of()); } }