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

Implement account management backend #308

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ aws cloudformation describe-stacks --query \
You can override any of the parameters in the template using the `--parameter-overrides key="value"` format. This will be necessary if you intend to deploy several instances of the developer portal or customize some of the features. You can see a full list of overridable parameters in `cloudformation/template.yaml` under the `Parameters` section.

## Registering Users
Users can self-register by clicking the 'Register' button in the developer portal. Cognito calls the `CognitoUserPoolsConfirmationStrategyFunction` to determine if the user is allowed to register themselves. By default, this function always accepts the user into the user pool, but you can customize the body of the function either in a local repository (followed by packaging and deploying) or in the lambda console. If you intend for the developer portal to be 'private' to some group of users (and not globally / freely accessible), you will need to write a lambda function that enforces your business logic for user registration. Documentation on this lambda function's use can be found [here](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html).
Users can self-register by clicking the 'Register' button in the developer portal. Cognito calls the `CognitoPreSignupTriggerFn` lambda to determine if the user is allowed to register themselves. By default, this function always accepts the user into the user pool, but you can customize the body of the function either in a local repository (followed by packaging and deploying) or in the lambda console. If you intend for the developer portal to be 'private' to some group of users (and not globally / freely accessible), you will need to write a lambda function that enforces your business logic for user registration. Documentation on this lambda function's use can be found [here](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html).

### Promoting a User to an Admin
Admin users can manage what APIs are visible to normal users and whether or not SDK generation is enabled (per api) for normal users. To promote a user to an admin, go to the Cognito console in the account the developer portal is in, select User Pools, then select the correct User Pool for the dev portal. From there, choose Users and groups, click on the users' name, choose Add to group, and select the group named `STACK-NAMEAdminsGroup`. This user is now an admin; if they're currently logged in, they will have to log out and back in to receive admin credentials.
Expand Down
267 changes: 258 additions & 9 deletions cloudformation/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ Parameters:
Description: The name of the DynamoDB Customers table.
Default: 'DevPortalCustomers'

DevPortalPreLoginAccountsTableName:
Type: String
Description: The name of the DynamoDB PreLoginAccounts table.
Default: 'DevPortalPreLoginAccounts'

DevPortalAdminEmail:
Type: String
Description: The email address where user submitted feedback notifications get sent.
Expand Down Expand Up @@ -628,6 +633,20 @@ Resources:
ReadCapacityUnits: 5
WriteCapacityUnits: 5

PreLoginAccountsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref DevPortalPreLoginAccountsTableName
AttributeDefinitions:
- AttributeName: UserId
AttributeType: S
KeySchema:
- AttributeName: UserId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5

FeedbackTable:
Type: AWS::DynamoDB::Table
Condition: EnableFeedbackSubmission
Expand Down Expand Up @@ -727,6 +746,15 @@ Resources:
- !Ref 'AWS::AccountId'
- :table/
- !Ref CustomersTable
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: !GetAtt PreLoginAccountsTable.Arn
- Effect: Allow
Action:
- dynamodb:Query
Expand Down Expand Up @@ -767,8 +795,72 @@ Resources:
- sns:Publish
Resource: !Ref FeedbackSubmittedSNSTopic
- !Ref 'AWS::NoValue'
- Effect: Allow
Action:
- cognito-idp:ListUsers
- cognito-idp:ListUsersInGroup
- cognito-idp:AdminAddUserToGroup
- cognito-idp:AdminCreateUser
- cognito-idp:AdminDeleteUser
- cognito-idp:AdminGetUser
- cognito-idp:AdminListGroupsForUser
Resource: !GetAtt CognitoUserPool.Arn

CognitoPreSignupTriggerExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: '/'
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*

CognitoPostConfirmationTriggerExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: '/'
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- dynamodb:PutItem
Resource: !GetAtt PreLoginAccountsTable.Arn
- Effect: Allow
Action:
- cognito-idp:AdminAddUserToGroup
Resource: !GetAtt CognitoUserPool.Arn

CognitoStrategyLambdaExecutionRole:
CognitoPostAuthenticationTriggerExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Expand All @@ -790,6 +882,20 @@ Resources:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- dynamodb:Scan
- dynamodb:PutItem
Resource: !GetAtt CustomersTable.Arn
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
Resource: !GetAtt PreLoginAccountsTable.Arn
- Effect: Allow
Action:
- cognito-idp:AdminAddUserToGroup
Resource: !GetAtt CognitoUserPool.Arn

CatalogUpdaterLambdaExecutionRole:
Type: AWS::IAM::Role
Expand Down Expand Up @@ -958,11 +1064,41 @@ Resources:
- !Ref ApiGatewayApi
- '/*/*'

LambdaCognitoUserPoolExecutionPermission:
CognitoPreSignupTriggerFnExecutionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt CognitoPreSignupTriggerFn.Arn
Principal: cognito-idp.amazonaws.com
SourceArn: !Join
- ''
- - 'arn:aws:cognito-idp:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- ':userpool/'
- !Ref CognitoUserPool

CognitoPostConfirmationTriggerFnExecutionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt CognitoPostConfirmationTriggerFn.Arn
Principal: cognito-idp.amazonaws.com
SourceArn: !Join
- ''
- - 'arn:aws:cognito-idp:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- ':userpool/'
- !Ref CognitoUserPool

CognitoPostAuthenticationTriggerFnExecutionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt CognitoUserPoolsConfirmationStrategyFunction.Arn
FunctionName: !GetAtt CognitoPostAuthenticationTriggerFn.Arn
Principal: cognito-idp.amazonaws.com
SourceArn: !Join
- ''
Expand Down Expand Up @@ -1020,10 +1156,15 @@ Resources:
WEBSITE_BUCKET_NAME: !Ref DevPortalSiteS3BucketName
StaticBucketName: !Ref ArtifactsS3BucketName
CustomersTableName: !Ref DevPortalCustomersTableName
PreLoginAccountsTableName: !Ref DevPortalPreLoginAccountsTableName
CatalogUpdaterFunctionArn: !GetAtt CatalogUpdaterLambdaFunction.Arn
FeedbackTableName: !Ref DevPortalFeedbackTableName
FeedbackSnsTopicArn:
!If [EnableFeedbackSubmission, !Ref FeedbackSubmittedSNSTopic, '']
UserPoolId: !Ref CognitoUserPool
AdminsGroupName: !Join ['', [!Ref 'AWS::StackName', 'AdminsGroup']]
RegisteredGroupName: !Sub '${AWS::StackName}-RegisteredGroup'
DevelopmentMode: !Ref DevelopmentMode
# Adds the API as a trigger
Events:
ProxyApiRoot:
Expand Down Expand Up @@ -1053,24 +1194,86 @@ Resources:
Layers:
- !Ref LambdaCommonLayer

CognitoUserPoolsConfirmationStrategyFunction:
CognitoPreSignupTriggerFn:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../lambdas/cognito-user-pools-confirmation-strategy
FunctionName: !Sub '${AWS::StackName}-CognitoPreSignupTriggerFn'
CodeUri: ../lambdas/cognito-pre-signup-trigger
Handler: index.handler
MemorySize: 128
Role: !GetAtt CognitoStrategyLambdaExecutionRole.Arn
Runtime: nodejs8.10
Role: !GetAtt CognitoPreSignupTriggerExecutionRole.Arn
Runtime: nodejs10.x
Timeout: 3
Environment:
Variables:
AccountRegistrationMode: !Ref AccountRegistrationMode
Layers:
- !Ref LambdaCommonLayer

CognitoPostConfirmationTriggerFn:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-CognitoPostConfirmationTriggerFn'
CodeUri: ../lambdas/cognito-post-confirmation-trigger
Handler: index.handler
MemorySize: 128
Role: !GetAtt CognitoPostConfirmationTriggerExecutionRole.Arn
Runtime: nodejs10.x
Timeout: 3
Environment:
Variables:
AccountRegistrationMode: !Ref AccountRegistrationMode
PreLoginAccountsTableName: !Ref DevPortalPreLoginAccountsTableName
RegisteredGroupName: !Sub '${AWS::StackName}-RegisteredGroup'
Layers:
- !Ref LambdaCommonLayer

CognitoPostAuthenticationTriggerFn:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-CognitoPostAuthenticationTriggerFn'
CodeUri: ../lambdas/cognito-post-authentication-trigger
Handler: index.handler
MemorySize: 128
Role: !GetAtt CognitoPostAuthenticationTriggerExecutionRole.Arn
Runtime: nodejs10.x
Timeout: 3
Environment:
Variables:
CustomersTableName: !Ref DevPortalCustomersTableName
PreLoginAccountsTableName: !Ref DevPortalPreLoginAccountsTableName
RegisteredGroupName: !Sub '${AWS::StackName}-RegisteredGroup'
Layers:
- !Ref LambdaCommonLayer

CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Ref CognitoIdentityPoolName
# Lambda trigger caveats:
#
# - We can't use the functions' ARNs here, because there would be a
# circular dependency: some functions reference either the UserPool or
# UserPoolGroups within it.
#
# - You must declare an AWS::Lambda::Permission for each lambda here, or
# else calls from Cognito will fail with an AccessDeniedException. See
# `CognitoPreSignupTriggerFnExecutionPermission` as an example. More
# reading: <https://stackoverflow.com/a/42460847> and
# <https://forums.aws.amazon.com/thread.jspa?messageID=748566#748566>
LambdaConfig:
PreSignUp: !GetAtt CognitoUserPoolsConfirmationStrategyFunction.Arn
PreSignUp: !Join
- ''
- - !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:'
- !Sub '${AWS::StackName}-CognitoPreSignupTriggerFn'
PostConfirmation: !Join
- ''
- - !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:'
- !Sub '${AWS::StackName}-CognitoPostConfirmationTriggerFn'
PostAuthentication: !Join
- ''
- - !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:'
- !Sub '${AWS::StackName}-CognitoPostAuthenticationTriggerFn'
Policies:
PasswordPolicy:
MinimumLength: 12
Expand Down Expand Up @@ -1254,6 +1457,7 @@ Resources:
Roles:
authenticated: !GetAtt CognitoAuthenticatedRole.Arn

# Every logged-in Cognito user is "authenticated".
CognitoAuthenticatedRole:
Type: AWS::IAM::Role
Properties:
Expand All @@ -1271,6 +1475,42 @@ Resources:
'cognito-identity.amazonaws.com:amr': authenticated
Policies:
- PolicyName: CognitoAuthenticatedRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- execute-api:Invoke
Resource: !Join
- ''
- - 'arn:aws:execute-api:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- ':'
- !Ref ApiGatewayApi
- /prod/*/signin
Path: '/'

# A logged-in Cognito user, who is not in a "pending" (invite or request)
# state, is "registered".
CognitoRegisteredRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Federated: cognito-identity.amazonaws.com
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
'cognito-identity.amazonaws.com:aud': !Ref CognitoIdentityPool
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': authenticated
Policies:
- PolicyName: CognitoRegisteredRole
PolicyDocument:
Version: '2012-10-17'
Statement:
Expand Down Expand Up @@ -1316,7 +1556,7 @@ Resources:
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': authenticated
Policies:
- PolicyName: CognitoAuthenticatedRole
- PolicyName: CognitoAdminRole
PolicyDocument:
Version: '2012-10-17'
Statement:
Expand Down Expand Up @@ -1344,6 +1584,15 @@ Resources:
RoleArn: !GetAtt CognitoAdminRole.Arn
UserPoolId: !Ref CognitoUserPool

CognitoRegisteredGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
Description: 'Registered users in the developer portal'
GroupName: !Sub '${AWS::StackName}-RegisteredGroup'
Precedence: 1
RoleArn: !GetAtt CognitoRegisteredRole.Arn
UserPoolId: !Ref CognitoUserPool

CatalogUpdaterLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Expand Down
Loading