Line data Source code
1 : // Copyright (C) 2015 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.gpg.server; 16 : 17 : import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString; 18 : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY; 19 : 20 : import com.google.common.collect.ImmutableList; 21 : import com.google.common.flogger.FluentLogger; 22 : import com.google.common.io.BaseEncoding; 23 : import com.google.gerrit.exceptions.EmailException; 24 : import com.google.gerrit.exceptions.StorageException; 25 : import com.google.gerrit.extensions.common.Input; 26 : import com.google.gerrit.extensions.restapi.ResourceNotFoundException; 27 : import com.google.gerrit.extensions.restapi.Response; 28 : import com.google.gerrit.extensions.restapi.RestApiException; 29 : import com.google.gerrit.extensions.restapi.RestModifyView; 30 : import com.google.gerrit.gpg.PublicKeyStore; 31 : import com.google.gerrit.server.GerritPersonIdent; 32 : import com.google.gerrit.server.UserInitiated; 33 : import com.google.gerrit.server.account.AccountsUpdate; 34 : import com.google.gerrit.server.account.externalids.ExternalId; 35 : import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory; 36 : import com.google.gerrit.server.account.externalids.ExternalIds; 37 : import com.google.gerrit.server.mail.send.DeleteKeySender; 38 : import com.google.inject.Inject; 39 : import com.google.inject.Provider; 40 : import java.io.IOException; 41 : import java.util.Optional; 42 : import org.bouncycastle.openpgp.PGPException; 43 : import org.bouncycastle.openpgp.PGPPublicKey; 44 : import org.eclipse.jgit.errors.ConfigInvalidException; 45 : import org.eclipse.jgit.lib.CommitBuilder; 46 : import org.eclipse.jgit.lib.PersonIdent; 47 : import org.eclipse.jgit.lib.RefUpdate; 48 : 49 : public class DeleteGpgKey implements RestModifyView<GpgKey, Input> { 50 2 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 51 : 52 : private final Provider<PersonIdent> serverIdent; 53 : private final Provider<PublicKeyStore> storeProvider; 54 : private final Provider<AccountsUpdate> accountsUpdateProvider; 55 : private final ExternalIds externalIds; 56 : private final DeleteKeySender.Factory deleteKeySenderFactory; 57 : private final ExternalIdKeyFactory externalIdKeyFactory; 58 : 59 : @Inject 60 : DeleteGpgKey( 61 : @GerritPersonIdent Provider<PersonIdent> serverIdent, 62 : Provider<PublicKeyStore> storeProvider, 63 : @UserInitiated Provider<AccountsUpdate> accountsUpdateProvider, 64 : ExternalIds externalIds, 65 : DeleteKeySender.Factory deleteKeySenderFactory, 66 2 : ExternalIdKeyFactory externalIdKeyFactory) { 67 2 : this.serverIdent = serverIdent; 68 2 : this.storeProvider = storeProvider; 69 2 : this.accountsUpdateProvider = accountsUpdateProvider; 70 2 : this.externalIds = externalIds; 71 2 : this.deleteKeySenderFactory = deleteKeySenderFactory; 72 2 : this.externalIdKeyFactory = externalIdKeyFactory; 73 2 : } 74 : 75 : @Override 76 : public Response<?> apply(GpgKey rsrc, Input input) 77 : throws RestApiException, PGPException, IOException, ConfigInvalidException { 78 2 : PGPPublicKey key = rsrc.getKeyRing().getPublicKey(); 79 2 : String fingerprint = BaseEncoding.base16().encode(key.getFingerprint()); 80 2 : Optional<ExternalId> extId = 81 2 : externalIds.get(externalIdKeyFactory.create(SCHEME_GPGKEY, fingerprint)); 82 2 : if (!extId.isPresent()) { 83 0 : throw new ResourceNotFoundException(fingerprint); 84 : } 85 : 86 2 : accountsUpdateProvider 87 2 : .get() 88 2 : .update( 89 : "Delete GPG Key via API", 90 2 : rsrc.getUser().getAccountId(), 91 2 : u -> u.deleteExternalId(extId.get())); 92 : 93 2 : try (PublicKeyStore store = storeProvider.get()) { 94 2 : store.remove(rsrc.getKeyRing().getPublicKey().getFingerprint()); 95 : 96 2 : CommitBuilder cb = new CommitBuilder(); 97 2 : PersonIdent committer = serverIdent.get(); 98 2 : cb.setAuthor(rsrc.getUser().newCommitterIdent(committer)); 99 2 : cb.setCommitter(committer); 100 2 : cb.setMessage("Delete public key " + keyIdToString(key.getKeyID())); 101 : 102 2 : RefUpdate.Result saveResult = store.save(cb); 103 2 : switch (saveResult) { 104 : case NO_CHANGE: 105 : case FAST_FORWARD: 106 : try { 107 2 : deleteKeySenderFactory 108 2 : .create(rsrc.getUser(), ImmutableList.of(PublicKeyStore.keyToString(key))) 109 2 : .send(); 110 0 : } catch (EmailException e) { 111 0 : logger.atSevere().withCause(e).log( 112 : "Cannot send GPG key deletion message to %s", 113 0 : rsrc.getUser().getAccount().preferredEmail()); 114 2 : } 115 0 : break; 116 : case LOCK_FAILURE: 117 : case FORCED: 118 : case IO_FAILURE: 119 : case NEW: 120 : case NOT_ATTEMPTED: 121 : case REJECTED: 122 : case REJECTED_CURRENT_BRANCH: 123 : case RENAMED: 124 : case REJECTED_MISSING_OBJECT: 125 : case REJECTED_OTHER_REASON: 126 : default: 127 0 : throw new StorageException(String.format("Failed to delete public key: %s", saveResult)); 128 : } 129 : } 130 2 : return Response.none(); 131 : } 132 : }