summaryrefslogtreecommitdiffstats
path: root/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java')
-rw-r--r--gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java296
1 files changed, 0 insertions, 296 deletions
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
deleted file mode 100644
index 39e789a614..0000000000
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (C) 2015 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.gpg.server;
-
-import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
-import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
-import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.io.BaseEncoding;
-import com.google.gerrit.common.errors.EmailException;
-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.extensions.restapi.RestModifyView;
-import com.google.gerrit.gpg.CheckResult;
-import com.google.gerrit.gpg.Fingerprint;
-import com.google.gerrit.gpg.GerritPublicKeyChecker;
-import com.google.gerrit.gpg.PublicKeyChecker;
-import com.google.gerrit.gpg.PublicKeyStore;
-import com.google.gerrit.gpg.server.PostGpgKeys.Input;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.AccountState;
-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.mail.send.AddKeySender;
-import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.bouncycastle.bcpg.ArmoredInputStream;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.bouncycastle.openpgp.PGPRuntimeOperationException;
-import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Singleton
-public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
- public static class Input {
- public List<String> add;
- public List<String> delete;
- }
-
- private final Logger log = LoggerFactory.getLogger(getClass());
- private final Provider<PersonIdent> serverIdent;
- private final Provider<CurrentUser> self;
- private final Provider<PublicKeyStore> storeProvider;
- private final GerritPublicKeyChecker.Factory checkerFactory;
- private final AddKeySender.Factory addKeyFactory;
- private final Provider<InternalAccountQuery> accountQueryProvider;
- private final ExternalIds externalIds;
- private final ExternalIdsUpdate.User externalIdsUpdateFactory;
-
- @Inject
- PostGpgKeys(
- @GerritPersonIdent Provider<PersonIdent> serverIdent,
- Provider<CurrentUser> self,
- Provider<PublicKeyStore> storeProvider,
- GerritPublicKeyChecker.Factory checkerFactory,
- AddKeySender.Factory addKeyFactory,
- Provider<InternalAccountQuery> accountQueryProvider,
- ExternalIds externalIds,
- ExternalIdsUpdate.User externalIdsUpdateFactory) {
- this.serverIdent = serverIdent;
- this.self = self;
- this.storeProvider = storeProvider;
- this.checkerFactory = checkerFactory;
- this.addKeyFactory = addKeyFactory;
- this.accountQueryProvider = accountQueryProvider;
- this.externalIds = externalIds;
- this.externalIdsUpdateFactory = externalIdsUpdateFactory;
- }
-
- @Override
- public Map<String, GpgKeyInfo> apply(AccountResource rsrc, Input input)
- throws ResourceNotFoundException, BadRequestException, ResourceConflictException,
- PGPException, OrmException, IOException, ConfigInvalidException {
- GpgKeys.checkVisible(self, rsrc);
-
- Collection<ExternalId> existingExtIds =
- externalIds.byAccount(rsrc.getUser().getAccountId(), SCHEME_GPGKEY);
- try (PublicKeyStore store = storeProvider.get()) {
- Set<Fingerprint> toRemove = readKeysToRemove(input, existingExtIds);
- List<PGPPublicKeyRing> newKeys = readKeysToAdd(input, toRemove);
- List<ExternalId> newExtIds = new ArrayList<>(existingExtIds.size());
-
- for (PGPPublicKeyRing keyRing : newKeys) {
- PGPPublicKey key = keyRing.getPublicKey();
- ExternalId.Key extIdKey = toExtIdKey(key.getFingerprint());
- Account account = getAccountByExternalId(extIdKey);
- if (account != null) {
- if (!account.getId().equals(rsrc.getUser().getAccountId())) {
- throw new ResourceConflictException("GPG key already associated with another account");
- }
- } else {
- newExtIds.add(ExternalId.create(extIdKey, rsrc.getUser().getAccountId()));
- }
- }
-
- storeKeys(rsrc, newKeys, toRemove);
-
- List<ExternalId.Key> extIdKeysToRemove =
- toRemove.stream().map(fp -> toExtIdKey(fp.get())).collect(toList());
- externalIdsUpdateFactory
- .create()
- .replace(rsrc.getUser().getAccountId(), extIdKeysToRemove, newExtIds);
- return toJson(newKeys, toRemove, store, rsrc.getUser());
- }
- }
-
- private Set<Fingerprint> readKeysToRemove(Input input, Collection<ExternalId> existingExtIds) {
- if (input.delete == null || input.delete.isEmpty()) {
- return ImmutableSet.of();
- }
- Set<Fingerprint> fingerprints = Sets.newHashSetWithExpectedSize(input.delete.size());
- for (String id : input.delete) {
- try {
- fingerprints.add(new Fingerprint(GpgKeys.parseFingerprint(id, existingExtIds)));
- } catch (ResourceNotFoundException e) {
- // Skip removal.
- }
- }
- return fingerprints;
- }
-
- private List<PGPPublicKeyRing> readKeysToAdd(Input input, Set<Fingerprint> toRemove)
- throws BadRequestException, IOException {
- if (input.add == null || input.add.isEmpty()) {
- return ImmutableList.of();
- }
- List<PGPPublicKeyRing> keyRings = new ArrayList<>(input.add.size());
- for (String armored : input.add) {
- try (InputStream in = new ByteArrayInputStream(armored.getBytes(UTF_8));
- ArmoredInputStream ain = new ArmoredInputStream(in)) {
- @SuppressWarnings("unchecked")
- List<Object> objs = Lists.newArrayList(new BcPGPObjectFactory(ain));
- if (objs.size() != 1 || !(objs.get(0) instanceof PGPPublicKeyRing)) {
- throw new BadRequestException("Expected exactly one PUBLIC KEY BLOCK");
- }
- PGPPublicKeyRing keyRing = (PGPPublicKeyRing) objs.get(0);
- if (toRemove.contains(new Fingerprint(keyRing.getPublicKey().getFingerprint()))) {
- throw new BadRequestException(
- "Cannot both add and delete key: " + keyToString(keyRing.getPublicKey()));
- }
- keyRings.add(keyRing);
- } catch (PGPRuntimeOperationException e) {
- throw new BadRequestException("Failed to parse GPG keys", e);
- }
- }
- return keyRings;
- }
-
- private void storeKeys(
- AccountResource rsrc, List<PGPPublicKeyRing> keyRings, Set<Fingerprint> toRemove)
- throws BadRequestException, ResourceConflictException, PGPException, IOException {
- try (PublicKeyStore store = storeProvider.get()) {
- List<String> addedKeys = new ArrayList<>();
- for (PGPPublicKeyRing keyRing : keyRings) {
- PGPPublicKey key = keyRing.getPublicKey();
- // Don't check web of trust; admins can fill in certifications later.
- CheckResult result = checkerFactory.create(rsrc.getUser(), store).disableTrust().check(key);
- if (!result.isOk()) {
- throw new BadRequestException(
- String.format(
- "Problems with public key %s:\n%s",
- keyToString(key), Joiner.on('\n').join(result.getProblems())));
- }
- addedKeys.add(PublicKeyStore.keyToString(key));
- store.add(keyRing);
- }
- for (Fingerprint fp : toRemove) {
- store.remove(fp.get());
- }
- CommitBuilder cb = new CommitBuilder();
- PersonIdent committer = serverIdent.get();
- cb.setAuthor(rsrc.getUser().newCommitterIdent(committer.getWhen(), committer.getTimeZone()));
- cb.setCommitter(committer);
-
- RefUpdate.Result saveResult = store.save(cb);
- switch (saveResult) {
- case NEW:
- case FAST_FORWARD:
- case FORCED:
- try {
- addKeyFactory.create(rsrc.getUser(), addedKeys).send();
- } catch (EmailException e) {
- log.error(
- "Cannot send GPG key added message to "
- + rsrc.getUser().getAccount().getPreferredEmail(),
- e);
- }
- break;
- case NO_CHANGE:
- break;
- case IO_FAILURE:
- case LOCK_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- // TODO(dborowitz): Backoff and retry on LOCK_FAILURE.
- throw new ResourceConflictException("Failed to save public keys: " + saveResult);
- }
- }
- }
-
- private ExternalId.Key toExtIdKey(byte[] fp) {
- return ExternalId.Key.create(SCHEME_GPGKEY, BaseEncoding.base16().encode(fp));
- }
-
- private Account getAccountByExternalId(ExternalId.Key extIdKey) throws OrmException {
- List<AccountState> accountStates = accountQueryProvider.get().byExternalId(extIdKey);
-
- if (accountStates.isEmpty()) {
- return null;
- }
-
- if (accountStates.size() > 1) {
- StringBuilder msg = new StringBuilder();
- msg.append("GPG key ")
- .append(extIdKey.get())
- .append(" associated with multiple accounts: ")
- .append(Lists.transform(accountStates, AccountState.ACCOUNT_ID_FUNCTION));
- throw new IllegalStateException(msg.toString());
- }
-
- return accountStates.get(0).getAccount();
- }
-
- private Map<String, GpgKeyInfo> toJson(
- Collection<PGPPublicKeyRing> keys,
- Set<Fingerprint> deleted,
- PublicKeyStore store,
- IdentifiedUser user)
- throws IOException {
- // Unlike when storing keys, include web-of-trust checks when producing
- // result JSON, so the user at least knows of any issues.
- PublicKeyChecker checker = checkerFactory.create(user, store);
- Map<String, GpgKeyInfo> infos = Maps.newHashMapWithExpectedSize(keys.size() + deleted.size());
- for (PGPPublicKeyRing keyRing : keys) {
- PGPPublicKey key = keyRing.getPublicKey();
- CheckResult result = checker.check(key);
- GpgKeyInfo info = GpgKeys.toJson(key, result);
- infos.put(info.id, info);
- info.id = null;
- }
- for (Fingerprint fp : deleted) {
- infos.put(keyIdToString(fp.getId()), new GpgKeyInfo());
- }
- return infos;
- }
-}