Skip to content

Commit

Permalink
Enable aws.iam#disableConditionKeyInference trait for service shape
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewFossAWS committed Oct 25, 2023
1 parent 6b2c80c commit 7d32e87
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 15 deletions.
37 changes: 36 additions & 1 deletion docs/source-2.0/aws/aws-iam.rst
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ Condition keys in IAM policies can be evaluated with `condition operators`_.
Summary
Declares that the condition keys of a resource should not be inferred.
Trait selector
``resource``
``:test(service, resource)``
Value type
Annotation trait

Expand Down Expand Up @@ -255,6 +255,41 @@ condition key inference disabled.
}
}
When a service is marked with the ``aws.iam#disableConditionKeyInference`` trait,
all the resources bound to the service will not have condition keys automatically inferred from its identifiers and
the identifiers of its ancestors.

The following example shows resources ``MyResource1`` and ``MyResource2`` have had condition key inference disabled
because they are bound to a service marked with ``aws.iam#disableConditionKeyInference`` trait.

.. code-block:: smithy
$version: "2"
namespace smithy.example
use aws.api#service
use aws.iam#disableConditionKeyInference
@service(sdkId: "My Value", arnNamespace: "myservice")
@disableConditionKeyInference
service MyService {
version: "2017-02-11"
resources: [MyResource1, MyResource2]
}
resource MyResource1 {
identifiers: {
foo: String
}
}
resource MyResource2 {
identifiers: {
foo: String
}
}
.. smithy-trait:: aws.iam#requiredActions
.. _aws.iam#requiredActions-trait:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ public ConditionKeysIndex(Model model) {
service.getResources().stream()
.flatMap(id -> OptionalUtils.stream(model.getShape(id)))
.forEach(resource -> {
compute(model, service.getId(), arnRoot, resource, null);
compute(model, service, arnRoot, resource, null);
});

// Compute the keys of operations of the service.
service.getOperations().stream()
.flatMap(id -> OptionalUtils.stream(model.getShape(id)))
.forEach(operation -> {
compute(model, service.getId(), arnRoot, operation, null);
compute(model, service, arnRoot, operation, null);
});
});
});
Expand Down Expand Up @@ -153,7 +153,7 @@ public Map<String, ConditionKeyDefinition> getDefinedConditionKeys(

private void compute(
Model model,
ShapeId service,
ServiceShape service,
String arnRoot,
Shape subject,
ResourceShape parent
Expand All @@ -163,21 +163,24 @@ private void compute(

private void compute(
Model model,
ShapeId service,
ServiceShape service,
String arnRoot,
Shape subject,
ResourceShape parent,
Set<String> parentDefinitions
) {
Set<String> definitions = new HashSet<>(parentDefinitions);
resourceConditionKeys.get(service).put(subject.getId(), definitions);
resourceConditionKeys.get(service.getId()).put(subject.getId(), definitions);
subject.getTrait(ConditionKeysTrait.class).ifPresent(trait -> definitions.addAll(trait.getValues()));

// Continue recursing into resources and computing keys.
subject.asResourceShape().ifPresent(resource -> {
boolean disableConditionKeyInference = resource.hasTrait(DisableConditionKeyInferenceTrait.class)
|| service.hasTrait(DisableConditionKeyInferenceTrait.class);

// Add any inferred resource identifiers to the resource and to the service-wide definitions.
Map<String, String> childIdentifiers = !resource.hasTrait(DisableConditionKeyInferenceTrait.class)
? inferChildResourceIdentifiers(model, service, arnRoot, resource, parent)
Map<String, String> childIdentifiers = !disableConditionKeyInference
? inferChildResourceIdentifiers(model, service.getId(), arnRoot, resource, parent)
: MapUtils.of();

// Compute the keys of each child operation, passing no keys.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
"type": "structure",
"traits": {
"smithy.api#trait": {
"selector": "resource"
"selector": ":test(service, resource)"
},
"smithy.api#documentation": "Disables the automatic inference of condition keys of a resource."
"smithy.api#documentation": "Disables the automatic inference of condition keys of service's resources or a specific resource."
}
},
"aws.iam#requiredActions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,11 @@
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.validation.Severity;
import software.amazon.smithy.model.validation.ValidatedResult;
import software.amazon.smithy.model.validation.ValidationEvent;

public class ConditionKeysIndexTest {
@Test
Expand Down Expand Up @@ -65,4 +60,49 @@ public void successfullyLoadsConditionKeys() {
assertThat(index.getDefinedConditionKeys(service, ShapeId.from("smithy.example#GetResource2")).keySet(),
is(empty()));
}

@Test
public void disableConditionKeyInferenceForResources() {
Model model = Model.assembler()
.addImport(getClass().getResource("disable-condition-key-inference-for-resources.smithy"))
.discoverModels(getClass().getClassLoader())
.assemble()
.unwrap();

ShapeId service = ShapeId.from("smithy.example#MyService");

ConditionKeysIndex index = ConditionKeysIndex.of(model);

// Verify inference key myservice:Resource2Id2 does not exist
assertThat(index.getConditionKeyNames(service), not(contains("myservice:Resource2Id2")));

assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource1")),
not(contains("myservice:Resource2Id2")));

assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource2")),
not(contains("myservice:Resource2Id2")));
}

@Test
public void disableConditionKeyInferenceForService() {
Model model = Model.assembler()
.addImport(getClass().getResource("disable-condition-key-inference-for-service.smithy"))
.discoverModels(getClass().getClassLoader())
.assemble()
.unwrap();

ShapeId service = ShapeId.from("smithy.example#MyService");

ConditionKeysIndex index = ConditionKeysIndex.of(model);

// Verify inference key myservice:Resource1Id1 AND myservice:Resource2Id2 do not exist
assertThat(index.getConditionKeyNames(service),
not(contains("myservice:Resource1Id1", "myservice:Resource2Id2")));

assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource1")),
not(contains("myservice:Resource1Id1", "myservice:Resource2Id2")));

assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Resource2")),
not(contains("myservice:Resource1Id1", "myservice:Resource2Id2")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
$version: "1.0"
namespace smithy.example

@aws.api#service(sdkId: "My")
@aws.iam#defineConditionKeys("my:service": {type: "String", documentation: "Foo baz"})
service MyService {
version: "2019-02-20",
operations: [Operation1],
resources: [Resource1]
}

@aws.iam#conditionKeys(["aws:operation1", "my:service"])
operation Operation1 {}

@aws.iam#conditionKeys(["resource:1"])
resource Resource1 {
identifiers: {
id1: ArnString,
},
resources: [Resource2]
}

@aws.iam#disableConditionKeyInference
resource Resource2 {
identifiers: {
id1: ArnString,
id2: FooString,
},
read: GetResource2,
list: ListResource2,
}

@readonly
operation GetResource2 {
input: GetResource2Input
}

structure GetResource2Input {
@required
id1: ArnString,

@required
id2: FooString
}

@documentation("This is Foo")
string FooString

@readonly
operation ListResource2 {
input: ListResource2Input,
output: ListResource2Output
}

structure ListResource2Input {
@required
id1: ArnString,
}

structure ListResource2Output {}

@aws.api#arnReference(type: "ec2:Instance")
string ArnString
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
$version: "1.0"
namespace smithy.example

@aws.api#service(sdkId: "My")
@aws.iam#defineConditionKeys("my:service": {type: "String", documentation: "Foo baz"})
@aws.iam#disableConditionKeyInference
service MyService {
version: "2019-02-20",
operations: [Operation1],
resources: [Resource1]
}

@aws.iam#conditionKeys(["aws:operation1", "my:service"])
operation Operation1 {}

@aws.iam#conditionKeys(["resource:1"])
resource Resource1 {
identifiers: {
id1: ArnString,
},
resources: [Resource2]
}

@aws.iam#disableConditionKeyInference
resource Resource2 {
identifiers: {
id1: ArnString,
id2: FooString,
},
read: GetResource2,
list: ListResource2,
}

@readonly
operation GetResource2 {
input: GetResource2Input
}

structure GetResource2Input {
@required
id1: ArnString,

@required
id2: FooString
}

@documentation("This is Foo")
string FooString

@readonly
operation ListResource2 {
input: ListResource2Input,
output: ListResource2Output
}

structure ListResource2Input {
@required
id1: ArnString,
}

structure ListResource2Output {}

@aws.api#arnReference(type: "ec2:Instance")
string ArnString

0 comments on commit 7d32e87

Please sign in to comment.