diff --git a/.brazil.json b/.brazil.json
index 1f0931d0747b..bce97f55c9f9 100644
--- a/.brazil.json
+++ b/.brazil.json
@@ -18,6 +18,7 @@
"codegen": { "packageName": "AwsJavaSdk-Codegen" },
"dynamodb-enhanced": { "packageName": "AwsJavaSdk-DynamoDb-Enhanced" },
"http-client-spi": { "packageName": "AwsJavaSdk-HttpClient" },
+ "iam-policy-builder": { "packageName": "AwsJavaSdk-Iam-PolicyBuilder" },
"json-utils": { "packageName": "AwsJavaSdk-Core-JsonUtils" },
"metrics-spi": { "packageName": "AwsJavaSdk-Core-MetricsSpi" },
"endpoints-spi": { "packageName": "AwsJavaSdk-Core-EndpointsSpi" },
diff --git a/.changes/next-release/feature-AWSSDKforJavav2-a3a039b.json b/.changes/next-release/feature-AWSSDKforJavav2-a3a039b.json
new file mode 100644
index 000000000000..d508c5b55f2a
--- /dev/null
+++ b/.changes/next-release/feature-AWSSDKforJavav2-a3a039b.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Added support for IamPolicy in new module software.amazon.awssdk:iam-policy-builder, a class to simplify the use of AWS policies."
+}
diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotNull.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotNull.java
new file mode 100644
index 000000000000..eb101d7b7cb0
--- /dev/null
+++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotNull.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.awssdk.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The annotated element must not be null. Accepts any type.
+ *
+ * This is useful to tell linting and testing tools that a particular value will never be null. It's not meant to be used on
+ * public interfaces as something that customers should rely on.
+ */
+@Documented
+@Target({ElementType.METHOD,
+ ElementType.FIELD,
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.PARAMETER})
+@Retention(RetentionPolicy.CLASS)
+@SdkProtectedApi
+public @interface NotNull {
+}
diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonWriter.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonWriter.java
index a36d6efc2efe..a7b6a89d0e9f 100644
--- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonWriter.java
+++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonWriter.java
@@ -20,6 +20,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
@@ -36,17 +37,17 @@
*/
@SdkProtectedApi
public class JsonWriter implements SdkAutoCloseable {
-
private static final int DEFAULT_BUFFER_SIZE = 1024;
- private final JsonFactory jsonFactory;
private final ByteArrayOutputStream baos;
private final JsonGenerator generator;
private JsonWriter(Builder builder) {
- jsonFactory = builder.jsonFactory != null ? builder.jsonFactory : DEFAULT_JSON_FACTORY;
+ JsonGeneratorFactory jsonGeneratorFactory = builder.jsonGeneratorFactory != null
+ ? builder.jsonGeneratorFactory
+ : DEFAULT_JSON_FACTORY::createGenerator;
try {
baos = new ByteArrayOutputStream(DEFAULT_BUFFER_SIZE);
- generator = jsonFactory.createGenerator(baos);
+ generator = jsonGeneratorFactory.createGenerator(baos);
} catch (IOException e) {
throw new JsonGenerationException(e);
}
@@ -170,7 +171,7 @@ private JsonWriter unsafeWrite(FunctionalUtils.UnsafeRunnable r) {
* A builder for configuring and creating {@link JsonWriter}. Created via {@link #builder()}.
*/
public static final class Builder {
- private JsonFactory jsonFactory;
+ private JsonGeneratorFactory jsonGeneratorFactory;
private Builder() {
}
@@ -179,13 +180,31 @@ private Builder() {
* The {@link JsonFactory} implementation to be used when parsing the input. This allows JSON extensions like CBOR or
* Ion to be supported.
*
- *
It's highly recommended us use a shared {@code JsonFactory} where possible, so they should be stored statically:
+ *
It's highly recommended to use a shared {@code JsonFactory} where possible, so they should be stored statically:
* http://wiki.fasterxml.com/JacksonBestPracticesPerformance
*
*
By default, this is {@link JsonNodeParser#DEFAULT_JSON_FACTORY}.
+ *
+ *
Setting this value will also override any values set via {@link #jsonGeneratorFactory}.
*/
public JsonWriter.Builder jsonFactory(JsonFactory jsonFactory) {
- this.jsonFactory = jsonFactory;
+ jsonGeneratorFactory(jsonFactory::createGenerator);
+ return this;
+ }
+
+ /**
+ * A factory for {@link JsonGenerator}s based on an {@link OutputStream}. This allows custom JSON generator
+ * configuration, like pretty-printing output.
+ *
+ *
It's highly recommended to use a shared {@code JsonFactory} within this generator factory, where possible, so they
+ * should be stored statically: http://wiki.fasterxml.com/JacksonBestPracticesPerformance
+ *
+ *
By default, this delegates to {@link JsonNodeParser#DEFAULT_JSON_FACTORY} to create the generator.
+ *
+ *
Setting this value will also override any values set via {@link #jsonFactory}.
+ */
+ public JsonWriter.Builder jsonGeneratorFactory(JsonGeneratorFactory jsonGeneratorFactory) {
+ this.jsonGeneratorFactory = jsonGeneratorFactory;
return this;
}
@@ -197,6 +216,14 @@ public JsonWriter build() {
}
}
+ /**
+ * Generate a {@link JsonGenerator} for a {@link OutputStream}. This will get called once for each "write" call.
+ */
+ @FunctionalInterface
+ public interface JsonGeneratorFactory {
+ JsonGenerator createGenerator(OutputStream outputStream) throws IOException;
+ }
+
/**
* Indicates an issue writing JSON content.
*/
diff --git a/services-custom/iam-policy-builder/pom.xml b/services-custom/iam-policy-builder/pom.xml
new file mode 100644
index 000000000000..54a4327e06c7
--- /dev/null
+++ b/services-custom/iam-policy-builder/pom.xml
@@ -0,0 +1,134 @@
+
+
+
+
+ 4.0.0
+
+ software.amazon.awssdk
+ aws-sdk-java-pom
+ 2.20.104-SNAPSHOT
+ ../../pom.xml
+
+ iam-policy-builder
+ ${awsjavasdk.version}
+ AWS Java SDK :: IAM :: Policy Builder
+
+ Library simplifying the building, marshalling and unmarshalling of IAM Policies.
+
+ https://aws.amazon.com/sdkforjava
+
+
+ 1.8
+ ${project.parent.version}
+
+
+
+
+
+ software.amazon.awssdk
+ bom-internal
+ ${awsjavasdk.version}
+ pom
+ import
+
+
+
+
+
+
+ software.amazon.awssdk
+ utils
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ annotations
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ json-utils
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ third-party-jackson-core
+ ${awsjavasdk.version}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit5.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.apache.logging.log4j
+ log4j-api
+ test
+
+
+ org.apache.logging.log4j
+ log4j-core
+ test
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ test
+
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ software.amazon.awssdk.policybuilder.iam
+
+
+
+
+
+
+
+
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamAction.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamAction.java
new file mode 100644
index 000000000000..4c0d82d42acb
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamAction.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamAction;
+
+/**
+ * The {@code Action} element of a {@link IamStatement}, specifying which service actions the statement applies to.
+ *
+ * @see Action user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamAction extends IamValue {
+ /**
+ * An {@link IamAction} representing ALL actions. When used on a statement, it means the policy should apply to
+ * every action.
+ */
+ IamAction ALL = create("*");
+
+ /**
+ * Create a new {@code IamAction} element with the provided {@link #value()}.
+ */
+ static IamAction create(String value) {
+ return new DefaultIamAction(value);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamCondition.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamCondition.java
new file mode 100644
index 000000000000..5db7a752850d
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamCondition.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static java.util.Collections.emptyList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamCondition;
+import software.amazon.awssdk.utils.builder.CopyableBuilder;
+import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
+
+/**
+ * The {@code Condition} element of a {@link IamStatement}, specifying the conditions in which the statement is in effect.
+ *
+ * @see
+ * Condition user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamCondition extends ToCopyableBuilder {
+ /**
+ * Create an {@link IamCondition} of the supplied operator, key and value (see
+ * {@link Builder#operator(IamConditionOperator)}}, {@link Builder#key(IamConditionKey)} and {@link Builder#value(String)}).
+ *
+ * All of operator, key and value are required. This is equivalent to {@code IamCondition.builder().operator(operator)
+ * .key(key).value(value).build()}.
+ */
+ static IamCondition create(IamConditionOperator operator, IamConditionKey key, String value) {
+ return builder().operator(operator).key(key).value(value).build();
+ }
+
+ /**
+ * Create an {@link IamCondition} of the supplied operator, key and value (see
+ * {@link Builder#operator(IamConditionOperator)}}, {@link Builder#key(String)} and {@link Builder#value(String)}).
+ *
+ * All of operator, key and value are required. This is equivalent to {@code IamCondition.builder().operator(operator)
+ * .key(key).value(value).build()}.
+ */
+ static IamCondition create(IamConditionOperator operator, String key, String value) {
+ return builder().operator(operator).key(key).value(value).build();
+ }
+
+ /**
+ * Create an {@link IamCondition} of the supplied operator, key and value (see
+ * {@link Builder#operator(String)}}, {@link Builder#key(String)} and {@link Builder#value(String)}).
+ *
+ * All of operator, key and value are required. This is equivalent to {@code IamCondition.builder().operator(operator)
+ * .key(key).value(value).build()}.
+ */
+ static IamCondition create(String operator, String key, String value) {
+ return builder().operator(operator).key(key).value(value).build();
+ }
+
+ /**
+ * Create multiple {@link IamCondition}s with the same {@link IamConditionOperator} and {@link IamConditionKey}, but
+ * different values (see {@link Builder#operator(IamConditionOperator)}}, {@link Builder#key(IamConditionKey)} and
+ * {@link Builder#value(String)}).
+ *
+ * Operator and key are required, and the values in the value list must not be null. This is equivalent to calling
+ * {@link #create(IamConditionOperator, IamConditionKey, String)} multiple times and collecting the results into a list.
+ */
+ static List createAll(IamConditionOperator operator, IamConditionKey key, Collection values) {
+ if (values == null) {
+ return emptyList();
+ }
+ return values.stream().map(value -> create(operator, key, value)).collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ /**
+ * Create multiple {@link IamCondition}s with the same {@link IamConditionOperator} and {@link IamConditionKey}, but
+ * different values (see {@link Builder#operator(IamConditionOperator)}}, {@link Builder#key(String)} and
+ * {@link Builder#value(String)}).
+ *
+ * Operator and key are required, and the values in the value list must not be null. This is equivalent to calling
+ * {@link #create(IamConditionOperator, String, String)} multiple times and collecting the results into a list.
+ */
+ static List createAll(IamConditionOperator operator, String key, Collection values) {
+ if (values == null) {
+ return emptyList();
+ }
+ return values.stream().map(value -> create(operator, key, value)).collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ /**
+ * Create multiple {@link IamCondition}s with the same {@link IamConditionOperator} and {@link IamConditionKey}, but
+ * different values (see {@link Builder#operator(String)}}, {@link Builder#key(String)} and {@link Builder#value(String)}).
+ *
+ * Operator and key are required, and the values in the value list must not be null. This is equivalent to calling
+ * {@link #create(String, String, String)} multiple times and collecting the results into a list.
+ */
+ static List createAll(String operator, String key, Collection values) {
+ if (values == null) {
+ return emptyList();
+ }
+
+ return values.stream().map(value -> create(operator, key, value)).collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ /**
+ * Create a {@link Builder} for an {@code IamCondition}.
+ */
+ static Builder builder() {
+ return DefaultIamCondition.builder();
+ }
+
+
+ /**
+ * Retrieve the value set by {@link Builder#operator(IamConditionOperator)}.
+ */
+ IamConditionOperator operator();
+
+ /**
+ * Retrieve the value set by {@link Builder#key(IamConditionKey)}.
+ */
+ IamConditionKey key();
+
+ /**
+ * Retrieve the value set by {@link Builder#value(String)}.
+ */
+ String value();
+
+ /**
+ * @see #builder()
+ */
+ interface Builder extends CopyableBuilder {
+ /**
+ * Set the {@link IamConditionOperator} of this condition.
+ *
+ * This value is required.
+ *
+ * @see IamConditionOperator
+ * @see Condition
+ * user guide
+ */
+ Builder operator(IamConditionOperator operator);
+
+ /**
+ * Set the {@link IamConditionOperator} of this condition.
+ *
+ * This is the same as {@link #operator(IamConditionOperator)}, except you do not need to call
+ * {@code IamConditionOperator.create()}. This value is required.
+ *
+ * @see IamConditionOperator
+ * @see Condition
+ * user guide
+ */
+ Builder operator(String operator);
+
+ /**
+ * Set the {@link IamConditionKey} of this condition.
+ *
+ * This value is required.
+ *
+ * @see IamConditionKey
+ * @see Condition
+ * user guide
+ */
+ Builder key(IamConditionKey key);
+
+ /**
+ * Set the {@link IamConditionKey} of this condition.
+ *
+ * This is the same as {@link #key(IamConditionKey)}, except you do not need to call
+ * {@code IamConditionKey.create()}. This value is required.
+ *
+ * @see IamConditionKey
+ * @see Condition
+ * user guide
+ */
+ Builder key(String key);
+
+ /**
+ * Set the "right hand side" value of this condition.
+ *
+ * @see Condition
+ * user guide
+ */
+ Builder value(String value);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamConditionKey.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamConditionKey.java
new file mode 100644
index 000000000000..b2b7a41c89d3
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamConditionKey.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamConditionKey;
+
+/**
+ * The {@code IamConditionKey} specifies the "left hand side" of an {@link IamCondition}.
+ *
+ * @see IamCondition
+ * @see Condition
+ * user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamConditionKey extends IamValue {
+ /**
+ * Create a new {@code IamConditionKey} element with the provided {@link #value()}.
+ */
+ static IamConditionKey create(String value) {
+ return new DefaultIamConditionKey(value);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamConditionOperator.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamConditionOperator.java
new file mode 100644
index 000000000000..02e77e81c321
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamConditionOperator.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamConditionOperator;
+
+/**
+ * The {@code IamConditionOperator} specifies the operator that should be applied to compare the {@link IamConditionKey} to an
+ * expected value in an {@link IamCondition}.
+ *
+ * @see IamCondition
+ * @see Condition
+ * user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamConditionOperator extends IamValue {
+ /**
+ * A string comparison of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * String conditions
+ */
+ IamConditionOperator STRING_EQUALS = create("StringEquals");
+
+ /**
+ * A negated string comparison of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * String conditions
+ */
+ IamConditionOperator STRING_NOT_EQUALS = create("StringNotEquals");
+
+ /**
+ * A string comparison, ignoring casing, of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * String conditions
+ */
+ IamConditionOperator STRING_EQUALS_IGNORE_CASE = create("StringEqualsIgnoreCase");
+
+ /**
+ * A negated string comparison, ignoring casing, of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * String conditions
+ */
+ IamConditionOperator STRING_NOT_EQUALS_IGNORE_CASE = create("StringNotEqualsIgnoreCase");
+
+ /**
+ * A case-sensitive pattern match between the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * String conditions
+ */
+ IamConditionOperator STRING_LIKE = create("StringLike");
+
+ /**
+ * A negated case-sensitive pattern match between the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * String conditions
+ */
+ IamConditionOperator STRING_NOT_LIKE = create("StringNotLike");
+
+ /**
+ * A numeric comparison of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Numeric conditions
+ */
+ IamConditionOperator NUMERIC_EQUALS = create("NumericEquals");
+
+ /**
+ * A negated numeric comparison of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Numeric conditions
+ */
+ IamConditionOperator NUMERIC_NOT_EQUALS = create("NumericNotEquals");
+
+ /**
+ * A numeric comparison of whether the {@link IamCondition#key()} is "less than" the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Numeric conditions
+ */
+ IamConditionOperator NUMERIC_LESS_THAN = create("NumericLessThan");
+
+ /**
+ * A numeric comparison of whether the {@link IamCondition#key()} is "less than or equal to" the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Numeric conditions
+ */
+ IamConditionOperator NUMERIC_LESS_THAN_EQUALS = create("NumericLessThanEquals");
+
+ /**
+ * A numeric comparison of whether the {@link IamCondition#key()} is "greater than" the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Numeric conditions
+ */
+ IamConditionOperator NUMERIC_GREATER_THAN = create("NumericGreaterThan");
+
+ /**
+ * A numeric comparison of whether the {@link IamCondition#key()} is "greater than or equal to" the
+ * {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Numeric conditions
+ */
+ IamConditionOperator NUMERIC_GREATER_THAN_EQUALS = create("NumericGreaterThanEquals");
+
+ /**
+ * A date comparison of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Date conditions
+ */
+ IamConditionOperator DATE_EQUALS = create("DateEquals");
+
+ /**
+ * A negated date comparison of the {@link IamCondition#key()} and {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Date conditions
+ */
+ IamConditionOperator DATE_NOT_EQUALS = create("DateNotEquals");
+
+ /**
+ * A date comparison of whether the {@link IamCondition#key()} "is earlier than" the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Date conditions
+ */
+ IamConditionOperator DATE_LESS_THAN = create("DateLessThan");
+
+ /**
+ * A date comparison of whether the {@link IamCondition#key()} "is earlier than or the same date as" the
+ * {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Date conditions
+ */
+ IamConditionOperator DATE_LESS_THAN_EQUALS = create("DateLessThanEquals");
+
+ /**
+ * A date comparison of whether the {@link IamCondition#key()} "is later than" the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Date conditions
+ */
+ IamConditionOperator DATE_GREATER_THAN = create("DateGreaterThan");
+
+ /**
+ * A date comparison of whether the {@link IamCondition#key()} "is later than or the same date as" the
+ * {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Date conditions
+ */
+ IamConditionOperator DATE_GREATER_THAN_EQUALS = create("DateGreaterThanEquals");
+
+ /**
+ * A boolean comparison of the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Boolean conditions
+ */
+ IamConditionOperator BOOL = create("Bool");
+
+ /**
+ * A binary comparison of the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * Binary conditions
+ */
+ IamConditionOperator BINARY_EQUALS = create("BinaryEquals");
+
+ /**
+ * An IP address comparison of the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * IP Address conditions
+ */
+ IamConditionOperator IP_ADDRESS = create("IpAddress");
+
+ /**
+ * A negated IP address comparison of the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * IP Address conditions
+ */
+ IamConditionOperator NOT_IP_ADDRESS = create("NotIpAddress");
+
+ /**
+ * An Amazon Resource Name (ARN) comparison of the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * ARN conditions
+ */
+ IamConditionOperator ARN_EQUALS = create("ArnEquals");
+
+ /**
+ * A negated Amazon Resource Name (ARN) comparison of the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * ARN conditions
+ */
+ IamConditionOperator ARN_NOT_EQUALS = create("ArnNotEquals");
+
+ /**
+ * A pattern match of the Amazon Resource Names (ARNs) in the {@link IamCondition#key()} and the {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * ARN conditions
+ */
+ IamConditionOperator ARN_LIKE = create("ArnLike");
+
+ /**
+ * A negated pattern match of the Amazon Resource Names (ARNs) in the {@link IamCondition#key()} and the
+ * {@link IamCondition#value()}.
+ *
+ * @see
+ *
+ * ARN conditions
+ */
+ IamConditionOperator ARN_NOT_LIKE = create("ArnNotLike");
+
+ /**
+ * A check to determine whether the {@link IamCondition#key()} is present (use "false" in the {@link IamCondition#value()})
+ * or not present (use "true" in the {@link IamCondition#value()}).
+ *
+ * @see
+ *
+ * ARN conditions
+ */
+ IamConditionOperator NULL = create("Null");
+
+ /**
+ * Create a new {@link IamConditionOperator} with the provided string added as a prefix.
+ *
+ * This is useful when adding
+ * the
+ * "ForAllValues:" or "ForAnyValues:" prefixes to an operator.
+ */
+ IamConditionOperator addPrefix(String prefix);
+
+ /**
+ * Create a new {@link IamConditionOperator} with the provided string added as a suffix.
+ *
+ * This is useful when adding
+ *
+ * the "IfExists" suffix to an operator.
+ */
+ IamConditionOperator addSuffix(String suffix);
+
+ /**
+ * Create a new {@code IamConditionOperator} element with the provided {@link #value()}.
+ */
+ static IamConditionOperator create(String value) {
+ return new DefaultIamConditionOperator(value);
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamEffect.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamEffect.java
new file mode 100644
index 000000000000..40dbf23ac8ae
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamEffect.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamEffect;
+
+/**
+ * The {@code Effect} element of a {@link IamStatement}, specifying whether the statement should ALLOW or DENY certain actions.
+ *
+ * @see Effect user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamEffect extends IamValue {
+ /**
+ * The {@link IamStatement} to which this effect is attached should ALLOW the actions described in the policy, and DENY
+ * everything else.
+ */
+ IamEffect ALLOW = create("Allow");
+
+ /**
+ * The {@link IamStatement} to which this effect is attached should DENY the actions described in the policy. This takes
+ * precedence over any other ALLOW statements. See the
+ * policy evaluation
+ * logic guide for more information on how to use the DENY effect.
+ */
+ IamEffect DENY = create("Deny");
+
+ /**
+ * Create a new {@code IamEffect} element with the provided {@link #value()}.
+ */
+ static IamEffect create(String value) {
+ return new DefaultIamEffect(value);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicy.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicy.java
new file mode 100644
index 000000000000..fd3b4d663b93
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicy.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamPolicy;
+import software.amazon.awssdk.utils.builder.CopyableBuilder;
+import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
+
+/**
+ * An AWS access control policy is a object that acts as a container for one or
+ * more statements, which specify fine grained rules for allowing or denying
+ * various types of actions from being performed on your AWS resources.
+ *
+ * By default, all requests to use your resource coming from anyone but you are
+ * denied. Access control polices can override that by allowing different types
+ * of access to your resources, or by explicitly denying different types of
+ * access.
+ *
+ * Each statement in an AWS access control policy takes the form:
+ * "A has permission to do B to C where D applies".
+ *
+ * - A is the principal - the AWS account that is making a request to
+ * access or modify one of your AWS resources.
+ *
- B is the action - the way in which your AWS resource is being accessed or modified, such
+ * as sending a message to an Amazon SQS queue, or storing an object in an Amazon S3 bucket.
+ *
- C is the resource - your AWS entity that the principal wants to access, such
+ * as an Amazon SQS queue, or an object stored in Amazon S3.
+ *
- D is the set of conditions - optional constraints that specify when to allow or deny
+ * access for the principal to access your resource. Many expressive conditions are available,
+ * some specific to each service. For example you can use date conditions to allow access to
+ * your resources only after or before a specific time.
+ *
+ *
+ * For more information, see The IAM User Guide
+ *
+ *
Usage Examples
+ * Create a new IAM identity policy that allows a role to write items to an Amazon DynamoDB table.
+ * {@snippet :
+ * // IamClient requires a dependency on software.amazon.awssdk:iam
+ * try (IamClient iam = IamClient.create()) {
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:PutItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build())
+ * .build();
+ * iam.createPolicy(r -> r.policyName("AllowWriteBookMetadata")
+ * .policyDocument(policy.toJson()));
+ * }
+ * }
+ *
+ *
+ * Download the policy uploaded in the previous example and create a new policy with "read" access added to it.
+ * {@snippet :
+ * // IamClient requires a dependency on software.amazon.awssdk:iam
+ * try (IamClient iam = IamClient.create()) {
+ * String policyArn = "arn:aws:iam::123456789012:policy/AllowWriteBookMetadata";
+ * GetPolicyResponse getPolicyResponse = iam.getPolicy(r -> r.policyArn(policyArn));
+ *
+ * String policyVersion = getPolicyResponse.defaultVersionId();
+ * GetPolicyVersionResponse getPolicyVersionResponse =
+ * iam.getPolicyVersion(r -> r.policyArn(policyArn).versionId(policyVersion));
+ *
+ * IamPolicy policy = IamPolicy.fromJson(getPolicyVersionResponse.policyVersion().document());
+ *
+ * IamStatement newStatement = policy.statements().get(0).copy(s -> s.addAction("dynamodb:GetItem"));
+ * IamPolicy newPolicy = policy.copy(p -> p.statements(Arrays.asList(newStatement)));
+ *
+ * iam.createPolicy(r -> r.policyName("AllowReadWriteBookMetadata")
+ * .policyDocument(newPolicy.toJson()));
+ * }
+ * }
+ *
+ * @see IamPolicyReader
+ * @see IamPolicyWriter
+ * @see IamStatement
+ * @see IAM User Guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamPolicy extends ToCopyableBuilder {
+ /**
+ * Create an {@code IamPolicy} from an IAM policy in JSON form.
+ *
+ * This will raise an exception if the provided JSON is invalid or does not appear to represent a valid policy document.
+ *
+ * This is equivalent to {@code IamPolicyReader.create().read(json)}.
+ */
+ static IamPolicy fromJson(String json) {
+ return IamPolicyReader.create().read(json);
+ }
+
+ /**
+ * Create an {@code IamPolicy} containing the provided statements.
+ *
+ * At least one statement is required.
+ *
+ * This is equivalent to {@code IamPolicy.builder().statements(statements).build()}
+ */
+ static IamPolicy create(Collection statements) {
+ return builder().statements(statements).build();
+ }
+
+ /**
+ * Create a {@link Builder} for an {@code IamPolicy}.
+ */
+ static Builder builder() {
+ return DefaultIamPolicy.builder();
+ }
+
+ /**
+ * Retrieve the value set by {@link Builder#id(String)}.
+ */
+ String id();
+
+ /**
+ * Retrieve the value set by {@link Builder#version(String)}.
+ */
+ String version();
+
+ /**
+ * Retrieve the value set by {@link Builder#statements(Collection)}.
+ */
+ List statements();
+
+ /**
+ * Convert this policy to the JSON format that is accepted by AWS services.
+ *
+ * This is equivalent to {@code IamPolicyWriter.create().writeToString(policy)}
+ *
+ * {@snippet :
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:PutItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build())
+ * .build();
+ * System.out.println("Policy:\n" + policy.toJson());
+ * }
+ */
+ String toJson();
+
+ /**
+ * Convert this policy to the JSON format that is accepted by AWS services, using the provided writer.
+ *
+ * This is equivalent to {@code writer.writeToString(policy)}
+ *
+ * {@snippet :
+ * IamPolicyWriter prettyWriter =
+ * IamPolicyWriter.builder()
+ * .prettyPrint(true)
+ * .build();
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:PutItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build())
+ * .build();
+ * System.out.println("Policy:\n" + policy.toJson(prettyWriter));
+ * }
+ */
+ String toJson(IamPolicyWriter writer);
+
+ /**
+ * @see #builder()
+ */
+ interface Builder extends CopyableBuilder {
+ /**
+ * Configure the {@code
+ * Id} element of the policy, specifying an optional identifier for the policy.
+ *
+ * The ID is used differently in different services. ID is allowed in resource-based policies, but not in
+ * identity-based policies.
+ *
+ * For services that let you set an ID element, we recommend you use a UUID (GUID) for the value, or incorporate a UUID
+ * as part of the ID to ensure uniqueness.
+ *
+ * This value is optional.
+ *
+ * {@snippet :
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .id("cd3ad3d9-2776-4ef1-a904-4c229d1642ee") // An identifier for the policy
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.DENY)
+ * .addAction(IamAction.ALL)
+ * .build())
+ * .build();
+ *}
+ *
+ * @see ID user guide
+ */
+ Builder id(String id);
+
+ /**
+ * Configure the
+ * {@code Version}
+ * element of the policy, specifying the language syntax rules that are to be used to
+ * process the policy.
+ *
+ * By default, this value is {@code 2012-10-17}.
+ *
+ * {@snippet :
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .version("2012-10-17") // The IAM policy language syntax version to use
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.DENY)
+ * .addAction(IamAction.ALL)
+ * .build())
+ * .build();
+ * }
+ *
+ * @see Version
+ * user guide
+ */
+ Builder version(String version);
+
+ /**
+ * Configure the
+ * {@code
+ * Statement} element of the policy, specifying the access rules for this policy.
+ *
+ * This will replace any other statements already added to the policy. At least one statement is required to
+ * create a policy.
+ *
+ * {@snippet :
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * // Add a statement to this policy that denies all actions:
+ * .statements(Arrays.asList(IamStatement.builder()
+ * .effect(IamEffect.DENY)
+ * .addAction(IamAction.ALL)
+ * .build()))
+ * .build();
+ * }
+ * @see
+ * Statement user guide
+ */
+ Builder statements(Collection statements);
+
+ /**
+ * Append a
+ * {@code
+ * Statement} element to this policy to specify additional access rules.
+ *
+ * At least one statement is required to create a policy.
+ *
+ * {@snippet :
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * // Add a statement to this policy that denies all actions:
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.DENY)
+ * .addAction(IamAction.ALL)
+ * .build())
+ * .build();
+ * }
+ * @see
+ * Statement user guide
+ */
+ Builder addStatement(IamStatement statement);
+
+ /**
+ * Append a
+ * {@code
+ * Statement} element to this policy to specify additional access rules.
+ *
+ * This works the same as {@link #addStatement(IamStatement)}, except you do not need to specify {@code IamStatement
+ * .builder()} or {@code build()}. At least one statement is required to create a policy.
+ *
+ * {@snippet :
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * // Add a statement to this policy that denies all actions:
+ * .addStatement(s -> s.effect(IamEffect.DENY)
+ * .addAction(IamAction.ALL))
+ * .build();
+ * }
+ * @see
+ * Statement user guide
+ */
+ Builder addStatement(Consumer statement);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicyReader.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicyReader.java
new file mode 100644
index 000000000000..92e6505548ae
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicyReader.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import java.io.InputStream;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamPolicyReader;
+
+/**
+ * The {@link IamPolicyReader} converts a JSON policy into an {@link IamPolicy}.
+ *
+ * Usage Examples
+ * Log the number of statements in a policy downloaded from IAM.
+ * {@snippet :
+ * // IamClient requires a dependency on software.amazon.awssdk:iam
+ * try (IamClient iam = IamClient.create()) {
+ * String policyArn = "arn:aws:iam::123456789012:policy/AllowWriteBookMetadata";
+ * GetPolicyResponse getPolicyResponse = iam.getPolicy(r -> r.policyArn(policyArn));
+ *
+ * String policyVersion = getPolicyResponse.defaultVersionId();
+ * GetPolicyVersionResponse getPolicyVersionResponse =
+ * iam.getPolicyVersion(r -> r.policyArn(policyArn).versionId(policyVersion));
+ *
+ * IamPolicy policy = IamPolicyReader.create().read(getPolicyVersionResponse.policyVersion().document());
+ *
+ * System.out.println("Number of statements in the " + policyArn + ": " + policy.statements().size());
+ * }
+ * }
+ *
+ * @see IamPolicy#fromJson(String)
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamPolicyReader {
+ /**
+ * Create a new {@link IamPolicyReader}.
+ *
+ * This method is inexpensive, allowing the creation of readers wherever they are needed.
+ */
+ static IamPolicyReader create() {
+ return new DefaultIamPolicyReader();
+ }
+
+ /**
+ * Read a policy from a {@link String}.
+ *
+ * This only performs minimal validation on the provided policy.
+ *
+ * @throws RuntimeException If the provided policy is not valid JSON or is missing a minimal set of required fields.
+ */
+ IamPolicy read(String policy);
+
+ /**
+ * Read a policy from an {@link InputStream}.
+ *
+ * The stream must provide a UTF-8 encoded string representing the policy. This only performs minimal validation on the
+ * provided policy.
+ *
+ * @throws RuntimeException If the provided policy is not valid JSON or is missing a minimal set of required fields.
+ */
+ IamPolicy read(InputStream policy);
+
+ /**
+ * Read a policy from a {@code byte} array.
+ *
+ * The stream must provide a UTF-8 encoded string representing the policy. This only performs minimal validation on the
+ * provided policy.
+ *
+ * @throws RuntimeException If the provided policy is not valid JSON or is missing a minimal set of required fields.
+ */
+ IamPolicy read(byte[] policy);
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicyWriter.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicyWriter.java
new file mode 100644
index 000000000000..2b64ccfb2ba8
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPolicyWriter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamPolicyWriter;
+import software.amazon.awssdk.utils.builder.CopyableBuilder;
+import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
+
+/**
+ * The {@link IamPolicyReader} converts an {@link IamPolicy} into JSON.
+ *
+ *
Usage Examples
+ * Create a new IAM identity policy that allows a role to write items to an Amazon DynamoDB table.
+ * {@snippet :
+ * // IamClient requires a dependency on software.amazon.awssdk:iam
+ * try (IamClient iam = IamClient.create()) {
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:PutItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build())
+ * .build();
+ *
+ * IamPolicyWriter writer = IamPolicyWriter.create();
+ * iam.createPolicy(r -> r.policyName("AllowWriteBookMetadata")
+ * .policyDocument(writer.writeToString(policy)));
+ * }
+ * }
+ *
+ * Create and use a writer that pretty-prints the IAM policy JSON:
+ * {@snippet :
+ * IamPolicyWriter prettyWriter =
+ * IamPolicyWriter.builder()
+ * .prettyPrint(true)
+ * .build();
+ * IamPolicy policy =
+ * IamPolicy.builder()
+ * .addStatement(IamStatement.builder()
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:PutItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build())
+ * .build();
+ * System.out.println("Policy:\n" + policy.toJson(prettyWriter));
+ * }
+ *
+ * @see IamPolicy#toJson()
+ * @see IamPolicy#toJson(IamPolicyWriter)
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamPolicyWriter extends ToCopyableBuilder {
+ /**
+ * Create a new {@link IamPolicyReader}.
+ *
+ * This method is inexpensive, allowing the creation of writers wherever they are needed.
+ */
+ static IamPolicyWriter create() {
+ return DefaultIamPolicyWriter.create();
+ }
+
+ /**
+ * Create a {@link Builder} for an {@code IamPolicyWriter}.
+ */
+ static Builder builder() {
+ return DefaultIamPolicyWriter.builder();
+ }
+
+ /**
+ * Write a policy to a {@link String}.
+ *
+ * This does not validate that the provided policy is correct or valid.
+ */
+ String writeToString(IamPolicy policy);
+
+ /**
+ * Write a policy to a {@code byte} array.
+ *
+ * This does not validate that the provided policy is correct or valid.
+ */
+ byte[] writeToBytes(IamPolicy policy);
+
+ /**
+ * @see #builder()
+ */
+ interface Builder extends CopyableBuilder {
+ /**
+ * Configure whether the writer should "pretty-print" the output.
+ *
+ * When set to true, this will add new lines and indentation to the output to make it easier for a human to read, at
+ * the expense of extra data (white space) being output.
+ *
+ * By default, this is {@code false}.
+ */
+ Builder prettyPrint(Boolean prettyPrint);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPrincipal.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPrincipal.java
new file mode 100644
index 000000000000..b4dc1a44f288
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPrincipal.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static java.util.Collections.emptyList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamPrincipal;
+import software.amazon.awssdk.utils.Validate;
+import software.amazon.awssdk.utils.builder.CopyableBuilder;
+import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
+
+/**
+ * The {@code Principal} element of a {@link IamStatement}, specifying who the statement should apply to.
+ *
+ * @see Principal
+ * user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamPrincipal extends ToCopyableBuilder {
+ /**
+ * An {@link IamPrincipal} representing ALL principals. When used on a statement, it means the policy should apply to
+ * everyone.
+ */
+ IamPrincipal ALL = create("*", "*");
+
+ /**
+ * Create an {@link IamPrincipal} of the supplied type and ID (see {@link Builder#type(IamPrincipalType)} and
+ * {@link Builder#id(String)}).
+ *
+ * Both type and ID are required. This is equivalent to {@code IamPrincipal.builder().type(principalType).id(principalId)
+ * .build()}.
+ */
+ static IamPrincipal create(IamPrincipalType principalType, String principalId) {
+ return builder().type(principalType).id(principalId).build();
+ }
+
+ /**
+ * Create an {@link IamPrincipal} of the supplied type and ID (see {@link Builder#type(String)} and
+ * {@link Builder#id(String)}).
+ *
+ * Both type and ID are required. This is equivalent to {@link #create(IamPrincipalType, String)}, except you do not need
+ * to call {@code IamPrincipalType.create()}.
+ */
+ static IamPrincipal create(String principalType, String principalId) {
+ return builder().type(principalType).id(principalId).build();
+ }
+
+ /**
+ * Create multiple {@link IamPrincipal}s with the same {@link IamPrincipalType} and different IDs (see
+ * {@link Builder#type(IamPrincipalType)} and {@link Builder#id(String)}).
+ *
+ * Type is required, and the IDs in the IDs list must not be null. This is equivalent to calling
+ * {@link #create(IamPrincipalType, String)} multiple times and collecting the results into a list.
+ */
+ static List createAll(IamPrincipalType principalType, Collection principalIds) {
+ Validate.paramNotNull(principalType, "principalType");
+ if (principalIds == null) {
+ return emptyList();
+ }
+ return principalIds.stream()
+ .map(principalId -> create(principalType, principalId))
+ .collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ /**
+ * Create multiple {@link IamPrincipal}s with the same {@link IamPrincipalType} and different IDs (see
+ * {@link Builder#type(String)} and {@link Builder#id(String)}).
+ *
+ * Type is required, and the IDs in the IDs list must not be null. This is equivalent to calling
+ * {@link #create(String, String)} multiple times and collecting the results into a list.
+ */
+ static List createAll(String principalType, Collection principalIds) {
+ Validate.paramNotNull(principalType, "principalType");
+ if (principalIds == null) {
+ return emptyList();
+ }
+ return principalIds.stream()
+ .map(principalId -> create(principalType, principalId))
+ .collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ /**
+ * Create a {@link IamStatement.Builder} for an {@code IamPrincipal}.
+ */
+ static Builder builder() {
+ return DefaultIamPrincipal.builder();
+ }
+
+ /**
+ * Retrieve the value set by {@link Builder#type(IamPrincipalType)}.
+ */
+ IamPrincipalType type();
+
+ /**
+ * Retrieve the value set by {@link Builder#id(String)}.
+ */
+ String id();
+
+ /**
+ * @see #builder()
+ */
+ interface Builder extends CopyableBuilder {
+ /**
+ * Set the {@link IamPrincipalType} associated with this principal.
+ *
+ * This value is required.
+ *
+ * @see IamPrincipalType
+ * @see Principal
+ * user guide
+ */
+ Builder type(IamPrincipalType type);
+
+ /**
+ * Set the {@link IamPrincipalType} associated with this principal.
+ *
+ * This is the same as {@link #type(IamPrincipalType)}, except you do not need to call {@code IamPrincipalType.create()}.
+ * This value is required.
+ *
+ * @see IamPrincipalType
+ * @see Principal
+ * user guide
+ */
+ Builder type(String type);
+
+ /**
+ * Set the identifier of the principal.
+ *
+ * The identifiers that can be used depend on the {@link #type(IamPrincipalType)} of the principal.
+ *
+ * @see Principal
+ * user guide
+ */
+ Builder id(String id);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalType.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalType.java
new file mode 100644
index 000000000000..315feed83caa
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalType.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamPrincipalType;
+
+/**
+ * The {@code IamPrincipalType} identifies what type of entity that the {@link IamPrincipal} refers to.
+ *
+ * @see Principal
+ * user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamPrincipalType extends IamValue {
+ /**
+ * An {@code AWS} principal.
+ *
+ * For example, this includes AWS accounts, IAM users, IAM roles, IAM role sessions or STS federated users.
+ *
+ * @see Principal
+ * user guide
+ */
+ IamPrincipalType AWS = create("AWS");
+
+ /**
+ * A {@code Federated} principal.
+ *
+ * This grants an external web identity, SAML identity provider, etc. permission to perform actions on your resources. For
+ * example, cognito-identity.amazonaws.com or www.amazon.com.
+ *
+ * @see Principal
+ * user guide
+ */
+ IamPrincipalType FEDERATED = create("Federated");
+
+ /**
+ * A {@code Service} principal.
+ *
+ * This grants other AWS services permissions to perform actions on your resources. Identifiers are usually in the format
+ * service-name.amazonaws.com. For example, ecs.amazonaws.com or lambda.amazonaws.com.
+ *
+ * @see Principal
+ * user guide
+ */
+ IamPrincipalType SERVICE = create("Service");
+
+ /**
+ * A {@code CanonicalUser} principal.
+ *
+ * Some services support a canonical user ID to identify your account without requiring your account ID to be shared. Such
+ * identifiers are often a 64-digit alphanumeric value.
+ *
+ * @see Principal
+ * user guide
+ */
+ IamPrincipalType CANONICAL_USER = create("CanonicalUser");
+
+ /**
+ * Create a new {@code IamPrincipalType} element with the provided {@link #value()}.
+ */
+ static IamPrincipalType create(String value) {
+ return new DefaultIamPrincipalType(value);
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamResource.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamResource.java
new file mode 100644
index 000000000000..b653c8dba956
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamResource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamResource;
+
+/**
+ * The {@code Resource} element of a {@link IamStatement}, specifying which resource the statement applies to.
+ *
+ * @see
+ * Resource user guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamResource extends IamValue {
+ /**
+ * An {@link IamResource} representing ALL resources. When used on a statement, it means the policy should apply to
+ * every resource.
+ */
+ IamResource ALL = create("*");
+
+ /**
+ * Create a new {@code IamResource} element with the provided {@link #value()}.
+ */
+ static IamResource create(String value) {
+ return new DefaultIamResource(value);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamStatement.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamStatement.java
new file mode 100644
index 000000000000..e2480bf4fd39
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamStatement.java
@@ -0,0 +1,1201 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.policybuilder.iam.internal.DefaultIamStatement;
+import software.amazon.awssdk.utils.builder.CopyableBuilder;
+import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
+
+/**
+ * A statement is the formal description of a single permission, and is always
+ * contained within a policy object.
+ *
+ * A statement describes a rule for allowing or denying access to a specific AWS
+ * resource based on how the resource is being accessed, and who is attempting
+ * to access the resource. Statements can also optionally contain a list of
+ * conditions that specify when a statement is to be honored.
+ *
+ * For example, consider a statement that:
+ *
+ * - allows access (the effect)
+ *
- for a list of specific AWS account IDs (the principals)
+ *
- when accessing an SQS queue (the resource)
+ *
- using the SendMessage operation (the action)
+ *
- and the request occurs before a specific date (a condition)
+ *
+ *
+ *
+ * Statements takes the form: "A has permission to do B to C where D applies".
+ *
+ * - A is the principal - the AWS account that is making a request to
+ * access or modify one of your AWS resources.
+ *
- B is the action - the way in which your AWS resource is being accessed or modified, such
+ * as sending a message to an Amazon SQS queue, or storing an object in an Amazon S3 bucket.
+ *
- C is the resource - your AWS entity that the principal wants to access, such
+ * as an Amazon SQS queue, or an object stored in Amazon S3.
+ *
- D is the set of conditions - optional constraints that specify when to allow or deny
+ * access for the principal to access your resource. Many expressive conditions are available,
+ * some specific to each service. For example you can use date conditions to allow access to
+ * your resources only after or before a specific time.
+ *
+ *
+ *
+ * There are many resources and conditions available for use in statements, and
+ * you can combine them to form fine grained custom access control polices.
+ *
+ *
+ * Statements are typically attached to a {@link IamPolicy}.
+ *
+ *
+ * For more information, see The IAM User guide
+ *
+ *
Usage Examples
+ * Create an
+ * identity-based policy
+ * statement that allows a role to write items to an Amazon DynamoDB table.
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantWriteBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:PutItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ *
+ *
+ * Create a
+ * resource-based policy
+ * statement that denies access to all users.
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .effect(IamEffect.DENY)
+ * .addPrincipal(IamPrincipal.ALL)
+ * .build();
+ * }
+ *
+ * @see IamPolicy
+ * @see Statement user
+ * guide
+ */
+@SdkPublicApi
+@ThreadSafe
+public interface IamStatement extends ToCopyableBuilder {
+ /**
+ * Create a {@link Builder} for an {@code IamStatement}.
+ */
+ static Builder builder() {
+ return DefaultIamStatement.builder();
+ }
+
+ /**
+ * Retrieve the value set by {@link Builder#sid(String)}.
+ */
+ String sid();
+
+ /**
+ * Retrieve the value set by {@link Builder#effect(IamEffect)}.
+ */
+ IamEffect effect();
+
+ /**
+ * Retrieve the value set by {@link Builder#principals(Collection)}.
+ */
+ List principals();
+
+ /**
+ * Retrieve the value set by {@link Builder#notPrincipals(Collection)}.
+ */
+ List notPrincipals();
+
+ /**
+ * Retrieve the value set by {@link Builder#actions(Collection)}.
+ */
+ List actions();
+
+ /**
+ * Retrieve the value set by {@link Builder#notActions(Collection)}.
+ */
+ List notActions();
+
+ /**
+ * Retrieve the value set by {@link Builder#resources(Collection)}.
+ */
+ List resources();
+
+ /**
+ * Retrieve the value set by {@link Builder#notResources(Collection)}.
+ */
+ List notResources();
+
+ /**
+ * Retrieve the value set by {@link Builder#conditions(Collection)}.
+ */
+ List conditions();
+
+ /**
+ * @see #builder()
+ */
+ interface Builder extends CopyableBuilder {
+ /**
+ * Configure the {@code
+ * Sid} element of the policy, specifying an identifier for the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata") // An identifier for the statement
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ *
+ * @see Sid user
+ * guide
+ */
+ Builder sid(String sid);
+
+ /**
+ * Configure the
+ * {@code Effect}
+ * element of the policy, specifying whether the statement results in an allow or deny.
+ *
+ * This value is required.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata")
+ * .effect(IamEffect.ALLOW) // The statement ALLOWS access
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ *
+ * @see IamEffect
+ * @see Effect user
+ * guide
+ */
+ Builder effect(IamEffect effect);
+
+ /**
+ * Configure the
+ * {@code Effect}
+ * element of the policy, specifying whether the statement results in an allow or deny.
+ *
+ * This works the same as {@link #effect(IamEffect)}, except you do not need to {@link IamEffect}. This value is required.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata")
+ * .effect("Allow") // The statement ALLOWs access
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ *
+ * @see IamEffect
+ * @see Effect user
+ * guide
+ */
+ Builder effect(String effect);
+
+ /**
+ * Configure the
+ * {@code
+ * Principal} element of the statement, specifying the principals that are allowed or denied
+ * access to a resource.
+ *
+ * This will replace any other principals already added to the statement.
+ *
+ * {@snippet :
+ * List bookReaderRoles =
+ * IamPrincipal.createAll("AWS",
+ * Arrays.asList("arn:aws:iam::123456789012:role/books-service",
+ * "arn:aws:iam::123456789012:role/books-operator"));
+ *
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * .principals(bookReaderRoles) // This statement allows access to the books service and operators
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ *
+ * @see IamPrincipal
+ * @see Principal
+ * user guide
+ */
+ Builder principals(Collection principals);
+
+ /**
+ * Append a
+ * {@code
+ * Principal} to this statement, specifying a principal that is allowed or denied access to
+ * a resource.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * // This statement allows access to the books service:
+ * .addPrincipal(IamPrincipal.create("AWS", "arn:aws:iam::123456789012:role/books-service"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see Principal
+ * user guide
+ */
+ Builder addPrincipal(IamPrincipal principal);
+
+ /**
+ * Append a
+ * {@code
+ * Principal} to this statement, specifying a principal that is allowed or denied access to
+ * a resource.
+ *
+ * This works the same as {@link #addPrincipal(IamPrincipal)}, except you do not need to specify {@code IamPrincipal
+ * .builder()} or {@code build()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * // This statement allows access to the books service:
+ * .addPrincipal(p -> p.type("AWS").id("arn:aws:iam::123456789012:role/books-service"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see Principal
+ * user guide
+ */
+ Builder addPrincipal(Consumer principal);
+
+ /**
+ * Append a
+ * {@code
+ * Principal} to this statement, specifying a principal that is allowed or denied access to
+ * a resource.
+ *
+ * This works the same as {@link #addPrincipal(IamPrincipal)}, except you do not need to specify {@code IamPrincipal
+ * .create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * // This statement allows access to the books service:
+ * .addPrincipal(IamPrincipalType.AWS, "arn:aws:iam::123456789012:role/books-service")
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see Principal
+ * user guide
+ */
+ Builder addPrincipal(IamPrincipalType iamPrincipalType, String principal);
+
+ /**
+ * Append a
+ * {@code
+ * Principal} to this statement, specifying a principal that is allowed or denied access to
+ * a resource.
+ *
+ * This works the same as {@link #addPrincipal(IamPrincipalType, String)}, except you do not need to specify {@code
+ * IamPrincipalType.create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * // This statement allows access to the books service:
+ * .addPrincipal("AWS", "arn:aws:iam::123456789012:role/books-service")
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see Principal
+ * user guide
+ */
+ Builder addPrincipal(String iamPrincipalType, String principal);
+
+ /**
+ * Append multiple
+ * {@code
+ * Principal}s to this statement, specifying principals that are allowed or denied access to
+ * a resource.
+ *
+ * This works the same as calling {@link #addPrincipal(IamPrincipalType, String)} multiple times with the same
+ * {@link IamPrincipalType}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * // This statement allows access to the books service and operators:
+ * .addPrincipals(IamPrincipalType.AWS,
+ * Arrays.asList("arn:aws:iam::123456789012:role/books-service",
+ * "arn:aws:iam::123456789012:role/books-operator"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see Principal
+ * user guide
+ */
+ Builder addPrincipals(IamPrincipalType iamPrincipalType, Collection principals);
+
+ /**
+ * Append multiple
+ * {@code
+ * Principal}s to this statement, specifying principals that are allowed or denied access to
+ * a resource.
+ *
+ * This works the same as calling {@link #addPrincipal(String, String)} multiple times with the same
+ * {@link IamPrincipalType}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.ALLOW)
+ * // This statement allows access to the books service and operators:
+ * .addPrincipals("AWS", Arrays.asList("arn:aws:iam::123456789012:role/books-service",
+ * "arn:aws:iam::123456789012:role/books-operator"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see Principal
+ * user guide
+ */
+ Builder addPrincipals(String iamPrincipalType, Collection principals);
+
+ /**
+ * Configure the
+ * {@code
+ * NotPrincipal} element of the statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * This will replace any other not-principals already added to the statement.
+ *
+ * {@snippet :
+ * List bookReaderRoles =
+ * IamPrincipal.createAll("AWS",
+ * Arrays.asList("arn:aws:iam::123456789012:role/books-service",
+ * "arn:aws:iam::123456789012:role/books-operator"));
+ *
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service and operators:
+ * .notPrincipals(bookReaderRoles)
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder notPrincipals(Collection notPrincipals);
+
+ /**
+ * Append a
+ * {@code
+ * NotPrincipal} to this statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service:
+ * .addNotPrincipal(IamPrincipal.create("AWS", "arn:aws:iam::123456789012:role/books-service"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder addNotPrincipal(IamPrincipal notPrincipal);
+
+ /**
+ * Append a
+ * {@code
+ * NotPrincipal} to this statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * This works the same as {@link #addNotPrincipal(IamPrincipal)}, except you do not need to specify {@code IamPrincipal
+ * .builder()} or {@code build()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service:
+ * .addNotPrincipal(p -> p.type("AWS").id("arn:aws:iam::123456789012:role/books-service"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder addNotPrincipal(Consumer notPrincipal);
+
+ /**
+ * Append a
+ * {@code
+ * NotPrincipal} to this statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * This works the same as {@link #addNotPrincipal(IamPrincipal)}, except you do not need to specify {@code IamPrincipal
+ * .create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service:
+ * .addNotPrincipal(IamPrincipalType.AWS, "arn:aws:iam::123456789012:role/books-service")
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder addNotPrincipal(IamPrincipalType iamPrincipalType, String notPrincipal);
+
+ /**
+ * Append a
+ * {@code
+ * NotPrincipal} to this statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * This works the same as {@link #addNotPrincipal(IamPrincipalType, String)}, except you do not need to specify {@code
+ * IamPrincipalType.create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service:
+ * .addNotPrincipal("AWS", "arn:aws:iam::123456789012:role/books-service")
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder addNotPrincipal(String iamPrincipalType, String notPrincipal);
+
+ /**
+ * Append multiple
+ * {@code
+ * NotPrincipal}s to this statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * This works the same as calling {@link #addNotPrincipal(IamPrincipalType, String)} multiple times with the same
+ * {@link IamPrincipalType}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service and operators:
+ * .addNotPrincipals(IamPrincipalType.AWS,
+ * Arrays.asList("arn:aws:iam::123456789012:role/books-service",
+ * "arn:aws:iam::123456789012:role/books-operator"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder addNotPrincipals(IamPrincipalType iamPrincipalType, Collection notPrincipals);
+
+ /**
+ * Append multiple
+ * {@code
+ * NotPrincipal}s to this statement, specifying that all principals are affected by the policy except the
+ * ones listed.
+ *
+ * Very few scenarios require the use of {@code NotPrincipal}. We recommend that you explore other authorization options
+ * before you decide to use {@code NotPrincipal}. {@code NotPrincipal} can only be used with {@link IamEffect#DENY}
+ * statements.
+ *
+ * This works the same as calling {@link #addNotPrincipal(String, String)} multiple times with the same
+ * {@link IamPrincipalType}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookContent")
+ * .effect(IamEffect.DENY)
+ * // This statement denies access to everyone except the books service and operators:
+ * .addNotPrincipals("AWS", Arrays.asList("arn:aws:iam::123456789012:role/books-service",
+ * "arn:aws:iam::123456789012:role/books-operator"))
+ * .addAction("s3:GetObject")
+ * .addResource("arn:aws:s3:us-west-2:123456789012:accesspoint/book-content/object/*")
+ * .build();
+ * }
+ * @see
+ * NotPrincipal user guide
+ */
+ Builder addNotPrincipals(String iamPrincipalType, Collection notPrincipals);
+
+ /**
+ * Configure the
+ * {@code Action}
+ * element of the statement, specifying the actions that are allowed or denied.
+ *
+ * This will replace any other actions already added to the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadWriteBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to read and write items in Amazon DynamoDB:
+ * .actions(Arrays.asList(IamAction.create("dynamodb:PutItem"),
+ * IamAction.create("dynamodb:GetItem")))
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see Action user
+ * guide
+ */
+ Builder actions(Collection actions);
+
+ /**
+ * Configure the
+ * {@code Action}
+ * element of the statement, specifying the actions that are allowed or denied.
+ *
+ * This works the same as {@link #actions(Collection)}, except you do not need to call {@code IamAction.create()
+ * } on each action. This will replace any other actions already added to the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadWriteBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to read and write items in Amazon DynamoDB:
+ * .actionIds(Arrays.asList("dynamodb:PutItem", "dynamodb:GetItem"))
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see Action user
+ * guide
+ */
+ Builder actionIds(Collection actions);
+
+ /**
+ * Append an {@code
+ * Action} element to this statement, specifying an action that is allowed or denied.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to read items in Amazon DynamoDB:
+ * .addAction(IamAction.create("dynamodb:GetItem"))
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see Action user
+ * guide
+ */
+ Builder addAction(IamAction action);
+
+ /**
+ * Append an {@code
+ * Action} element to this statement, specifying an action that is allowed or denied.
+ *
+ * This works the same as {@link #addAction(IamAction)}, except you do not need to call {@code IamAction.create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to read items in Amazon DynamoDB:
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see Action user
+ * guide
+ */
+ Builder addAction(String action);
+
+ /**
+ * Configure the
+ * {@code
+ * NotAction} element of the statement, specifying actions that are denied or allowed.
+ *
+ * This will replace any other not-actions already added to the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantAllButDeleteBookMetadataTable")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to do ALL CURRENT AND FUTURE actions against the books table, except
+ * // dynamodb:DeleteTable
+ * .notActions(Arrays.asList(IamAction.create("dynamodb:DeleteTable")))
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see NotAction
+ * user guide
+ */
+ Builder notActions(Collection actions);
+
+ /**
+ * Configure the
+ * {@code
+ * NotAction} element of the statement, specifying actions that are denied or allowed.
+ *
+ * This works the same as {@link #notActions(Collection)}, except you do not need to call {@code IamAction.create()}
+ * on each action. This will replace any other not-actions already added to the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantAllButDeleteBookMetadataTable")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to do ALL CURRENT AND FUTURE actions against the books table, except
+ * // dynamodb:DeleteTable
+ * .notActionIds(Arrays.asList("dynamodb:DeleteTable"))
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see NotAction
+ * user guide
+ */
+ Builder notActionIds(Collection actions);
+
+ /**
+ * Append a
+ * {@code
+ * NotAction} element to this statement, specifying an action that is denied or allowed.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantAllButDeleteBookMetadataTable")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to do ALL CURRENT AND FUTURE actions against the books table, except
+ * // dynamodb:DeleteTable
+ * .addNotAction(IamAction.create("dynamodb:DeleteTable"))
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see NotAction
+ * user guide
+ */
+ Builder addNotAction(IamAction action);
+
+ /**
+ * Append a
+ * {@code
+ * NotAction} element to this statement, specifying an action that is denied or allowed.
+ *
+ * This works the same as {@link #addNotAction(IamAction)}, except you do not need to call {@code IamAction.create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantAllButDeleteBookMetadataTable")
+ * .effect(IamEffect.ALLOW)
+ * // This statement grants access to do ALL CURRENT AND FUTURE actions against the books table, except
+ * // dynamodb:DeleteTable
+ * .addNotAction("dynamodb:DeleteTable")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see NotAction
+ * user guide
+ */
+ Builder addNotAction(String action);
+
+ /**
+ * Configure the
+ * {@code Resource}
+ * element of the statement, specifying the resource(s) that the statement covers.
+ *
+ * This will replace any other resources already added to the statement.
+ *
+ * {@snippet :
+ * List resources =
+ * Arrays.asList(IamResource.create("arn:aws:dynamodb:us-east-2:123456789012:table/books"),
+ * IamResource.create("arn:aws:dynamodb:us-east-2:123456789012:table/customers"));
+ *
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookAndCustomersMetadata")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to the books and customers tables:
+ * .resources(resources)
+ * .build();
+ * }
+ * @see Resource
+ * user guide
+ */
+ Builder resources(Collection resources);
+
+ /**
+ * Configure the
+ * {@code Resource}
+ * element of the statement, specifying the resource(s) that the statement covers.
+ *
+ * This works the same as {@link #resources(Collection)}, except you do not need to call {@code IamResource.create()}
+ * on each resource. This will replace any other resources already added to the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookAndCustomersMetadata")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to the books and customers tables:
+ * .resourceIds(Arrays.asList("arn:aws:dynamodb:us-east-2:123456789012:table/books",
+ * "arn:aws:dynamodb:us-east-2:123456789012:table/customers"))
+ * .build();
+ * }
+ * @see Resource
+ * user guide
+ */
+ Builder resourceIds(Collection resources);
+
+ /**
+ * Append a
+ * {@code Resource}
+ * element to the statement, specifying a resource that the statement covers.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to the books table:
+ * .addResource(IamResource.create("arn:aws:dynamodb:us-east-2:123456789012:table/books"))
+ * .build();
+ * }
+ * @see Resource
+ * user guide
+ */
+ Builder addResource(IamResource resource);
+
+ /**
+ * Append a
+ * {@code Resource}
+ * element to the statement, specifying a resource that the statement covers.
+ *
+ * This works the same as {@link #addResource(IamResource)}, except you do not need to call {@code IamResource.create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBookMetadata")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to the books table:
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * .build();
+ * }
+ * @see Resource
+ * user guide
+ */
+ Builder addResource(String resource);
+
+ /**
+ * Configure the
+ * {@code
+ * NotResource} element of the statement, specifying that the statement should apply to every resource except the
+ * ones listed.
+ *
+ * This will replace any other not-resources already added to the statement.
+ *
+ * {@snippet :
+ * List notResources =
+ * Arrays.asList(IamResource.create("arn:aws:dynamodb:us-east-2:123456789012:table/customers"));
+ *
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadNotCustomers")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to EVERY CURRENT AND FUTURE RESOURCE except the customers table:
+ * .notResources(notResources)
+ * .build();
+ * }
+ * @see
+ * NotResource user guide
+ */
+ Builder notResources(Collection resources);
+
+ /**
+ * Configure the
+ * {@code
+ * NotResource} element of the statement, specifying that the statement should apply to every resource except the
+ * ones listed.
+ *
+ * This works the same as {@link #notResources(Collection)}, except you do not need to call {@code IamResource.create()}
+ * on each resource. This will replace any other not-resources already added to the statement.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadNotCustomers")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to EVERY CURRENT AND FUTURE RESOURCE except the customers table:
+ * .notResourceIds(Arrays.asList("arn:aws:dynamodb:us-east-2:123456789012:table/customers"))
+ * .build();
+ * }
+ * @see
+ * NotResource user guide
+ */
+ Builder notResourceIds(Collection resources);
+
+ /**
+ * Append a
+ * {@code
+ * NotResource} element to the statement, specifying that the statement should apply to every resource except the
+ * ones listed.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadNotCustomers")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to EVERY CURRENT AND FUTURE RESOURCE except the customers table:
+ * .addNotResource(IamResource.create("arn:aws:dynamodb:us-east-2:123456789012:table/customers"))
+ * .build();
+ * }
+ * @see
+ * NotResource user guide
+ */
+ Builder addNotResource(IamResource resource);
+
+ /**
+ * Append a
+ * {@code
+ * NotResource} element to the statement, specifying that the statement should apply to every resource except the
+ * ones listed.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadNotCustomers")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * // This statement grants access to EVERY CURRENT AND FUTURE RESOURCE except the customers table:
+ * .addNotResource("arn:aws:dynamodb:us-east-2:123456789012:table/customers")
+ * .build();
+ * }
+ * @see
+ * NotResource user guide
+ */
+ Builder addNotResource(String resource);
+
+ /**
+ * Configure the
+ * {@code
+ * Condition} element of the statement, specifying the conditions in which the statement is in effect.
+ *
+ * This will replace any other conditions already added to the statement.
+ *
+ * {@snippet :
+ * IamCondition startTime = IamCondition.create(IamConditionOperator.DATE_GREATER_THAN,
+ * "aws:CurrentTime",
+ * "1988-05-21T00:00:00Z");
+ * IamCondition endTime = IamCondition.create(IamConditionOperator.DATE_LESS_THAN,
+ * "aws:CurrentTime",
+ * "2065-09-01T00:00:00Z");
+ *
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access between the specified start and end times:
+ * .conditions(Arrays.asList(startTime, endTime))
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder conditions(Collection conditions);
+
+ /**
+ * Append a
+ * {@code
+ * Condition} to the statement, specifying a condition in which the statement is in effect.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access after a specified start time:
+ * .addCondition(IamCondition.create(IamConditionOperator.DATE_GREATER_THAN,
+ * "aws:CurrentTime",
+ * "1988-05-21T00:00:00Z"))
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addCondition(IamCondition condition);
+
+ /**
+ * Append a
+ * {@code
+ * Condition} to the statement, specifying a condition in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(IamCondition)}, except you do not need to specify {@code IamCondition
+ * .builder()} or {@code build()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access after a specified start time:
+ * .addCondition(c -> c.operator(IamConditionOperator.DATE_GREATER_THAN)
+ * .key("aws:CurrentTime")
+ * .value("1988-05-21T00:00:00Z"))
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addCondition(Consumer condition);
+
+ /**
+ * Append a
+ * {@code
+ * Condition} to the statement, specifying a condition in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(IamCondition)}, except you do not need to specify {@code IamCondition
+ * .create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access after a specified start time:
+ * .addCondition(IamConditionOperator.DATE_GREATER_THAN,
+ * IamConditionKey.create("aws:CurrentTime"),
+ * "1988-05-21T00:00:00Z")
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addCondition(IamConditionOperator operator, IamConditionKey key, String value);
+
+ /**
+ * Append a
+ * {@code
+ * Condition} to the statement, specifying a condition in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(IamCondition)}, except you do not need to specify {@code IamCondition
+ * .create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access after a specified start time:
+ * .addCondition(IamConditionOperator.DATE_GREATER_THAN, "aws:CurrentTime", "1988-05-21T00:00:00Z")
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addCondition(IamConditionOperator operator, String key, String value);
+
+ /**
+ * Append a
+ * {@code
+ * Condition} to the statement, specifying a condition in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(IamCondition)}, except you do not need to specify {@code IamCondition
+ * .create()}.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access after a specified start time:
+ * .addCondition("DateGreaterThan", "aws:CurrentTime", "1988-05-21T00:00:00Z")
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addCondition(String operator, String key, String values);
+
+ /**
+ * Append multiple
+ * {@code
+ * Condition}s to the statement, specifying conditions in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(IamConditionOperator, IamConditionKey, String)} multiple times with the
+ * same operator and key, but different values.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access only in the us-east-1 and us-west-2 regions:
+ * .addConditions(IamConditionOperator.STRING_EQUALS,
+ * IamConditionKey.create("aws:RequestedRegion"),
+ * Arrays.asList("us-east-1", "us-west-2"))
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addConditions(IamConditionOperator operator, IamConditionKey key, Collection values);
+
+ /**
+ * Append multiple
+ * {@code
+ * Condition}s to the statement, specifying conditions in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(IamConditionOperator, String, String)} multiple times with the
+ * same operator and key, but different values.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access only in the us-east-1 and us-west-2 regions:
+ * .addConditions(IamConditionOperator.STRING_EQUALS,
+ * "aws:RequestedRegion",
+ * Arrays.asList("us-east-1", "us-west-2"))
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addConditions(IamConditionOperator operator, String key, Collection values);
+
+ /**
+ * Append multiple
+ * {@code
+ * Condition}s to the statement, specifying conditions in which the statement is in effect.
+ *
+ * This works the same as {@link #addCondition(String, String, String)} multiple times with the
+ * same operator and key, but different values.
+ *
+ * {@snippet :
+ * IamStatement statement =
+ * IamStatement.builder()
+ * .sid("GrantReadBooks")
+ * .effect(IamEffect.ALLOW)
+ * .addAction("dynamodb:GetItem")
+ * .addResource("arn:aws:dynamodb:us-east-2:123456789012:table/books")
+ * // This statement grants access only in the us-east-1 and us-west-2 regions:
+ * .addConditions("StringEquals", "aws:RequestedRegion", Arrays.asList("us-east-1", "us-west-2"))
+ * .build();
+ * }
+ * @see Condition
+ * user guide
+ */
+ Builder addConditions(String operator, String key, Collection values);
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamValue.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamValue.java
new file mode 100644
index 000000000000..69fa6f4061f4
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/IamValue.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import software.amazon.awssdk.annotations.SdkPublicApi;
+
+@SdkPublicApi
+public interface IamValue {
+ /**
+ * Retrieve the string that should represent this element in the serialized IAM policy when it is marshalled via
+ * {@link IamPolicyWriter}.
+ */
+ String value();
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamAction.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamAction.java
new file mode 100644
index 000000000000..2cc335a78c71
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamAction.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamAction;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamAction}.
+ *
+ * @see IamAction#create
+ */
+@SdkInternalApi
+public final class DefaultIamAction implements IamAction {
+ @NotNull private final String value;
+
+ public DefaultIamAction(String value) {
+ this.value = Validate.paramNotNull(value, "actionValue");
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamAction that = (DefaultIamAction) o;
+
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamAction")
+ .add("value", value)
+ .build();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamCondition.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamCondition.java
new file mode 100644
index 000000000000..9634e5c84925
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamCondition.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamCondition;
+import software.amazon.awssdk.policybuilder.iam.IamConditionKey;
+import software.amazon.awssdk.policybuilder.iam.IamConditionOperator;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamCondition}.
+ *
+ * @see IamCondition#create
+ */
+@SdkInternalApi
+public final class DefaultIamCondition implements IamCondition {
+ @NotNull private final IamConditionOperator operator;
+ @NotNull private final IamConditionKey key;
+ @NotNull private final String value;
+
+ private DefaultIamCondition(Builder builder) {
+ this.operator = Validate.paramNotNull(builder.operator, "conditionOperator");
+ this.key = Validate.paramNotNull(builder.key, "conditionKey");
+ this.value = Validate.paramNotNull(builder.value, "conditionValue");
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public IamConditionOperator operator() {
+ return operator;
+ }
+
+ @Override
+ public IamConditionKey key() {
+ return key;
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamCondition that = (DefaultIamCondition) o;
+
+ if (!operator.equals(that.operator)) {
+ return false;
+ }
+ if (!key.equals(that.key)) {
+ return false;
+ }
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = operator.hashCode();
+ result = 31 * result + key.hashCode();
+ result = 31 * result + value.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamCondition")
+ .add("operator", operator.value())
+ .add("key", key.value())
+ .add("value", value)
+ .build();
+ }
+
+ public static class Builder implements IamCondition.Builder {
+ private IamConditionOperator operator;
+ private IamConditionKey key;
+ private String value;
+
+ private Builder() {
+ }
+
+ private Builder(DefaultIamCondition condition) {
+ this.operator = condition.operator;
+ this.key = condition.key;
+ this.value = condition.value;
+ }
+
+ @Override
+ public IamCondition.Builder operator(IamConditionOperator operator) {
+ this.operator = operator;
+ return this;
+ }
+
+ @Override
+ public IamCondition.Builder operator(String operator) {
+ this.operator = operator == null ? null : IamConditionOperator.create(operator);
+ return this;
+ }
+
+ @Override
+ public IamCondition.Builder key(IamConditionKey key) {
+ this.key = key;
+ return this;
+ }
+
+ @Override
+ public IamCondition.Builder key(String key) {
+ this.key = key == null ? null : IamConditionKey.create(key);
+ return this;
+ }
+
+ @Override
+ public IamCondition.Builder value(String value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public IamCondition build() {
+ return new DefaultIamCondition(this);
+ }
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamConditionKey.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamConditionKey.java
new file mode 100644
index 000000000000..658cfd1ac5df
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamConditionKey.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamConditionKey;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamConditionKey}.
+ *
+ * @see IamConditionKey#create
+ */
+@SdkInternalApi
+public final class DefaultIamConditionKey implements IamConditionKey {
+ @NotNull private final String value;
+
+ public DefaultIamConditionKey(String value) {
+ this.value = Validate.paramNotNull(value, "conditionKeyValue");
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamConditionKey that = (DefaultIamConditionKey) o;
+
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamConditionKey")
+ .add("value", value)
+ .build();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamConditionOperator.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamConditionOperator.java
new file mode 100644
index 000000000000..92554b7674d3
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamConditionOperator.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamConditionOperator;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamConditionOperator}.
+ *
+ * @see IamConditionOperator#create
+ */
+@SdkInternalApi
+public final class DefaultIamConditionOperator implements IamConditionOperator {
+ @NotNull private final String value;
+
+ public DefaultIamConditionOperator(String value) {
+ this.value = Validate.paramNotNull(value, "conditionOperatorValue");
+ }
+
+ @Override
+ public IamConditionOperator addPrefix(String prefix) {
+ Validate.paramNotNull(prefix, "prefix");
+ return IamConditionOperator.create(prefix + value);
+ }
+
+ @Override
+ public IamConditionOperator addSuffix(String suffix) {
+ Validate.paramNotNull(suffix, "suffix");
+ return IamConditionOperator.create(value + suffix);
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamConditionOperator that = (DefaultIamConditionOperator) o;
+
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamConditionOperator")
+ .add("value", value)
+ .build();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamEffect.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamEffect.java
new file mode 100644
index 000000000000..1194882c0171
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamEffect.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamEffect;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamEffect}.
+ *
+ * @see IamEffect#create
+ */
+@SdkInternalApi
+public final class DefaultIamEffect implements IamEffect {
+ @NotNull private final String value;
+
+ public DefaultIamEffect(String value) {
+ this.value = Validate.paramNotNull(value, "effectValue");
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamEffect that = (DefaultIamEffect) o;
+
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamEffect")
+ .add("value", value)
+ .build();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicy.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicy.java
new file mode 100644
index 000000000000..9b9c1279433e
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicy.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamPolicy;
+import software.amazon.awssdk.policybuilder.iam.IamPolicyWriter;
+import software.amazon.awssdk.policybuilder.iam.IamStatement;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamPolicy}.
+ *
+ * @see IamPolicy#create
+ * @see IamPolicy#fromJson(String)
+ * @see IamPolicy#builder()
+ */
+@SdkInternalApi
+public final class DefaultIamPolicy implements IamPolicy {
+ private final String id;
+ @NotNull private final String version;
+ @NotNull private final List statements;
+
+ public DefaultIamPolicy(Builder builder) {
+ this.id = builder.id;
+ this.version = builder.version != null ? builder.version : "2012-10-17";
+ this.statements = new ArrayList<>(Validate.notEmpty(builder.statements,
+ "At least one policy statement is required."));
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public String version() {
+ return version;
+ }
+
+ @Override
+ public List statements() {
+ return Collections.unmodifiableList(statements);
+ }
+
+ @Override
+ public String toJson() {
+ return toJson(IamPolicyWriter.create());
+ }
+
+ @Override
+ public String toJson(IamPolicyWriter writer) {
+ return writer.writeToString(this);
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamPolicy that = (DefaultIamPolicy) o;
+
+ if (!Objects.equals(id, that.id)) {
+ return false;
+ }
+ if (!version.equals(that.version)) {
+ return false;
+ }
+ if (!statements.equals(that.statements)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + version.hashCode();
+ result = 31 * result + statements.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamPolicy")
+ .add("id", id)
+ .add("version", version)
+ .add("statements", statements.isEmpty() ? null : statements)
+ .build();
+ }
+
+ public static class Builder implements IamPolicy.Builder {
+ private String id;
+ private String version;
+ private final List statements = new ArrayList<>();
+
+ private Builder() {
+ }
+
+ private Builder(DefaultIamPolicy policy) {
+ this.id = policy.id;
+ this.version = policy.version;
+ this.statements.addAll(policy.statements);
+ }
+
+ @Override
+ public IamPolicy.Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ @Override
+ public IamPolicy.Builder version(String version) {
+ this.version = version;
+ return this;
+ }
+
+ @Override
+ public IamPolicy.Builder statements(Collection statements) {
+ this.statements.clear();
+ if (statements != null) {
+ this.statements.addAll(statements);
+ }
+ return this;
+ }
+
+ @Override
+ public IamPolicy.Builder addStatement(IamStatement statement) {
+ Validate.paramNotNull(statement, "statement");
+ this.statements.add(statement);
+ return this;
+ }
+
+ @Override
+ public IamPolicy.Builder addStatement(Consumer statement) {
+ Validate.paramNotNull(statement, "statement");
+ this.statements.add(IamStatement.builder().applyMutation(statement).build());
+ return this;
+ }
+
+ @Override
+ public IamPolicy build() {
+ return new DefaultIamPolicy(this);
+ }
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicyReader.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicyReader.java
new file mode 100644
index 000000000000..aafdeaeffd4c
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicyReader.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamCondition;
+import software.amazon.awssdk.policybuilder.iam.IamPolicy;
+import software.amazon.awssdk.policybuilder.iam.IamPolicyReader;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipal;
+import software.amazon.awssdk.policybuilder.iam.IamStatement;
+import software.amazon.awssdk.protocols.jsoncore.JsonNode;
+import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamPolicyReader}.
+ *
+ * @see IamPolicyReader#create
+ */
+@SdkInternalApi
+public final class DefaultIamPolicyReader implements IamPolicyReader {
+ private static final JsonNodeParser JSON_NODE_PARSER = JsonNodeParser.create();
+
+ @Override
+ public IamPolicy read(String policy) {
+ return read(policy.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public IamPolicy read(byte[] policy) {
+ return read(new ByteArrayInputStream(policy));
+ }
+
+ @Override
+ public IamPolicy read(InputStream policy) {
+ return readPolicy(JSON_NODE_PARSER.parse(policy));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ private IamPolicy readPolicy(JsonNode policyNode) {
+ Map policyObject = expectObject(policyNode, "Policy did not start with {");
+
+ return IamPolicy.builder()
+ .version(getString(policyObject, "Version"))
+ .id(getString(policyObject, "Id"))
+ .statements(readStatements(policyObject.get("Statement")))
+ .build();
+ }
+
+ private List readStatements(JsonNode statementsNode) {
+ if (statementsNode == null) {
+ return null;
+ }
+
+ if (statementsNode.isArray()) {
+ return statementsNode.asArray()
+ .stream()
+ .map(n -> expectObject(n, "Statement entry"))
+ .map(this::readStatement)
+ .collect(toList());
+ }
+
+ if (statementsNode.isObject()) {
+ return singletonList(readStatement(statementsNode.asObject()));
+ }
+
+ throw new IllegalArgumentException("Statement was not an array or object.");
+ }
+
+ private IamStatement readStatement(Map statementObject) {
+ return IamStatement.builder()
+ .sid(getString(statementObject, "Sid"))
+ .effect(getString(statementObject, "Effect"))
+ .principals(readPrincipals(statementObject, "Principal"))
+ .notPrincipals(readPrincipals(statementObject, "NotPrincipal"))
+ .actionIds(readStringArray(statementObject, "Action"))
+ .notActionIds(readStringArray(statementObject, "NotAction"))
+ .resourceIds(readStringArray(statementObject, "Resource"))
+ .notResourceIds(readStringArray(statementObject, "NotResource"))
+ .conditions(readConditions(statementObject.get("Condition")))
+ .build();
+ }
+
+ private List readPrincipals(Map statementObject, String name) {
+ JsonNode principalsNode = statementObject.get(name);
+
+ if (principalsNode == null) {
+ return null;
+ }
+
+ if (principalsNode.isString() && principalsNode.asString().equals(IamPrincipal.ALL.id())) {
+ return singletonList(IamPrincipal.ALL);
+ }
+
+ if (principalsNode.isObject()) {
+ List result = new ArrayList<>();
+ principalsNode.asObject().forEach((id, value) -> {
+ result.add(IamPrincipal.create(id, expectString(value, name + " entry value")));
+ });
+ return result;
+ }
+
+ throw new IllegalArgumentException(name + " was not \"" + IamPrincipal.ALL.id() + "\" or an object");
+ }
+
+ private List readConditions(JsonNode conditionNode) {
+ if (conditionNode == null) {
+ return null;
+ }
+
+ Map conditionObject = expectObject(conditionNode, "Condition");
+
+ List result = new ArrayList<>();
+
+ conditionObject.forEach((operator, keyValueNode) -> {
+ Map keyValueObject = expectObject(keyValueNode, "Condition key");
+ keyValueObject.forEach((key, value) -> {
+ if (value.isString()) {
+ result.add(IamCondition.create(operator, key, value.asString()));
+ } else if (value.isArray()) {
+ List values =
+ value.asArray()
+ .stream()
+ .map(valueNode -> expectString(valueNode, "Condition values entry"))
+ .collect(toList());
+ result.addAll(IamCondition.createAll(operator, key, values));
+ }
+ });
+
+ });
+
+ return result;
+ }
+
+ private List readStringArray(Map statementObject, String nodeKey) {
+ JsonNode node = statementObject.get(nodeKey);
+
+ if (node == null) {
+ return null;
+ }
+
+ if (node.isString()) {
+ return singletonList(node.asString());
+ }
+
+ if (node.isArray()) {
+ return node.asArray()
+ .stream()
+ .map(n -> expectString(n, nodeKey + " entry"))
+ .collect(toList());
+ }
+
+ throw new IllegalArgumentException(nodeKey + " was not an array or string");
+ }
+
+ private String getString(Map object, String key) {
+ JsonNode node = object.get(key);
+ if (node == null) {
+ return null;
+ }
+
+ return expectString(node, key);
+ }
+
+ private String expectString(JsonNode node, String name) {
+ Validate.isTrue(node.isString(), "%s was not a string", name);
+ return node.asString();
+ }
+
+ private Map expectObject(JsonNode node, String name) {
+ Validate.isTrue(node.isObject(), "%s was not an object", name);
+ return node.asObject();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicyWriter.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicyWriter.java
new file mode 100644
index 000000000000..553df49d3e10
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPolicyWriter.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamCondition;
+import software.amazon.awssdk.policybuilder.iam.IamConditionKey;
+import software.amazon.awssdk.policybuilder.iam.IamConditionOperator;
+import software.amazon.awssdk.policybuilder.iam.IamPolicy;
+import software.amazon.awssdk.policybuilder.iam.IamPolicyWriter;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipal;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipalType;
+import software.amazon.awssdk.policybuilder.iam.IamStatement;
+import software.amazon.awssdk.policybuilder.iam.IamValue;
+import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
+import software.amazon.awssdk.protocols.jsoncore.JsonWriter;
+import software.amazon.awssdk.protocols.jsoncore.JsonWriter.JsonGeneratorFactory;
+import software.amazon.awssdk.thirdparty.jackson.core.JsonGenerator;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamPolicyWriter}.
+ *
+ * @see IamPolicyWriter#create
+ */
+@SdkInternalApi
+public final class DefaultIamPolicyWriter implements IamPolicyWriter {
+ private static final IamPolicyWriter INSTANCE = IamPolicyWriter.builder().build();
+
+ private final Boolean prettyPrint;
+ @NotNull private final transient JsonGeneratorFactory jsonGeneratorFactory;
+
+ public DefaultIamPolicyWriter(Builder builder) {
+ this.prettyPrint = builder.prettyPrint;
+ if (Boolean.TRUE.equals(builder.prettyPrint)) {
+ this.jsonGeneratorFactory = os -> {
+ JsonGenerator generator = JsonNodeParser.DEFAULT_JSON_FACTORY.createGenerator(os);
+ generator.useDefaultPrettyPrinter();
+ return generator;
+ };
+ } else {
+ this.jsonGeneratorFactory = null;
+ }
+ }
+
+ public static IamPolicyWriter create() {
+ return INSTANCE;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String writeToString(IamPolicy policy) {
+ return new String(writeToBytes(policy), StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public byte[] writeToBytes(IamPolicy policy) {
+ return writePolicy(policy).getBytes();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamPolicyWriter that = (DefaultIamPolicyWriter) o;
+
+ return Objects.equals(prettyPrint, that.prettyPrint);
+ }
+
+ @Override
+ public int hashCode() {
+ return prettyPrint != null ? prettyPrint.hashCode() : 0;
+ }
+
+ private JsonWriter writePolicy(IamPolicy policy) {
+ JsonWriter writer =
+ JsonWriter.builder()
+ .jsonGeneratorFactory(jsonGeneratorFactory)
+ .build();
+
+ writer.writeStartObject();
+
+ writeFieldIfNotNull(writer, "Version", policy.version());
+ writeFieldIfNotNull(writer, "Id", policy.id());
+ writeStatements(writer, policy.statements());
+
+ writer.writeEndObject();
+ return writer;
+ }
+
+ private void writeStatements(JsonWriter writer, List statements) {
+ if (statements.isEmpty()) {
+ return;
+ }
+
+ writer.writeFieldName("Statement");
+
+ if (statements.size() == 1) {
+ writeStatement(writer, statements.get(0));
+ return;
+ }
+
+ writer.writeStartArray();
+ statements.forEach(statement -> {
+ writeStatement(writer, statement);
+ });
+ writer.writeEndArray();
+ }
+
+ private void writeStatement(JsonWriter writer, IamStatement statement) {
+ writer.writeStartObject();
+ writeFieldIfNotNull(writer, "Sid", statement.sid());
+ writeFieldIfNotNull(writer, "Effect", statement.effect());
+ writePrincipals(writer, "Principal", statement.principals());
+ writePrincipals(writer, "NotPrincipal", statement.notPrincipals());
+ writeValueArrayField(writer, "Action", statement.actions());
+ writeValueArrayField(writer, "NotAction", statement.notActions());
+ writeValueArrayField(writer, "Resource", statement.actions());
+ writeValueArrayField(writer, "NotResource", statement.notResources());
+ writeConditions(writer, statement.conditions());
+ writer.writeEndObject();
+ }
+
+ private void writePrincipals(JsonWriter writer, String fieldName, List principals) {
+ if (principals.isEmpty()) {
+ return;
+ }
+
+ if (principals.size() == 1 && principals.get(0).equals(IamPrincipal.ALL)) {
+ writeFieldIfNotNull(writer, fieldName, IamPrincipal.ALL.id());
+ return;
+ }
+
+ principals.forEach(p -> Validate.isTrue(!IamPrincipal.ALL.equals(p),
+ "IamPrincipal.ALL must not be combined with other principals."));
+
+ Map> aggregatedPrincipals = new LinkedHashMap<>();
+ principals.forEach(principal -> {
+ aggregatedPrincipals.computeIfAbsent(principal.type(), t -> new ArrayList<>())
+ .add(principal.id());
+ });
+
+ writer.writeFieldName(fieldName);
+ writer.writeStartObject();
+ aggregatedPrincipals.forEach((principalType, ids) -> {
+ writeArrayField(writer, principalType.value(), ids);
+ });
+ writer.writeEndObject();
+ }
+
+
+ private void writeConditions(JsonWriter writer, List conditions) {
+ if (conditions.isEmpty()) {
+ return;
+ }
+
+ Map>> aggregatedConditions = new LinkedHashMap<>();
+ conditions.forEach(condition -> {
+ aggregatedConditions.computeIfAbsent(condition.operator(), t -> new LinkedHashMap<>())
+ .computeIfAbsent(condition.key(), t -> new ArrayList<>())
+ .add(condition.value());
+ });
+
+ writer.writeFieldName("Condition");
+ writer.writeStartObject();
+ aggregatedConditions.forEach((operator, keyValues) -> {
+ writer.writeFieldName(operator.value());
+ writer.writeStartObject();
+ keyValues.forEach((key, values) -> {
+ writeArrayField(writer, key.value(), values);
+ });
+ writer.writeEndObject();
+ });
+ writer.writeEndObject();
+ }
+
+ private void writeValueArrayField(JsonWriter writer, String fieldName, List extends IamValue> fieldValues) {
+ List values = new ArrayList<>(fieldValues.size());
+ fieldValues.forEach(v -> values.add(v.value()));
+ writeArrayField(writer, fieldName, values);
+ }
+
+ private void writeArrayField(JsonWriter writer,
+ String fieldName, List fieldValues) {
+ if (fieldValues.isEmpty()) {
+ return;
+ }
+
+ if (fieldValues.size() == 1) {
+ writeFieldIfNotNull(writer, fieldName, fieldValues.get(0));
+ return;
+ }
+
+ writer.writeFieldName(fieldName);
+ writer.writeStartArray();
+ fieldValues.forEach(writer::writeValue);
+ writer.writeEndArray();
+ }
+
+ private void writeFieldIfNotNull(JsonWriter writer, String key, IamValue value) {
+ if (value == null) {
+ return;
+ }
+
+ writeFieldIfNotNull(writer, key, value.value());
+ }
+
+ private void writeFieldIfNotNull(JsonWriter writer, String key, String value) {
+ if (value != null) {
+ writer.writeFieldName(key);
+ writer.writeValue(value);
+ }
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ public static class Builder implements IamPolicyWriter.Builder {
+ private Boolean prettyPrint;
+
+ private Builder() {
+ }
+
+ private Builder(DefaultIamPolicyWriter writer) {
+ this.prettyPrint = writer.prettyPrint;
+ }
+
+ @Override
+ public IamPolicyWriter.Builder prettyPrint(Boolean prettyPrint) {
+ this.prettyPrint = prettyPrint;
+ return this;
+ }
+
+ @Override
+ public IamPolicyWriter build() {
+ return new DefaultIamPolicyWriter(this);
+ }
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPrincipal.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPrincipal.java
new file mode 100644
index 000000000000..9e99d45ba8cb
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPrincipal.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipal;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipalType;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamPrincipal}.
+ *
+ * @see IamPrincipal#create
+ */
+@SdkInternalApi
+public final class DefaultIamPrincipal implements IamPrincipal {
+ @NotNull private final IamPrincipalType type;
+ @NotNull private final String id;
+
+ private DefaultIamPrincipal(Builder builder) {
+ this.type = Validate.paramNotNull(builder.type, "principalType");
+ this.id = Validate.paramNotNull(builder.id, "principalId");
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public IamPrincipalType type() {
+ return type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamPrincipal that = (DefaultIamPrincipal) o;
+
+ if (!type.equals(that.type)) {
+ return false;
+ }
+ return id.equals(that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = type.hashCode();
+ result = 31 * result + id.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamPrincipal")
+ .add("type", type.value())
+ .add("id", id)
+ .build();
+ }
+
+ public static class Builder implements IamPrincipal.Builder {
+ private IamPrincipalType type;
+ private String id;
+
+ private Builder() {
+ }
+
+ private Builder(DefaultIamPrincipal principal) {
+ this.type = principal.type;
+ this.id = principal.id;
+ }
+
+ @Override
+ public IamPrincipal.Builder type(IamPrincipalType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public IamPrincipal.Builder type(String type) {
+ this.type = type == null ? null : IamPrincipalType.create(type);
+ return this;
+ }
+
+ @Override
+ public IamPrincipal.Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ @Override
+ public IamPrincipal build() {
+ return new DefaultIamPrincipal(this);
+ }
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPrincipalType.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPrincipalType.java
new file mode 100644
index 000000000000..2e909220d135
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamPrincipalType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipalType;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamPrincipalType}.
+ *
+ * @see IamPrincipalType#create
+ */
+@SdkInternalApi
+public final class DefaultIamPrincipalType implements IamPrincipalType {
+ @NotNull private final String value;
+
+ public DefaultIamPrincipalType(String value) {
+ this.value = Validate.paramNotNull(value, "principalTypeValue");
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamPrincipalType that = (DefaultIamPrincipalType) o;
+
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamPrincipalType")
+ .add("value", value)
+ .build();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamResource.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamResource.java
new file mode 100644
index 000000000000..8e928dad37b1
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamResource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamResource;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamResource}.
+ *
+ * @see IamResource#create
+ */
+@SdkInternalApi
+public final class DefaultIamResource implements IamResource {
+ @NotNull private final String value;
+
+ public DefaultIamResource(String value) {
+ this.value = Validate.paramNotNull(value, "resourceValue");
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamResource that = (DefaultIamResource) o;
+
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamResource")
+ .add("value", value)
+ .build();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamStatement.java b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamStatement.java
new file mode 100644
index 000000000000..274da7e75e01
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/main/java/software/amazon/awssdk/policybuilder/iam/internal/DefaultIamStatement.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import software.amazon.awssdk.annotations.NotNull;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.policybuilder.iam.IamAction;
+import software.amazon.awssdk.policybuilder.iam.IamCondition;
+import software.amazon.awssdk.policybuilder.iam.IamConditionKey;
+import software.amazon.awssdk.policybuilder.iam.IamConditionOperator;
+import software.amazon.awssdk.policybuilder.iam.IamEffect;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipal;
+import software.amazon.awssdk.policybuilder.iam.IamPrincipalType;
+import software.amazon.awssdk.policybuilder.iam.IamResource;
+import software.amazon.awssdk.policybuilder.iam.IamStatement;
+import software.amazon.awssdk.utils.ToString;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * Default implementation of {@link IamStatement}.
+ *
+ * @see IamStatement#builder
+ */
+@SdkInternalApi
+public final class DefaultIamStatement implements IamStatement {
+ private final String sid;
+ @NotNull private final IamEffect effect;
+ @NotNull private final List principals;
+ @NotNull private final List notPrincipals;
+ @NotNull private final List actions;
+ @NotNull private final List notActions;
+ @NotNull private final List resources;
+ @NotNull private final List notResources;
+ @NotNull private final List conditions;
+
+ public DefaultIamStatement(Builder builder) {
+ this.sid = builder.sid;
+ this.effect = Validate.paramNotNull(builder.effect, "statementEffect");
+ this.principals = new ArrayList<>(builder.principals);
+ this.notPrincipals = new ArrayList<>(builder.notPrincipals);
+ this.actions = new ArrayList<>(builder.actions);
+ this.notActions = new ArrayList<>(builder.notActions);
+ this.resources = new ArrayList<>(builder.resources);
+ this.notResources = new ArrayList<>(builder.notResources);
+ this.conditions = new ArrayList<>(builder.conditions);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String sid() {
+ return sid;
+ }
+
+ @Override
+ public IamEffect effect() {
+ return effect;
+ }
+
+ @Override
+ public List principals() {
+ return Collections.unmodifiableList(principals);
+ }
+
+ @Override
+ public List notPrincipals() {
+ return Collections.unmodifiableList(notPrincipals);
+ }
+
+ @Override
+ public List actions() {
+ return Collections.unmodifiableList(actions);
+ }
+
+ @Override
+ public List notActions() {
+ return Collections.unmodifiableList(notActions);
+ }
+
+ @Override
+ public List resources() {
+ return Collections.unmodifiableList(resources);
+ }
+
+ @Override
+ public List notResources() {
+ return Collections.unmodifiableList(notResources);
+ }
+
+ @Override
+ public List conditions() {
+ return Collections.unmodifiableList(conditions);
+ }
+
+ @Override
+ public IamStatement.Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultIamStatement that = (DefaultIamStatement) o;
+
+ if (!Objects.equals(sid, that.sid)) {
+ return false;
+ }
+ if (!effect.equals(that.effect)) {
+ return false;
+ }
+ if (!principals.equals(that.principals)) {
+ return false;
+ }
+ if (!notPrincipals.equals(that.notPrincipals)) {
+ return false;
+ }
+ if (!actions.equals(that.actions)) {
+ return false;
+ }
+ if (!notActions.equals(that.notActions)) {
+ return false;
+ }
+ if (!resources.equals(that.resources)) {
+ return false;
+ }
+ if (!notResources.equals(that.notResources)) {
+ return false;
+ }
+ if (!conditions.equals(that.conditions)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = sid != null ? sid.hashCode() : 0;
+ result = 31 * result + effect.hashCode();
+ result = 31 * result + principals.hashCode();
+ result = 31 * result + notPrincipals.hashCode();
+ result = 31 * result + actions.hashCode();
+ result = 31 * result + notActions.hashCode();
+ result = 31 * result + resources.hashCode();
+ result = 31 * result + notResources.hashCode();
+ result = 31 * result + conditions.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return ToString.builder("IamStatement")
+ .add("sid", sid)
+ .add("effect", effect)
+ .add("principals", principals.isEmpty() ? null : principals)
+ .add("notPrincipals", notPrincipals.isEmpty() ? null : notPrincipals)
+ .add("actions", actions.isEmpty() ? null : actions)
+ .add("notActions", notActions.isEmpty() ? null : notActions)
+ .add("resources", resources.isEmpty() ? null : resources)
+ .add("notResources", notResources.isEmpty() ? null : notResources)
+ .add("conditions", conditions.isEmpty() ? null : conditions)
+ .build();
+ }
+
+ public static class Builder implements IamStatement.Builder {
+ private String sid;
+ private IamEffect effect;
+ private final List principals = new ArrayList<>();
+ private final List notPrincipals = new ArrayList<>();
+ private final List actions = new ArrayList<>();
+ private final List notActions = new ArrayList<>();
+ private final List resources = new ArrayList<>();
+ private final List notResources = new ArrayList<>();
+ private final List conditions = new ArrayList<>();
+
+ private Builder() {
+ }
+
+ private Builder(DefaultIamStatement statement) {
+ this.sid = statement.sid;
+ this.effect = statement.effect;
+ this.principals.addAll(statement.principals);
+ this.notPrincipals.addAll(statement.notPrincipals);
+ this.actions.addAll(statement.actions);
+ this.notActions.addAll(statement.notActions);
+ this.resources.addAll(statement.resources);
+ this.notResources.addAll(statement.notResources);
+ this.conditions.addAll(statement.conditions);
+
+ }
+
+ @Override
+ public IamStatement.Builder sid(String sid) {
+ this.sid = sid;
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder effect(IamEffect effect) {
+ this.effect = effect;
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder effect(String effect) {
+ this.effect = IamEffect.create(effect);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder principals(Collection principals) {
+ this.principals.clear();
+ if (principals != null) {
+ this.principals.addAll(principals);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addPrincipal(IamPrincipal principal) {
+ Validate.paramNotNull(principal, "principal");
+ this.principals.add(principal);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addPrincipal(Consumer principal) {
+ Validate.paramNotNull(principal, "principal");
+ this.principals.add(IamPrincipal.builder().applyMutation(principal).build());
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addPrincipal(IamPrincipalType iamPrincipalType, String principal) {
+ return addPrincipal(IamPrincipal.create(iamPrincipalType, principal));
+ }
+
+ @Override
+ public IamStatement.Builder addPrincipal(String iamPrincipalType, String principal) {
+ return addPrincipal(IamPrincipal.create(iamPrincipalType, principal));
+ }
+
+ @Override
+ public IamStatement.Builder addPrincipals(IamPrincipalType principalType, Collection principals) {
+ Validate.paramNotNull(principalType, "principals");
+ for (String principal : principals) {
+ this.principals.add(IamPrincipal.create(principalType, principal));
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addPrincipals(String principalType, Collection principals) {
+ return addPrincipals(IamPrincipalType.create(principalType), principals);
+ }
+
+ @Override
+ public IamStatement.Builder notPrincipals(Collection notPrincipals) {
+ this.notPrincipals.clear();
+ if (notPrincipals != null) {
+ this.notPrincipals.addAll(notPrincipals);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotPrincipal(IamPrincipal notPrincipal) {
+ Validate.paramNotNull(notPrincipal, "notPrincipal");
+ this.notPrincipals.add(notPrincipal);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotPrincipal(Consumer notPrincipal) {
+ Validate.paramNotNull(notPrincipal, "notPrincipal");
+ this.notPrincipals.add(IamPrincipal.builder().applyMutation(notPrincipal).build());
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotPrincipal(IamPrincipalType iamPrincipalType, String principal) {
+ return addNotPrincipal(IamPrincipal.create(iamPrincipalType, principal));
+ }
+
+ @Override
+ public IamStatement.Builder addNotPrincipal(String iamPrincipalType, String principal) {
+ return addNotPrincipal(IamPrincipal.create(iamPrincipalType, principal));
+ }
+
+ @Override
+ public IamStatement.Builder addNotPrincipals(IamPrincipalType notPrincipalType, Collection notPrincipals) {
+ Validate.paramNotNull(notPrincipals, "notPrincipals");
+ for (String notPrincipal : notPrincipals) {
+ this.notPrincipals.add(IamPrincipal.create(notPrincipalType, notPrincipal));
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotPrincipals(String notPrincipalType, Collection notPrincipals) {
+ return addNotPrincipals(IamPrincipalType.create(notPrincipalType), notPrincipals);
+ }
+
+ @Override
+ public IamStatement.Builder actions(Collection actions) {
+ this.actions.clear();
+ if (actions != null) {
+ this.actions.addAll(actions);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder actionIds(Collection actions) {
+ this.actions.clear();
+ if (actions != null) {
+ actions.forEach(this::addAction);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addAction(IamAction action) {
+ Validate.paramNotNull(action, "action");
+ this.actions.add(action);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addAction(String action) {
+ this.actions.add(IamAction.create(action));
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder notActions(Collection notActions) {
+ this.notActions.clear();
+ if (notActions != null) {
+ this.notActions.addAll(notActions);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder notActionIds(Collection notActions) {
+ this.notActions.clear();
+ if (notActions != null) {
+ notActions.forEach(this::addNotAction);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotAction(IamAction notAction) {
+ Validate.paramNotNull(notAction, "notAction");
+ this.notActions.add(notAction);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotAction(String notAction) {
+ this.notActions.add(IamAction.create(notAction));
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder resources(Collection resources) {
+ this.resources.clear();
+ if (resources != null) {
+ this.resources.addAll(resources);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder resourceIds(Collection resources) {
+ this.resources.clear();
+ if (resources != null) {
+ resources.forEach(this::addResource);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addResource(IamResource resource) {
+ Validate.paramNotNull(resource, "resource");
+ this.resources.add(resource);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addResource(String resource) {
+ this.resources.add(IamResource.create(resource));
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder notResources(Collection notResources) {
+ this.notResources.clear();
+ if (notResources != null) {
+ this.notResources.addAll(notResources);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder notResourceIds(Collection notResources) {
+ this.notResources.clear();
+ if (notResources != null) {
+ notResources.forEach(this::addNotResource);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotResource(IamResource notResource) {
+ this.notResources.add(notResource);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addNotResource(String notResource) {
+ this.notResources.add(IamResource.create(notResource));
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder conditions(Collection conditions) {
+ this.conditions.clear();
+ if (conditions != null) {
+ this.conditions.addAll(conditions);
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addCondition(IamCondition condition) {
+ Validate.paramNotNull(condition, "condition");
+ this.conditions.add(condition);
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addCondition(Consumer condition) {
+ Validate.paramNotNull(condition, "condition");
+ this.conditions.add(IamCondition.builder().applyMutation(condition).build());
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addCondition(IamConditionOperator operator, IamConditionKey key, String value) {
+ this.conditions.add(IamCondition.create(operator, key, value));
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addCondition(IamConditionOperator operator, String key, String value) {
+ return addCondition(operator, IamConditionKey.create(key), value);
+ }
+
+ @Override
+ public IamStatement.Builder addCondition(String operator, String key, String value) {
+ this.conditions.add(IamCondition.create(operator, key, value));
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addConditions(IamConditionOperator operator,
+ IamConditionKey key,
+ Collection values) {
+ Validate.paramNotNull(values, "values");
+ for (String value : values) {
+ this.conditions.add(IamCondition.create(operator, key, value));
+ }
+ return this;
+ }
+
+ @Override
+ public IamStatement.Builder addConditions(IamConditionOperator operator, String key, Collection values) {
+ return addConditions(operator, IamConditionKey.create(key), values);
+ }
+
+ @Override
+ public IamStatement.Builder addConditions(String operator, String key, Collection values) {
+ return addConditions(IamConditionOperator.create(operator), IamConditionKey.create(key), values);
+ }
+
+ @Override
+ public IamStatement build() {
+ return new DefaultIamStatement(this);
+ }
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/EqualsHashCodeTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/EqualsHashCodeTest.java
new file mode 100644
index 000000000000..e0f11fc61c79
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/EqualsHashCodeTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class EqualsHashCodeTest {
+ @Test
+ public void allClasses_equalsHashCode_isCorrect() {
+ EqualsVerifier.forPackage("software.amazon.awssdk.policybuilder.iam", true)
+ .except(c -> c.isMemberClass() || c.isAnonymousClass())
+ .verify();
+ }
+}
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamActionTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamActionTest.java
new file mode 100644
index 000000000000..f1f588d790e1
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamActionTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class IamActionTest {
+ @Test
+ public void iamActionValueNotNull() {
+ assertThatThrownBy(() -> IamAction.create(null)).hasMessageMatching(".*[Aa]ction.*");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionKeyTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionKeyTest.java
new file mode 100644
index 000000000000..28b635ed4abd
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionKeyTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class IamConditionKeyTest {
+ @Test
+ public void iamConditionKeyValueNotNull() {
+ assertThatThrownBy(() -> IamConditionKey.create(null)).hasMessageMatching(".*[Cc]ondition.*[Kk]ey.*");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionOperatorTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionOperatorTest.java
new file mode 100644
index 000000000000..e13f092469fb
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionOperatorTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class IamConditionOperatorTest {
+ @Test
+ public void iamConditionOperatorValueNotNull() {
+ assertThatThrownBy(() -> IamConditionOperator.create(null)).hasMessageMatching(".*[Cc]ondition.*[Oo]perator.*");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionTest.java
new file mode 100644
index 000000000000..10e2542a9806
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamConditionTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.function.Consumer;
+import org.junit.jupiter.api.Test;
+
+class IamConditionTest {
+ private static final IamCondition FULL_CONDITION =
+ IamCondition.builder()
+ .operator("Operator")
+ .key("Key")
+ .value("Value") // TODO: Id?
+ .build();
+
+ @Test
+ public void constructorsWork() {
+ assertThat(IamCondition.create("Operator", "Key", "Value")).isEqualTo(FULL_CONDITION);
+
+ assertThat(IamCondition.create(IamConditionOperator.create("Operator"),
+ IamConditionKey.create("Key"),
+ "Value"))
+ .isEqualTo(FULL_CONDITION);
+
+ assertThat(IamCondition.create(IamConditionOperator.create("Operator"),
+ "Key",
+ "Value"))
+ .isEqualTo(FULL_CONDITION);
+
+ assertThat(IamCondition.createAll("Operator",
+ "Key",
+ asList("Value1", "Value2")))
+ .containsExactly(IamCondition.create("Operator", "Key", "Value1"),
+ IamCondition.create("Operator", "Key", "Value2"));
+
+ assertThat(IamCondition.createAll(IamConditionOperator.create("Operator"),
+ "Key",
+ asList("Value1", "Value2")))
+ .containsExactly(IamCondition.create("Operator", "Key", "Value1"),
+ IamCondition.create("Operator", "Key", "Value2"));
+
+ assertThat(IamCondition.createAll(IamConditionOperator.create("Operator"),
+ IamConditionKey.create("Key"),
+ asList("Value1", "Value2")))
+ .containsExactly(IamCondition.create("Operator", "Key", "Value1"),
+ IamCondition.create("Operator", "Key", "Value2"));
+ }
+
+ @Test
+ public void simpleGettersSettersWork() {
+ assertThat(FULL_CONDITION.operator().value()).isEqualTo("Operator");
+ assertThat(FULL_CONDITION.key().value()).isEqualTo("Key");
+ assertThat(FULL_CONDITION.value()).isEqualTo("Value");
+ }
+
+ @Test
+ public void toBuilderPreservesValues() {
+ assertThat(FULL_CONDITION.toBuilder().build()).isEqualTo(FULL_CONDITION);
+ }
+
+ @Test
+ public void operatorSettersWork() {
+ assertThat(condition(c -> c.operator("Operator")).operator().value()).isEqualTo("Operator");
+ assertThat(condition(c -> c.operator(IamConditionOperator.create("Operator"))).operator().value()).isEqualTo("Operator");
+ }
+
+ @Test
+ public void keySettersWork() {
+ assertThat(condition(c -> c.key("Key")).key().value()).isEqualTo("Key");
+ assertThat(condition(c -> c.key(IamConditionKey.create("Key"))).key().value()).isEqualTo("Key");
+ }
+
+ public IamCondition condition(Consumer condition) {
+ return IamCondition.builder()
+ .operator("Operator")
+ .key("Key")
+ .value("Value")
+ .applyMutation(condition)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamEffectTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamEffectTest.java
new file mode 100644
index 000000000000..58c688d24da1
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamEffectTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class IamEffectTest {
+ @Test
+ public void iamEffectValueNotNull() {
+ assertThatThrownBy(() -> IamEffect.create(null)).hasMessageMatching(".*[Ee]ffect.*");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyReaderTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyReaderTest.java
new file mode 100644
index 000000000000..4b1f5d341b0c
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyReaderTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static software.amazon.awssdk.policybuilder.iam.IamEffect.ALLOW;
+
+import org.junit.jupiter.api.Test;
+
+class IamPolicyReaderTest {
+ private static final IamPrincipal PRINCIPAL_1 = IamPrincipal.ALL;
+ private static final IamPrincipal PRINCIPAL_2 = IamPrincipal.create("2", "*");
+ private static final IamResource RESOURCE_1 = IamResource.create("1");
+ private static final IamResource RESOURCE_2 = IamResource.create("2");
+ private static final IamAction ACTION_1 = IamAction.create("1");
+ private static final IamAction ACTION_2 = IamAction.create("2");
+ private static final IamCondition CONDITION_1 = IamCondition.create("1", "K1", "V1");
+ private static final IamCondition CONDITION_2 = IamCondition.create("1", "K2", "V1");
+ private static final IamCondition CONDITION_3 = IamCondition.create("1", "K2", "V2");
+ private static final IamCondition CONDITION_4 = IamCondition.create("2", "K1", "V1");
+
+ private static final IamStatement FULL_STATEMENT =
+ IamStatement.builder()
+ .effect(ALLOW)
+ .sid("Sid")
+ .principals(asList(PRINCIPAL_1, PRINCIPAL_2))
+ .notPrincipals(asList(PRINCIPAL_1, PRINCIPAL_2))
+ .resources(asList(RESOURCE_1, RESOURCE_2))
+ .notResources(asList(RESOURCE_1, RESOURCE_2))
+ .actions(asList(ACTION_1, ACTION_2))
+ .notActions(asList(ACTION_1, ACTION_2))
+ .conditions(asList(CONDITION_1, CONDITION_2, CONDITION_3, CONDITION_4))
+ .build();
+
+ private static final IamPolicy FULL_POLICY =
+ IamPolicy.builder()
+ .id("Id")
+ .version("Version")
+ .statements(asList(FULL_STATEMENT, FULL_STATEMENT))
+ .build();
+
+ private static final IamStatement MINIMAL_STATEMENT = IamStatement.builder().effect(ALLOW).build();
+
+ private static final IamPolicy MINIMAL_POLICY =
+ IamPolicy.builder()
+ .version("Version")
+ .statements(singletonList(MINIMAL_STATEMENT))
+ .build();
+
+ private static final IamStatement ONE_ELEMENT_LISTS_STATEMENT =
+ IamStatement.builder()
+ .effect(ALLOW)
+ .sid("Sid")
+ .principals(singletonList(PRINCIPAL_1))
+ .notPrincipals(singletonList(PRINCIPAL_1))
+ .resources(singletonList(RESOURCE_1))
+ .notResources(singletonList(RESOURCE_1))
+ .actions(singletonList(ACTION_1))
+ .notActions(singletonList(ACTION_1))
+ .conditions(singletonList(CONDITION_1))
+ .build();
+
+ private static final IamPolicy ONE_ELEMENT_LISTS_POLICY =
+ IamPolicy.builder()
+ .version("Version")
+ .statements(singletonList(ONE_ELEMENT_LISTS_STATEMENT))
+ .build();
+
+ private static final IamPolicyReader READER = IamPolicyReader.create();
+
+ @Test
+ public void readFullPolicyWorks() {
+ assertThat(READER.read("{\"Version\":\"Version\","
+ + "\"Id\":\"Id\","
+ + "\"Statement\":["
+ + "{\"Sid\":\"Sid\",\"Effect\":\"Allow\",\"Principal\":{\"*\":\"*\",\"2\":\"*\"},\"NotPrincipal\":{\"*\":\"*\",\"2\":\"*\"},\"Action\":[\"1\",\"2\"],\"NotAction\":[\"1\",\"2\"],\"Resource\":[\"1\",\"2\"],\"NotResource\":[\"1\",\"2\"],\"Condition\":{\"1\":{\"K1\":\"V1\",\"K2\":[\"V1\",\"V2\"]},\"2\":{\"K1\":\"V1\"}}},"
+ + "{\"Sid\":\"Sid\",\"Effect\":\"Allow\",\"Principal\":{\"*\":\"*\",\"2\":\"*\"},\"NotPrincipal\":{\"*\":\"*\",\"2\":\"*\"},\"Action\":[\"1\",\"2\"],\"NotAction\":[\"1\",\"2\"],\"Resource\":[\"1\",\"2\"],\"NotResource\":[\"1\",\"2\"],\"Condition\":{\"1\":{\"K1\":\"V1\",\"K2\":[\"V1\",\"V2\"]},\"2\":{\"K1\":\"V1\"}}}"
+ + "]}"))
+ .isEqualTo(FULL_POLICY);
+ }
+
+ @Test
+ public void prettyWriteFullPolicyWorks() {
+ assertThat(READER.read("{\n"
+ + " \"Version\" : \"Version\",\n"
+ + " \"Id\" : \"Id\",\n"
+ + " \"Statement\" : [ {\n"
+ + " \"Sid\" : \"Sid\",\n"
+ + " \"Effect\" : \"Allow\",\n"
+ + " \"Principal\" : {\n"
+ + " \"*\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"NotPrincipal\" : {\n"
+ + " \"*\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"Action\" : [ \"1\", \"2\" ],\n"
+ + " \"NotAction\" : [ \"1\", \"2\" ],\n"
+ + " \"Resource\" : [ \"1\", \"2\" ],\n"
+ + " \"NotResource\" : [ \"1\", \"2\" ],\n"
+ + " \"Condition\" : {\n"
+ + " \"1\" : {\n"
+ + " \"K1\" : \"V1\",\n"
+ + " \"K2\" : [ \"V1\", \"V2\" ]\n"
+ + " },\n"
+ + " \"2\" : {\n"
+ + " \"K1\" : \"V1\"\n"
+ + " }\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"Sid\" : \"Sid\",\n"
+ + " \"Effect\" : \"Allow\",\n"
+ + " \"Principal\" : {\n"
+ + " \"*\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"NotPrincipal\" : {\n"
+ + " \"*\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"Action\" : [ \"1\", \"2\" ],\n"
+ + " \"NotAction\" : [ \"1\", \"2\" ],\n"
+ + " \"Resource\" : [ \"1\", \"2\" ],\n"
+ + " \"NotResource\" : [ \"1\", \"2\" ],\n"
+ + " \"Condition\" : {\n"
+ + " \"1\" : {\n"
+ + " \"K1\" : \"V1\",\n"
+ + " \"K2\" : [ \"V1\", \"V2\" ]\n"
+ + " },\n"
+ + " \"2\" : {\n"
+ + " \"K1\" : \"V1\"\n"
+ + " }\n"
+ + " }\n"
+ + " } ]\n"
+ + "}"))
+ .isEqualTo(FULL_POLICY);
+ }
+
+ @Test
+ public void writeMinimalPolicyWorks() {
+ assertThat(READER.read("{\n"
+ + " \"Version\" : \"Version\",\n"
+ + " \"Statement\" : {\n"
+ + " \"Effect\" : \"Allow\"\n"
+ + " }\n"
+ + "}"))
+ .isEqualTo(MINIMAL_POLICY);
+ }
+
+ @Test
+ public void singleElementListsAreWrittenAsNonArrays() {
+ assertThat(READER.read("{\n"
+ + " \"Version\" : \"Version\",\n"
+ + " \"Statement\" : {\n"
+ + " \"Sid\" : \"Sid\",\n"
+ + " \"Effect\" : \"Allow\",\n"
+ + " \"Principal\" : \"*\",\n"
+ + " \"NotPrincipal\" : \"*\",\n"
+ + " \"Action\" : \"1\",\n"
+ + " \"NotAction\" : \"1\",\n"
+ + " \"Resource\" : \"1\",\n"
+ + " \"NotResource\" : \"1\",\n"
+ + " \"Condition\" : {\n"
+ + " \"1\" : {\n"
+ + " \"K1\" : \"V1\"\n"
+ + " }\n"
+ + " }\n"
+ + " }\n"
+ + "}"))
+ .isEqualTo(ONE_ELEMENT_LISTS_POLICY);
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyTest.java
new file mode 100644
index 000000000000..7127e32e943b
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static software.amazon.awssdk.policybuilder.iam.IamEffect.ALLOW;
+import static software.amazon.awssdk.policybuilder.iam.IamEffect.DENY;
+
+import java.util.function.Consumer;
+import org.junit.jupiter.api.Test;
+
+class IamPolicyTest {
+ private static final IamStatement ALLOW_STATEMENT = IamStatement.builder().effect(ALLOW).build();
+ private static final IamStatement DENY_STATEMENT = IamStatement.builder().effect(DENY).build();
+ private static final String SMALLEST_POLICY_JSON = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\"}}";
+ private static final IamPolicy SMALLEST_POLICY = IamPolicy.builder().addStatement(ALLOW_STATEMENT).build();
+
+
+ private static final IamPolicy FULL_POLICY = IamPolicy.builder()
+ .id("Id")
+ .version("Version")
+ .statements(singletonList(ALLOW_STATEMENT))
+ .build();
+
+ @Test
+ public void fromJson_delegatesToIamPolicyReader() {
+ IamPolicy iamPolicy = IamPolicy.fromJson(SMALLEST_POLICY_JSON);
+ assertThat(iamPolicy.version()).isNotNull();
+ assertThat(iamPolicy.statements()).containsExactly(ALLOW_STATEMENT);
+ }
+
+ @Test
+ public void toJson_delegatesToIamPolicyWriter() {
+ assertThat(SMALLEST_POLICY.toJson()).isEqualTo(SMALLEST_POLICY_JSON);
+ }
+
+ @Test
+ public void simpleGettersSettersWork() {
+ assertThat(FULL_POLICY.id()).isEqualTo("Id");
+ assertThat(FULL_POLICY.version()).isEqualTo("Version");
+ assertThat(FULL_POLICY.statements()).containsExactly(ALLOW_STATEMENT);
+ }
+
+ @Test
+ public void toBuilderPreservesValues() {
+ assertThat(FULL_POLICY.toBuilder().build()).isEqualTo(FULL_POLICY);
+ }
+
+ @Test
+ public void toStringIncludesAllValues() {
+ assertThat(FULL_POLICY.toString())
+ .isEqualTo("IamPolicy(id=Id, version=Version, statements=[IamStatement(effect=IamEffect(value=Allow))])");
+ }
+
+ @Test
+ public void statementGettersSettersWork() {
+ assertThat(policy(p -> p.statements(asList(ALLOW_STATEMENT, DENY_STATEMENT))).statements())
+ .containsExactly(ALLOW_STATEMENT, DENY_STATEMENT);
+ assertThat(policy(p -> p.addStatement(ALLOW_STATEMENT).addStatement(DENY_STATEMENT)).statements())
+ .containsExactly(ALLOW_STATEMENT, DENY_STATEMENT);
+ assertThat(policy(p -> p.addStatement(s -> s.effect(ALLOW)).addStatement(s -> s.effect(DENY))).statements())
+ .containsExactly(ALLOW_STATEMENT, DENY_STATEMENT);
+ }
+
+ @Test
+ public void statementCollectionSettersResetsList() {
+ assertThat(policy(p -> p.statements(asList(ALLOW_STATEMENT, DENY_STATEMENT))
+ .statements(singletonList(DENY_STATEMENT))).statements())
+ .containsExactly(DENY_STATEMENT);
+ }
+
+ private IamPolicy policy(Consumer policy) {
+ return IamPolicy.builder().applyMutation(policy).build();
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyWriterTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyWriterTest.java
new file mode 100644
index 000000000000..8e2c6811b66b
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPolicyWriterTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static software.amazon.awssdk.policybuilder.iam.IamEffect.ALLOW;
+
+import org.junit.jupiter.api.Test;
+
+class IamPolicyWriterTest {
+ private static final IamPrincipal PRINCIPAL_1 = IamPrincipal.create("1", "*");
+ private static final IamPrincipal PRINCIPAL_2 = IamPrincipal.create("2", "*");
+ private static final IamResource RESOURCE_1 = IamResource.create("1");
+ private static final IamResource RESOURCE_2 = IamResource.create("2");
+ private static final IamAction ACTION_1 = IamAction.create("1");
+ private static final IamAction ACTION_2 = IamAction.create("2");
+ private static final IamCondition CONDITION_1 = IamCondition.create("1", "K1", "V1");
+ private static final IamCondition CONDITION_2 = IamCondition.create("2", "K1", "V1");
+ private static final IamCondition CONDITION_3 = IamCondition.create("1", "K2", "V1");
+ private static final IamCondition CONDITION_4 = IamCondition.create("1", "K2", "V2");
+
+ private static final IamStatement FULL_STATEMENT =
+ IamStatement.builder()
+ .effect(ALLOW)
+ .sid("Sid")
+ .principals(asList(PRINCIPAL_1, PRINCIPAL_2))
+ .notPrincipals(asList(PRINCIPAL_1, PRINCIPAL_2))
+ .resources(asList(RESOURCE_1, RESOURCE_2))
+ .notResources(asList(RESOURCE_1, RESOURCE_2))
+ .actions(asList(ACTION_1, ACTION_2))
+ .notActions(asList(ACTION_1, ACTION_2))
+ .conditions(asList(CONDITION_1, CONDITION_2, CONDITION_3, CONDITION_4))
+ .build();
+
+ private static final IamPolicy FULL_POLICY =
+ IamPolicy.builder()
+ .id("Id")
+ .version("Version")
+ .statements(asList(FULL_STATEMENT, FULL_STATEMENT))
+ .build();
+
+ private static final IamStatement MINIMAL_STATEMENT = IamStatement.builder().effect(ALLOW).build();
+
+ private static final IamPolicy MINIMAL_POLICY =
+ IamPolicy.builder()
+ .version("Version")
+ .statements(singletonList(MINIMAL_STATEMENT))
+ .build();
+
+ private static final IamStatement ONE_ELEMENT_LISTS_STATEMENT =
+ IamStatement.builder()
+ .effect(ALLOW)
+ .sid("Sid")
+ .principals(singletonList(IamPrincipal.ALL))
+ .notPrincipals(singletonList(IamPrincipal.ALL))
+ .resources(singletonList(RESOURCE_1))
+ .notResources(singletonList(RESOURCE_1))
+ .actions(singletonList(ACTION_1))
+ .notActions(singletonList(ACTION_1))
+ .conditions(singletonList(CONDITION_1))
+ .build();
+
+ private static final IamPolicy ONE_ELEMENT_LISTS_POLICY =
+ IamPolicy.builder()
+ .version("Version")
+ .statements(singletonList(ONE_ELEMENT_LISTS_STATEMENT))
+ .build();
+
+ private static final IamPolicyWriter DEFAULT_WRITER = IamPolicyWriter.create();
+ private static final IamPolicyWriter PRETTY_WRITER = IamPolicyWriter.builder().prettyPrint(true).build();
+
+ @Test
+ public void toBuilderPreservesSettings() {
+ assertThat(PRETTY_WRITER.toBuilder().build()).isEqualTo(PRETTY_WRITER);
+ }
+
+ @Test
+ public void writeFullPolicyWorks() {
+ assertThat(DEFAULT_WRITER.writeToString(FULL_POLICY))
+ .isEqualTo("{\"Version\":\"Version\","
+ + "\"Id\":\"Id\","
+ + "\"Statement\":["
+ + "{\"Sid\":\"Sid\",\"Effect\":\"Allow\",\"Principal\":{\"1\":\"*\",\"2\":\"*\"},\"NotPrincipal\":{\"1\":\"*\",\"2\":\"*\"},\"Action\":[\"1\",\"2\"],\"NotAction\":[\"1\",\"2\"],\"Resource\":[\"1\",\"2\"],\"NotResource\":[\"1\",\"2\"],\"Condition\":{\"1\":{\"K1\":\"V1\",\"K2\":[\"V1\",\"V2\"]},\"2\":{\"K1\":\"V1\"}}},"
+ + "{\"Sid\":\"Sid\",\"Effect\":\"Allow\",\"Principal\":{\"1\":\"*\",\"2\":\"*\"},\"NotPrincipal\":{\"1\":\"*\",\"2\":\"*\"},\"Action\":[\"1\",\"2\"],\"NotAction\":[\"1\",\"2\"],\"Resource\":[\"1\",\"2\"],\"NotResource\":[\"1\",\"2\"],\"Condition\":{\"1\":{\"K1\":\"V1\",\"K2\":[\"V1\",\"V2\"]},\"2\":{\"K1\":\"V1\"}}}"
+ + "]}");
+ }
+
+ @Test
+ public void prettyWriteFullPolicyWorks() {
+ assertThat(PRETTY_WRITER.writeToString(FULL_POLICY))
+ .isEqualTo("{\n"
+ + " \"Version\" : \"Version\",\n"
+ + " \"Id\" : \"Id\",\n"
+ + " \"Statement\" : [ {\n"
+ + " \"Sid\" : \"Sid\",\n"
+ + " \"Effect\" : \"Allow\",\n"
+ + " \"Principal\" : {\n"
+ + " \"1\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"NotPrincipal\" : {\n"
+ + " \"1\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"Action\" : [ \"1\", \"2\" ],\n"
+ + " \"NotAction\" : [ \"1\", \"2\" ],\n"
+ + " \"Resource\" : [ \"1\", \"2\" ],\n"
+ + " \"NotResource\" : [ \"1\", \"2\" ],\n"
+ + " \"Condition\" : {\n"
+ + " \"1\" : {\n"
+ + " \"K1\" : \"V1\",\n"
+ + " \"K2\" : [ \"V1\", \"V2\" ]\n"
+ + " },\n"
+ + " \"2\" : {\n"
+ + " \"K1\" : \"V1\"\n"
+ + " }\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"Sid\" : \"Sid\",\n"
+ + " \"Effect\" : \"Allow\",\n"
+ + " \"Principal\" : {\n"
+ + " \"1\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"NotPrincipal\" : {\n"
+ + " \"1\" : \"*\",\n"
+ + " \"2\" : \"*\"\n"
+ + " },\n"
+ + " \"Action\" : [ \"1\", \"2\" ],\n"
+ + " \"NotAction\" : [ \"1\", \"2\" ],\n"
+ + " \"Resource\" : [ \"1\", \"2\" ],\n"
+ + " \"NotResource\" : [ \"1\", \"2\" ],\n"
+ + " \"Condition\" : {\n"
+ + " \"1\" : {\n"
+ + " \"K1\" : \"V1\",\n"
+ + " \"K2\" : [ \"V1\", \"V2\" ]\n"
+ + " },\n"
+ + " \"2\" : {\n"
+ + " \"K1\" : \"V1\"\n"
+ + " }\n"
+ + " }\n"
+ + " } ]\n"
+ + "}");
+ }
+
+ @Test
+ public void writeMinimalPolicyWorks() {
+ assertThat(PRETTY_WRITER.writeToString(MINIMAL_POLICY))
+ .isEqualTo("{\n"
+ + " \"Version\" : \"Version\",\n"
+ + " \"Statement\" : {\n"
+ + " \"Effect\" : \"Allow\"\n"
+ + " }\n"
+ + "}");
+ }
+
+ @Test
+ public void singleElementListsAreWrittenAsNonArrays() {
+ assertThat(PRETTY_WRITER.writeToString(ONE_ELEMENT_LISTS_POLICY))
+ .isEqualTo("{\n"
+ + " \"Version\" : \"Version\",\n"
+ + " \"Statement\" : {\n"
+ + " \"Sid\" : \"Sid\",\n"
+ + " \"Effect\" : \"Allow\",\n"
+ + " \"Principal\" : \"*\",\n"
+ + " \"NotPrincipal\" : \"*\",\n"
+ + " \"Action\" : \"1\",\n"
+ + " \"NotAction\" : \"1\",\n"
+ + " \"Resource\" : \"1\",\n"
+ + " \"NotResource\" : \"1\",\n"
+ + " \"Condition\" : {\n"
+ + " \"1\" : {\n"
+ + " \"K1\" : \"V1\"\n"
+ + " }\n"
+ + " }\n"
+ + " }\n"
+ + "}");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalTest.java
new file mode 100644
index 000000000000..63ce795d9700
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+class IamPrincipalTest {
+ private static final IamPrincipal FULL_PRINCIPAL =
+ IamPrincipal.builder()
+ .type("Type")
+ .id("Id")
+ .build();
+
+ @Test
+ public void constructorsWork() {
+ assertThat(IamPrincipal.create("Type", "Id")).isEqualTo(FULL_PRINCIPAL);
+ assertThat(IamPrincipal.create(IamPrincipalType.create("Type"), "Id")).isEqualTo(FULL_PRINCIPAL);
+ assertThat(IamPrincipal.createAll("Type", asList("Id1", "Id2")))
+ .containsExactly(IamPrincipal.create("Type", "Id1"), IamPrincipal.create("Type", "Id2"));
+ assertThat(IamPrincipal.createAll(IamPrincipalType.create("Type"), asList("Id1", "Id2")))
+ .containsExactly(IamPrincipal.create("Type", "Id1"), IamPrincipal.create("Type", "Id2"));
+ }
+
+ @Test
+ public void simpleGettersSettersWork() {
+ assertThat(FULL_PRINCIPAL.id()).isEqualTo("Id");
+ assertThat(FULL_PRINCIPAL.type().value()).isEqualTo("Type");
+ }
+
+ @Test
+ public void toBuilderPreservesValues() {
+ IamPrincipal principal = FULL_PRINCIPAL.toBuilder().build();
+ assertThat(principal.id()).isEqualTo("Id");
+ assertThat(principal.type().value()).isEqualTo("Type");
+ }
+
+ @Test
+ public void typeSettersWork() {
+ assertThat(IamPrincipal.builder().type("Type").id("Id").build().type().value()).isEqualTo("Type");
+ assertThat(IamPrincipal.builder().type(IamPrincipalType.create("Type")).id("Id").build().type().value()).isEqualTo("Type");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalTypeTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalTypeTest.java
new file mode 100644
index 000000000000..8000f47befbd
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamPrincipalTypeTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class IamPrincipalTypeTest {
+ @Test
+ public void iamPrincipalTypeValueNotNull() {
+ assertThatThrownBy(() -> IamPrincipalType.create(null)).hasMessageMatching(".*[Pp]rincipal.*[Tt]ype.*");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamResourceTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamResourceTest.java
new file mode 100644
index 000000000000..7926fd41fa5e
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamResourceTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class IamResourceTest {
+ @Test
+ public void iamResourceValueNotNull() {
+ assertThatThrownBy(() -> IamResource.create(null)).hasMessageMatching(".*[Rr]esource.*");
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamStatementTest.java b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamStatementTest.java
new file mode 100644
index 000000000000..68cec4766eb6
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/java/software/amazon/awssdk/policybuilder/iam/IamStatementTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 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.awssdk.policybuilder.iam;
+
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static software.amazon.awssdk.policybuilder.iam.IamEffect.ALLOW;
+
+import java.util.function.Consumer;
+import org.junit.jupiter.api.Test;
+
+class IamStatementTest {
+ private static final IamPrincipal PRINCIPAL_1 = IamPrincipal.create("1", "*");
+ private static final IamPrincipal PRINCIPAL_2 = IamPrincipal.create("2", "*");
+ private static final IamResource RESOURCE_1 = IamResource.create("1");
+ private static final IamResource RESOURCE_2 = IamResource.create("2");
+ private static final IamAction ACTION_1 = IamAction.create("1");
+ private static final IamAction ACTION_2 = IamAction.create("2");
+ private static final IamCondition CONDITION_1 = IamCondition.create("1", "K", "V");
+ private static final IamCondition CONDITION_2 = IamCondition.create("2", "K", "V");
+
+ private static final IamStatement FULL_STATEMENT =
+ IamStatement.builder()
+ .effect(ALLOW)
+ .sid("Sid")
+ .principals(singletonList(PRINCIPAL_1))
+ .notPrincipals(singletonList(PRINCIPAL_2))
+ .resources(singletonList(RESOURCE_1))
+ .notResources(singletonList(RESOURCE_2))
+ .actions(singletonList(ACTION_1))
+ .notActions(singletonList(ACTION_2))
+ .conditions(singletonList(CONDITION_1))
+ .build();
+
+ @Test
+ public void simpleGettersSettersWork() {
+ assertThat(FULL_STATEMENT.sid()).isEqualTo("Sid");
+ assertThat(FULL_STATEMENT.effect()).isEqualTo(ALLOW);
+ assertThat(FULL_STATEMENT.principals()).containsExactly(PRINCIPAL_1);
+ assertThat(FULL_STATEMENT.notPrincipals()).containsExactly(PRINCIPAL_2);
+ assertThat(FULL_STATEMENT.resources()).containsExactly(RESOURCE_1);
+ assertThat(FULL_STATEMENT.notResources()).containsExactly(RESOURCE_2);
+ assertThat(FULL_STATEMENT.actions()).containsExactly(ACTION_1);
+ assertThat(FULL_STATEMENT.notActions()).containsExactly(ACTION_2);
+ assertThat(FULL_STATEMENT.conditions()).containsExactly(CONDITION_1);
+ }
+
+ @Test
+ public void toBuilderPreservesValues() {
+ assertThat(FULL_STATEMENT.toBuilder().build()).isEqualTo(FULL_STATEMENT);
+ }
+
+ @Test
+ public void toStringIncludesAllValues() {
+ assertThat(FULL_STATEMENT.toString())
+ .isEqualTo("IamStatement("
+ + "sid=Sid, "
+ + "effect=IamEffect(value=Allow), "
+ + "principals=[IamPrincipal(type=1, id=*)], "
+ + "notPrincipals=[IamPrincipal(type=2, id=*)], "
+ + "actions=[IamAction(value=1)], "
+ + "notActions=[IamAction(value=2)], "
+ + "resources=[IamResource(value=1)], "
+ + "notResources=[IamResource(value=2)], "
+ + "conditions=[IamCondition(operator=1, key=K, value=V)])");
+ }
+
+ @Test
+ public void effectIsRequired() {
+ assertThatThrownBy(() -> IamStatement.builder().build()).hasMessageMatching(".*[Ee]ffect.*");
+ }
+
+ @Test
+ public void effectGettersSettersWork() {
+ assertThat(statement(s -> s.effect(ALLOW)).effect()).isEqualTo(ALLOW);
+ assertThat(statement(s -> s.effect("Allow")).effect()).isEqualTo(ALLOW);
+ }
+
+ @Test
+ public void principalGettersSettersWork() {
+ assertThat(statement(s -> s.principals(asList(PRINCIPAL_1, PRINCIPAL_2))).principals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addPrincipal(PRINCIPAL_1)
+ .addPrincipal(PRINCIPAL_2)).principals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addPrincipal(p -> p.type("1").id("*"))
+ .addPrincipal(p -> p.type("2").id("*"))).principals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addPrincipal("1", "*")
+ .addPrincipal("2", "*")).principals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addPrincipal(IamPrincipalType.create("1"), "*")
+ .addPrincipal(IamPrincipalType.create("2"), "*")).principals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addPrincipals(IamPrincipalType.create("1"), asList("x", "y"))).principals())
+ .containsExactly(IamPrincipal.create("1", "x"),
+ IamPrincipal.create("1", "y"));
+ assertThat(statement(s -> s.addPrincipals("1", asList("x", "y"))).principals())
+ .containsExactly(IamPrincipal.create("1", "x"),
+ IamPrincipal.create("1", "y"));
+ }
+
+ @Test
+ public void principalsCollectionSettersResetsList() {
+ assertThat(statement(s -> s.principals(asList(PRINCIPAL_1, PRINCIPAL_2))
+ .principals(singletonList(PRINCIPAL_1))).principals())
+ .containsExactly(PRINCIPAL_1);
+ }
+
+ @Test
+ public void notPrincipalGettersSettersWork() {
+ assertThat(statement(s -> s.notPrincipals(asList(PRINCIPAL_1, PRINCIPAL_2))).notPrincipals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addNotPrincipal(PRINCIPAL_1)
+ .addNotPrincipal(PRINCIPAL_2)).notPrincipals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addNotPrincipal(p -> p.type("1").id("*"))
+ .addNotPrincipal(p -> p.type("2").id("*"))).notPrincipals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addNotPrincipal("1", "*")
+ .addNotPrincipal("2", "*")).notPrincipals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addNotPrincipal(IamPrincipalType.create("1"), "*")
+ .addNotPrincipal(IamPrincipalType.create("2"), "*")).notPrincipals())
+ .containsExactly(PRINCIPAL_1, PRINCIPAL_2);
+ assertThat(statement(s -> s.addNotPrincipals(IamPrincipalType.create("1"), asList("x", "y"))).notPrincipals())
+ .containsExactly(IamPrincipal.create("1", "x"),
+ IamPrincipal.create("1", "y"));
+ assertThat(statement(s -> s.addNotPrincipals("1", asList("x", "y"))).notPrincipals())
+ .containsExactly(IamPrincipal.create("1", "x"),
+ IamPrincipal.create("1", "y"));
+ }
+
+ @Test
+ public void notPrincipalsCollectionSettersResetsList() {
+ assertThat(statement(s -> s.notPrincipals(asList(PRINCIPAL_1, PRINCIPAL_2))
+ .notPrincipals(singletonList(PRINCIPAL_1))).notPrincipals())
+ .containsExactly(PRINCIPAL_1);
+ }
+
+ @Test
+ public void actionGettersSettersWork() {
+ assertThat(statement(s -> s.actions(asList(ACTION_1, ACTION_2))).actions())
+ .containsExactly(ACTION_1, ACTION_2);
+ assertThat(statement(s -> s.actionIds(asList("1", "2"))).actions())
+ .containsExactly(ACTION_1, ACTION_2);
+ assertThat(statement(s -> s.addAction(ACTION_1).addAction(ACTION_2)).actions())
+ .containsExactly(ACTION_1, ACTION_2);
+ assertThat(statement(s -> s.addAction("1").addAction("2")).actions())
+ .containsExactly(ACTION_1, ACTION_2);
+ }
+
+ @Test
+ public void actionCollectionSettersResetsList() {
+ assertThat(statement(s -> s.actions(asList(ACTION_1, ACTION_2))
+ .actions(singletonList(ACTION_2))).actions())
+ .containsExactly(ACTION_2);
+ }
+
+ @Test
+ public void notActionGettersSettersWork() {
+ assertThat(statement(s -> s.notActions(asList(ACTION_1, ACTION_2))).notActions())
+ .containsExactly(ACTION_1, ACTION_2);
+ assertThat(statement(s -> s.notActionIds(asList("1", "2"))).notActions())
+ .containsExactly(ACTION_1, ACTION_2);
+ assertThat(statement(s -> s.addNotAction(ACTION_1).addNotAction(ACTION_2)).notActions())
+ .containsExactly(ACTION_1, ACTION_2);
+ assertThat(statement(s -> s.addNotAction("1").addNotAction("2")).notActions())
+ .containsExactly(ACTION_1, ACTION_2);
+ }
+
+ @Test
+ public void notActionCollectionSettersResetsList() {
+ assertThat(statement(s -> s.notActions(asList(ACTION_1, ACTION_2))
+ .notActions(singletonList(ACTION_2))).notActions())
+ .containsExactly(ACTION_2);
+ }
+
+ @Test
+ public void resourceGettersSettersWork() {
+ assertThat(statement(s -> s.resources(asList(RESOURCE_1, RESOURCE_2))).resources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ assertThat(statement(s -> s.resourceIds(asList("1", "2"))).resources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ assertThat(statement(s -> s.addResource(RESOURCE_1).addResource(RESOURCE_2)).resources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ assertThat(statement(s -> s.addResource("1").addResource("2")).resources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ }
+
+ @Test
+ public void resourceCollectionSettersResetsList() {
+ assertThat(statement(s -> s.resources(asList(RESOURCE_1, RESOURCE_2))
+ .resources(singletonList(RESOURCE_2))).resources())
+ .containsExactly(RESOURCE_2);
+ }
+
+ @Test
+ public void notResourceGettersSettersWork() {
+ assertThat(statement(s -> s.notResources(asList(RESOURCE_1, RESOURCE_2))).notResources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ assertThat(statement(s -> s.notResourceIds(asList("1", "2"))).notResources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ assertThat(statement(s -> s.addNotResource(RESOURCE_1).addNotResource(RESOURCE_2)).notResources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ assertThat(statement(s -> s.addNotResource("1").addNotResource("2")).notResources())
+ .containsExactly(RESOURCE_1, RESOURCE_2);
+ }
+
+ @Test
+ public void notResourceCollectionSettersResetsList() {
+ assertThat(statement(s -> s.notResources(asList(RESOURCE_1, RESOURCE_2))
+ .notResources(singletonList(RESOURCE_2))).notResources())
+ .containsExactly(RESOURCE_2);
+ }
+
+ @Test
+ public void conditionGettersSettersWork() {
+ assertThat(statement(s -> s.conditions(asList(CONDITION_1, CONDITION_2))).conditions())
+ .containsExactly(CONDITION_1, CONDITION_2);
+ assertThat(statement(s -> s.addCondition(CONDITION_1)
+ .addCondition(CONDITION_2)).conditions())
+ .containsExactly(CONDITION_1, CONDITION_2);
+ assertThat(statement(s -> s.addCondition(p -> p.operator("1").key("K").value("V"))
+ .addCondition(p -> p.operator("2").key("K").value("V"))).conditions())
+ .containsExactly(CONDITION_1, CONDITION_2);
+ assertThat(statement(s -> s.addCondition(IamConditionOperator.create("1"), IamConditionKey.create("K"), "V")
+ .addCondition(IamConditionOperator.create("2"), IamConditionKey.create("K"), "V")).conditions())
+ .containsExactly(CONDITION_1, CONDITION_2);
+ assertThat(statement(s -> s.addCondition(IamConditionOperator.create("1"), "K", "V")
+ .addCondition(IamConditionOperator.create("2"), "K", "V")).conditions())
+ .containsExactly(CONDITION_1, CONDITION_2);
+ assertThat(statement(s -> s.addCondition("1", "K", "V")
+ .addCondition("2", "K", "V")).conditions())
+ .containsExactly(CONDITION_1, CONDITION_2);
+ assertThat(statement(s -> s.addConditions(IamConditionOperator.create("1"),
+ IamConditionKey.create("K"),
+ asList("V1", "V2"))).conditions())
+ .containsExactly(IamCondition.create("1", "K", "V1"),
+ IamCondition.create("1", "K", "V2"));
+ assertThat(statement(s -> s.addConditions(IamConditionOperator.create("1"), "K", asList("V1", "V2"))).conditions())
+ .containsExactly(IamCondition.create("1", "K", "V1"),
+ IamCondition.create("1", "K", "V2"));
+ assertThat(statement(s -> s.addConditions("1", "K", asList("V1", "V2"))).conditions())
+ .containsExactly(IamCondition.create("1", "K", "V1"),
+ IamCondition.create("1", "K", "V2"));
+ }
+
+ @Test
+ public void conditionsCollectionSettersResetsList() {
+ assertThat(statement(s -> s.conditions(asList(CONDITION_1, CONDITION_1))
+ .conditions(singletonList(CONDITION_1))).conditions())
+ .containsExactly(CONDITION_1);
+ }
+
+ private IamStatement statement(Consumer statement) {
+ return IamStatement.builder().effect(ALLOW).applyMutation(statement).build();
+ }
+}
\ No newline at end of file
diff --git a/services-custom/iam-policy-builder/src/test/resources/log4j2.properties b/services-custom/iam-policy-builder/src/test/resources/log4j2.properties
new file mode 100644
index 000000000000..827f0c09a093
--- /dev/null
+++ b/services-custom/iam-policy-builder/src/test/resources/log4j2.properties
@@ -0,0 +1,41 @@
+#
+# Copyright 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.
+#
+
+status = warn
+
+appender.console.type = Console
+appender.console.name = ConsoleAppender
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n%throwable
+
+rootLogger.level = info
+rootLogger.appenderRef.stdout.ref = ConsoleAppender
+
+# Uncomment below to enable more specific logging
+#
+#logger.sdk.name = software.amazon.awssdk
+#logger.sdk.level = debug
+#
+#logger.tm.name = software.amazon.awssdk.transfer.s3
+#logger.tm.level = info
+#
+#logger.request.name = software.amazon.awssdk.request
+#logger.request.level = debug
+#
+#logger.apache.name = org.apache.http.wire
+#logger.apache.level = debug
+#
+#logger.netty.name = io.netty.handler.logging
+#logger.netty.level = debug
diff --git a/services-custom/pom.xml b/services-custom/pom.xml
index 56f3cd257afc..97cbd3d9b85b 100644
--- a/services-custom/pom.xml
+++ b/services-custom/pom.xml
@@ -30,6 +30,7 @@
dynamodb-enhanced
s3-transfer-manager
+ iam-policy-builder