Skip to content

Commit 436942e

Browse files
hpmellemamtdowling
andauthored
Add validator to check consistency of resource name used for IamResource (smithy-lang#1819)
* Add validator to check consistency of resource name used for IamResource * Update arn parsing method to be private * Update smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitValidator.java Co-authored-by: Michael Dowling <[email protected]> * Update smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitValidator.java Co-authored-by: Michael Dowling <[email protected]> * Address PR comments * Add errorfiles test runner * Revert "Address PR comments" This reverts commit 3753ac2. * Re-add changes without accidentally popped stashed changes * Update to use orElseGet * Re-add change that was accidentally removed * Add newline at end of file --------- Co-authored-by: Michael Dowling <[email protected]>
1 parent f8cc33c commit 436942e

File tree

9 files changed

+135
-14
lines changed

9 files changed

+135
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.aws.iam.traits;
17+
18+
import java.util.ArrayList;
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import software.amazon.smithy.aws.traits.ArnTrait;
22+
import software.amazon.smithy.model.Model;
23+
import software.amazon.smithy.model.shapes.ResourceShape;
24+
import software.amazon.smithy.model.validation.AbstractValidator;
25+
import software.amazon.smithy.model.validation.ValidationEvent;
26+
import software.amazon.smithy.utils.SmithyInternalApi;
27+
import software.amazon.smithy.utils.StringUtils;
28+
29+
/**
30+
* Ensures that any resource name defined in the {@link IamResourceTrait} is
31+
* consistent with the resource name used in any {@link ArnTrait} definition
32+
* applied to the resource.
33+
*/
34+
@SmithyInternalApi
35+
public class IamResourceTraitValidator extends AbstractValidator {
36+
@Override
37+
public List<ValidationEvent> validate(Model model) {
38+
List<ValidationEvent> results = new ArrayList<>();
39+
for (ResourceShape resource : model.getResourceShapesWithTrait(IamResourceTrait.class)) {
40+
// If the resource has both the IamResourceTrait and Arn trait,
41+
// check that the resource name is consistent between the two traits
42+
if (resource.hasTrait(ArnTrait.class)) {
43+
String resourceName = resource.expectTrait(IamResourceTrait.class).getName()
44+
.orElseGet(() -> StringUtils.lowerCase(resource.getId().getName()));
45+
ArnTrait arnTrait = resource.expectTrait(ArnTrait.class);
46+
List<String> arnComponents = parseArnComponents(arnTrait.getTemplate());
47+
if (!arnComponents.contains(resourceName)) {
48+
results.add(danger(resource, String.format(
49+
"The `@aws.iam#iamResource` trait applied to the resource "
50+
+ "defines an IAM resource name, `%s`, that does not match the `@arn` template, "
51+
+ "`%s`, of the resource.",
52+
resourceName, arnTrait.getTemplate())));
53+
}
54+
}
55+
}
56+
return results;
57+
}
58+
59+
private List<String> parseArnComponents(String arnTemplate) {
60+
return Arrays.asList(arnTemplate.split("/"));
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
software.amazon.smithy.aws.iam.traits.ConditionKeysValidator
2+
software.amazon.smithy.aws.iam.traits.IamResourceTraitValidator

smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java

-14
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,4 @@ public void successfullyLoadsConditionKeys() {
6565
assertThat(index.getDefinedConditionKeys(service, ShapeId.from("smithy.example#GetResource2")).keySet(),
6666
is(empty()));
6767
}
68-
69-
@Test
70-
public void detectsUnknownConditionKeys() {
71-
ValidatedResult<Model> result = Model.assembler()
72-
.addImport(getClass().getResource("invalid-condition-keys.smithy"))
73-
.discoverModels(getClass().getClassLoader())
74-
.assemble();
75-
76-
assertTrue(result.isBroken());
77-
assertThat(result.getValidationEvents(Severity.ERROR).stream()
78-
.map(ValidationEvent::getId)
79-
.collect(Collectors.toSet()),
80-
contains("ConditionKeys"));
81-
}
8268
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package software.amazon.smithy.aws.iam.traits;
2+
3+
import org.junit.jupiter.params.ParameterizedTest;
4+
import org.junit.jupiter.params.provider.MethodSource;
5+
import software.amazon.smithy.model.validation.testrunner.SmithyTestCase;
6+
import software.amazon.smithy.model.validation.testrunner.SmithyTestSuite;
7+
8+
import java.util.concurrent.Callable;
9+
import java.util.stream.Stream;
10+
11+
public class TestRunnerTest {
12+
@ParameterizedTest(name = "{0}")
13+
@MethodSource("source")
14+
public void testRunner(String filename, Callable<SmithyTestCase.Result> callable) throws Exception {
15+
callable.call();
16+
}
17+
18+
public static Stream<?> source() {
19+
return SmithyTestSuite.defaultParameterizedTestSource(TestRunnerTest.class);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[ERROR] smithy.example#Operation: This operation scoped within the `smithy.example#MyService` service refers to an undefined condition key `foo:qux`. Expected one of the following defined condition keys: [`foo:baz`] | ConditionKeys
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[DANGER] smithy.example#BadIamResourceName: The `@aws.iam#iamResource` trait applied to the resource defines an IAM resource name, `bad-iam-resourceName`, that does not match the `@arn` template, `bad-iam-resource-name/{id}`, of the resource. | IamResourceTrait
2+
[DANGER] smithy.example#IncompatibleResourceName: The `@aws.iam#iamResource` trait applied to the resource defines an IAM resource name, `IncompatibleResourceName`, that does not match the `@arn` template, `beer/{beerId}/incompatible-resource-name`, of the resource. | IamResourceTrait
3+
[DANGER] smithy.example#InvalidResource: The `@aws.iam#iamResource` trait applied to the resource defines an IAM resource name, `invalidResource`, that does not match the `@arn` template, `invalid-resource`, of the resource. | IamResourceTrait
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
$version: "2"
2+
namespace smithy.example
3+
4+
use aws.api#arn
5+
6+
@aws.api#service(sdkId: "My")
7+
@aws.iam#defineConditionKeys("foo:baz": {type: "String", documentation: "Foo baz"})
8+
service MyService {
9+
version: "2019-02-20",
10+
resources: [
11+
BadIamResourceName,
12+
Beer,
13+
InvalidResource
14+
]
15+
}
16+
17+
@aws.iam#iamResource(name: "bad-iam-resourceName")
18+
@arn(template: "bad-iam-resource-name/{id}")
19+
resource BadIamResourceName {
20+
identifiers: {
21+
id: String
22+
}
23+
}
24+
25+
@aws.iam#iamResource(name: "beer")
26+
@arn(template: "beer/{beerId}")
27+
resource Beer {
28+
identifiers: {
29+
beerId: String
30+
}
31+
resources: [IncompatibleResourceName]
32+
}
33+
34+
@arn(template: "beer/{beerId}/incompatible-resource-name")
35+
@aws.iam#iamResource(name: "IncompatibleResourceName")
36+
resource IncompatibleResourceName {
37+
identifiers: {
38+
beerId: String
39+
}
40+
}
41+
42+
@aws.iam#iamResource(name: "invalidResource")
43+
@arn(template: "invalid-resource")
44+
resource InvalidResource {}

smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy

+3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ $version: "1.0"
22

33
namespace smithy.example
44

5+
use aws.api#arn
6+
57
@aws.api#service(sdkId: "My")
68
service MyService {
79
version: "2020-07-02",
810
resources: [SuperResource]
911
}
1012

1113
@aws.iam#iamResource(name: "super")
14+
@arn(template: "super/{id1}")
1215
resource SuperResource {
1316
identifiers: {
1417
id1: String,

0 commit comments

Comments
 (0)