Using custom attribute values to restrict access to dynamo DB rows - amazon-web-services

I need to restrict a users access to only rows from tables keyed by a unique ID which is used as the primary key in dynamo DB.
This unique ID is present in the JWT from the OIDC provider but the issue is that IAM doesn't allow custom attributes to be used to restrict access to Leading Keys.
Does anyone know if it is possible to do this via some sort of workaround like mapping the custom attribute to a value that can be used in an IAM policy?
I need to do something like the following:
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${cognito-identity.amazonaws.com:my_unique_id}"
],
"dynamodb:Attributes": [
"UserId",
"GameTitle",
"Wins",
"Losses",
"TopScore",
"TopScoreDateTime"
]
},

Related

Get proper usernames to populate on Superset with Azure SSO instead of ID string

I've finally gotten Azure Single Sign-On (SSO) connected to Apache Superset running via docker-compose, following the Flask docs. Users in my company's Azure group can create and access Superset accounts by logging in with Azure and they are assigned roles based on their identity. This is good.
The usernames they get assigned, however, are long Azure ID strings. These are undesirable in displays. Here's what my account looks like on the List Users screen and on my profile:
How can I modify either my Azure application SSO setup or my Superset config to have Superset populate usernames like SFirke for the account usernames, instead of values like 3ee660ff-a274 ... ?
The security part of my config.py looks like this, almost identical to the Flask template:
OAUTH_PROVIDERS = [
{
"name": "azure",
"icon": "fa-windows",
"token_key": "access_token",
"remote_app": {
"client_id": "CLIENT_ID",
"client_secret": "CLIENT_SECRET",
"api_base_url": "https://login.microsoftonline.com/TENANT_ID/oauth2",
"client_kwargs": {
"scope": "User.read name preferred_username email profile upn groups",
"resource": "RESOURCE_ID",
},
"request_token_url": None,
"access_token_url": "https://login.microsoftonline.com/TENANT_ID/oauth2/token",
"authorize_url": "https://login.microsoftonline.com/TENANT_ID/oauth2/authorize",
},
},
]
EDIT: Looks like the way to go is writing a custom userinfo retrieval method, there's a template on the Flask page linked above and an example used for Superset in this Github comment. I think I would use a line like "id": me["preferred_username"] or "id": me["upn"], based on the field names in the Microsoft docs.
But Microsoft notes that this value can change over time and should not be used for authorization changes. Since the oid value is immutable, and it is hardly visible to the typical user, I plan to just stick to it.

Cognito Group Permission Dynamically from Database

I created a cognito pool
Created users
Created 2 Groups WITHOUT any IAM roles
Assigned users to 2 different groups.
I store policies for a group in database and cache them .
In the lambda authorizer that has been configured , the deny policy works with principalId set to a random string.
For allowing access , i set the principal Id to the cognito User name. I get the policy from the database with permissions allowed for all api gateway end points. ( For testing )
But even after this i get the "User is not authorized" message.
Is my understanding wrong ? What am i doing wrong.
This is my policy for allowing access with the userId being the cognito user name.
authResponse = {}
authResponse['principalId'] = userId
authResponse['policyDocument'] = {
'Version': '2012-10-17',
'Statement': [
{
'Sid': 'FirstStatement',
'Action': 'execute-api:Invoke',
'Effect': 'Allow',
'Resource': 'arn:aws:execute-api:us-east-1:*:ppg7tavcld/test/GET/test-api-1/users/*'
}
]
}
return authResponse
Sorry . this was a mistake from me.
It was solved due to mixing up the stage position in the Resource

AWS Step Function get all Range keys with a single primary key in DynamoDB

I'm building AWS Step Function state machines. My goal is to read all Items from a DynamoDB table with a specific value for the Hash key (username) and any (wildcard) Sort keys (order_id).
Basically something that would be done from SQL with:
SELECT username,order_id FROM UserOrdersTable
WHERE username = 'daffyduck'
I'm using Step Functions to get configurations for AWS Glue jobs from a DynamoDB table and then spawn a Glue job via step function's Map for each dynamodb item (with parameters read from the database).
"Read Items from DynamoDB": {
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:getItem",
"Parameters": {
"TableName": "UserOrdersTable",
"Key": {
"username": {
"S": "daffyduck"
},
"order_id": {
"S": "*"
}
}
},
"ResultPath": "$",
"Next": "Invoke Glue jobs"
}
But I can't bring the state machine to read all order_id's for the user daffyduck in the step function task above. No output is displayed using the code above, apart from http stats.
Is there a wildcard for order_id ? Is there another way of getting all order_ids? The query customization seems to be rather limited inside step functions:
https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_GetItem.html#API_GetItem_RequestSyntax
Basically I'm trying to accomplish what can be done from the command line like so:
$ aws dynamodb query \
--table-name UserOrdersTable \
--key-condition-expression "Username = :username" \
--expression-attribute-values '{
":username": { "S": "daffyduck" }
}'
Any ideas? Thanks
I don't think that is possible with Step functions Dynamodb Service yet.
currently supports get, put, delete & update Item, not query or scan.
For GetItem we need to pass entire KEY (Partition + Range Key)
For the primary key, you must provide all of the attributes. For
example, with a simple primary key, you only need to provide a value
for the partition key. For a composite primary key, you must provide
values for both the partition key and the sort key.
We need to write a Lambda function to query Dynamo and return a map and invoke the lambda function from step.

I want to pass multiple bucket names from parameter section and allow them in iam resouce section in one go - cloud formation

I have a use case where.
I want to pass multiple bucket names from parameter section and grant them read only access to those buckets in resources.i am able to achieve when count is restricted but how to achieve when count is not defined.
"Parameters": {
"S3Bucket": {
"Type": "CommaDelimitedList",
"Description": "Select Bucket Names to Associate with the policy"
},
"Resource": [{"Fn::Join": ["", ["arn:aws:s3:::","Fn::Select": ["0", {
"Ref": "S3Bucket"
}]]
}
You cant pass values dynamically as cloud-formation template doest have a concept of looping.
If you want to use CommaDelimitedList and passing e.g. 5 values from parameter you have to fetch same value count in template.
Fn::Select does not check for null values or if the index is out of bounds of the array. Both conditions will result in a stack error, so you should be certain that the index you choose is valid, and that the list contains non-null values.
Source - https://docs.amazonaws.cn/en_us/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html

Dynamodb restrict access to user data only

how do I restrict dynamoddb user access to its own data owned by them.
I came access Using IAM Policy Conditions for Fine-Grained Access Control
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${www.amazon.com:user_id}"
],
"dynamodb:Attributes": [
"UserId",
"GameTitle",
"Wins",
"Losses",
"TopScore",
"TopScoreDateTime"
]
}
}
problem with above condition is that my partition key is not the userId.
its something else.
here is what my DB looks like like
Hash : "Sales" # its just plain text Sales
Range Date # its date
attributes : Sale : [ # array of maps
{
name : abc,
userId : idabc,
some-other : stuff
},
{
name : xyz,
userId : idxyz,
some-other : stuff
}
]
any idea how to restrict access based on sale[x].userId ?
or any better design how to handle this kind of design ?
I use Date range to query 90% of the data.
other option is to use different table for each and every logical table.
like sales,expense,payroll etc but I don't want to create different tables
and it defeats the purpose or NoSQL I guess.
FYI I am using javascript sdk to access dynamodb from browser.
app has 3 different user types
customer (access to its own data)
merchants (its own data and access to its customer data)
admin (access to all the data)
I think for this I have to create 3 different userPools, correct me if I am wrong.
but cant restrict access to own data, if I use partition key as userId
then querying for merchants becomes difficult.
any suggestion on how do I handle this db design?
thanx