| 1 | /** | |
| 2 | Copyright 2018 Carlos Macasaet | |
| 3 | ||
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | you may not use this file except in compliance with the License. | |
| 6 | You may obtain a copy of the License at | |
| 7 | ||
| 8 | https://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | ||
| 10 | Unless required by applicable law or agreed to in writing, software | |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 | See the License for the specific language governing permissions and | |
| 14 | limitations under the License. | |
| 15 | */ | |
| 16 | package com.macasaet.fernet.aws.secretsmanager.rotation; | |
| 17 | ||
| 18 | import static com.macasaet.fernet.aws.secretsmanager.rotation.Stage.CURRENT; | |
| 19 | import static java.util.Collections.singletonList; | |
| 20 | ||
| 21 | import java.io.ByteArrayOutputStream; | |
| 22 | import java.io.IOException; | |
| 23 | import java.nio.ByteBuffer; | |
| 24 | import java.util.Collection; | |
| 25 | ||
| 26 | import com.amazonaws.services.secretsmanager.AWSSecretsManager; | |
| 27 | import com.amazonaws.services.secretsmanager.model.DescribeSecretRequest; | |
| 28 | import com.amazonaws.services.secretsmanager.model.DescribeSecretResult; | |
| 29 | import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; | |
| 30 | import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; | |
| 31 | import com.amazonaws.services.secretsmanager.model.PutSecretValueRequest; | |
| 32 | import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException; | |
| 33 | import com.amazonaws.services.secretsmanager.model.UpdateSecretVersionStageRequest; | |
| 34 | import com.macasaet.fernet.Key; | |
| 35 | ||
| 36 | /** | |
| 37 | * <p>Service façade for AWS Secrets Manager.</p> | |
| 38 | * | |
| 39 | * <p>This requires the following permissions: <code>secretsmanager:DescribeSecret</code>, | |
| 40 | * <code>secretsmanager:GetSecretValue</code>, <code>secretsmanager:UpdateSecretVersionStage</code>, and | |
| 41 | * <code>secretsmanager:PutSecretValue</code>.</p> | |
| 42 | * <p>Copyright © 2018 Carlos Macasaet.</p> | |
| 43 | * | |
| 44 | * @author Carlos Macasaet | |
| 45 | */ | |
| 46 | @SuppressWarnings("PMD.LawOfDemeter") | |
| 47 | class SecretsManager { | |
| 48 | ||
| 49 | private final AWSSecretsManager delegate; | |
| 50 | ||
| 51 | public SecretsManager(final AWSSecretsManager delegate) { | |
| 52 |
1
1. <init> : negated conditional → KILLED |
if (delegate == null) { |
| 53 | throw new IllegalArgumentException("delegate cannot be null"); | |
| 54 | } | |
| 55 | this.delegate = delegate; | |
| 56 | } | |
| 57 | ||
| 58 | public void shutdown() { | |
| 59 |
1
1. shutdown : removed call to com/amazonaws/services/secretsmanager/AWSSecretsManager::shutdown → NO_COVERAGE |
getDelegate().shutdown(); |
| 60 | } | |
| 61 | ||
| 62 | /** | |
| 63 | * Ensure that the given secret has an AWSCURRENT value. This requires the permission | |
| 64 | * <code>secretsmanager:GetSecretValue</code> | |
| 65 | * | |
| 66 | * @param secretId | |
| 67 | * the ARN of the secret. | |
| 68 | * @throws ResourceNotFoundException if the secret doesn't exist or it has no AWSCURRENT stage | |
| 69 | */ | |
| 70 | public void assertCurrentStageExists(final String secretId) { | |
| 71 | final GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest(); | |
| 72 |
1
1. assertCurrentStageExists : removed call to com/amazonaws/services/secretsmanager/model/GetSecretValueRequest::setSecretId → KILLED |
getSecretValueRequest.setSecretId(secretId); |
| 73 |
1
1. assertCurrentStageExists : removed call to com/amazonaws/services/secretsmanager/model/GetSecretValueRequest::setVersionStage → KILLED |
getSecretValueRequest.setVersionStage(CURRENT.getAwsName()); |
| 74 | getDelegate().getSecretValue(getSecretValueRequest); | |
| 75 | } | |
| 76 | ||
| 77 | /** | |
| 78 | * Obtain a secret's metadata. This requires the permission <code>secretsmanager:DescribeSecret</code> | |
| 79 | * | |
| 80 | * @param secretId the ARN of the secret | |
| 81 | * @return the secret's metadata | |
| 82 | */ | |
| 83 | public DescribeSecretResult describeSecret(final String secretId) { | |
| 84 | final DescribeSecretRequest describeSecretRequest = new DescribeSecretRequest(); | |
| 85 |
1
1. describeSecret : removed call to com/amazonaws/services/secretsmanager/model/DescribeSecretRequest::setSecretId → KILLED |
describeSecretRequest.setSecretId(secretId); |
| 86 |
1
1. describeSecret : mutated return of Object value for com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::describeSecret to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getDelegate().describeSecret(describeSecretRequest); |
| 87 | } | |
| 88 | ||
| 89 | /** | |
| 90 | * Retrieve a specific version of the secret. This requires the permission <code>secretsmanager:GetSecretValue</code> | |
| 91 | * | |
| 92 | * @param secretId the ARN of the secret | |
| 93 | * @param clientRequestToken the version identifier of the secret | |
| 94 | * @return the Fernet key or keys in binary form | |
| 95 | */ | |
| 96 | public ByteBuffer getSecretVersion(final String secretId, final String clientRequestToken) { | |
| 97 | final GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest(); | |
| 98 |
1
1. getSecretVersion : removed call to com/amazonaws/services/secretsmanager/model/GetSecretValueRequest::setSecretId → KILLED |
getSecretValueRequest.setSecretId(secretId); |
| 99 |
1
1. getSecretVersion : removed call to com/amazonaws/services/secretsmanager/model/GetSecretValueRequest::setVersionId → KILLED |
getSecretValueRequest.setVersionId(clientRequestToken); |
| 100 | final GetSecretValueResult result = getDelegate().getSecretValue(getSecretValueRequest); | |
| 101 |
1
1. getSecretVersion : mutated return of Object value for com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::getSecretVersion to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return result.getSecretBinary(); |
| 102 | } | |
| 103 | ||
| 104 | /** | |
| 105 | * Retrieve a specific stage of the secret. | |
| 106 | * | |
| 107 | * @param secretId the ARN of the secret | |
| 108 | * @param stage the stage of the secret to retrieve | |
| 109 | * @return the Fernet key or keys in binary form | |
| 110 | */ | |
| 111 | public ByteBuffer getSecretStage(final String secretId, final Stage stage) { | |
| 112 | final GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest(); | |
| 113 |
1
1. getSecretStage : removed call to com/amazonaws/services/secretsmanager/model/GetSecretValueRequest::setSecretId → KILLED |
getSecretValueRequest.setSecretId(secretId); |
| 114 |
1
1. getSecretStage : removed call to com/amazonaws/services/secretsmanager/model/GetSecretValueRequest::setVersionStage → KILLED |
getSecretValueRequest.setVersionStage(stage.getAwsName()); |
| 115 | final GetSecretValueResult result = getDelegate().getSecretValue(getSecretValueRequest); | |
| 116 |
1
1. getSecretStage : mutated return of Object value for com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::getSecretStage to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return result.getSecretBinary(); |
| 117 | } | |
| 118 | ||
| 119 | /** | |
| 120 | * Rotate a secret. This requires the permission <code>secretsmanager:UpdateSecretVersionStage</code> | |
| 121 | * | |
| 122 | * @param secretId | |
| 123 | * the ARN of the secret | |
| 124 | * @param clientRequestToken | |
| 125 | * the version ID to be made "current" | |
| 126 | * @param currentVersion | |
| 127 | * the current active version ID to be made "previous" | |
| 128 | */ | |
| 129 | public void rotateSecret(final String secretId, final String clientRequestToken, | |
| 130 | final String currentVersion) { | |
| 131 | final UpdateSecretVersionStageRequest updateSecretVersionStageRequest = new UpdateSecretVersionStageRequest(); | |
| 132 |
1
1. rotateSecret : removed call to com/amazonaws/services/secretsmanager/model/UpdateSecretVersionStageRequest::setSecretId → KILLED |
updateSecretVersionStageRequest.setSecretId(secretId); |
| 133 |
1
1. rotateSecret : removed call to com/amazonaws/services/secretsmanager/model/UpdateSecretVersionStageRequest::setVersionStage → KILLED |
updateSecretVersionStageRequest.setVersionStage(CURRENT.getAwsName()); |
| 134 |
1
1. rotateSecret : removed call to com/amazonaws/services/secretsmanager/model/UpdateSecretVersionStageRequest::setMoveToVersionId → KILLED |
updateSecretVersionStageRequest.setMoveToVersionId(clientRequestToken); |
| 135 |
1
1. rotateSecret : removed call to com/amazonaws/services/secretsmanager/model/UpdateSecretVersionStageRequest::setRemoveFromVersionId → KILLED |
updateSecretVersionStageRequest.setRemoveFromVersionId(currentVersion); |
| 136 | getDelegate().updateSecretVersionStage(updateSecretVersionStageRequest); | |
| 137 | } | |
| 138 | ||
| 139 | /** | |
| 140 | * Store a single Fernet key in a secret. This requires the permission <code>secretsmanager:PutSecretValue</code> | |
| 141 | * | |
| 142 | * @param secretId | |
| 143 | * the ARN of the secret | |
| 144 | * @param clientRequestToken | |
| 145 | * the secret version identifier | |
| 146 | * @param key | |
| 147 | * the key to store in the secret | |
| 148 | * @param stage | |
| 149 | * the stage with which to tag the version | |
| 150 | */ | |
| 151 | public void putSecretValue(final String secretId, final String clientRequestToken, final Key key, final Stage stage) { | |
| 152 |
1
1. putSecretValue : removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::putSecretValue → KILLED |
putSecretValue(secretId, clientRequestToken, singletonList(key), stage); |
| 153 | } | |
| 154 | ||
| 155 | /** | |
| 156 | * Store Fernet keys in the secret. This requires the permission <code>secretsmanager:PutSecretValue</code> | |
| 157 | * | |
| 158 | * @param secretId | |
| 159 | * the ARN of the secret | |
| 160 | * @param clientRequestToken | |
| 161 | * the secret version identifier | |
| 162 | * @param keys | |
| 163 | * the keys to store in the secret | |
| 164 | * @param stage | |
| 165 | * the stage with which to tag the version | |
| 166 | */ | |
| 167 | public void putSecretValue(final String secretId, final String clientRequestToken, final Collection<? extends Key> keys, | |
| 168 | final Stage stage) { | |
| 169 | final PutSecretValueRequest putSecretValueRequest = new PutSecretValueRequest(); | |
| 170 |
1
1. putSecretValue : removed call to com/amazonaws/services/secretsmanager/model/PutSecretValueRequest::setSecretId → KILLED |
putSecretValueRequest.setSecretId(secretId); |
| 171 |
1
1. putSecretValue : removed call to com/amazonaws/services/secretsmanager/model/PutSecretValueRequest::setClientRequestToken → KILLED |
putSecretValueRequest.setClientRequestToken(clientRequestToken); |
| 172 |
1
1. putSecretValue : removed call to com/amazonaws/services/secretsmanager/model/PutSecretValueRequest::setVersionStages → KILLED |
putSecretValueRequest.setVersionStages(singletonList(stage.getAwsName())); |
| 173 |
3
1. putSecretValue : Replaced integer multiplication with division → SURVIVED 2. putSecretValue : removed call to java/io/ByteArrayOutputStream::close → NO_COVERAGE 3. putSecretValue : removed call to java/lang/Throwable::addSuppressed → NO_COVERAGE |
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(32 * keys.size())) { |
| 174 | for (final Key key : keys) { | |
| 175 |
1
1. putSecretValue : removed call to com/macasaet/fernet/Key::writeTo → KILLED |
key.writeTo(outputStream); |
| 176 | } | |
| 177 | final ByteBuffer buffer = ByteBuffer.wrap(outputStream.toByteArray()); | |
| 178 |
1
1. putSecretValue : removed call to com/amazonaws/services/secretsmanager/model/PutSecretValueRequest::setSecretBinary → KILLED |
putSecretValueRequest.setSecretBinary(buffer); |
| 179 | | |
| 180 |
1
1. putSecretValue : removed call to java/io/ByteArrayOutputStream::reset → SURVIVED |
outputStream.reset(); |
| 181 |
4
1. putSecretValue : changed conditional boundary → SURVIVED 2. putSecretValue : negated conditional → SURVIVED 3. putSecretValue : removed call to java/io/ByteArrayOutputStream::write → SURVIVED 4. putSecretValue : Changed increment from -1 to 1 → KILLED |
for (int i = keys.size(); --i >= 0; outputStream.write(0)); |
| 182 |
1
1. putSecretValue : removed call to java/io/ByteArrayOutputStream::close → SURVIVED |
} catch (final IOException ioe) { |
| 183 | // this really should not happen as I/O is to memory only | |
| 184 | throw new IllegalStateException(ioe.getMessage(), ioe); | |
| 185 | } | |
| 186 | ||
| 187 | getDelegate().putSecretValue(putSecretValueRequest); | |
| 188 | } | |
| 189 | ||
| 190 | protected AWSSecretsManager getDelegate() { | |
| 191 |
1
1. getDelegate : mutated return of Object value for com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::getDelegate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return delegate; |
| 192 | } | |
| 193 | ||
| 194 | } | |
Mutations | ||
| 52 |
1.1 |
|
| 59 |
1.1 |
|
| 72 |
1.1 |
|
| 73 |
1.1 |
|
| 85 |
1.1 |
|
| 86 |
1.1 |
|
| 98 |
1.1 |
|
| 99 |
1.1 |
|
| 101 |
1.1 |
|
| 113 |
1.1 |
|
| 114 |
1.1 |
|
| 116 |
1.1 |
|
| 132 |
1.1 |
|
| 133 |
1.1 |
|
| 134 |
1.1 |
|
| 135 |
1.1 |
|
| 152 |
1.1 |
|
| 170 |
1.1 |
|
| 171 |
1.1 |
|
| 172 |
1.1 |
|
| 173 |
1.1 2.2 3.3 |
|
| 175 |
1.1 |
|
| 178 |
1.1 |
|
| 180 |
1.1 |
|
| 181 |
1.1 2.2 3.3 4.4 |
|
| 182 |
1.1 |
|
| 191 |
1.1 |