0
votes

I have a table with some elements. I am trying to use Fine-Grained Access Control (Limit Access to Specific Attributes in a Table) on unauth (I want to return specific attributes to user who are not authenticated yet) role where user scan on specific attributes based on following url Using IAM Policy Conditions for Fine-Grained Access Control.

{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "LimitAccessToSpecificAttributes",
        "Effect": "Allow",
        "Action": [
            "dynamodb:GetItem",
            "dynamodb:Query",
            "dynamodb:BatchGetItem",
            "dynamodb:Scan"
        ],
        "Resource": [
            "arn:aws:dynamodb:us-west-2:AccountID:table/MyTable"
        ],
        "Condition": {
            "ForAllValues:StringEquals": {
                "dynamodb:Attributes": [
                    "startDate",
                    "endDate"
                ]
            },
            "StringEqualsIfExists": {
                "dynamodb:Select": "SPECIFIC_ATTRIBUTES",
                "dynamodb:ReturnValues": [
                    "NONE",
                    "UPDATED_OLD",
                    "UPDATED_NEW"
                ]
            }
        }
    }
]
}

When I am removing following role it works:

 "Condition": {
            "ForAllValues:StringEquals": {
                "dynamodb:Attributes": [
                    "startDate",
                    "endDate"
                ]
            },
            "StringEqualsIfExists": {
                "dynamodb:Select": "SPECIFIC_ATTRIBUTES",
                "dynamodb:ReturnValues": [
                    "NONE",
                    "UPDATED_OLD",
                    "UPDATED_NEW"
                ]
            }

The problem is that when I am running my code (android) I am getting following exception:

User: arn:aws:sts::AccountID:assumed-role/Cognito_XXXUnauth_Role/CognitoIdentityCredentials is not authorized to perform: dynamodb:Scan on resource: arn:aws:dynamodb:us-east-1:AccountID:table/MyTable (Service: AmazonDynamoDB; Status Code: 400; Error Code: AccessDeniedException;

I would like to know what I am doing wrong which causes the exception. Is there any other way to get specific attributes?

I am using following android code:

CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(getApplicationContext(),
                        "identityPoolId",
                        Regions.US_EAST_1
                );

AmazonDynamoDBClient ddb = new AmazonDynamoDBClient(credentialsProvider);

ScanRequest scanRequest = new ScanRequest();
scanRequest = scanRequest.withProjectionExpression("startDate, endDate");
scanRequest.setTableName("MyTable");

try {
    ScanResult scanResult = ddb.scan(scanRequest);
} catch (Exception ex) {
    log(ex.getMessage());
}

Any help would be appreciated.

3

3 Answers

1
votes

Looking at your IAM policy, it looks like the Scan operation is allowed on the resource: arn:aws:dynamodb:us-west-2:AccountID:table/MyTable.

Note that the resource is for us-west-2. But you're trying to perform the operation on a resource in us-east-1.

0
votes

Make sure the identity pool has enough DynamoDB permissions, in this case, to perform scans. Here's an example of using Cognito by creating an unauthenticated role and granting it DynamoDB access: http://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/GettingStarted.Js.Summary.html

Then, as the other person said, make sure you point at the same region you are granting permissions to. In this case, make sure US_EAST_1 has enough Cognito and DDB access.

0
votes

I found the reason. The attributes in the role should include hashkey and range key. When replacing the attributes with hashkey and range key I got it work, also changing region as Lisa answered.

I will post here the policies and android code in case anyone in the future would like to see how it should be done:

{
"Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "dynamodb:DeleteItem",
            "dynamodb:GetItem",
            "dynamodb:PutItem",
            "dynamodb:Scan",
            "dynamodb:Query",
            "dynamodb:UpdateItem",
            "dynamodb:BatchWriteItem"
        ],
        "Resource": [
            "arn:aws:dynamodb:us-east-1:AcountID:table/MyTable",
            "arn:aws:dynamodb:us-east-1:AcountID:table/MyTable/index/*"
        ]
    }
]}

Policy for UnAuthenticated user:

{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "LimitAccessToSpecificAttributes",
        "Effect": "Allow",
        "Action": [
            "dynamodb:GetItem",
            "dynamodb:Query",
            "dynamodb:BatchGetItem",
            "dynamodb:Scan"
        ],
        "Resource": [
            "arn:aws:dynamodb:us-east-1:AcountID:table/MyTable"
        ],
        "Condition": {
            "ForAllValues:StringEquals": {
                "dynamodb:Attributes": [
                    "userId",
                    "startDate",
                    "endDate",
                    "Price"
                ]
            },
            "StringEqualsIfExists": {
                "dynamodb:Select": "SPECIFIC_ATTRIBUTES"
            }
        }
    }
]}

android code contains code for authenticated and unauthenticated user (messy).

public PaginatedScanList<Book> scan() {

    PaginatedScanList<Book> result = null;
    AmazonDynamoDBClient ddb = new AmazonDynamoDBClient(_cognito.getCredentialsProvider());

    if (_cognito.getJWTToken() != null) {
        //Authenticated (SignedIn) user flow
        _cognito.credentialsProviderSetLogIn();
        mapper = new DynamoDBMapper(ddb);

        DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
        result = mapper.scan(Book.class, scanExpression);

        if (result != null) {
            for (int i = 0; i < result.size(); i++) {
                log("UserId: " + result.get(i).getUserId());
                log("Price: " + result.get(i).getPrice());
                log("startDate: " + result.get(i).getPrice());
                log("endDate: " + result.get(i).getPrice());
            }
        }
    } else {
        //UnAuthenticated user flow
        ScanRequest scanRequest = new ScanRequest();
        //Write attributes to be retreived. if Price is not exsits, items.get(i).get("Price").getS() will be null
        //If Price is not exists in the policy, policy is null
        scanRequest = scanRequest.withProjectionExpression("UserId, Price, startDate, endDate");//setter for projectionExpression
        //scanRequest.setProjectionExpression("UserId, Price, startDate, endDate"); //not sure why this API exists. It does the same as withProjectionExpression. this is setter for projectionExpression
        scanRequest.setSelect(Select.SPECIFIC_ATTRIBUTES); //Not usre this is needed. works well without it.
        scanRequest.setTableName("MyTable");

        try {
            ScanResult scanResult = ddb.scan(scanRequest);
            final List<Map<String, AttributeValue>> items = scanResult.getItems();

            for (int i = 0; i < items.size(); i++) {
                log("UserId: " + items.get(i).get("UserId").getS());
                log("Price: " + items.get(i).get("Price").getS());
                log("startDate: " + items.get(i).get("startDate").getS());
                log("endDate: " + items.get(i).get("endDate").getS());
            }
        } catch (Exception ex) {
            log(ex.getMessage());
        }
    }

    return result;
}