diff --git a/docs/source-2.0/aws/aws-iam.rst b/docs/source-2.0/aws/aws-iam.rst index a7e2ae28ed6..ee39923dbb8 100644 --- a/docs/source-2.0/aws/aws-iam.rst +++ b/docs/source-2.0/aws/aws-iam.rst @@ -100,6 +100,12 @@ members: - ``string`` - A relative URL path that defines more information about the resource within a set of IAM-related documentation. + * - disableConditionKeyInheritance + - ``boolean`` + - When set to ``true``, decouples this IAM resource's condition keys from + those of its parent resource(s). This can be used in combination with + the :ref:`aws.iam#conditionKeys-trait` trait to isolate a resource's + condition keys from those of its parent(s). The following example defines a simple resource with a name in AWS IAM that deviates from the :ref:`shape name of the shape ID ` of the resource. @@ -751,7 +757,7 @@ Given the following model, resource MyResource { identifiers: {foo: String} operations: [MyOperation] - resources: [MyInnerResource] + resources: [MyInnerResource, MyDetachedResource, MyCustomResource] } @iamResource(name: "InnerResource") @@ -759,6 +765,19 @@ Given the following model, identifiers: {yum: String} } + @disableConditionKeyInference + @iamResource(disableConditionKeyInheritance: true) + resource MyDetachedResource { + identifiers: {yum: String} + } + + @disableConditionKeyInference + @iamResource(disableConditionKeyInheritance: true) + @conditionKeys(["aws:region"]) + resource MyCustomResource { + identifiers: {yum: String} + } + @conditionKeys(["aws:region"]) operation MyOperation {} @@ -779,6 +798,11 @@ The computed condition keys for the service are: * ``myservice:MyResourceFoo`` * ``otherservice:Bar`` * ``myservice:InnerResourceYum`` + * - ``MyDetachedResource`` + - None + * - ``MyCustomResource`` + - + * ``aws:region`` * - ``MyOperation`` - * ``aws:region`` diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndex.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndex.java index af5c1ed4b5f..346616d513d 100644 --- a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndex.java +++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndex.java @@ -169,7 +169,12 @@ private void compute( ResourceShape parent, Set parentDefinitions ) { - Set definitions = new HashSet<>(parentDefinitions); + Set definitions = new HashSet<>(); + if (!subject.hasTrait(IamResourceTrait.ID) + || !subject.expectTrait(IamResourceTrait.class).isDisableConditionKeyInheritance() + ) { + definitions.addAll(parentDefinitions); + } resourceConditionKeys.get(service.getId()).put(subject.getId(), definitions); subject.getTrait(ConditionKeysTrait.class).ifPresent(trait -> definitions.addAll(trait.getValues())); diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java index 2e3bed2fbbf..1787b320d95 100644 --- a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java +++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java @@ -32,13 +32,15 @@ public final class IamResourceTrait extends AbstractTrait implements ToSmithyBuilder { public static final ShapeId ID = ShapeId.from("aws.iam#iamResource"); - public final String name; - public final String relativeDocumentation; + private final String name; + private final String relativeDocumentation; + private final boolean disableConditionKeyInheritance; private IamResourceTrait(Builder builder) { super(ID, builder.getSourceLocation()); name = builder.name; relativeDocumentation = builder.relativeDocumentation; + disableConditionKeyInheritance = builder.disableConditionKeyInheritance; } /** @@ -47,7 +49,7 @@ private IamResourceTrait(Builder builder) { * @return Returns the name. */ public Optional getName() { - return Optional.of(name); + return Optional.ofNullable(name); } /** @@ -60,6 +62,16 @@ public Optional getRelativeDocumentation() { return Optional.ofNullable(relativeDocumentation); } + /** + * Gets if this IAM resource's condition keys are decoupled from + * those of its parent resource(s). + * + * @return Returns true if condition key inheritance is disabled. + */ + public boolean isDisableConditionKeyInheritance() { + return disableConditionKeyInheritance; + } + public static Builder builder() { return new Builder(); } @@ -93,6 +105,7 @@ public Trait createTrait(ShapeId target, Node value) { public static final class Builder extends AbstractTraitBuilder { private String name; private String relativeDocumentation; + private boolean disableConditionKeyInheritance; private Builder() {} @@ -110,5 +123,10 @@ public Builder relativeDocumentation(String relativeDocumentation) { this.relativeDocumentation = relativeDocumentation; return this; } + + public Builder disableConditionKeyInheritance(boolean disableConditionKeyInheritance) { + this.disableConditionKeyInheritance = disableConditionKeyInheritance; + return this; + } } } diff --git a/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy b/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy index 47fd142938e..15702860215 100644 --- a/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy +++ b/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy @@ -25,7 +25,7 @@ list conditionKeys { string conditionKeyValue /// Defines the set of condition keys that appear within a service in addition to -/// inferred and global condition keys. +/// inferred and global condition keys. @trait(selector: "service") map defineConditionKeys { key: IamIdentifier @@ -65,9 +65,13 @@ structure iamResource { /// The name of the resource in AWS IAM. name: String - /// A relative URL path that defines more information about the resource - /// within a set of IAM-related documentation. + /// A relative URL path that defines more information about the resource + /// within a set of IAM-related documentation. relativeDocumentation: String + + /// When set to `true`, decouples this IAM resource's condition keys from + /// those of its parent resource(s). + disableConditionKeyInheritance: Boolean } /// Other actions that the invoker must be authorized to perform when executing the targeted operation. @@ -112,8 +116,8 @@ structure ConditionKeyDefinition { /// A valid URL that defines more information about the condition key. externalDocumentation: String - /// A relative URL path that defines more information about the condition key - /// within a set of IAM-related documentation. + /// A relative URL path that defines more information about the condition key + /// within a set of IAM-related documentation. relativeDocumentation: String } diff --git a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java index e470547809e..d5e7f9c8bc9 100644 --- a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java +++ b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java @@ -49,6 +49,11 @@ public void successfullyLoadsConditionKeys() { assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource2")), containsInAnyOrder("aws:accountId", "foo:baz", "myservice:Resource1Id1", "myservice:ResourceTwoId2")); + // This resource has inheritance disabled. + assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource3")), empty()); + // This resource has inheritance disabled and an explicit list provided. + assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource4")), + contains("foo:baz")); // Note that while this operation binds identifiers, it contains no unique ConditionKeys to bind. assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#GetResource2")), is(empty())); diff --git a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java index eb7bc183769..dc03eb967f1 100644 --- a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java +++ b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java @@ -16,6 +16,7 @@ package software.amazon.smithy.aws.iam.traits; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -37,5 +38,6 @@ public void loadsFromModel() { assertTrue(superResource.hasTrait(IamResourceTrait.class)); assertEquals(superResource.expectTrait(IamResourceTrait.class).getName().get(), "super"); assertEquals(superResource.expectTrait(IamResourceTrait.class).getRelativeDocumentation().get(), "API-Super.html"); + assertFalse(superResource.expectTrait(IamResourceTrait.class).isDisableConditionKeyInheritance()); } } diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy index 55fc02b9ae6..627e7260278 100644 --- a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy @@ -1,31 +1,34 @@ -$version: "1.0" +$version: "2.0" namespace smithy.example use aws.api#arn +use aws.api#service +use aws.iam#iamResource -@aws.api#service(sdkId: "My") +@service(sdkId: "My") service MyService { - version: "2020-07-02", + version: "2020-07-02" resources: [SuperResource] } -@aws.iam#iamResource( - name: "super", +@iamResource( + name: "super" relativeDocumentation: "API-Super.html" + disableConditionKeyInheritance: false ) @arn(template: "super/{id1}") resource SuperResource { identifiers: { - id1: String, - }, + id1: String + } read: GetResource } @readonly operation GetResource { - input: GetResourceInput, - output: GetResourceOutput, + input: GetResourceInput + output: GetResourceOutput } structure GetResourceInput { @@ -34,9 +37,9 @@ structure GetResourceInput { } structure GetResourceOutput { - super: Super, + super: Super } structure Super { - id1: String, + id1: String } diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy index a7c0999c086..f6a209a0565 100644 --- a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy @@ -1,8 +1,15 @@ $version: "1.0" namespace smithy.example -@aws.api#service(sdkId: "My") -@aws.iam#defineConditionKeys( +use aws.api#arnReference +use aws.api#service +use aws.iam#conditionKeys +use aws.iam#defineConditionKeys +use aws.iam#disableConditionKeyInference +use aws.iam#iamResource + +@service(sdkId: "My") +@defineConditionKeys( "foo:baz": { type: "String", documentation: "Foo baz", @@ -15,18 +22,18 @@ service MyService { resources: [Resource1] } -@aws.iam#conditionKeys(["aws:accountId", "foo:baz"]) +@conditionKeys(["aws:accountId", "foo:baz"]) operation Operation1 {} -@aws.iam#conditionKeys(["aws:accountId", "foo:baz"]) +@conditionKeys(["aws:accountId", "foo:baz"]) resource Resource1 { identifiers: { id1: ArnString, }, - resources: [Resource2] + resources: [Resource2, Resource3, Resource4] } -@aws.iam#iamResource(name: "ResourceTwo") +@iamResource(name: "ResourceTwo") resource Resource2 { identifiers: { id1: ArnString, @@ -36,6 +43,27 @@ resource Resource2 { list: ListResource2, } +@disableConditionKeyInference +@iamResource(disableConditionKeyInheritance: true) +resource Resource3 { + identifiers: { + id1: ArnString + id2: FooString + id3: String + } +} + +@disableConditionKeyInference +@iamResource(disableConditionKeyInheritance: true) +@conditionKeys(["foo:baz"]) +resource Resource4 { + identifiers: { + id1: ArnString + id2: FooString + id4: String + } +} + @readonly operation GetResource2 { input: GetResource2Input @@ -65,5 +93,5 @@ structure ListResource2Input { structure ListResource2Output {} -@aws.api#arnReference(type: "ec2:Instance") +@arnReference(type: "ec2:Instance") string ArnString