From fbb1c752ff29f63fe0efba1a6d79c0b1391f9f91 Mon Sep 17 00:00:00 2001 From: Todd Hill Date: Thu, 7 Nov 2024 14:34:23 -0500 Subject: [PATCH 1/3] Create code example for Java V1's AmazonS3Client#doesBucketExistV2() https://issues.amazon.com/issues/V1416992068 --- .../java/com/example/s3/DoesBucketExist.java | 61 +++++++ .../S3DirectoriesDownloader.java | 2 +- .../s3/src/main/resources/xml_testing.xml | 22 +++ .../com/example/s3/DoesBucketExistTest.java | 162 ++++++++++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 javav2/example_code/s3/src/main/java/com/example/s3/DoesBucketExist.java create mode 100644 javav2/example_code/s3/src/main/resources/xml_testing.xml create mode 100644 javav2/example_code/s3/src/test/java/com/example/s3/DoesBucketExistTest.java diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/DoesBucketExist.java b/javav2/example_code/s3/src/main/java/com/example/s3/DoesBucketExist.java new file mode 100644 index 00000000000..3b8f8ab20b3 --- /dev/null +++ b/javav2/example_code/s3/src/main/java/com/example/s3/DoesBucketExist.java @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.example.s3; +// snippet-start:[s3.java2.does-bucket-exist-main] + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.http.HttpStatusCode; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.utils.Validate; + +public class DoesBucketExist { + private static final Logger logger = LoggerFactory.getLogger(DoesBucketExist.class); + + public static void main(String[] args) { + DoesBucketExist doesBucketExist = new DoesBucketExist(); + + final S3Client s3SyncClient = S3Client.builder().build(); + final String bucketName = "amzn-s3-demo-bucket"; // Change to the bucket name that you want to check. + + boolean exists = doesBucketExist.doesBucketExist(bucketName, s3SyncClient); + logger.info("Bucket exists: {}", exists); + } + + /** + * Checks if the specified bucket exists. Amazon S3 buckets are named in a global namespace; use this method to + * determine if a specified bucket name already exists, and therefore can't be used to create a new bucket. + *

+ * Internally this method uses the S3Client.getBucketAcl(String) + * operation to determine whether the bucket exists. + *

+ * This method is equivalent to the AWS SDK for Java V1's AmazonS3Client#doesBucketExistV2(String). + * + * @param bucketName The name of the bucket to check. + * @param s3SyncClient An S3Client instance. The method checks for the bucket in the AWS Region + * configured on the instance. + * @return The value true if the specified bucket exists in Amazon S3; the value false if there is no bucket in + * Amazon S3 with that name. + */ + public boolean doesBucketExist(String bucketName, S3Client s3SyncClient) { + try { + Validate.notEmpty(bucketName, "The bucket name must not be null or an empty string.", ""); + s3SyncClient.getBucketAcl(r -> r.bucket(bucketName)); + return true; + } catch (AwsServiceException ase) { + // A redirect error or an AccessDenied exception means the bucket exists but it's not in this region + // or we don't have permissions to it. + if ((ase.statusCode() == HttpStatusCode.MOVED_PERMANENTLY) || "AccessDenied".equals(ase.awsErrorDetails().errorCode())) { + return true; + } + if (ase.statusCode() == HttpStatusCode.NOT_FOUND) { + return false; + } + throw ase; + } + } +} +// snippet-end:[s3.java2.does-bucket-exist-main] diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java index b81831a993d..d1efabfe168 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java @@ -42,7 +42,7 @@ public class S3DirectoriesDownloader { private static final Logger logger = LoggerFactory.getLogger(S3DirectoriesDownloader.class); - public final String bucketName = "amzn-s3-demo-bucket" + UUID.randomUUID(); // Change bucket name. + public final String bucketName = "junk-s3-demo-bucket" + UUID.randomUUID(); // Change bucket name. public URI destinationPathURI; private final Set downloadedFileNameSet = new HashSet<>(); private final String destinationDirName = "downloadDirectory"; diff --git a/javav2/example_code/s3/src/main/resources/xml_testing.xml b/javav2/example_code/s3/src/main/resources/xml_testing.xml new file mode 100644 index 00000000000..5f1a0c3071d --- /dev/null +++ b/javav2/example_code/s3/src/main/resources/xml_testing.xml @@ -0,0 +1,22 @@ + + + + + + owner + dad784318ac45facc6a8c74b9b44f18afd317c85fa8776eeb2b000c8942bc8d9 + + FULL_CONTROL + + + + my-grantee + http://acs.amazonaws.com/groups/global/AuthenticatedUsers + + READ_ACP + + + + dad784318ac45facc6a8c74b9b44f18afd317c85fa8776eeb2b000c8942bc8d9 + + \ No newline at end of file diff --git a/javav2/example_code/s3/src/test/java/com/example/s3/DoesBucketExistTest.java b/javav2/example_code/s3/src/test/java/com/example/s3/DoesBucketExistTest.java new file mode 100644 index 00000000000..a21c26047fc --- /dev/null +++ b/javav2/example_code/s3/src/test/java/com/example/s3/DoesBucketExistTest.java @@ -0,0 +1,162 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.example.s3; +// + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +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.IamPolicy; +import software.amazon.awssdk.policybuilder.iam.IamPrincipalType; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.iam.IamClient; +import software.amazon.awssdk.services.iam.model.PutRolePolicyResponse; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DoesBucketExistTest { + private static final String ROLE_NAME = "minimal-s3-perms-role"; + private static final String POLICY_NAME = "minimum-s3-ability-policy"; + private static final IamClient iamClient = IamClient.builder().build(); + + @Test + @Tag("IntegrationTest") + void doesBucketExist_exists_but_not_in_client_region_should_return_true() { + + S3Client usWestS3Client = S3Client.builder().region(Region.US_WEST_2).build(); + final String bucketName = "my-us-west2-bucket-" + UUID.randomUUID(); + createBucket(usWestS3Client, bucketName); + + DoesBucketExist doesBucketExist = new DoesBucketExist(); + S3Client euCentralS3Client = S3Client.builder().region(Region.EU_CENTRAL_1).build(); + boolean exists = doesBucketExist.doesBucketExist(bucketName, euCentralS3Client); + assertTrue(exists); + + deleteBucket(usWestS3Client, bucketName); + } + + @Test + @Tag("IntegrationTest") + void doesBucketExist_does_not_exist_should_return_false() { + + DoesBucketExist doesBucketExist = new DoesBucketExist(); + final String bucketName = "xx-xx-xxxx-xxxx" + UUID.randomUUID(); + + boolean exists = doesBucketExist.doesBucketExist(bucketName, S3Client.create()); + assertFalse(exists); + } + + @Test + @Tag("IntegrationTest") + void doesBucketExist_returns_true_when_bucket_exists_but_caller_does_not_have_permission() { + StsClient stsClient = StsClient.create(); + createAssumableRole(stsClient); + + S3Client s3Client = S3Client.create(); + final String bucketName = "my-bucket-" + UUID.randomUUID(); + createBucket(s3Client, bucketName); + + try { + + String roleArn = iamClient.getRole(b -> b.roleName(ROLE_NAME)).role().arn(); + + S3Client s3ClientWithoutPermission = S3Client.builder() + .credentialsProvider(StsAssumeRoleCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest(arr -> arr + .roleArn(roleArn) + .roleSessionName("test-session")) + .build()) + .build(); + DoesBucketExist doesBucketExist = new DoesBucketExist(); + boolean existsButNoAccess = doesBucketExist.doesBucketExist(bucketName, s3ClientWithoutPermission); + assertTrue(existsButNoAccess); + + boolean exists = doesBucketExist.doesBucketExist("non-existent-bucket" + UUID.randomUUID(), s3ClientWithoutPermission); + assertFalse(exists); + } finally { + deleteBucket(s3Client, bucketName); + deleteRole(); + + } + } + + private static void createBucket(S3Client s3Client, String bucketName) { + s3Client.createBucket(b -> b.bucket(bucketName)); + s3Client.waiter().waitUntilBucketExists(b -> b.bucket(bucketName)); + } + + private static void deleteBucket(S3Client s3Client, String bucketName) { + s3Client.deleteBucket(b -> b.bucket(bucketName)); + s3Client.waiter().waitUntilBucketNotExists(b -> b.bucket(bucketName)); + } + + + private static void createAssumableRole(StsClient stsClient) { + final String accountID = stsClient.getCallerIdentity().account(); + + IamPolicy trustIamPolicyForAnyoneInSameAccount = IamPolicy.builder() + .addStatement(statement -> statement + .effect(IamEffect.ALLOW) + .addPrincipal(principal -> principal + .type(IamPrincipalType.AWS) + .id("arn:aws:iam::" + accountID + ":root") + ) + .addAction("sts:AssumeRole") + .addConditions(IamConditionOperator.STRING_EQUALS, + IamConditionKey.create("aws:PrincipalType"), + List.of("User", "AssumedRole") + ).addConditions(IamConditionOperator.STRING_LIKE, + "aws:userId", + List.of("AIDAX*", "AROA*:*", "AIDA*:*")) + ) + .build(); + + iamClient.createRole(crb -> crb + .roleName(ROLE_NAME) + .assumeRolePolicyDocument(trustIamPolicyForAnyoneInSameAccount.toJson()) + ); + + IamPolicy getBucketLocationOnlyPolicy = IamPolicy.builder() + .addStatement(statement -> statement + .effect(IamEffect.ALLOW) + .addAction("s3:GetBucketLocation") + .addResource("arn:aws:s3:::*") + ) + .build(); + + iamClient.putRolePolicy(prprb -> prprb + .roleName(ROLE_NAME) + .policyName(POLICY_NAME) + .policyDocument(getBucketLocationOnlyPolicy.toJson())); + + // Add a delay for the role to propagate. + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + } + + private static void deleteRole(){ + + iamClient.deleteRolePolicy(drbrb -> drbrb + .roleName(ROLE_NAME) + .policyName(POLICY_NAME)); + + iamClient.deleteRole(drb -> drb + .roleName(ROLE_NAME) + ); + } +} + From cdf54cb41c2b368ed380315dcb38df2586bf36af Mon Sep 17 00:00:00 2001 From: Todd Hill Date: Mon, 11 Nov 2024 16:32:09 -0500 Subject: [PATCH 2/3] add metadata --- .doc_gen/metadata/s3_metadata.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.doc_gen/metadata/s3_metadata.yaml b/.doc_gen/metadata/s3_metadata.yaml index 3a298d854f1..820cbc490a0 100644 --- a/.doc_gen/metadata/s3_metadata.yaml +++ b/.doc_gen/metadata/s3_metadata.yaml @@ -3524,3 +3524,20 @@ s3_Scenario_DownloadS3Directory: - s3.tm.java2.download-s3-directories.main services: s3: {} +s3_Scenario_DoesBucketExist: + title: Check if a bucket exists + title_abbrev: Check if a bucket exists + synopsis: check if a bucket exists. + category: Scenarios + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/s3 + sdkguide: + excerpts: + - description: You can use the following doesBucketExists method as a replacement for the the &Java; V1 AmazonS3Client#doesBucketExistV2(String) method. + snippet_tags: + - s3.java2.does-bucket-exist-main + services: + s3: {GetBucketAcl} From 0879708379e661f028404bd67da65fd12e905010 Mon Sep 17 00:00:00 2001 From: Todd Hill Date: Mon, 11 Nov 2024 16:55:23 -0500 Subject: [PATCH 3/3] update README.md --- javav2/example_code/s3/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/javav2/example_code/s3/README.md b/javav2/example_code/s3/README.md index 2cb2d77f762..39d43368f83 100644 --- a/javav2/example_code/s3/README.md +++ b/javav2/example_code/s3/README.md @@ -79,6 +79,7 @@ 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. +- [Check if a bucket exists](src/main/java/com/example/s3/DoesBucketExist.java) - [Delete incomplete multipart uploads](src/main/java/com/example/s3/AbortMultipartUploadExamples.java) - [Download S3 'directories'](src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java) - [Download objects to a local directory](src/main/java/com/example/s3/transfermanager/DownloadToDirectory.java) @@ -129,6 +130,18 @@ This example shows you how to do the following: +#### Check if a bucket exists + +This example shows you how to check if a bucket exists. + + + + + + + + + #### Delete incomplete multipart uploads This example shows you how to how to delete or stop incomplete Amazon S3 multipart uploads.