diff --git a/resources/cognito-identity-pool.yml b/resources/cognito-identity-pool.yml new file mode 100644 index 0000000..c4ca748 --- /dev/null +++ b/resources/cognito-identity-pool.yml @@ -0,0 +1,92 @@ +Resources: + # The federated identity for our user pool to auth with + CognitoIdentityPool: + Type: AWS::Cognito::IdentityPool + Properties: + # Generate a name based on the stage + IdentityPoolName: ${self:custom.stage}IdentityPool + # Don't allow unathenticated users + AllowUnauthenticatedIdentities: false + # Link to our User Pool + CognitoIdentityProviders: + - ClientId: + Ref: CognitoUserPoolClient + ProviderName: + Fn::GetAtt: [ "CognitoUserPool", "ProviderName" ] + + # IAM roles + CognitoIdentityPoolRoles: + Type: AWS::Cognito::IdentityPoolRoleAttachment + Properties: + IdentityPoolId: + Ref: CognitoIdentityPool + Roles: + authenticated: + Fn::GetAtt: [CognitoAuthRole, Arn] + + # IAM role used for authenticated users + CognitoAuthRole: + Type: AWS::IAM::Role + Properties: + Path: / + 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: 'CognitoAuthorizedPolicy' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Action: + - 'mobileanalytics:PutEvents' + - 'cognito-sync:*' + - 'cognito-identity:*' + Resource: '*' + + # Allow users to invoke our API + - Effect: 'Allow' + Action: + - 'execute-api:Invoke' + Resource: + Fn::Join: + - '' + - + - 'arn:aws:execute-api:' + - Ref: AWS::Region + - ':' + - Ref: AWS::AccountId + - ':' + - Ref: ApiGatewayRestApi + - '/*' + + # Allow users to upload attachments to their + # folder inside our S3 bucket + - Effect: 'Allow' + Action: + - 's3:*' + Resource: + - Fn::Join: + - '' + - + - Fn::GetAtt: [AttachmentsBucket, Arn] + - '/private/' + - '$' + - '{cognito-identity.amazonaws.com:sub}/*' + +# Print out the Id of the Identity Pool that is created +Outputs: + IdentityPoolId: + Value: + Ref: CognitoIdentityPool diff --git a/resources/cognito-user-pool.yml b/resources/cognito-user-pool.yml new file mode 100644 index 0000000..29ba78d --- /dev/null +++ b/resources/cognito-user-pool.yml @@ -0,0 +1,32 @@ +Resources: + CognitoUserPool: + Type: AWS::Cognito::UserPool + Properties: + # Generate a name based on the stage + UserPoolName: ${self:custom.stage}-user-pool + # Set email as an alias + UsernameAttributes: + - email + AutoVerifiedAttributes: + - email + + CognitoUserPoolClient: + Type: AWS::Cognito::UserPoolClient + Properties: + # Generate an app client name based on the stage + ClientName: ${self:custom.stage}-user-pool-client + UserPoolId: + Ref: CognitoUserPool + ExplicitAuthFlows: + - ADMIN_NO_SRP_AUTH + GenerateSecret: false + +# Print out the Id of the User Pool that is created +Outputs: + UserPoolId: + Value: + Ref: CognitoUserPool + + UserPoolClientId: + Value: + Ref: CognitoUserPoolClient diff --git a/resources/dynamodb-table.yml b/resources/dynamodb-table.yml new file mode 100644 index 0000000..a973acc --- /dev/null +++ b/resources/dynamodb-table.yml @@ -0,0 +1,17 @@ +Resources: + NotesTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: ${self:custom.tableName} + AttributeDefinitions: + - AttributeName: userId + AttributeType: S + - AttributeName: noteId + AttributeType: S + KeySchema: + - AttributeName: userId + KeyType: HASH + - AttributeName: noteId + KeyType: RANGE + # Set the capacity to auto-scale + BillingMode: PAY_PER_REQUEST diff --git a/resources/s3-bucket.yml b/resources/s3-bucket.yml new file mode 100644 index 0000000..38dd506 --- /dev/null +++ b/resources/s3-bucket.yml @@ -0,0 +1,25 @@ +Resources: + AttachmentsBucket: + Type: AWS::S3::Bucket + Properties: + # Set the CORS policy + CorsConfiguration: + CorsRules: + - + AllowedOrigins: + - '*' + AllowedHeaders: + - '*' + AllowedMethods: + - GET + - PUT + - POST + - DELETE + - HEAD + MaxAge: 3000 + +# Print out the name of the bucket that is created +Outputs: + AttachmentsBucketName: + Value: + Ref: AttachmentsBucket diff --git a/serverless.yml b/serverless.yml index e62e82b..d53639b 100644 --- a/serverless.yml +++ b/serverless.yml @@ -1,4 +1,4 @@ -service: notes-app-api +service: notes-app-2-api # Create an optimized package for our functions package: @@ -9,20 +9,25 @@ plugins: - serverless-offline - serverless-dotenv-plugin # Load .env as environment variables +custom: + # Our stage is based on what is passed in when running serverless + # commands. Or fallsback to what we have set in the provider section. + stage: ${opt:stage, self:provider.stage} + # Set the table name here so we can use it while testing locally + tableName: ${self:custom.stage}-notes + provider: name: aws runtime: nodejs12.x - stage: prod + stage: dev region: us-east-1 # These environment variables are made available to our functions # under process.env. environment: - tableName: notes + tableName: ${self:custom.tableName} stripeSecretKey: ${env:STRIPE_SECRET_KEY} - # 'iamRoleStatements' defines the permission policy for the Lambda function. - # In this case Lambda functions are granted with permissions to access DynamoDB. iamRoleStatements: - Effect: Allow Action: @@ -33,7 +38,10 @@ provider: - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:DeleteItem - Resource: "arn:aws:dynamodb:us-east-1:*:*" + # Restrict our IAM role permissions to + # the specific table for the stage + Resource: + - "Fn::GetAtt": [ NotesTable, Arn ] functions: # Defines an HTTP API endpoint that calls the main function in create.js @@ -115,3 +123,10 @@ functions: resources: # API Gateway Errors - ${file(resources/api-gateway-errors.yml)} + # DynamoDB + - ${file(resources/dynamodb-table.yml)} + # S3 + - ${file(resources/s3-bucket.yml)} + # Cognito + - ${file(resources/cognito-user-pool.yml)} + - ${file(resources/cognito-identity-pool.yml)}