Reputation: 33
I am developing a service that requires access to a DynamoDB table which must be managed by authorizing user access to the table. Account management is handled by Cognito. I am currently investigating direct access to the DynamoDB table with read/write access limited based on User Groups with associated IAM policies.
Multiple organisations exist within the table, and multiple users are tied to an organisation. An example of the model is below. I also store sector and department information in a many-to-one relationship.
The Cognito Sub for a user is stored as their user id within the database under USR#.
+-------+-------+-----------------+------------+--------+
| PK | SK | Name | GSI1PK | GSI2PK |
+-------+-------+-----------------+------------+--------+
| ORG#1 | ORG#1 | Acme Inc | | |
| ORG#1 | USR#1 | John Doe | | |
| ORG#2 | ORG#2 | Globetex | | |
| ORG#2 | USR#2 | Jane Doe | | |
| ORG#1 | SEC#1 | Sector A1 | ORG#1SEC#1 | SEC#1 |
| DEP#1 | DEP#1 | Human Resources | ORG#1SEC#1 | DEP#1 |
+-------+-------+-----------------+------------+--------+
So far I can limit access in a hardcoded manner to each organisation in a specific IAM policy. However, this is not scalable. If a hundred organisations were to exist, a hundred user groups must also exist with a separate policy. An example of this policy is below.
Is there any way to create an IAM policy that utilises a custom Cognito variable, such as 'organization' that would allow me to create a single policy that limits access to only rows leading with that organization? I am unable to get this working with the below code.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query"
],
"Resource": [
"arn:aws:dynamodb:region:id:table/TableName"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${cognito-identity.amazonaws.com:org}"
]
}
}
}
]
}
Edit: For clarity, my query is to insert a custom Cognito variable dynamically into the IAM policy at validation.
For instance, User A has custom:org = Acme as a Cognito attribute and User B has custom:org = Globex as their custom Cognito attribute.
A single policy as detailed in the code above can insert this attribute directly into the policy, so one policy may be used for multiple users in separate orgs.
After further research I am unsure this is possible at all, but if anyone has any experience with trying something like this I'd love to hear it.
Upvotes: 3
Views: 1116
Reputation: 23793
I think you're close, according to this article it should be StringLike
not StringEquals
"Condition": {
"ForAllValues:StringLike": {
"dynamodb:LeadingKeys": [
"{TENANTID}-*"
]
}
May also want to read the Multi-tenant SaaS Storage Strategies whitepaper
Edit I don't beleive it's possible to have a static policy do what you want.
However the code in the linked article does provide the ability to "manage access from users from any tenant".
The key points are the use of the role/AccessDynamoWithTenantContext
tenantPolicy = getPolicy(event['tenantID'])
assumed_role = sts_client.assume_role(
RoleArn="arn:aws:iam::<account-id>:role/AccessDynamoWithTenantContext",
RoleSessionName="tenant-aware-product",
Policy=tenantPolicy,
)
And the dynamic injection of the tenentId in getPolicy()
policy = json.dumps(policyTemplate).replace("{TENANTID}", tenantID)
return policy
Upvotes: 2