From 11c00cb4e44dd874ad563d83422471217d68209d Mon Sep 17 00:00:00 2001 From: kstich Date: Tue, 7 Nov 2023 23:18:22 -0800 Subject: [PATCH] Add aws.iam#iamAction trait This commit introduces the iamAction trait to both consolidate and enhance the customization for IAM actions in derived from Smithy operations. It deprecates the actionName, actionPermissionDescription, and requiredAction traits - placing their behavior in the name, documentation, and requiredAction members respectively. A relativeDocumentation member is new, allowing for linking to docs within IAM documentation. A createsResources member is new, allowing for the explicit listing of resources that performing an IAM action will create. A resources member is new, allowing for the override configuration of required and optional resources that an IAM action can be authorized against (including the condition keys that can be used). This enables the detachment of an IAM action from being required to authorize against its resource hierarchy for special cases. The specification is also reorganized into sections for traits affecting resources, actions, and condition keys for easier navigation. --- docs/source-2.0/aws/aws-iam.rst | 576 +++++++++++------- .../smithy/aws/iam/traits/ActionResource.java | 112 ++++ .../aws/iam/traits/ActionResources.java | 165 +++++ .../smithy/aws/iam/traits/IamActionTrait.java | 218 +++++++ .../aws/iam/traits/IamActionValidator.java | 80 +++ ...re.amazon.smithy.model.traits.TraitService | 1 + ...e.amazon.smithy.model.validation.Validator | 1 + .../resources/META-INF/smithy/aws.iam.smithy | 70 +++ .../aws/iam/traits/IamActionTraitTest.java | 57 ++ .../aws/iam/traits/SmithyErrorFilesTest.java | 21 - .../errorfiles/iam-action-conflicts.errors | 3 + .../errorfiles/iam-action-conflicts.smithy | 18 + .../iam-action-resource-detachment.errors | 0 .../iam-action-resource-detachment.smithy | 23 + .../iam-action-resource-duplicates.errors | 1 + .../iam-action-resource-duplicates.smithy | 27 + .../smithy/aws/iam/traits/iam-action.smithy | 26 + 17 files changed, 1159 insertions(+), 240 deletions(-) create mode 100644 smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResource.java create mode 100644 smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResources.java create mode 100644 smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionTrait.java create mode 100644 smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionValidator.java create mode 100644 smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamActionTraitTest.java delete mode 100644 smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/SmithyErrorFilesTest.java create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.errors create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.smithy create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.errors create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.smithy create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.errors create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.smithy create mode 100644 smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-action.smithy diff --git a/docs/source-2.0/aws/aws-iam.rst b/docs/source-2.0/aws/aws-iam.rst index d1556ead369..245f6a81b07 100644 --- a/docs/source-2.0/aws/aws-iam.rst +++ b/docs/source-2.0/aws/aws-iam.rst @@ -17,12 +17,283 @@ policies for resources in a service. Condition keys for services defined in Smithy are automatically inferred. These can be disabled or augmented. For more information, see :ref:`deriving-condition-keys`. + +.. smithy-trait:: aws.iam#supportedPrincipalTypes +.. _aws.iam#supportedPrincipalTypes-trait: + +----------------------------------------- +``aws.iam#supportedPrincipalTypes`` trait +----------------------------------------- + +Summary + The `IAM principal types`_ that can use the service or operation. +Trait selector + ``:test(service, operation)`` +Value type + ``list`` where each string is an IAM principal type: ``Root``, + ``IAMUser``, ``IAMRole``, or ``FederatedUser``. + +Operations that are not annotated with the ``supportedPrincipalTypes`` trait +inherit the ``supportedPrincipalTypes`` of the service they are bound to. + +The following example defines two operations: + +* OperationA defines an explicit list of the IAM principal types it supports + using the ``supportedPrincipalTypes`` trait. +* OperationB is not annotated with the ``supportedPrincipalTypes`` trait, so + the IAM principal types supported by this operation are the principal types + applied to the service. + +.. code-block:: smithy + + $version: "2" + + namespace smithy.example + + use aws.iam#supportedPrincipalTypes + + @supportedPrincipalTypes(["Root", "IAMUser", "IAMRole", "FederatedUser"]) + service MyService { + version: "2020-07-02" + operations: [OperationA, OperationB] + } + + @supportedPrincipalTypes(["Root"]) + operation OperationA {} + + operation OperationB {} + + +.. _aws-iam_traits-resources: + +--------------- +Resource traits +--------------- + +.. smithy-trait:: aws.iam#iamResource +.. _aws.iam#iamResource-trait: + +``aws.iam#iamResource`` trait +============================= + +Summary + Indicates properties of a Smithy resource in AWS IAM. +Trait selector + ``resource`` +Value type + ``structure`` + +The ``aws.iam#iamResource`` trait is a structure that supports the following +members: + +.. list-table:: + :header-rows: 1 + :widths: 10 20 70 + + * - Property + - Type + - Description + * - name + - ``string`` + - The name of the resource in AWS IAM. + * - relativeDocumentation + - ``string`` + - A relative URL path that defines more information about the resource + within a set of IAM-related documentation. + +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. + +.. code-block:: smithy + + $version: "2" + + namespace smithy.example + + use aws.iam#iamResource + + @iamResource(name: "super") + resource SuperResource { + identifiers: { + superId: String, + } + } + + +.. _aws-iam_traits-actions: + +------------- +Action traits +------------- + +.. smithy-trait:: aws.iam#iamAction +.. _aws.iam#iamAction-trait: + +``aws.iam#iamAction`` trait +=========================== + +Summary + Indicates properties of a Smithy operation in AWS IAM. +Trait selector + ``operation`` +Value type + ``structure`` + +The ``aws.iam#iamAction`` trait is a structure that supports the following +members: + +.. list-table:: + :header-rows: 1 + :widths: 10 20 70 + + * - Property + - Type + - Description + * - name + - ``string`` + - The name of the resource in AWS IAM. + * - documentation + - ``string`` + - A brief description of what granting the user permission to invoke an + operation would entail. + * - relativeDocumentation + - ``string`` + - A relative URL path that defines more information about the operation + within a set of IAM-related documentation. + * - requiredActions + - ``list`` where each string value is the name of another action. + - The list of actions that the invoker must be authorized to perform when + executing the targeted operation. + * - resources + - `ActionResources object`_ + - The resources an IAM action can be authorized against. + * - createsResources + - ``list`` where each string value is the name of a resource. + - The list of resources that performing this IAM action will create. If + this member is present, all inferred created resources are ignored. + +The following example defines a simple operation with a name in AWS IAM that +deviates from the :ref:`shape name of the shape ID ` of the operation. + +.. code-block:: smithy + + $version: "2" + + namespace smithy.example + + use aws.iam#iamAction + + @iamAction(name: "PutEvent") + operation OperationA {} + + +.. _aws.iam#iamAction-trait-ActionResources: + +``ActionResources`` object +-------------------------- + +The ``ActionResources`` object is a container for information on the resources +that an IAM action may be authorized against. The ``ActionResources`` object +contains the following properties: + +.. list-table:: + :header-rows: 1 + :widths: 10 20 70 + + * - Property + - Type + - Description + * - required + - ``map`` of resource name to `ActionResource object`_ + - Resources that will always be authorized against for functionality of + the IAM action. If this member is present, all inferred required + resources are ignored. + * - optional + - ``map`` of resource name to `ActionResource object`_ + - Resources that will be authorized against based on optional behavior of + the IAM action. If this member is present, all inferred optional + resources are ignored. + + For example, an action may create an instance that can optionally be + configured based on a snapshot that would be authorized against. Most + actions do not need this property. + + +.. _aws.iam#iamAction-trait-ActionResource: + +``ActionResource`` object +------------------------- + +The ``ActionResource`` object is a container for information about a resource +that an IAM action can be authorized against. The ``ActionResource`` object +contains the following properties: + +.. list-table:: + :header-rows: 1 + :widths: 10 20 70 + + * - Property + - Type + - Description + * - conditionKeys + - ``list`` + - The condition keys used for authorizing against this resource. + + +.. smithy-trait:: aws.iam#actionName +.. _aws.iam#actionName-trait: + +``aws.iam#actionName`` trait +============================ + +.. danger:: + This trait is deprecated. The ``name`` property of the + :ref:`aws.iam#iamAction-trait` should be used instead. + +Summary + Provides a custom IAM action name. +Trait selector + ``operation`` +Value type + ``string`` + +Operations not annotated with the ``actionName`` trait, default to the +:ref:`shape name of the shape ID ` of the targeted operation. + +The following example defines two operations: + +* ``OperationA`` is not annotated with the ``actionName`` trait, and + resolves the action name of ``OperationA``. +* ``OperationB`` has the ``actionName`` trait, so has the action + name ``OverridingActionName``. + +.. code-block:: smithy + + $version: "2" + + namespace smithy.example + + use aws.iam#actionName + + service MyService { + version: "2020-07-02" + operations: [OperationA, OperationB] + } + + operation OperationA {} + + @actionName("OverridingActionName") + operation OperationB {} + .. smithy-trait:: aws.iam#actionPermissionDescription .. _aws.iam#actionPermissionDescription-trait: ---------------------------------------------- ``aws.iam#actionPermissionDescription`` trait ---------------------------------------------- +============================================= + +.. danger:: + This trait is deprecated. The ``documentation`` property of the + :ref:`aws.iam#iamAction-trait` should be used instead. Summary A brief description of what granting the user permission to invoke an @@ -43,27 +314,31 @@ Value type @actionPermissionDescription("This will allow the user to Foo.") operation FooOperation {} -.. smithy-trait:: aws.iam#conditionKeys -.. _aws.iam#conditionKeys-trait: -------------------------------- -``aws.iam#conditionKeys`` trait -------------------------------- +.. smithy-trait:: aws.iam#requiredActions +.. _aws.iam#requiredActions-trait: + +``aws.iam#requiredActions`` trait +================================= + +.. danger:: + This trait is deprecated. The ``requiredActions`` property of the + :ref:`aws.iam#iamAction-trait` should be used instead. Summary - Applies condition keys, by name, to a resource or operation. + Other actions that the invoker must be authorized to perform when + executing the targeted operation. Trait selector - ``:test(resource, operation)`` + ``operation`` Value type - ``list`` + ``list`` where each string value references other actions + required for the service to authorize. -Condition keys derived automatically can be applied to a resource or operation -explicitly. Condition keys applied this way MUST be either inferred or -explicitly defined via the :ref:`aws.iam#defineConditionKeys-trait` trait. - -The following example's ``MyResource`` resource has the -``myservice:MyResourceFoo`` and ``otherservice:Bar`` condition keys. The -``MyOperation`` operation has the ``aws:region`` condition key. +Defines the actions, in addition to the targeted operation, that a user must +be authorized to execute in order invoke an operation. The following example +indicates that, in order to invoke the ``MyOperation`` operation, the invoker +must also be authorized to execute the ``otherservice:OtherOperation`` +operation for it to complete successfully. .. code-block:: smithy @@ -72,37 +347,34 @@ The following example's ``MyResource`` resource has the namespace smithy.example use aws.api#service - use aws.iam#definedContextKeys - use aws.iam#conditionKeys + use aws.iam#requiredActions @service(sdkId: "My Value", arnNamespace: "myservice") - @defineConditionKeys("otherservice:Bar": { type: "String" }) service MyService { version: "2017-02-11" resources: [MyResource] } - @conditionKeys(["otherservice:Bar"]) resource MyResource { identifiers: {foo: String} operations: [MyOperation] } - @conditionKeys(["aws:region"]) + @requiredActions(["otherservice:OtherOperation"]) operation MyOperation {} -.. note:: - Condition keys that refer to global ``"aws:*"`` keys can be referenced - without being defined on the service. +.. _aws-iam_traits-condition-keys: +-------------------- +Condition key traits +-------------------- .. smithy-trait:: aws.iam#defineConditionKeys .. _aws.iam#defineConditionKeys-trait: -------------------------------------- ``aws.iam#defineConditionKeys`` trait -------------------------------------- +===================================== Summary Defines the set of condition keys that appear within a service in @@ -174,7 +446,7 @@ Each condition key structure supports the following members: .. _condition-key-types: Condition Key Types -=================== +------------------- The following table describes the available types a condition key can have. Condition keys in IAM policies can be evaluated with `condition operators`_. @@ -215,12 +487,64 @@ Condition keys in IAM policies can be evaluated with `condition operators`_. - An unordered list of String types. +.. smithy-trait:: aws.iam#conditionKeys +.. _aws.iam#conditionKeys-trait: + +``aws.iam#conditionKeys`` trait +=============================== + +Summary + Applies condition keys, by name, to a resource or operation. +Trait selector + ``:test(resource, operation)`` +Value type + ``list`` + +Condition keys derived automatically can be applied to a resource or operation +explicitly. Condition keys applied this way MUST be either inferred or +explicitly defined via the :ref:`aws.iam#defineConditionKeys-trait` trait. + +The following example's ``MyResource`` resource has the +``myservice:MyResourceFoo`` and ``otherservice:Bar`` condition keys. The +``MyOperation`` operation has the ``aws:region`` condition key. + +.. code-block:: smithy + + $version: "2" + + namespace smithy.example + + use aws.api#service + use aws.iam#definedContextKeys + use aws.iam#conditionKeys + + @service(sdkId: "My Value", arnNamespace: "myservice") + @defineConditionKeys("otherservice:Bar": { type: "String" }) + service MyService { + version: "2017-02-11" + resources: [MyResource] + } + + @conditionKeys(["otherservice:Bar"]) + resource MyResource { + identifiers: {foo: String} + operations: [MyOperation] + } + + @conditionKeys(["aws:region"]) + operation MyOperation {} + +.. note:: + + Condition keys that refer to global ``"aws:*"`` keys can be referenced + without being defined on the service. + + .. smithy-trait:: aws.iam#disableConditionKeyInference .. _aws.iam#disableConditionKeyInference-trait: ----------------------------------------------- ``aws.iam#disableConditionKeyInference`` trait ----------------------------------------------- +============================================== Summary Declares that the condition keys of a resource should not be inferred. @@ -296,195 +620,11 @@ condition key inference disabled. } } -.. smithy-trait:: aws.iam#requiredActions -.. _aws.iam#requiredActions-trait: - ---------------------------------- -``aws.iam#requiredActions`` trait ---------------------------------- - -Summary - Other actions that the invoker must be authorized to perform when - executing the targeted operation. -Trait selector - ``operation`` -Value type - ``list`` where each string value references condition keys - defined in the closure of the service. - -Defines the actions, in addition to the targeted operation, that a user must -be authorized to execute in order invoke an operation. The following example -indicates that, in order to invoke the ``MyOperation`` operation, the invoker -must also be authorized to execute the ``otherservice:OtherOperation`` -operation for it to complete successfully. - -.. code-block:: smithy - - $version: "2" - - namespace smithy.example - - use aws.api#service - use aws.iam#requiredActions - - @service(sdkId: "My Value", arnNamespace: "myservice") - service MyService { - version: "2017-02-11" - resources: [MyResource] - } - - resource MyResource { - identifiers: {foo: String} - operations: [MyOperation] - } - - @requiredActions(["otherservice:OtherOperation"]) - operation MyOperation {} - -.. smithy-trait:: aws.iam#supportedPrincipalTypes -.. _aws.iam#supportedPrincipalTypes-trait: - ------------------------------------------ -``aws.iam#supportedPrincipalTypes`` trait ------------------------------------------ - -Summary - The `IAM principal types`_ that can use the service or operation. -Trait selector - ``:test(service, operation)`` -Value type - ``list`` where each string is an IAM principal type: ``Root``, - ``IAMUser``, ``IAMRole``, or ``FederatedUser``. - -Operations that are not annotated with the ``supportedPrincipalTypes`` trait -inherit the ``supportedPrincipalTypes`` of the service they are bound to. - -The following example defines two operations: - -* OperationA defines an explicit list of the IAM principal types it supports - using the ``supportedPrincipalTypes`` trait. -* OperationB is not annotated with the ``supportedPrincipalTypes`` trait, so - the IAM principal types supported by this operation are the principal types - applied to the service. - -.. code-block:: smithy - - $version: "2" - - namespace smithy.example - - use aws.iam#supportedPrincipalTypes - - @supportedPrincipalTypes(["Root", "IAMUser", "IAMRole", "FederatedUser"]) - service MyService { - version: "2020-07-02" - operations: [OperationA, OperationB] - } - - @supportedPrincipalTypes(["Root"]) - operation OperationA {} - - operation OperationB {} - - -.. smithy-trait:: aws.iam#iamResource -.. _aws.iam#iamResource-trait: - ------------------------------ -``aws.iam#iamResource`` trait ------------------------------ - -Summary - Indicates properties of a Smithy resource in AWS IAM. -Trait selector - ``resource`` -Value type - ``structure`` - -The ``aws.iam#iamResource`` trait is a structure that supports the following -members: - -.. list-table:: - :header-rows: 1 - :widths: 10 20 70 - - * - Property - - Type - - Description - * - name - - ``string`` - - The name of the resource in AWS IAM. - * - relativeDocumentation - - ``string`` - - A relative URL path that defines more information about the resource - within a set of IAM-related documentation. - -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. - -.. code-block:: smithy - - $version: "2" - - namespace smithy.example - - use aws.iam#iamResource - - @iamResource(name: "super") - resource SuperResource { - identifiers: { - superId: String, - } - } - -.. smithy-trait:: aws.iam#actionName -.. _aws.iam#actionName-trait: - ----------------------------- -``aws.iam#actionName`` trait ----------------------------- - -Summary - Provides a custom IAM action name. -Trait selector - ``operation`` -Value type - ``string`` - -Operations not annotated with the ``actionName`` trait, default to the -:ref:`shape name of the shape ID ` of the targeted operation. - -The following example defines two operations: - -* ``OperationA`` is not annotated with the ``actionName`` trait, and - resolves the action name of ``OperationA``. -* ``OperationB`` has the ``actionName`` trait, so has the action - name ``OverridingActionName``. - -.. code-block:: smithy - - $version: "2" - - namespace smithy.example - - use aws.iam#actionName - - service MyService { - version: "2020-07-02" - operations: [OperationA, OperationB] - } - - operation OperationA {} - - @actionName("OverridingActionName") - operation OperationB {} - .. smithy-trait:: aws.iam#serviceResolvedConditionKeys .. _aws.iam#serviceResolvedConditionKeys-trait: ----------------------------------------------- ``aws.iam#serviceResolvedConditionKeys`` trait ----------------------------------------------- +============================================== Summary Specifies the list of IAM condition keys which must be resolved by the @@ -524,9 +664,8 @@ The following example defines two service-specific condition keys: .. smithy-trait:: aws.iam#conditionKeyValue .. _aws.iam#conditionKeyValue-trait: ------------------------------------ ``aws.iam#conditionKeyValue`` trait ------------------------------------ +=================================== Summary Uses the associated member’s value for the specified condition key. @@ -572,9 +711,8 @@ explicitly binds ``ActionContextKey1`` to the field ``key``. .. _deriving-condition-keys: ------------------------ Deriving Condition Keys ------------------------ +======================= Smithy will automatically derive condition key information for a service, as well as its resources and operations. diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResource.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResource.java new file mode 100644 index 00000000000..9c9708e5a2c --- /dev/null +++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResource.java @@ -0,0 +1,112 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.aws.iam.traits; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import software.amazon.smithy.model.node.ArrayNode; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.node.StringNode; +import software.amazon.smithy.model.node.ToNode; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Contains information about a resource an IAM action can be authorized against. + */ +public final class ActionResource implements ToNode, ToSmithyBuilder { + private static final String CONDITION_KEYS = "conditionKeys"; + + private final List conditionKeys; + + private ActionResource(Builder builder) { + this.conditionKeys = builder.conditionKeys.copy(); + } + + /** + * Gets the condition keys used for authorizing against this resource. + * + * @return the condition keys. + */ + public List getConditionKeys() { + return conditionKeys; + } + + public static Builder builder() { + return new Builder(); + } + + public static ActionResource fromNode(Node value) { + Builder builder = builder(); + value.expectObjectNode() + .warnIfAdditionalProperties(Collections.singletonList(CONDITION_KEYS)) + .getArrayMember(CONDITION_KEYS, StringNode::getValue, builder::conditionKeys); + return builder.build(); + } + + @Override + public Node toNode() { + ObjectNode.Builder builder = Node.objectNodeBuilder(); + if (!conditionKeys.isEmpty()) { + builder.withMember(CONDITION_KEYS, ArrayNode.fromStrings(conditionKeys)); + } + return builder.build(); + } + + @Override + public SmithyBuilder toBuilder() { + return builder().conditionKeys(conditionKeys); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + ActionResource that = (ActionResource) o; + return Objects.equals(conditionKeys, that.conditionKeys); + } + + @Override + public int hashCode() { + return Objects.hash(conditionKeys); + } + + public static final class Builder implements SmithyBuilder { + private final BuilderRef> conditionKeys = BuilderRef.forList(); + + @Override + public ActionResource build() { + return new ActionResource(this); + } + + public Builder conditionKeys(List conditionKeys) { + clearConditionKeys(); + this.conditionKeys.get().addAll(conditionKeys); + return this; + } + + public Builder clearConditionKeys() { + conditionKeys.get().clear(); + return this; + } + + public Builder addConditionKey(String conditionKey) { + conditionKeys.get().add(conditionKey); + return this; + } + + public Builder removeConditionKey(String conditionKey) { + conditionKeys.get().remove(conditionKey); + return this; + } + } +} diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResources.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResources.java new file mode 100644 index 00000000000..4c3f79dbb1c --- /dev/null +++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ActionResources.java @@ -0,0 +1,165 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.aws.iam.traits; + +import java.util.Map; +import java.util.Objects; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.node.ToNode; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.ListUtils; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Contains information about the resources an IAM action can be authorized against. + */ +public final class ActionResources implements ToNode, ToSmithyBuilder { + private static final String REQUIRED = "required"; + private static final String OPTIONAL = "optional"; + + private final Map required; + private final Map optional; + + private ActionResources(Builder builder) { + required = builder.required.copy(); + optional = builder.optional.copy(); + } + + /** + * Gets the resources that will always be authorized against for + * functionality of the IAM action. + * + * @return the required resources. + */ + public Map getRequired() { + return required; + } + + /** + * Gets the resources that will be authorized against based on + * optional behavior of the IAM action. + * + * @return the optional resources. + */ + public Map getOptional() { + return optional; + } + + private static Builder builder() { + return new Builder(); + } + + public static ActionResources fromNode(Node value) { + Builder builder = builder(); + ObjectNode node = value.expectObjectNode() + .warnIfAdditionalProperties(ListUtils.of(REQUIRED, OPTIONAL)); + if (node.containsMember(REQUIRED)) { + for (Map.Entry entry : node.expectObjectMember(REQUIRED).getStringMap().entrySet()) { + builder.putRequired(entry.getKey(), ActionResource.fromNode(entry.getValue())); + } + } + if (node.containsMember(OPTIONAL)) { + for (Map.Entry entry : node.expectObjectMember(OPTIONAL).getStringMap().entrySet()) { + builder.putOptional(entry.getKey(), ActionResource.fromNode(entry.getValue())); + } + } + return builder.build(); + } + + @Override + public Node toNode() { + ObjectNode.Builder builder = Node.objectNodeBuilder(); + if (!required.isEmpty()) { + ObjectNode.Builder requiredBuilder = Node.objectNodeBuilder(); + for (Map.Entry requiredEntry : required.entrySet()) { + requiredBuilder.withMember(requiredEntry.getKey(), requiredEntry.getValue().toNode()); + } + } + if (!optional.isEmpty()) { + ObjectNode.Builder optionalBuilder = Node.objectNodeBuilder(); + for (Map.Entry optionalEntry : optional.entrySet()) { + optionalBuilder.withMember(optionalEntry.getKey(), optionalEntry.getValue().toNode()); + } + } + return builder.build(); + } + + @Override + public SmithyBuilder toBuilder() { + return builder().required(required).optional(optional); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + ActionResources that = (ActionResources) o; + return Objects.equals(required, that.required) + && Objects.equals(optional, that.optional); + } + + @Override + public int hashCode() { + return Objects.hash(required, optional); + } + + public static final class Builder implements SmithyBuilder { + private final BuilderRef> required = BuilderRef.forOrderedMap(); + private final BuilderRef> optional = BuilderRef.forOrderedMap(); + + @Override + public ActionResources build() { + return new ActionResources(this); + } + + public Builder clearRequired() { + required.get().clear(); + return this; + } + + public Builder required(Map required) { + clearRequired(); + this.required.get().putAll(required); + return this; + } + + public Builder putRequired(String resourceName, ActionResource actionResource) { + required.get().put(resourceName, actionResource); + return this; + } + + public Builder removeRequired(String resourceName) { + required.get().remove(resourceName); + return this; + } + + public Builder clearOptional() { + optional.get().clear(); + return this; + } + + public Builder optional(Map optional) { + clearOptional(); + this.optional.get().putAll(optional); + return this; + } + + public Builder putOptional(String resourceName, ActionResource actionResource) { + optional.get().put(resourceName, actionResource); + return this; + } + + public Builder removeOptional(String resourceName) { + optional.get().remove(resourceName); + return this; + } + } +} diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionTrait.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionTrait.java new file mode 100644 index 00000000000..67377e186f3 --- /dev/null +++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionTrait.java @@ -0,0 +1,218 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.iam.traits; + +import java.util.List; +import java.util.Optional; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.NodeMapper; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AbstractTrait; +import software.amazon.smithy.model.traits.AbstractTraitBuilder; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Indicates properties of a Smithy operation as an IAM action. + */ +public final class IamActionTrait extends AbstractTrait + implements ToSmithyBuilder { + public static final ShapeId ID = ShapeId.from("aws.iam#iamAction"); + + private final String name; + private final String documentation; + private final String relativeDocumentation; + private final List requiredActions; + private final ActionResources resources; + private final List createsResources; + + private IamActionTrait(Builder builder) { + super(ID, builder.getSourceLocation()); + name = builder.name; + documentation = builder.documentation; + relativeDocumentation = builder.relativeDocumentation; + requiredActions = builder.requiredActions.copy(); + resources = builder.resources; + createsResources = builder.createsResources.copy(); + } + + /** + * Get the AWS IAM resource name. + * + * @return Returns the name. + */ + public Optional getName() { + return Optional.ofNullable(name); + } + + /** + * Gets the description of what granting the user permission to + * invoke an operation would entail. + * + * @return Returns the documentation. + */ + public Optional getDocumentation() { + return Optional.ofNullable(documentation); + } + + /** + * Gets the relative URL path for the action within a set of + * IAM-related documentation. + * + * @return Returns the relative URL path to documentation. + */ + public Optional getRelativeDocumentation() { + return Optional.ofNullable(relativeDocumentation); + } + + /** + * Gets other actions that the invoker must be authorized to + * perform when executing the targeted operation. + * + * @return Returns the list of required actions. + */ + public List getRequiredActions() { + return requiredActions; + } + + /** + * Gets the resources an IAM action can be authorized against. + * + * @return Returns the action's resources. + */ + public Optional getResources() { + return Optional.ofNullable(resources); + } + + /** + * Gets the resources that performing this IAM action will create. + * + * @return Returns the resources created by the action. + */ + public List getCreatesResources() { + return createsResources; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + protected Node createNode() { + NodeMapper mapper = new NodeMapper(); + mapper.disableToNodeForClass(IamActionTrait.class); + mapper.setOmitEmptyValues(true); + return mapper.serialize(this).expectObjectNode(); + } + + @Override + public SmithyBuilder toBuilder() { + return builder().sourceLocation(getSourceLocation()).name(name); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public Trait createTrait(ShapeId target, Node value) { + IamActionTrait result = new NodeMapper().deserialize(value, IamActionTrait.class); + result.setNodeCache(value); + return result; + } + } + + public static final class Builder extends AbstractTraitBuilder { + private String name; + private String documentation; + private String relativeDocumentation; + private final BuilderRef> requiredActions = BuilderRef.forList(); + private ActionResources resources; + private final BuilderRef> createsResources = BuilderRef.forList(); + + private Builder() {} + + @Override + public IamActionTrait build() { + return new IamActionTrait(this); + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder documentation(String documentation) { + this.documentation = documentation; + return this; + } + + public Builder relativeDocumentation(String relativeDocumentation) { + this.relativeDocumentation = relativeDocumentation; + return this; + } + + public Builder requiredActions(List requiredActions) { + clearRequiredActions(); + this.requiredActions.get().addAll(requiredActions); + return this; + } + + public Builder clearRequiredActions() { + requiredActions.get().clear(); + return this; + } + + public Builder addRequiredAction(String requiredAction) { + requiredActions.get().add(requiredAction); + return this; + } + + public Builder removeRequiredAction(String requiredAction) { + requiredActions.get().remove(requiredAction); + return this; + } + + public Builder resources(ActionResources resources) { + this.resources = resources; + return this; + } + + public Builder createsResources(List createsResources) { + clearCreatesResources(); + this.createsResources.get().addAll(createsResources); + return this; + } + + public Builder clearCreatesResources() { + createsResources.get().clear(); + return this; + } + + public Builder addCreatesResource(String createsResource) { + createsResources.get().add(createsResource); + return this; + } + + public Builder removeCreatesResource(String createsResource) { + createsResources.get().remove(createsResource); + return this; + } + } +} diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionValidator.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionValidator.java new file mode 100644 index 00000000000..b04a2a26889 --- /dev/null +++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamActionValidator.java @@ -0,0 +1,80 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.aws.iam.traits; + +import static java.lang.String.format; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.model.validation.AbstractValidator; +import software.amazon.smithy.model.validation.ValidationEvent; +import software.amazon.smithy.utils.SmithyInternalApi; + +@SmithyInternalApi +public final class IamActionValidator extends AbstractValidator { + @Override + public List validate(Model model) { + List events = new ArrayList<>(); + for (OperationShape operation : model.getOperationShapesWithTrait(IamActionTrait.class)) { + IamActionTrait trait = operation.expectTrait(IamActionTrait.class); + events.addAll(validateDuplicateTraits(operation, trait)); + validateUniqueResourceNames(operation, trait).ifPresent(events::add); + } + return events; + } + + private List validateDuplicateTraits(OperationShape operation, IamActionTrait trait) { + List events = new ArrayList<>(); + if (operation.hasTrait(ActionNameTrait.ID) && trait.getName().isPresent()) { + events.add(emitDeprecatedOverride(operation, + operation.expectTrait(ActionNameTrait.class), + "name")); + } + + if (operation.hasTrait(ActionPermissionDescriptionTrait.ID) && trait.getDocumentation().isPresent()) { + events.add(emitDeprecatedOverride(operation, + operation.expectTrait(ActionPermissionDescriptionTrait.class), + "documentation")); + } + + if (operation.hasTrait(RequiredActionsTrait.ID) && !trait.getRequiredActions().isEmpty()) { + events.add(emitDeprecatedOverride(operation, + operation.expectTrait(RequiredActionsTrait.class), + "requiredActions")); + } + return events; + } + + private ValidationEvent emitDeprecatedOverride(OperationShape operation, Trait trait, String name) { + return error(operation, trait, format("Operation has the `%s` property of the " + + "`@aws.iam#iamAction` trait set and the deprecated `@%s` trait applied.", + name, trait.toShapeId()), "ConflictingProperty", name); + } + + private Optional validateUniqueResourceNames(OperationShape operation, IamActionTrait trait) { + if (!trait.getResources().isPresent() + || trait.getResources().get().getRequired().isEmpty() + || trait.getResources().get().getOptional().isEmpty() + ) { + return Optional.empty(); + } + + Set duplicateNames = new LinkedHashSet<>(trait.getResources().get().getRequired().keySet()); + duplicateNames.retainAll(trait.getResources().get().getOptional().keySet()); + if (duplicateNames.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(danger(operation, trait, "Operation has the following resource names defined as both " + + "required and optional: " + duplicateNames, "Resources", "DuplicateEntries")); + } +} diff --git a/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index 2853cf73ca0..7dfe76966af 100644 --- a/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -4,6 +4,7 @@ software.amazon.smithy.aws.iam.traits.DefineConditionKeysTrait$Provider software.amazon.smithy.aws.iam.traits.DisableConditionKeyInferenceTrait$Provider software.amazon.smithy.aws.iam.traits.RequiredActionsTrait$Provider software.amazon.smithy.aws.iam.traits.SupportedPrincipalTypesTrait$Provider +software.amazon.smithy.aws.iam.traits.IamActionTrait$Provider software.amazon.smithy.aws.iam.traits.IamResourceTrait$Provider software.amazon.smithy.aws.iam.traits.ActionNameTrait$Provider software.amazon.smithy.aws.iam.traits.ServiceResolvedConditionKeysTrait$Provider diff --git a/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator b/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator index d03291f5128..856e5dbe68a 100644 --- a/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator +++ b/smithy-aws-iam-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator @@ -1,2 +1,3 @@ software.amazon.smithy.aws.iam.traits.ConditionKeysValidator +software.amazon.smithy.aws.iam.traits.IamActionValidator software.amazon.smithy.aws.iam.traits.IamResourceTraitValidator 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 ae2ca3b5eed..47fd142938e 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 @@ -3,11 +3,13 @@ $version: "2.0" namespace aws.iam /// Provides a custom IAM action name. By default, the action name is the same as the operation name. +@deprecated(since: "2023-11-10", message: "Use the `name` member of the `aws.iam#iamAction` trait instead.") @trait(selector: "operation") string actionName /// A brief description of what granting the user permission to invoke an operation would entail. /// This description should begin with something similar to 'Enables the user to...' or 'Grants permission to...' +@deprecated(since: "2023-11-10", message: "Use the `documentation` member of the `aws.iam#iamAction` trait instead.") @trait(selector: "operation") string actionPermissionDescription @@ -34,6 +36,29 @@ map defineConditionKeys { @trait(selector: ":test(service, resource)") structure disableConditionKeyInference {} +/// Indicates properties of a Smithy operation as an IAM action. +@trait(selector: "operation") +structure iamAction { + /// The name of the action in AWS IAM. + name: String + + /// A brief description of what granting the user permission to invoke an operation would entail. + /// This description should begin with something similar to 'Enables the user to...' or 'Grants permission to...' + documentation: String + + /// A relative URL path that defines more information about the action within a set of IAM-related documentation. + relativeDocumentation: String + + /// Other actions that the invoker must be authorized to perform when executing the targeted operation. + requiredActions: RequiredActionsList + + /// The resources an IAM action can be authorized against. + resources: ActionResources + + /// The resources that performing this IAM action will create. + createsResources: ResourceNameList +} + /// Indicates properties of a Smithy resource in AWS IAM. @trait(selector: "resource") structure iamResource { @@ -46,6 +71,7 @@ structure iamResource { } /// Other actions that the invoker must be authorized to perform when executing the targeted operation. +@deprecated(since: "2023-11-10", message: "Use the `requiredActions` member of the `aws.iam#iamAction` trait instead.") @trait(selector: "operation") list requiredActions { member: IamIdentifier @@ -64,6 +90,16 @@ list supportedPrincipalTypes { member: PrincipalType } +/// A container for information on the resources that an IAM action may be authorized against. +@private +structure ActionResources { + /// Resources that will always be authorized against for functionality of the IAM action. + required: ActionResourceMap + + /// Resources that will be authorized against based on optional behavior of the IAM action. + optional: ActionResourceMap +} + /// A defined condition key to appear within a service in addition to inferred and global condition keys. @private structure ConditionKeyDefinition { @@ -81,6 +117,37 @@ structure ConditionKeyDefinition { relativeDocumentation: String } +/// Contains information about a resource an IAM action can be authorized against. +@private +structure ActionResource { + /// The condition keys used for authorizing against this resource. + conditionKeys: ConditionKeysList +} + +@private +map ActionResourceMap { + key: ResourceName + value: ActionResource +} + +@private +@uniqueItems +list ConditionKeysList { + member: String +} + +@private +@uniqueItems +list RequiredActionsList { + member: IamIdentifier +} + +@private +@uniqueItems +list ResourceNameList { + member: ResourceName +} + /// The IAM policy type of the value that will supplied for this context key @private enum ConditionKeyType { @@ -131,6 +198,9 @@ enum ConditionKeyType { @private string IamIdentifier +@private +string ResourceName + /// An IAM policy principal type. @private enum PrincipalType { diff --git a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamActionTraitTest.java b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamActionTraitTest.java new file mode 100644 index 00000000000..e0cad6b3e78 --- /dev/null +++ b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamActionTraitTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.iam.traits; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; + +public class IamActionTraitTest { + @Test + public void loadsFromModel() { + Model result = Model.assembler() + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("iam-action.smithy")) + .assemble() + .unwrap(); + + Shape fooOperation = result.expectShape(ShapeId.from("smithy.example#Foo")); + + assertTrue(fooOperation.hasTrait(IamActionTrait.class)); + IamActionTrait trait = fooOperation.expectTrait(IamActionTrait.class); + assertEquals(trait.getName().get(), "foo"); + assertEquals(trait.getDocumentation().get(), "docs"); + assertEquals(trait.getRelativeDocumentation().get(), "page.html#actions"); + assertThat(trait.getRequiredActions(), contains("iam:PassRole", "ec2:RunInstances")); + assertThat(trait.getCreatesResources(), contains("kettle")); + + assertTrue(trait.getResources().isPresent()); + ActionResources actionResources = trait.getResources().get(); + assertTrue(actionResources.getRequired().containsKey("bar")); + assertThat(actionResources.getRequired().get("bar").getConditionKeys(), contains("foo:asdf")); + assertTrue(actionResources.getRequired().containsKey("bap")); + assertThat(actionResources.getRequired().get("bap").getConditionKeys(), contains("foo:zxcv", "foo:hjkl")); + assertTrue(actionResources.getOptional().containsKey("baz")); + assertTrue(actionResources.getOptional().get("baz").getConditionKeys().isEmpty()); + } +} diff --git a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/SmithyErrorFilesTest.java b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/SmithyErrorFilesTest.java deleted file mode 100644 index 67bf4d3b45a..00000000000 --- a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/SmithyErrorFilesTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package software.amazon.smithy.aws.iam.traits; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import software.amazon.smithy.model.validation.testrunner.SmithyTestCase; -import software.amazon.smithy.model.validation.testrunner.SmithyTestSuite; - -import java.util.concurrent.Callable; -import java.util.stream.Stream; - -public class SmithyErrorFilesTest { - @ParameterizedTest(name = "{0}") - @MethodSource("source") - public void testRunner(String filename, Callable callable) throws Exception { - callable.call(); - } - - public static Stream source() { - return SmithyTestSuite.defaultParameterizedTestSource(SmithyErrorFilesTest.class); - } -} diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.errors b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.errors new file mode 100644 index 00000000000..51dfda99edf --- /dev/null +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.errors @@ -0,0 +1,3 @@ +[ERROR] smithy.example#Foo: Operation has the `name` property of the `@aws.iam#iamAction` trait set and the deprecated `@aws.iam#actionName` trait applied. | IamAction.ConflictingProperty.name +[ERROR] smithy.example#Foo: Operation has the `documentation` property of the `@aws.iam#iamAction` trait set and the deprecated `@aws.iam#actionPermissionDescription` trait applied. | IamAction.ConflictingProperty.documentation +[ERROR] smithy.example#Foo: Operation has the `requiredActions` property of the `@aws.iam#iamAction` trait set and the deprecated `@aws.iam#requiredActions` trait applied. | IamAction.ConflictingProperty.requiredActions diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.smithy new file mode 100644 index 00000000000..f22f066b986 --- /dev/null +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-conflicts.smithy @@ -0,0 +1,18 @@ +$version: "2.0" + +namespace smithy.example + +use aws.iam#actionName +use aws.iam#actionPermissionDescription +use aws.iam#iamAction +use aws.iam#requiredActions + +@iamAction( + name: "foo" + documentation: "docs" + requiredActions: ["foo:Bar"] +) +@actionName("foo") +@actionPermissionDescription("docs") +@requiredActions(["foo:Bar"]) +operation Foo {} diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.errors b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.errors new file mode 100644 index 00000000000..e69de29bb2d diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.smithy new file mode 100644 index 00000000000..4d63061516e --- /dev/null +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-detachment.smithy @@ -0,0 +1,23 @@ +$version: "2.0" + +namespace smithy.example + +use aws.iam#iamAction + +resource Monitor { + resources: [HealthEvent] +} + +resource HealthEvent { + read: GetHealthEvent +} + +@iamAction( + resources: { + required: { + "HealthEvent": {} + } + } +) +@readonly +operation GetHealthEvent {} diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.errors b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.errors new file mode 100644 index 00000000000..7473735a040 --- /dev/null +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.errors @@ -0,0 +1 @@ +[DANGER] smithy.example#Foo: Operation has the following resource names defined as both required and optional: [bap] | IamAction.Resources.DuplicateEntries diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.smithy new file mode 100644 index 00000000000..d83a54be105 --- /dev/null +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/iam-action-resource-duplicates.smithy @@ -0,0 +1,27 @@ +$version: "2.0" + +namespace smithy.example + +use aws.iam#iamAction + +@iamAction( + resources: { + required: { + "bar": { + conditionKeys: ["foo:asdf"] + } + "bap": { + conditionKeys: ["foo:zxcv"] + } + } + optional: { + "baz": { + conditionKeys: ["foo:qwer"] + } + "bap": { + conditionKeys: ["foo:zxcv"] + } + } + } +) +operation Foo {} diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-action.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-action.smithy new file mode 100644 index 00000000000..b52ac3f6fba --- /dev/null +++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-action.smithy @@ -0,0 +1,26 @@ +$version: "2.0" + +namespace smithy.example + +use aws.iam#iamAction + +@iamAction(name: "foo" + documentation: "docs" + relativeDocumentation: "page.html#actions" + requiredActions: ["iam:PassRole", "ec2:RunInstances"] + resources: { + required: { + "bar": { + conditionKeys: ["foo:asdf"] + } + "bap": { + conditionKeys: ["foo:zxcv", "foo:hjkl"] + } + } + optional: { + "baz": {} + } + } + createsResources: ["kettle"] +) +operation Foo {}