Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(authorization): Add curators group permissions #830

Merged
merged 4 commits into from
Feb 2, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add curators permissions
williamputraintan committed Jan 29, 2025
commit 1c466ef91a6d485117f302924670779410301041
10 changes: 5 additions & 5 deletions lib/workload/stateful/stacks/authorization-manager/README.md
Original file line number Diff line number Diff line change
@@ -11,13 +11,13 @@ The current stack deploys AWS Verified Permissions, defining an identity source
- **UMCCR Cognito User Pool**

Sourced from the UMCCR Cognito User Pool, defined in the infrastructure Terraform repository. The AWS Cognito User Pool
is expected to have an `admin` group, which will be used in the policy. Note that the JWT must be generated with the
is expected to have groups, which will be used in the policy. Note that the JWT must be generated with the
latest token containing the proper Cognito group claims for it to work. This also applies when a user is removed from
the group; the JWT must expire to become invalid.

### Policy
### Group

- **AdminPolicy**
Policies are currently assigned based on groups from the Cognito User Pool. The policies are defined in `stack.ts` within the class where the function is named `setup{GROUP_NAME}CedarPolicy`.

A static policy defined in the stack that allows anyone in the `admin` group of the Cognito user pool to perform any
action. This essentially checks if a user is in the `admin` group, integrated with the Cognito setup.
- **Admin**: For admins/service users (all actions are granted to this group).
- **Curators**: For curators (all policies are applied to all curators in this group).
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"OrcaBus": {
"actions": {
"writeAccess": {
"POST /api/v1/workflowrun/{orcabusId}/rerun/{proxy+}": {
"appliesTo": {
"principalTypes": ["User"]
"principalTypes": ["User", "CognitoUserGroup"],
"resourceTypes": ["Microservice"]
}
}
},
@@ -26,6 +27,12 @@
},
"type": "Record"
}
},
"Microservice": {
"shape": {
"type": "Record",
"attributes": {}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -5,6 +5,42 @@


def handler(event, context):
"""
The event example:
{
"version": "2.0",
"type": "REQUEST",
"routeArn": "arn:aws:execute-api:ap-southeast-2:}{ACC_NUM}:{API_ID}/$default/POST/{FULL_PATH}/",
"identitySource": [...],
"routeKey": "POST /api/v1/.../{PROXY+}",
"rawPath": "/api/v1/...",
"rawQueryString": "",
"headers": {
...HTTP Headers...
},
"requestContext": {
"accountId": "1234567890",
"apiId": "123ABCD",
"domainName": "microservice.umccr.org",
"domainPrefix": "microservice",
"http": {
"method": "POST",
"path": "/api/v1/.../",
"protocol": "HTTP/1.1",
"sourceIp": "123.123.12.123",
"userAgent": "Mozilla/5.0 ..."
},
"requestId": "123ABCD=",
"routeKey": "POST /api/v1/.../{PROXY+}",
"stage": "$default",
"time": "01/Jan/2000:00:00:00 +0000",
"timeEpoch": 1234567890
},
"pathParameters": {
"PROXY": ""
}
}
"""
response = {
"isAuthorized": False,
}
@@ -14,19 +50,31 @@ def handler(event, context):
try:

if auth_token:

if auth_token.lower().startswith("bearer "):
auth_token = auth_token.split(" ")[1]


entityType = event.get("requestContext", {}).get("domainPrefix", "")
routeKey = event.get("routeKey", "")

avp_response = client.is_authorized_with_token(
policyStoreId=os.environ.get("POLICY_STORE_ID"),
identityToken=auth_token,
action={
"actionType": f"OrcaBus::Action",
"actionId": routeKey,
},
resource={
"entityType": "OrcaBus::Microservice",
"entityId": entityType.upper(),
},
)

return {
"isAuthorized": avp_response.get("decision", None) == "ALLOW",
}
else:
return response
except BaseException:
except BaseException as e:
print("ERROR: ", e)
return response
113 changes: 87 additions & 26 deletions lib/workload/stateful/stacks/authorization-manager/stack.ts
Original file line number Diff line number Diff line change
@@ -33,6 +33,13 @@ export class AuthorizationManagerStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps & AuthorizationManagerStackProps) {
super(scope, id, props);

// Grab the user pool ID from SSM
const userPoolId = StringParameter.fromStringParameterName(
this,
'CognitoUserPoolIdStringParameter',
props.cognito.userPoolIdParameterName
).stringValue;

// Amazon Verified Permission
const policyStore = new CfnPolicyStore(this, 'VerifiedPermissionPolicyStore', {
validationSettings: { mode: 'STRICT' },
@@ -43,6 +50,7 @@ export class AuthorizationManagerStack extends Stack {
});

this.setupCognitoIntegrationAndPolicy({
userPoolId,
cognito: props.cognito,
policyStoreId: policyStore.attrPolicyStoreId,
});
@@ -52,6 +60,16 @@ export class AuthorizationManagerStack extends Stack {
policyStoreId: policyStore.attrPolicyStoreId,
authStackHttpLambdaAuthorizerParameterName: props.authStackHttpLambdaAuthorizerParameterName,
});

// Create a policies for respective groups
this.setupAdminCedarPolicy({
userPoolId,
cfnPolicyStore: policyStore,
});
this.setupCuratorsCedarPolicy({
userPoolId,
cfnPolicyStore: policyStore,
});
}

/**
@@ -62,21 +80,15 @@ export class AuthorizationManagerStack extends Stack {
* @param props Cognito properties
*/
private setupCognitoIntegrationAndPolicy(props: {
userPoolId: string;
policyStoreId: string;
cognito: CognitoConfig;
}) {
// Grab the user pool ID from SSM
const userPoolId = StringParameter.fromStringParameterName(
this,
'CognitoUserPoolIdStringParameter',
props.cognito.userPoolIdParameterName
).stringValue;

// Allow the policy store to source the identity from existing Cognito User Pool Id
new CfnIdentitySource(this, 'VerifiedPermissionIdentitySource', {
configuration: {
cognitoUserPoolConfiguration: {
userPoolArn: `arn:aws:cognito-idp:${props.cognito.region}:${props.cognito.accountNumber}:userpool/${userPoolId}`,
userPoolArn: `arn:aws:cognito-idp:${props.cognito.region}:${props.cognito.accountNumber}:userpool/${props.userPoolId}`,
groupConfiguration: {
groupEntityType: 'OrcaBus::CognitoUserGroup', // Refer to './cedarSchema.json'
},
@@ -85,24 +97,6 @@ export class AuthorizationManagerStack extends Stack {
principalEntityType: 'OrcaBus::User',
policyStoreId: props.policyStoreId,
});

// Create a static policy that allow user from the admin group to allow all actions
new CfnPolicy(this, 'CognitoPortalAdminPolicy', {
definition: {
static: {
statement: `
permit (
principal in OrcaBus::CognitoUserGroup::"${userPoolId}|admin",
action,
resource
);
`,
description:
'Allow all action for all resource for user in the admin cognito user pool group',
},
},
policyStoreId: props.policyStoreId,
});
}

private setupTokenLambdaAuthorization(props: {
@@ -132,4 +126,71 @@ export class AuthorizationManagerStack extends Stack {
stringValue: lambdaAuth.functionArn,
});
}

/**
* This sets up all policies for the admin group in the Cognito user pool.
* @param userPoolId
* @param cfnPolicyStore
*/
private setupAdminCedarPolicy({
userPoolId,
cfnPolicyStore,
}: {
userPoolId: string;
cfnPolicyStore: CfnPolicyStore;
}) {
const policyStoreId = cfnPolicyStore.attrPolicyStoreId;

// 1. Policy to allow everything for the admin group
const allowAllPolicy = new CfnPolicy(this, 'CognitoPortalAdminPolicy', {
definition: {
static: {
statement: `
permit (
principal in OrcaBus::CognitoUserGroup::"${userPoolId}|admin",
action,
resource
);
`,
description: 'admin - Allow all action for all resource for the admin cognito group',
},
},
policyStoreId: policyStoreId,
});
allowAllPolicy.node.addDependency(cfnPolicyStore);
}

/**
* This sets up all policies for the curators group in the Cognito user pool.
* @param userPoolId
* @param cfnPolicyStore
*/
private setupCuratorsCedarPolicy({
userPoolId,
cfnPolicyStore,
}: {
userPoolId: string;
cfnPolicyStore: CfnPolicyStore;
}) {
const policyStoreId = cfnPolicyStore.attrPolicyStoreId;

// 1. Policy to allow rerun workflows in the WORKFLOW microservice
const allowRerunPolicy = new CfnPolicy(this, 'CognitoPortalCuratorsPolicy', {
definition: {
static: {
statement: `
permit (
principal in OrcaBus::CognitoUserGroup::"${userPoolId}|curators",
action in
[OrcaBus::Action::"POST /api/v1/workflowrun/{orcabusId}/rerun/{proxy+}"],
resource == OrcaBus::Microservice::"WORKFLOW"
);
`,
description: 'curators - Permissions for rerun workflowrun in WORKFLOW microservice',
},
},
policyStoreId: policyStoreId,
});
allowRerunPolicy.node.addDependency(cfnPolicyStore);
}
}