AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Serverlesspresso core stack.

Parameters:
  Source:
    Type: String
    Description: Event bus source by application
    Default: 'awsserverlessda.serverlesspresso'

Resources:
##########################################
# Custom event bus                       #
##########################################
  ServerlesspressoEventBus:
    Type: AWS::Events::EventBus
    Properties:
      Name: "Serverlesspresso"

  LogsRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: !Ref ServerlesspressoEventBus
      EventPattern:
        source: 
          - 'awsserverlessda.serverlesspresso'
      Targets:
        - Arn: !GetAtt LogGroupForEvents.Arn
          Id: LogTarget

  LogGroupForEvents:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /aws/serverlesspresso-event-bus
      
  LogGroupForEventsPolicy:
    Type: AWS::Logs::ResourcePolicy
    Properties:
      PolicyName: EventBridgeToCWLogsPolicy
      PolicyDocument: !Sub >
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "EventBridgetoCWLogsCreateLogStreamPolicy",
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "events.amazonaws.com"
                ]
              },
              "Action": [
                "logs:CreateLogStream"
              ],
              "Resource": [
                "${LogGroupForEvents.Arn}"
              ]
            },
            {
              "Sid": "EventBridgetoCWLogsPutLogEventsPolicy",
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "events.amazonaws.com"
                ]
              },
              "Action": [
                "logs:PutLogEvents"
              ],
              "Resource": [
                "${LogGroupForEvents.Arn}"
              ],
              "Condition": {
                "ArnEquals": {"AWS:SourceArn": "${LogsRule.Arn}"}
              }
            }
          ]
        }

#############################################
# Resources for IoT Core realtime messaging #
#############################################
  ServerlesspressoRealtime:
    Type: "AWS::IoT::Thing"
    Properties:
      ThingName: "serverlesspresso-realtime-workshop"
      AttributePayload:
        Attributes: {}
  GetIoTEndpointFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: GetIoTEndpoint.handler
      Runtime: nodejs14.x
      CodeUri: ./
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - iot:DescribeEndpoint
            Resource:
              - '*'

  GetIoTEndpointLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${GetIoTEndpointFunction}"

  IotEndpoint:
    Type: 'Custom::IotEndpoint'
    Properties:
      ServiceToken: !GetAtt GetIoTEndpointFunction.Arn

##########################################
# Custom auth flow Lambda functions      #
##########################################

  DefineAuthChallenge:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./cognito-triggers/
      Handler: define-auth-challenge.handler
      Runtime: nodejs14.x

  CreateAuthChallenge:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./cognito-triggers/
      Handler: create-auth-challenge.handler
      Runtime: nodejs14.x
      Policies:
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - mobiletargeting:*
                - sns:*
              Resource: "*"

  VerifyAuthChallengeResponse:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./cognito-triggers/
      Handler: verify-auth-challenge-response.handler
      Runtime: nodejs14.x

  PreSignUp:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./cognito-triggers/
      Handler: pre-sign-up.handler
      Runtime: nodejs14.x

##########################################
# Cognito Resources                      #
##########################################
  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: ServerlesspressoUserPool
      MfaConfiguration: "OFF"
      Schema:
        - Name: phone_number
          AttributeDataType: String
          Mutable: false
          Required: true
      Policies:
        PasswordPolicy:
          MinimumLength: 6
          RequireLowercase: false
          RequireNumbers: false
          RequireSymbols: false
          RequireUppercase: false
      UsernameAttributes:
        - phone_number
      MfaConfiguration: "OFF"
      LambdaConfig:
        CreateAuthChallenge: !GetAtt CreateAuthChallenge.Arn
        DefineAuthChallenge: !GetAtt DefineAuthChallenge.Arn
        PreSignUp: !GetAtt PreSignUp.Arn
        VerifyAuthChallengeResponse: !GetAtt VerifyAuthChallengeResponse.Arn

  DefineAuthChallengeInvocationPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt DefineAuthChallenge.Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt UserPool.Arn
  CreateAuthChallengeInvocationPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt CreateAuthChallenge.Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt UserPool.Arn
  VerifyAuthChallengeResponseInvocationPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt VerifyAuthChallengeResponse.Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt UserPool.Arn
  PreSignUpInvocationPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt PreSignUp.Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt UserPool.Arn

  UserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      GenerateSecret: false
      UserPoolId: !Ref UserPool

  # Creates a federated Identity pool
  IdentityPool:
    Type: "AWS::Cognito::IdentityPool"
    Properties:
      IdentityPoolName: ServerlesspressoIdentityPool
      AllowUnauthenticatedIdentities: true
      CognitoIdentityProviders:
        - ClientId: !Ref UserPoolClient
          ProviderName: !GetAtt UserPool.ProviderName

  # Create a role for unauthorized access to AWS resources.
  CognitoUnAuthorizedRole:
    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 IdentityPool
              "ForAnyValue:StringLike":
                "cognito-identity.amazonaws.com:amr": unauthenticated
      Policies:
        - PolicyName: "CognitoUnauthorizedPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "cognito-sync:*"
                Resource: !Join [ "", [ "arn:aws:cognito-sync:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":identitypool/", !Ref IdentityPool] ]
              - Effect: Allow
                Action:
                  - iot:Connect
                Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":client/serverlesspresso-*" ] ]
              - Effect: Allow
                Action:
                  - iot:Subscribe
                Resource: "*"
              - Effect: Allow
                Action:
                  - iot:Receive
                Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":topic/*" ] ]

  # Create a role for authorized acces to AWS resources.
  CognitoAuthorizedRole:
    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 IdentityPool
              "ForAnyValue:StringLike":
                "cognito-identity.amazonaws.com:amr": authenticated
      Policies:
        - PolicyName: "CognitoAuthorizedPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "cognito-sync:*"
                Resource: !Join [ "", [ "arn:aws:cognito-sync:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":identitypool/", !Ref IdentityPool] ]
              - Effect: Allow
                Action:
                  - iot:Connect
                Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":client/serverlesspresso-*" ] ]
              - Effect: Allow
                Action:
                  - iot:Subscribe
                Resource: "*"
              - Effect: Allow
                Action:
                  - iot:Receive
                Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":topic/*" ] ]
  # Assigns the roles to the Identity Pool
  IdentityPoolRoleMapping:
    Type: "AWS::Cognito::IdentityPoolRoleAttachment"
    Properties:
      IdentityPoolId: !Ref IdentityPool
      Roles:
        authenticated: !GetAtt CognitoAuthorizedRole.Arn
        unauthenticated: !GetAtt CognitoUnAuthorizedRole.Arn

##########################################
# Outputs                                #
##########################################

Outputs:

  UserPoolID:
    Description: UserPool ID
    Value: !Ref UserPool

  UserPoolWebClientId:
    Description: UserPool Web Client ID
    Value: !Ref UserPoolClient

  CoreEventBusARN:
    Description: CoreEventBus ARN
    Value: !GetAtt ServerlesspressoEventBus.Arn

  IoTRealtimeName:
    Description: IoTRealtime Name
    Value: !Ref ServerlesspressoRealtime

  IotEndpointAddress:
    Description: IotEndpointAddress URL
    Value: !GetAtt IotEndpoint.IotEndpointAddress
    Export:
      Name: !Sub "${AWS::StackName}-IotEndpointAddress"

  UserPoolParameter:
    Value: !Ref UserPool

  ServerlesspressoEventBusARNParameter:
    Value: !GetAtt ServerlesspressoEventBus.Arn

  UserPoolClientParameter:
    Value: !Ref UserPoolClient

  UserPoolARNParameter:
    Value: !GetAtt UserPool.Arn

  IOTDataEndpoint:
    Value: !GetAtt IotEndpoint.IotEndpointAddress