AWSTemplateFormatVersion: 2010-09-09

Parameters:
  env:
    Type: String
  authRoleArn:
    Type: String
  unauthRoleArn:
    Type: String

  functionKsUsersPostConfirmationArn:
    Type: String
    Default: functionKsUsersPostConfirmationArn

  functionKsUsersPostConfirmationName:
    Type: String
    Default: functionKsUsersPostConfirmationName

  identityPoolName:
    Type: String

  allowUnauthenticatedIdentities:
    Type: String

  resourceNameTruncated:
    Type: String

  userPoolName:
    Type: String

  autoVerifiedAttributes:
    Type: CommaDelimitedList

  mfaConfiguration:
    Type: String

  mfaTypes:
    Type: CommaDelimitedList

  smsAuthenticationMessage:
    Type: String

  smsVerificationMessage:
    Type: String

  emailVerificationSubject:
    Type: String

  emailVerificationMessage:
    Type: String

  defaultPasswordPolicy:
    Type: String

  passwordPolicyMinLength:
    Type: Number

  passwordPolicyCharacters:
    Type: CommaDelimitedList

  requiredAttributes:
    Type: CommaDelimitedList

  userpoolClientGenerateSecret:
    Type: String

  userpoolClientRefreshTokenValidity:
    Type: Number

  userpoolClientWriteAttributes:
    Type: CommaDelimitedList

  userpoolClientReadAttributes:
    Type: CommaDelimitedList

  userpoolClientLambdaRole:
    Type: String

  userpoolClientSetAttributes:
    Type: String

  useDefault:
    Type: String

  authSelections:
    Type: String

  resourceName:
    Type: String

  thirdPartyAuth:
    Type: String

  userPoolGroups:
    Type: String

  adminQueries:
    Type: String

  triggers:
    Type: String

  hostedUI:
    Type: String

  userPoolGroupList:
    Type: CommaDelimitedList

  adminQueryGroup:
    Type: String

  parentStack:
    Type: String

  permissions:
    Type: CommaDelimitedList

  dependsOn:
    Type: CommaDelimitedList

Conditions:
  ShouldNotCreateEnvResources: !Equals [!Ref env, NONE]

Resources:
  # BEGIN SNS ROLE RESOURCE
  SNSRole:
    # Created to allow the UserPool SMS Config to publish via the Simple Notification Service during MFA Process
    Type: AWS::IAM::Role
    Properties:
      RoleName:
        !If [
          ShouldNotCreateEnvResources,
          'KsUser5fca1828_sns-role',
          !Join [
            '',
            [
              'sns',
              !Select [3, !Split ['-', !Ref 'AWS::StackName']],
              '-',
              !Ref env,
            ],
          ],
        ]
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ''
            Effect: 'Allow'
            Principal:
              Service: 'cognito-idp.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
            Condition:
              StringEquals:
                sts:ExternalId: KsUser5fca1828_role_external_id
      Policies:
        - PolicyName: KsUser5fca1828-sns-policy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'sns:Publish'
                Resource: '*'
  # BEGIN USER POOL RESOURCES
  UserPool:
    # Created upon user selection
    # Depends on SNS Role for Arn if MFA is enabled
    Type: AWS::Cognito::UserPool
    UpdateReplacePolicy: Retain
    Properties:
      UserPoolName:
        !If [
          ShouldNotCreateEnvResources,
          !Ref userPoolName,
          !Join ['', [!Ref userPoolName, '-', !Ref env]],
        ]

      Schema:
        - Name: email
          Required: true
          Mutable: true

      LambdaConfig:
        PostConfirmation: !Ref functionKsUsersPostConfirmationArn

      AutoVerifiedAttributes: !Ref autoVerifiedAttributes

      EmailVerificationMessage: !Ref emailVerificationMessage
      EmailVerificationSubject: !Ref emailVerificationSubject

      Policies:
        PasswordPolicy:
          MinimumLength: !Ref passwordPolicyMinLength
          RequireLowercase: false
          RequireNumbers: false
          RequireSymbols: false
          RequireUppercase: false

      MfaConfiguration: !Ref mfaConfiguration
      SmsVerificationMessage: !Ref smsVerificationMessage
      SmsConfiguration:
        SnsCallerArn: !GetAtt SNSRole.Arn
        ExternalId: KsUser5fca1828_role_external_id

  UserPoolPostConfirmationLambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn: UserPool
    Properties:
      Action: 'lambda:invokeFunction'
      Principal: 'cognito-idp.amazonaws.com'
      FunctionName: !Ref functionKsUsersPostConfirmationName
      SourceArn: !GetAtt UserPool.Arn

  # Updating lambda role with permissions to Cognito

  KsUsersPostConfirmationAddToGroupCognito:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: KsUsersPostConfirmationAddToGroupCognito
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - cognito-idp:AdminAddUserToGroup

              - cognito-idp:GetGroup

              - cognito-idp:CreateGroup

            Resource: !GetAtt

              - UserPool

              - Arn

      Roles:
        - !Join ['', ['KsUsersPostConfirmation', '-', !Ref env]]

  UserPoolClientWeb:
    # Created provide application access to user pool
    # Depends on UserPool for ID reference
    Type: 'AWS::Cognito::UserPoolClient'
    Properties:
      ClientName: KsUser5fca1828_app_clientWeb

      ReadAttributes: !Ref userpoolClientReadAttributes
      WriteAttributes: !Ref userpoolClientWriteAttributes

      RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
      UserPoolId: !Ref UserPool
    DependsOn: UserPool
  UserPoolClient:
    # Created provide application access to user pool
    # Depends on UserPool for ID reference
    Type: 'AWS::Cognito::UserPoolClient'
    Properties:
      ClientName: KsUser5fca1828_app_client

      ReadAttributes: !Ref userpoolClientReadAttributes
      WriteAttributes: !Ref userpoolClientWriteAttributes

      GenerateSecret: !Ref userpoolClientGenerateSecret
      RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
      UserPoolId: !Ref UserPool
    DependsOn: UserPool
  # BEGIN USER POOL LAMBDA RESOURCES
  UserPoolClientRole:
    # Created to execute Lambda which gets userpool app client config values
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName:
        !If [
          ShouldNotCreateEnvResources,
          !Ref userpoolClientLambdaRole,
          !Join [
            '',
            [
              'upClientLambdaRole',
              !Select [3, !Split ['-', !Ref 'AWS::StackName']],
              '-',
              !Ref env,
            ],
          ],
        ]
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
    DependsOn: UserPoolClient
  UserPoolClientLambda:
    # Lambda which gets userpool app client config values
    # Depends on UserPool for id
    # Depends on UserPoolClientRole for role ARN
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: !Join
          - |+
          - - "const response = require('cfn-response');"
            - "const aws = require('aws-sdk');"
            - 'const identity = new aws.CognitoIdentityServiceProvider();'
            - 'exports.handler = (event, context, callback) => {'
            - " if (event.RequestType == 'Delete') { "
            - '   response.send(event, context, response.SUCCESS, {})'
            - ' }'
            - " if (event.RequestType == 'Update' || event.RequestType == 'Create') {"
            - '   const params = {'
            - '     ClientId: event.ResourceProperties.clientId,'
            - '     UserPoolId: event.ResourceProperties.userpoolId'
            - '   };'
            - '   identity.describeUserPoolClient(params).promise()'
            - '     .then((res) => {'
            - "       response.send(event, context, response.SUCCESS, {'appSecret': res.UserPoolClient.ClientSecret});"
            - '     })'
            - '     .catch((err) => {'
            - '       response.send(event, context, response.FAILED, {err});'
            - '     });'
            - ' }'
            - '};'
      Handler: index.handler
      Runtime: nodejs8.10
      Timeout: '300'
      Role: !GetAtt
        - UserPoolClientRole
        - Arn
    DependsOn: UserPoolClientRole
  UserPoolClientLambdaPolicy:
    # Sets userpool policy for the role that executes the Userpool Client Lambda
    # Depends on UserPool for Arn
    # Marked as depending on UserPoolClientRole for easier to understand CFN sequencing
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: KsUser5fca1828_userpoolclient_lambda_iam_policy
      Roles:
        - !Ref UserPoolClientRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - 'cognito-idp:DescribeUserPoolClient'
            Resource: !GetAtt UserPool.Arn
    DependsOn: UserPoolClientLambda
  UserPoolClientLogPolicy:
    # Sets log policy for the role that executes the Userpool Client Lambda
    # Depends on UserPool for Arn
    # Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: KsUser5fca1828_userpoolclient_lambda_log_policy
      Roles:
        - !Ref UserPoolClientRole
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - 'logs:CreateLogGroup'
              - 'logs:CreateLogStream'
              - 'logs:PutLogEvents'
            Resource: !Sub
              - arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*
              - {
                  region: !Ref 'AWS::Region',
                  account: !Ref 'AWS::AccountId',
                  lambda: !Ref UserPoolClientLambda,
                }
    DependsOn: UserPoolClientLambdaPolicy
  UserPoolClientInputs:
    # Values passed to Userpool client Lambda
    # Depends on UserPool for Id
    # Depends on UserPoolClient for Id
    # Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
    Type: 'Custom::LambdaCallout'
    Properties:
      ServiceToken: !GetAtt UserPoolClientLambda.Arn
      clientId: !Ref UserPoolClient
      userpoolId: !Ref UserPool
    DependsOn: UserPoolClientLogPolicy

  # BEGIN IDENTITY POOL RESOURCES

  IdentityPool:
    # Always created
    Type: AWS::Cognito::IdentityPool
    Properties:
      IdentityPoolName:
        !If [
          ShouldNotCreateEnvResources,
          'KsUsersIdPool',
          !Join ['', ['KsUsersIdPool', '__', !Ref env]],
        ]

      CognitoIdentityProviders:
        - ClientId: !Ref UserPoolClient
          ProviderName: !Sub
            - cognito-idp.${region}.amazonaws.com/${client}
            - { region: !Ref 'AWS::Region', client: !Ref UserPool }
        - ClientId: !Ref UserPoolClientWeb
          ProviderName: !Sub
            - cognito-idp.${region}.amazonaws.com/${client}
            - { region: !Ref 'AWS::Region', client: !Ref UserPool }

      AllowUnauthenticatedIdentities: !Ref allowUnauthenticatedIdentities

    DependsOn: UserPoolClientInputs

  IdentityPoolRoleMap:
    # Created to map Auth and Unauth roles to the identity pool
    # Depends on Identity Pool for ID ref
    Type: AWS::Cognito::IdentityPoolRoleAttachment
    Properties:
      IdentityPoolId: !Ref IdentityPool
      Roles:
        unauthenticated: !Ref unauthRoleArn
        authenticated: !Ref authRoleArn
    DependsOn: IdentityPool

Outputs:
  IdentityPoolId:
    Value: !Ref 'IdentityPool'
    Description: Id for the identity pool
  IdentityPoolName:
    Value: !GetAtt IdentityPool.Name

  UserPoolId:
    Value: !Ref 'UserPool'
    Description: Id for the user pool
  UserPoolName:
    Value: !Ref userPoolName
  AppClientIDWeb:
    Value: !Ref 'UserPoolClientWeb'
    Description: The user pool app client id for web
  AppClientID:
    Value: !Ref 'UserPoolClient'
    Description: The user pool app client id
  AppClientSecret:
    Value: !GetAtt UserPoolClientInputs.appSecret
