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 |