diff --git a/.doc_gen/metadata/iam_metadata.yaml b/.doc_gen/metadata/iam_metadata.yaml index 1a50e216dbf..883bec651de 100644 --- a/.doc_gen/metadata/iam_metadata.yaml +++ b/.doc_gen/metadata/iam_metadata.yaml @@ -3523,3 +3523,40 @@ iam_Scenario_CreateUserAssumeRole: iam: {CreateUser, CreateAccessKey, CreateRole, CreatePolicy, AttachRolePolicy, PutUserPolicy, DeleteUserPolicy, DetachRolePolicy, DeletePolicy, DeleteRole, DeleteAccessKey, DeleteUser} +iam_Scenario_IamPolicyBuilder: + title: Work with the &IAM; Policy Builder API using an &AWS; SDK + title_abbrev: Work with the &IAM; Policy Builder API + synopsis_list: + - Create &IAM; policies by using the object-oriented API. + - Use the &IAM; Policy Builder API with the &IAM; service. + category: Scenarios + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/iam + sdkguide: sdk-for-java/latest/developer-guide/feature-iam-policy-builder.html + excerpts: + - description: The examples use the following imports. + snippet_tags: + - iam.java2.policy_builder.policy_builder_examples.imports + - description: Create a time-based policy. + snippet_tags: + - iam.java2.policy_builder.time_based + - description: Create a policy with multiple conditions. + snippet_tags: + - iam.java2.policy_builder.multiple_conditions + - description: Use principals in a policy. + snippet_tags: + - iam.java2.policy_builder.specify_principals + - description: Allow cross-account access. + snippet_tags: + - iam.java2.policy_builder.allow_cross_account_access + - description: Build and upload an IamPolicy. + snippet_tags: + - iam.java2.policy_builder.create_and_upload_policy + - description: Download and work with an IamPolicy. + snippet_tags: + - iam.java2.policy_builder.create_new_based_on_existing_policy + services: + iam: {CreatePolicy, GetPolicy, GetPolicyVersion} diff --git a/javav2/example_code/iam/Readme.md b/javav2/example_code/iam/Readme.md index e1561d11c31..e71bfb9a421 100644 --- a/javav2/example_code/iam/Readme.md +++ b/javav2/example_code/iam/Readme.md @@ -56,7 +56,8 @@ Code excerpts that show you how to call individual service functions. Code examples that show you how to accomplish a specific task by calling multiple functions within the same service. -* [Create a user and assume a role](src/main/java/com/example/iam/IAMScenario.java) +* [Create a user and assume a role](src/main/java/com/example/iam/IAMScenario.java) +* [Work with the IAM Policy Builder API](src/main/java/com/example/iam/IamPolicyBuilderExamples.java) ## Run the examples diff --git a/javav2/example_code/iam/pom.xml b/javav2/example_code/iam/pom.xml index eb23a6ea66b..ce39b5f3b01 100644 --- a/javav2/example_code/iam/pom.xml +++ b/javav2/example_code/iam/pom.xml @@ -7,8 +7,9 @@ IAMJ2Project 1.0-SNAPSHOT - 1.8 - 1.8 + 11 + 11 + 1.7.28 @@ -27,7 +28,14 @@ software.amazon.awssdk bom - 2.20.45 + 2.20.112 + pom + import + + + org.apache.logging.log4j + log4j-bom + 2.20.0 pom import @@ -70,6 +78,14 @@ software.amazon.awssdk iam + + software.amazon.awssdk + iam-policy-builder + + + software.amazon.awssdk + dynamodb + com.googlecode.json-simple json-simple @@ -87,5 +103,31 @@ software.amazon.awssdk sts + + software.amazon.awssdk + sso + + + software.amazon.awssdk + ssooidc + + + org.apache.logging.log4j + log4j-slf4j2-impl + + + software.amazon.awssdk + accessanalyzer + + + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + + \ No newline at end of file diff --git a/javav2/example_code/iam/src/main/java/com/example/iam/IamPolicyBuilderExamples.java b/javav2/example_code/iam/src/main/java/com/example/iam/IamPolicyBuilderExamples.java new file mode 100644 index 00000000000..9551ef2831f --- /dev/null +++ b/javav2/example_code/iam/src/main/java/com/example/iam/IamPolicyBuilderExamples.java @@ -0,0 +1,249 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package com.example.iam; + +// snippet-start:[iam.java2.policy_builder.policy_builder_examples.imports] + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.policybuilder.iam.IamConditionOperator; +import software.amazon.awssdk.policybuilder.iam.IamEffect; +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.IamResource; +import software.amazon.awssdk.policybuilder.iam.IamStatement; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.iam.IamClient; +import software.amazon.awssdk.services.iam.model.GetPolicyResponse; +import software.amazon.awssdk.services.iam.model.GetPolicyVersionResponse; +import software.amazon.awssdk.services.sts.StsClient; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +// snippet-end:[iam.java2.policy_builder.policy_builder_examples.imports] + +public class IamPolicyBuilderExamples { + private static final Logger logger = LoggerFactory.getLogger(IamPolicyBuilderExamples.class); + + public static void main(String[] args) { + IamPolicyBuilderExamples iamPolicyBuilderExamples = new IamPolicyBuilderExamples(); + // snippet-start:[iam.java2.policy_builder.policy_builder_examples.iamclient_build] + IamClient iam = IamClient.builder().region(Region.AWS_GLOBAL).build(); + // snippet-end:[iam.java2.policy_builder.policy_builder_examples.iamclient_build] + + iamPolicyBuilderExamples.runCreateAndUploadPolicyExample(iam); + iamPolicyBuilderExamples.runCreateNewBasedOnExisingPolicyExample(iam); + + if (iam != null) { + iam.close(); + } + + iamPolicyBuilderExamples.runTimeBasedPolicyExample(); + iamPolicyBuilderExamples.runMultipleConditionsExample(); + iamPolicyBuilderExamples.runSpecifyPrincipalsExample(); + iamPolicyBuilderExamples.runAllowCrossAccountAccessExample(); + } + + // snippet-start:[iam.java2.policy_builder.create_and_upload_policy] + public String createAndUploadPolicyExample(IamClient iam, String accountID, String policyName) { + // Build the policy. + IamPolicy policy = + IamPolicy.builder() // 'version' defaults to "2012-10-17". + .addStatement(IamStatement.builder() + .effect(IamEffect.ALLOW) + .addAction("dynamodb:PutItem") + .addResource("arn:aws:dynamodb:us-east-1:" + accountID + ":table/exampleTableName") + .build()) + .build(); + // Upload the policy. + iam.createPolicy(r -> r.policyName(policyName).policyDocument(policy.toJson())); + return policy.toJson(IamPolicyWriter.builder().prettyPrint(true).build()); + } + // snippet-end:[iam.java2.policy_builder.create_and_upload_policy] + + private void runCreateAndUploadPolicyExample(IamClient iam) { + String accountId = getAccountID(); + String policyName = "AllowPutItemToExampleTable"; + + String jsonPolicy = createAndUploadPolicyExample(iam, accountId, policyName); + logger.info(jsonPolicy); + + GetPolicyResponse putItemPolicy = iam.getPolicy(b -> b.policyArn("arn:aws:iam::" + accountId + ":policy/" + policyName)); + iam.deletePolicy(b -> b.policyArn(putItemPolicy.policy().arn())); + logger.info("Policy [{}] deleted", putItemPolicy.policy().arn()); + } + + // snippet-start:[iam.java2.policy_builder.create_new_based_on_existing_policy] + public String createNewBasedOnExistingPolicyExample(IamClient iam, String accountID, String policyName, String newPolicyName) { + + String policyArn = "arn:aws:iam::" + accountID + ":policy/" + policyName; + GetPolicyResponse getPolicyResponse = iam.getPolicy(r -> r.policyArn(policyArn)); + + String policyVersion = getPolicyResponse.policy().defaultVersionId(); + GetPolicyVersionResponse getPolicyVersionResponse = + iam.getPolicyVersion(r -> r.policyArn(policyArn).versionId(policyVersion)); + + // Create an IamPolicy instance from the JSON string returned from IAM. + String decodedPolicy = URLDecoder.decode(getPolicyVersionResponse.policyVersion().document(), StandardCharsets.UTF_8); + IamPolicy policy = IamPolicy.fromJson(decodedPolicy); + + /* + All IamPolicy components are immutable, so use the copy method that creates a new instance that + can be altered in the same method call. + + Add the ability to get an item from DynamoDB as an additional action. + */ + IamStatement newStatement = policy.statements().get(0).copy(s -> s.addAction("dynamodb:GetItem")); + + // Create a new statement that replaces the original statement. + IamPolicy newPolicy = policy.copy(p -> p.statements(Arrays.asList(newStatement))); + + // Upload the new policy. IAM now has both policies. + iam.createPolicy(r -> r.policyName(newPolicyName) + .policyDocument(newPolicy.toJson())); + + return newPolicy.toJson(IamPolicyWriter.builder().prettyPrint(true).build()); + } + // snippet-end:[iam.java2.policy_builder.create_new_based_on_existing_policy] + + private void runCreateNewBasedOnExisingPolicyExample(IamClient iam) { + String accountID = getAccountID(); + String policyName = "AllowPutItemToExampleTable"; + String newPolicyName = "AllowGetAndPutItemToExampleTable"; + // First part of the example is to create the policy. + String jsonPolicy = createAndUploadPolicyExample(iam, accountID, policyName); + logger.info(jsonPolicy); + + String jsonNewPolicy = createNewBasedOnExistingPolicyExample(iam, accountID, policyName, newPolicyName); + logger.info(jsonNewPolicy); + + // Delete the two policies + GetPolicyResponse putItemPolicy = iam.getPolicy(b -> b.policyArn("arn:aws:iam::" + accountID + ":policy/" + policyName)); + iam.deletePolicy(b -> b.policyArn(putItemPolicy.policy().arn())); + logger.info("Policy [{}] deleted", putItemPolicy.policy().arn()); + + GetPolicyResponse getAndPutItemPolicy = iam.getPolicy(b -> b.policyArn("arn:aws:iam::" + accountID + ":policy/" + newPolicyName)); + iam.deletePolicy(b -> b.policyArn(getAndPutItemPolicy.policy().arn())); + logger.info("Policy [{}] deleted", getAndPutItemPolicy.policy().arn()); + } + + // snippet-start:[iam.java2.policy_builder.multiple_conditions] + public String multipleConditionsExample() { + IamPolicy policy = IamPolicy.builder() + .addStatement(b -> b + .effect(IamEffect.ALLOW) + .addAction("dynamodb:GetItem") + .addAction("dynamodb:BatchGetItem") + .addAction("dynamodb:Query") + .addAction("dynamodb:PutItem") + .addAction("dynamodb:UpdateItem") + .addAction("dynamodb:DeleteItem") + .addAction("dynamodb:BatchWriteItem") + .addResource("arn:aws:dynamodb:*:*:table/table-name") + .addConditions(IamConditionOperator.STRING_EQUALS.addPrefix("ForAllValues:"), + "dynamodb:Attributes", + List.of("column-name1", "column-name2", "column-name3")) + .addCondition(b1 -> b1.operator(IamConditionOperator.STRING_EQUALS.addSuffix("IfExists")) + .key("dynamodb:Select") + .value("SPECIFIC_ATTRIBUTES"))) + .build(); + + return policy.toJson(IamPolicyWriter.builder() + .prettyPrint(true).build()); + } + // snippet-end:[iam.java2.policy_builder.multiple_conditions] + + private void runMultipleConditionsExample() { + String jsonPolicy = multipleConditionsExample(); + logger.info(jsonPolicy); + + } + + // snippet-start:[iam.java2.policy_builder.time_based] + public String timeBasedPolicyExample() { + IamPolicy policy = IamPolicy.builder() + .addStatement(b -> b + .effect(IamEffect.ALLOW) + .addAction("dynamodb:GetItem") + .addResource(IamResource.ALL) + .addCondition(b1 -> b1 + .operator(IamConditionOperator.DATE_GREATER_THAN) + .key("aws:CurrentTime") + .value("2020-04-01T00:00:00Z")) + .addCondition(b1 -> b1 + .operator(IamConditionOperator.DATE_LESS_THAN) + .key("aws:CurrentTime") + .value("2020-06-30T23:59:59Z"))) + .build(); + + // Use an IamPolicyWriter to write out the JSON string to a more readable format. + return policy.toJson(IamPolicyWriter.builder() + .prettyPrint(true) + .build()); + } + // snippet-end:[iam.java2.policy_builder.time_based] + + private void runTimeBasedPolicyExample() { + String policyJson = timeBasedPolicyExample(); + logger.info(policyJson); + } + + // snippet-start:[iam.java2.policy_builder.specify_principals] + public String specifyPrincipalsExample() { + IamPolicy policy = IamPolicy.builder() + .addStatement(b -> b + .effect(IamEffect.DENY) + .addAction("s3:*") + .addPrincipal(IamPrincipal.ALL) + .addResource("arn:aws:s3:::BUCKETNAME/*") + .addResource("arn:aws:s3:::BUCKETNAME") + .addCondition(b1 -> b1 + .operator(IamConditionOperator.ARN_NOT_EQUALS) + .key("aws:PrincipalArn") + .value("arn:aws:iam::444455556666:user/user-name"))) + .build(); + return policy.toJson(IamPolicyWriter.builder() + .prettyPrint(true).build()); + } + // snippet-end:[iam.java2.policy_builder.specify_principals] + + private void runSpecifyPrincipalsExample() { + String policyJson = specifyPrincipalsExample(); + logger.info(policyJson); + } + + // snippet-start:[iam.java2.policy_builder.allow_cross_account_access] + public String allowCrossAccountAccessExample() { + IamPolicy policy = IamPolicy.builder() + .addStatement(b -> b + .effect(IamEffect.ALLOW) + .addPrincipal(IamPrincipalType.AWS, "111122223333") + .addAction("s3:PutObject") + .addResource("arn:aws:s3:::DOC-EXAMPLE-BUCKET/*") + .addCondition(b1 -> b1 + .operator(IamConditionOperator.STRING_EQUALS) + .key("s3:x-amz-acl") + .value("bucket-owner-full-control"))) + .build(); + return policy.toJson(IamPolicyWriter.builder() + .prettyPrint(true).build()); + } + // snippet-end:[iam.java2.policy_builder.allow_cross_account_access] + + private void runAllowCrossAccountAccessExample() { + String policyJson = allowCrossAccountAccessExample(); + logger.info(policyJson); + } + + String getAccountID() { + try (StsClient stsClient = StsClient.create()) { + return stsClient.getCallerIdentity().account(); + } + } +} diff --git a/javav2/example_code/iam/src/main/resources/log4j2.xml b/javav2/example_code/iam/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..f212ad40e01 --- /dev/null +++ b/javav2/example_code/iam/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/javav2/example_code/iam/src/test/java/com/example/iam/IamPolicyBuilderExamplesTest.java b/javav2/example_code/iam/src/test/java/com/example/iam/IamPolicyBuilderExamplesTest.java new file mode 100644 index 00000000000..b203764751f --- /dev/null +++ b/javav2/example_code/iam/src/test/java/com/example/iam/IamPolicyBuilderExamplesTest.java @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package com.example.iam; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.accessanalyzer.AccessAnalyzerClient; +import software.amazon.awssdk.services.accessanalyzer.model.PolicyType; +import software.amazon.awssdk.services.accessanalyzer.model.ValidatePolicyResponse; +import software.amazon.awssdk.services.iam.IamClient; +import software.amazon.awssdk.services.iam.model.GetPolicyResponse; + +class IamPolicyBuilderExamplesTest { + private static final Logger logger = LoggerFactory.getLogger(IamPolicyBuilderExamplesTest.class); + private IamPolicyBuilderExamples examples; + private IamClient iam; + + private static void analyze(String policyJson, PolicyType policyType) { + try (AccessAnalyzerClient analyzerClient = AccessAnalyzerClient.create()) { + final ValidatePolicyResponse response = analyzerClient.validatePolicy(b -> b + .policyDocument(policyJson) + .policyType(policyType)); + response.findings().forEach(f -> + logger.info("Type [{}]; Detail [{}]", f.findingType().name(), f.findingDetails())); + Assertions.assertEquals(0, response.findings().size()); + } + } + + @BeforeEach + void setUp() { + examples = new IamPolicyBuilderExamples(); + iam = IamClient.builder().region(Region.AWS_GLOBAL).build(); + } + + @AfterEach + void tearDown() { + if (iam != null) { + iam.close(); + } + } + + /** + * If this test succeeds, the syntax of the policy that is created is checked by the IAM service on upload. + */ + @Test + @Tag("IntegrationTest") + void createAndUploadPolicyExample() { + String accountId = examples.getAccountID(); + String policyName = "AllowPutItemToExampleTable"; + String jsonPolicy = examples.createAndUploadPolicyExample(iam, accountId, policyName); + logger.info(jsonPolicy); + + GetPolicyResponse putItemPolicy = iam.getPolicy(b -> b.policyArn("arn:aws:iam::" + accountId + ":policy/" + policyName)); + iam.deletePolicy(b -> b.policyArn(putItemPolicy.policy().arn())); + logger.info("Policy [{}] deleted", putItemPolicy.policy().arn()); + } + + /** + * If this test succeeds, the syntax of the policies that are created and uploaded will be checked by the IAM service. + */ + @Test + @Tag("IntegrationTest") + void createNewBasedOnExisingPolicyExample() { + String accountID = examples.getAccountID(); + String policyName = "AllowPutItemToExampleTable"; + String newPolicyName = "AllowGetAndPutItemToExampleTable"; + // First part of the example is to create the policy. + String jsonPolicy = examples.createAndUploadPolicyExample(iam, accountID, policyName); + logger.info(jsonPolicy); + + String jsonNewPolicy = examples.createNewBasedOnExistingPolicyExample(iam, accountID, policyName, newPolicyName); + logger.info(jsonNewPolicy); + + // Delete the two policies + GetPolicyResponse putItemPolicy = iam.getPolicy(b -> b.policyArn("arn:aws:iam::" + accountID + ":policy/" + policyName)); + iam.deletePolicy(b -> b.policyArn(putItemPolicy.policy().arn())); + logger.info("Policy [{}] deleted", putItemPolicy.policy().arn()); + + GetPolicyResponse getAndPutItemPolicy = iam.getPolicy(b -> b.policyArn("arn:aws:iam::" + accountID + ":policy/" + newPolicyName)); + iam.deletePolicy(b -> b.policyArn(getAndPutItemPolicy.policy().arn())); + logger.info("Policy [{}] deleted", getAndPutItemPolicy.policy().arn()); + } + + @Test + @Tag("IntegrationTest") + void multipleConditionsExample() { + String jsonPolicy = examples.multipleConditionsExample(); + logger.info(jsonPolicy); + analyze(jsonPolicy, PolicyType.IDENTITY_POLICY); + } + + @Test + @Tag("IntegrationTest") + void timeBasedPolicyExample() { + String policyJson = examples.timeBasedPolicyExample(); + logger.info(policyJson); + analyze(policyJson, PolicyType.IDENTITY_POLICY); + } + + @Test + @Tag("IntegrationTest") + void specifyPrincipalsExample() { + String policyJson = examples.specifyPrincipalsExample(); + logger.info(policyJson); + analyze(policyJson, PolicyType.RESOURCE_POLICY); + + } + + @Test + @Tag("IntegrationTest") + void allowCrossAccountAccessExample() { + String policyJson = examples.allowCrossAccountAccessExample(); + logger.info(policyJson); + analyze(policyJson, PolicyType.RESOURCE_POLICY); + } + +} \ No newline at end of file