Sergey1
Sergey1

Reputation: 23

software.amazon.awssdk.services.s3.model.s3exception: null with using GetObjectRequest

I am trying to use this code but i'm getting an exeption

var awsBasicCredentials  =  Packages.software.amazon.awssdk.auth.credentials.AwsBasicCredentials.create("ackey","secretkey");
var credentials =  Packages.software.amazon.awssdk.auth.credentials.StaticCredentialsProvider.create(awsBasicCredentials);

var region = Packages.software.amazon.awssdk.regions.Region.AWS_CN_GLOBAL;
var uri =  Packages.java.net.URI.create("http://host");
var client =  Packages.software.amazon.awssdk.services.s3.S3Client.builder()
                                                            
                                                            .credentialsProvider(credentials)
                                                            .region(region)
                                                            .endpointOverride(uri)
                                                            .build(); 
var request  = Packages.software.amazon.awssdk.services.s3.model.GetObjectRequest.builder()
                                                                        .bucket("/bucketname")                                      
                                                                        .key("key")
                                                                        .build();
var response = client.getObject(request);
return response;

I am using /bucketname because the final link looks like host/bucketname instead bucketname.host

S3Exeption:

Caused by: software.amazon.awssdk.services.s3.model.S3Exception: null (Service: S3, Status Code: 403, Request ID: tx0000000000000162eece0-00633fea7f-306fc-msk-rt1)
    at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handleErrorResponse(CombinedResponseHandler.java:125)
    at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handleResponse(CombinedResponseHandler.java:82)
    at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handle(CombinedResponseHandler.java:60)
    

Upvotes: 1

Views: 8867

Answers (2)

dutoitns
dutoitns

Reputation: 2243

You don't have the required permissions to perform a GetObject request against the specified S3 bucket.

I know the null in the Exception throws one off a bit, but if you look subsequent to that you'll see that the request returned a 403 status code. 403 status code means "Access Denied". So I would review your S3 bucket policy and make sure that your user have the required s3:GetObject permission required to perform a GetObject request. You can find some example bucket policies by searching the internet or looking through the AWS documentation, for example here or here.

It's also worthwhile reading the documentation on the GetObject request. From that documentation:

If the object that you request doesn’t exist, the error that Amazon S3 returns depends on whether you also have the s3:ListBucket permission.

If you have the s3:ListBucket permission on the bucket, Amazon S3 returns an HTTP status code 404 Not Found error.

If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP status code 403 Access Denied error.

So the other reason you might be getting a 403 status code might be that the specified key doesn't exist in the bucket. And as per the preceding quote, the API will return a 403 status code in that case if you don't have the s3:ListBucket permission. Maybe it's a good practice to always consider assigning s3:ListBucket permission to users who have s3:GetObject permission to make their life a bit easier - to make it easier for them to differentiate between (1) a bucket permission issue and (2) a bad key.

Aside:

The link between your GetObject request and the s3:GetObject permission are clear. The link between the GetObject request and the s3:ListBucket permission is probably a bit more obscure. So yeah, if you ever encounter this problem again - and it's not just a case of "okay, I was trying to do something I don't have permission to do" - you might have to do some reading.

An example of what I'm alluding to: You can issue a S3 HeadBucket request to determine if a S3 bucket exists. It also requires the s3:ListBucket permission - so without it, you'll also get a 403 Access Denied response.

Interesting, that one link I shared earlier actually lists a few cases you might encounter. I'm just putting it here - might help others that stumble upon your post and are also encountering hard-to-debug 403 permission errors.

The following shows different types of mapping relationship between S3 API operations and the required policy actions.

One-to-one mapping with the same name. For example, to use the PutBucketPolicy API operation, the s3:PutBucketPolicy policy action is required.

One-to-one mapping with different names. For example, to use the ListObjectsV2 API operation, the s3:ListBucket policy action is required.

One-to-many mapping. For example, to use the HeadObject API operation, the s3:GetObject is required. Also, when you use S3 Object Lock and want to get an object's Legal Hold status or retention settings, the corresponding s3:GetObjectLegalHold or s3:GetObjectRetention policy actions are also required before you can use the HeadObject API operation.

Many-to-one mapping. For example, to use the ListObjectsV2 or HeadBucket API operations, the s3:ListBucket policy action is required.

Bucket policy example:

Okay, I actually just checked my own code and I do have a S3 bucket policy that gives s3:GetObject permission. It also blocks people from retrieving objects from the S3 bucket without "encryption in transit", eg HTTP. In this specific example I didn't give the user s3:ListBucket permission:

{
  "Version" : "2012-10-17",
  "Statement" : [ {
    "Sid" : "Only-allow-encrypted-traffic",
    "Effect" : "Deny",
    "Principal" : "*",
    "Action" : "s3:*",
    "Resource" : [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ],
    "Condition" : {
      "Bool" : {
        "aws:SecureTransport" : "false"
      }
    }
  }, {
    "Sid" : "Allow-public-access",
    "Effect" : "Allow",
    "Principal" : "*",
    "Action" : "s3:GetObject",
    "Resource" : "arn:aws:s3:::YOUR_BUCKET_NAME/*",
    "Condition" : {
      "Bool" : {
        "aws:SecureTransport" : "true"
      }
    }
  } ]
}

Upvotes: 0

smac2020
smac2020

Reputation: 10704

Bucket names can consist only of lowercase letters, numbers, dots (.), and hyphens (-).

Here is S3 Java code that works and returns a byte[] that represents the object that is located in the given Amazon S3 bucket.

In this example, the path represents the local file system where the object is written to . FOr example, a PDF file.

package com.example.s3;

// snippet-start:[s3.java2.getobjectdata.import]
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
// snippet-end:[s3.java2.getobjectdata.import]

/**
 * Before running this Java V2 code example, set up your development environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class GetObjectData {

    public static void main(String[] args) {

     final String usage = "\n" +
         "Usage:\n" +
         "    <bucketName> <keyName> <path>\n\n" +
         "Where:\n" +
         "    bucketName - The Amazon S3 bucket name. \n\n"+
         "    keyName - The key name. \n\n"+
         "    path - The path where the file is written to. \n\n";

        if (args.length != 3) {
            System.out.println(usage);
            System.exit(1);
        }

        String bucketName = args[0];
        String keyName = args[1];
        String path = args[2];

        ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();
        Region region = Region.US_EAST_1;
        S3Client s3 = S3Client.builder()
            .region(region)
            .credentialsProvider(credentialsProvider)
            .build();

        getObjectBytes(s3,bucketName,keyName, path);
        s3.close();
    }

    // snippet-start:[s3.java2.getobjectdata.main]
    public static void getObjectBytes (S3Client s3, String bucketName, String keyName, String path) {

        try {
            GetObjectRequest objectRequest = GetObjectRequest
                .builder()
                .key(keyName)
                .bucket(bucketName)
                .build();

            ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
            byte[] data = objectBytes.asByteArray();

            // Write the data to a local file.
            File myFile = new File(path );
            OutputStream os = new FileOutputStream(myFile);
            os.write(data);
            System.out.println("Successfully obtained bytes from an S3 object");
            os.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
    // snippet-end:[s3.java2.getobjectdata.main]
}

Upvotes: 0

Related Questions