def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        servu_userpool = aws_cognito.UserPool(
            self,
            'servu-userpool',
            user_pool_name='ServU Users',
            sign_in_aliases=aws_cognito.SignInAliases(email=True),
            standard_attributes=aws_cognito.StandardAttributes(
                email=aws_cognito.StandardAttribute(required=True,
                                                    mutable=True),
                fullname=aws_cognito.StandardAttribute(required=True,
                                                       mutable=True),
                address=aws_cognito.StandardAttribute(required=True,
                                                      mutable=True),
                phone_number=aws_cognito.StandardAttribute(required=True,
                                                           mutable=True)),
            password_policy=aws_cognito.PasswordPolicy(
                min_length=8,
                require_digits=True,
                require_lowercase=True,
                require_symbols=True,
                require_uppercase=True,
                temp_password_validity=core.Duration.days(1)),
            sign_in_case_sensitive=False)

        servu_userpool_web_client = aws_cognito.UserPoolClient(
            self,
            'servu-userpool-web-client',
            user_pool=servu_userpool,
            auth_flows=aws_cognito.AuthFlow(custom=True,
                                            refresh_token=True,
                                            user_srp=True),
            user_pool_client_name='ServU Web Client')

        # TODO: Manually configure the domain and callback URLs. Look at CFN for the Pool ID and Client ID
        core.CfnOutput(self,
                       'servu-userpool-id',
                       value=servu_userpool.user_pool_id,
                       export_name='servu-userpool-id')

        core.CfnOutput(self,
                       'servu-userpool-client-id',
                       value=servu_userpool_web_client.user_pool_client_id,
                       export_name='servu-userpool-client-id')
Ejemplo n.º 2
0
    def _create_userpool(self):
        user_pool = cognito.UserPool(
            self,
            "movio",
            account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
            auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=False),
            mfa=cognito.Mfa.OFF,
            mfa_second_factor=cognito.MfaSecondFactor(otp=True, sms=False),
            self_sign_up_enabled=False,
            sign_in_aliases=cognito.SignInAliases(email=True, username=True),
            standard_attributes=cognito.StandardAttributes(
                email=cognito.StandardAttribute(mutable=False,
                                                required=True), ),
            user_invitation=cognito.UserInvitationConfig(
                email_subject="Moshan email verification",
                email_body=
                "Thanks for signing up to moshan! Your username is {username} and temporary password is {####}\nYou can now login at https://moshan.tv",
            ),
            user_verification=cognito.UserVerificationConfig(
                email_subject="Moshan email verification",
                email_body=
                "Thanks for signing up to moshan! Verify your account by clicking on {##Verify Email##}",
                email_style=cognito.VerificationEmailStyle.LINK),
        )

        user_pool.add_client(
            "moshan",
            auth_flows=cognito.AuthFlow(refresh_token=True),
            o_auth=cognito.OAuthSettings(
                flows=cognito.OAuthFlows(authorization_code_grant=True),
                callback_urls=[
                    "https://moshan.tv/callback.html",
                    "https://beta.moshan.tv/callback.html"
                ],
                scopes=[
                    cognito.OAuthScope.EMAIL, cognito.OAuthScope.OPENID,
                    cognito.OAuthScope.PROFILE
                ]),
            prevent_user_existence_errors=True,
        )

        cert = Certificate.from_certificate_arn(self, "domainCert",
                                                self.cert_arn)
        user_pool.add_domain("CognitoDomain",
                             custom_domain=cognito.CustomDomainOptions(
                                 domain_name=self.domain_name,
                                 certificate=cert))
Ejemplo n.º 3
0
 def _create_user_pool(self) -> cognito.UserPool:
     pool = cognito.UserPool(
         scope=self,
         id="orbit-user-pool",
         account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
         auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=False),
         custom_attributes=None,
         email_settings=None,
         lambda_triggers=None,
         mfa=cognito.Mfa.OFF,
         mfa_second_factor=None,
         password_policy=cognito.PasswordPolicy(
             min_length=8,
             require_digits=True,
             require_lowercase=True,
             require_symbols=True,
             require_uppercase=True,
             temp_password_validity=Duration.days(5),
         ),
         self_sign_up_enabled=False,
         sign_in_aliases=cognito.SignInAliases(email=True,
                                               phone=False,
                                               preferred_username=False,
                                               username=True),
         sign_in_case_sensitive=True,
         sms_role=None,
         sms_role_external_id=None,
         standard_attributes=cognito.StandardAttributes(
             email=cognito.StandardAttribute(required=True, mutable=True)),
         user_invitation=cognito.UserInvitationConfig(
             email_subject="Invite to join Orbit Workbench!",
             email_body=
             "Hello, you have been invited to join Orbit Workbench!<br/><br/>"
             "Username: {username}<br/>"
             "Temporary password: {####}<br/><br/>"
             "Regards",
         ),
         user_pool_name=f"orbit-{self.env_name}-user-pool",
     )
     pool.apply_removal_policy(policy=core.RemovalPolicy.DESTROY)
     pool.add_domain(
         id="orbit-user-pool-domain",
         cognito_domain=cognito.CognitoDomainOptions(
             domain_prefix=f"orbit-{self.context.account_id}-{self.env_name}"
         ),
     )
     return pool
Ejemplo n.º 4
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        with open("stack/config.yml", 'r') as stream:
            configs = yaml.safe_load(stream)

        ### S3 core
        images_S3_bucket = _s3.Bucket(self, "ICS_IMAGES")

        images_S3_bucket.add_cors_rule(
            allowed_methods=[_s3.HttpMethods.POST],
            allowed_origins=["*"] # add API gateway web resource URL
        )

        ### SQS core
        image_deadletter_queue = _sqs.Queue(self, "ICS_IMAGES_DEADLETTER_QUEUE")
        image_queue = _sqs.Queue(self, "ICS_IMAGES_QUEUE",
            dead_letter_queue={
                "max_receive_count": configs["DeadLetterQueue"]["MaxReceiveCount"],
                "queue": image_deadletter_queue
            })

        ### api gateway core
        api_gateway = RestApi(self, 'ICS_API_GATEWAY', rest_api_name='ImageContentSearchApiGateway')
        api_gateway_resource = api_gateway.root.add_resource(configs["ProjectName"])
        api_gateway_landing_page_resource = api_gateway_resource.add_resource('web')
        api_gateway_get_signedurl_resource = api_gateway_resource.add_resource('signedUrl')
        api_gateway_image_search_resource = api_gateway_resource.add_resource('search')

        ### landing page function
        get_landing_page_function = Function(self, "ICS_GET_LANDING_PAGE",
            function_name="ICS_GET_LANDING_PAGE",
            runtime=Runtime.PYTHON_3_7,
            handler="main.handler",
            code=Code.asset("./src/landingPage"))

        get_landing_page_integration = LambdaIntegration(
            get_landing_page_function, 
            proxy=True, 
            integration_responses=[{
                'statusCode': '200',
               'responseParameters': {
                   'method.response.header.Access-Control-Allow-Origin': "'*'",
                }
            }])

        api_gateway_landing_page_resource.add_method('GET', get_landing_page_integration,
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Origin': True,
                }
            }])

        ### cognito
        required_attribute = _cognito.StandardAttribute(required=True)

        users_pool = _cognito.UserPool(self, "ICS_USERS_POOL",
            auto_verify=_cognito.AutoVerifiedAttrs(email=True), #required for self sign-up
            standard_attributes=_cognito.StandardAttributes(email=required_attribute), #required for self sign-up
            self_sign_up_enabled=configs["Cognito"]["SelfSignUp"])

        user_pool_app_client = _cognito.CfnUserPoolClient(self, "ICS_USERS_POOL_APP_CLIENT", 
            supported_identity_providers=["COGNITO"],
            allowed_o_auth_flows=["implicit"],
            allowed_o_auth_scopes=configs["Cognito"]["AllowedOAuthScopes"],
            user_pool_id=users_pool.user_pool_id,
            callback_ur_ls=[api_gateway_landing_page_resource.url],
            allowed_o_auth_flows_user_pool_client=True,
            explicit_auth_flows=["ALLOW_REFRESH_TOKEN_AUTH"])

        user_pool_domain = _cognito.UserPoolDomain(self, "ICS_USERS_POOL_DOMAIN", 
            user_pool=users_pool, 
            cognito_domain=_cognito.CognitoDomainOptions(domain_prefix=configs["Cognito"]["DomainPrefix"]))

        ### get signed URL function
        get_signedurl_function = Function(self, "ICS_GET_SIGNED_URL",
            function_name="ICS_GET_SIGNED_URL",
            environment={
                "ICS_IMAGES_BUCKET": images_S3_bucket.bucket_name,
                "DEFAULT_SIGNEDURL_EXPIRY_SECONDS": configs["Functions"]["DefaultSignedUrlExpirySeconds"]
            },
            runtime=Runtime.PYTHON_3_7,
            handler="main.handler",
            code=Code.asset("./src/getSignedUrl"))

        get_signedurl_integration = LambdaIntegration(
            get_signedurl_function, 
            proxy=True, 
            integration_responses=[{
                'statusCode': '200',
               'responseParameters': {
                   'method.response.header.Access-Control-Allow-Origin': "'*'",
                }
            }])

        api_gateway_get_signedurl_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",
            rest_api_id=api_gateway_get_signedurl_resource.rest_api.rest_api_id,
            name="ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",
            type="COGNITO_USER_POOLS",
            identity_source="method.request.header.Authorization",
            provider_arns=[users_pool.user_pool_arn])

        api_gateway_get_signedurl_resource.add_method('GET', get_signedurl_integration,
            authorization_type=AuthorizationType.COGNITO,
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Origin': True,
                }
            }]
            ).node.find_child('Resource').add_property_override('AuthorizerId', api_gateway_get_signedurl_authorizer.ref)

        images_S3_bucket.grant_put(get_signedurl_function, objects_key_pattern="new/*")

        ### image massage function
        image_massage_function = Function(self, "ICS_IMAGE_MASSAGE",
            function_name="ICS_IMAGE_MASSAGE",
            timeout=core.Duration.seconds(6),
            runtime=Runtime.PYTHON_3_7,
            environment={"ICS_IMAGE_MASSAGE": image_queue.queue_name},
            handler="main.handler",
            code=Code.asset("./src/imageMassage"))

        images_S3_bucket.grant_write(image_massage_function, "processed/*")
        images_S3_bucket.grant_delete(image_massage_function, "new/*")
        images_S3_bucket.grant_read(image_massage_function, "new/*")
        
        new_image_added_notification = _s3notification.LambdaDestination(image_massage_function)

        images_S3_bucket.add_event_notification(_s3.EventType.OBJECT_CREATED, 
            new_image_added_notification, 
            _s3.NotificationKeyFilter(prefix="new/")
            )

        image_queue.grant_send_messages(image_massage_function)

        ### image analyzer function
        image_analyzer_function = Function(self, "ICS_IMAGE_ANALYSIS",
            function_name="ICS_IMAGE_ANALYSIS",
            runtime=Runtime.PYTHON_3_7,
            timeout=core.Duration.seconds(10),
            environment={
                "ICS_IMAGES_BUCKET": images_S3_bucket.bucket_name,
                "DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],
                "REGION": core.Aws.REGION,
                },
            handler="main.handler",
            code=Code.asset("./src/imageAnalysis")) 

        image_analyzer_function.add_event_source(_lambda_event_source.SqsEventSource(queue=image_queue, batch_size=10))
        image_queue.grant_consume_messages(image_massage_function)

        lambda_rekognition_access = _iam.PolicyStatement(
            effect=_iam.Effect.ALLOW, 
            actions=["rekognition:DetectLabels", "rekognition:DetectModerationLabels"],
            resources=["*"]                    
        )

        image_analyzer_function.add_to_role_policy(lambda_rekognition_access)
        images_S3_bucket.grant_read(image_analyzer_function, "processed/*")

        ### API gateway finalizing
        self.add_cors_options(api_gateway_get_signedurl_resource)
        self.add_cors_options(api_gateway_landing_page_resource)
        self.add_cors_options(api_gateway_image_search_resource)

        ### database 
        database_secret = _secrets_manager.Secret(self, "ICS_DATABASE_SECRET",
            secret_name="rds-db-credentials/image-content-search-rds-secret",
            generate_secret_string=_secrets_manager.SecretStringGenerator(
                generate_string_key='password',
                secret_string_template='{"username": "******"}',
                exclude_punctuation=True,
                exclude_characters='/@\" \\\'',
                require_each_included_type=True
            )
        )

        database = _rds.CfnDBCluster(self, "ICS_DATABASE",
            engine=_rds.DatabaseClusterEngine.aurora_mysql(version=_rds.AuroraMysqlEngineVersion.VER_5_7_12).engine_type,
            engine_mode="serverless",
            database_name=configs["Database"]["Name"],
            enable_http_endpoint=True,
            deletion_protection=configs["Database"]["DeletionProtection"],
            master_username=database_secret.secret_value_from_json("username").to_string(),
            master_user_password=database_secret.secret_value_from_json("password").to_string(),
            scaling_configuration=_rds.CfnDBCluster.ScalingConfigurationProperty(
                auto_pause=configs["Database"]["Scaling"]["AutoPause"],
                min_capacity=configs["Database"]["Scaling"]["Min"],
                max_capacity=configs["Database"]["Scaling"]["Max"],
                seconds_until_auto_pause=configs["Database"]["Scaling"]["SecondsToAutoPause"]
            ),
        )

        database_cluster_arn = "arn:aws:rds:{}:{}:cluster:{}".format(core.Aws.REGION, core.Aws.ACCOUNT_ID, database.ref)
   
        secret_target = _secrets_manager.CfnSecretTargetAttachment(self,"ICS_DATABASE_SECRET_TARGET",
            target_type="AWS::RDS::DBCluster",
            target_id=database.ref,
            secret_id=database_secret.secret_arn
        )

        secret_target.node.add_dependency(database)

        ### database function
        image_data_function_role = _iam.Role(self, "ICS_IMAGE_DATA_FUNCTION_ROLE",
            role_name="ICS_IMAGE_DATA_FUNCTION_ROLE",
            assumed_by=_iam.ServicePrincipal("lambda.amazonaws.com"),
            managed_policies=[
                _iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"),
                _iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole"),
                _iam.ManagedPolicy.from_aws_managed_policy_name("AmazonRDSDataFullAccess")
            ]
        )
        
        image_data_function = Function(self, "ICS_IMAGE_DATA",
            function_name="ICS_IMAGE_DATA",
            runtime=Runtime.PYTHON_3_7,
            timeout=core.Duration.seconds(5),
            role=image_data_function_role,
            environment={
                "DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],
                "CLUSTER_ARN": database_cluster_arn,
                "CREDENTIALS_ARN": database_secret.secret_arn,
                "DB_NAME": database.database_name,
                "REGION": core.Aws.REGION
                },
            handler="main.handler",
            code=Code.asset("./src/imageData")
        ) 

        image_search_integration = LambdaIntegration(
            image_data_function, 
            proxy=True, 
            integration_responses=[{
                'statusCode': '200',
               'responseParameters': {
                   'method.response.header.Access-Control-Allow-Origin': "'*'",
                }
            }])

        api_gateway_image_search_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",
            rest_api_id=api_gateway_image_search_resource.rest_api.rest_api_id,
            name="ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",
            type="COGNITO_USER_POOLS", 
            identity_source="method.request.header.Authorization",
            provider_arns=[users_pool.user_pool_arn])

        api_gateway_image_search_resource.add_method('POST', image_search_integration,
            authorization_type=AuthorizationType.COGNITO,
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Origin': True,
                }
            }]
            ).node.find_child('Resource').add_property_override('AuthorizerId', api_gateway_image_search_authorizer.ref)


        lambda_access_search = _iam.PolicyStatement(
            effect=_iam.Effect.ALLOW, 
            actions=["translate:TranslateText"],
            resources=["*"]            
        ) 

        image_data_function.add_to_role_policy(lambda_access_search)

        ### custom resource
        lambda_provider = Provider(self, 'ICS_IMAGE_DATA_PROVIDER', 
            on_event_handler=image_data_function
        )

        core.CustomResource(self, 'ICS_IMAGE_DATA_RESOURCE', 
            service_token=lambda_provider.service_token,
            pascal_case_properties=False,
            resource_type="Custom::SchemaCreation",
            properties={
                "source": "Cloudformation"
            }
        )

        ### event bridge
        event_bus = _events.EventBus(self, "ICS_IMAGE_CONTENT_BUS")

        event_rule = _events.Rule(self, "ICS_IMAGE_CONTENT_RULE",
            rule_name="ICS_IMAGE_CONTENT_RULE",
            description="The event from image analyzer to store the data",
            event_bus=event_bus,
            event_pattern=_events.EventPattern(resources=[image_analyzer_function.function_arn]),
        )

        event_rule.add_target(_event_targets.LambdaFunction(image_data_function))

        event_bus.grant_put_events(image_analyzer_function)
        image_analyzer_function.add_environment("EVENT_BUS", event_bus.event_bus_name)

        ### outputs
        core.CfnOutput(self, 'CognitoHostedUILogin',
            value='https://{}.auth.{}.amazoncognito.com/login?client_id={}&response_type=token&scope={}&redirect_uri={}'.format(user_pool_domain.domain_name, core.Aws.REGION, user_pool_app_client.ref, '+'.join(user_pool_app_client.allowed_o_auth_scopes), api_gateway_landing_page_resource.url),
            description='The Cognito Hosted UI Login Page'
        )
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # -----------------------------------
        #           Cognito User Pool
        # -----------------------------------
        userpool = cognito.UserPool(
            self,
            "ServerlessTodoUserPool",
            user_pool_name="ServerlessTodoUserPool",
            sign_in_aliases=cognito.SignInAliases(username=True, email=True),
            password_policy=cognito.PasswordPolicy(
                min_length=6,
                require_digits=True,
                require_lowercase=True,
                require_symbols=True,
                require_uppercase=True,
                temp_password_validity=core.Duration.days(7)),
            auto_verify=cognito.AutoVerifiedAttrs(email=True),
            standard_attributes=cognito.StandardAttributes(
                email=cognito.StandardAttribute(mutable=True, required=True),
                family_name=cognito.StandardAttribute(mutable=True,
                                                      required=True),
                given_name=cognito.StandardAttribute(mutable=True,
                                                     required=True)))
        user_pool_client = userpool.add_client(
            "UserPoolClient",
            auth_flows=cognito.AuthFlow(admin_user_password=True))

        # -----------------------------------
        #           dynamodb
        # -----------------------------------
        dynamodbTable = dynamodb.Table(
            self,
            "TaskTable",
            partition_key=dynamodb.Attribute(
                name="id", type=dynamodb.AttributeType.STRING),
            sort_key=dynamodb.Attribute(name="meta",
                                        type=dynamodb.AttributeType.STRING),
            billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
            point_in_time_recovery=True,
            server_side_encryption=True)
        dynamodbTable.add_global_secondary_index(
            partition_key=dynamodb.Attribute(
                name="meta", type=dynamodb.AttributeType.STRING),
            sort_key=dynamodb.Attribute(name="id",
                                        type=dynamodb.AttributeType.STRING),
            index_name="meta-id-index")
        dynamodbTable.add_global_secondary_index(
            partition_key=dynamodb.Attribute(
                name="owner", type=dynamodb.AttributeType.STRING),
            sort_key=dynamodb.Attribute(name="meta",
                                        type=dynamodb.AttributeType.STRING),
            index_name="owner-meta-index")

        # -----------------------------------
        #             apigateway
        # -----------------------------------
        acm_arn = self.node.try_get_context('acm_arn')
        domain_name = self.node.try_get_context("domain_name")
        hosted_zone = self.node.try_get_context("hosted_zone")

        api_policy = iam.PolicyDocument(
            statements=iam.PolicyStatement(actions=["lambda:InvokeFunction"], )
            .add_resources("arn:aws:lambda:{}:{}:function:*".format(
                self.region, self.account)))

        if acm_arn and domain_name and hosted_zone:

            api = apigw.RestApi(
                self,
                'API',
                domain_name=apigw.DomainNameOptions(
                    certificate=acm.Certificate.from_certificate_arn(
                        self, 'ApiCertificate', acm_arn),
                    domain_name=domain_name,
                    endpoint_type=apigw.EndpointType.REGIONAL),
                deploy_options=apigw.StageOptions(metrics_enabled=True),
                policy=api_policy,
                rest_api_name="Serverless TODO API",
                endpoint_types=[apigw.EndpointType.REGIONAL],
                default_cors_preflight_options=apigw.CorsOptions(
                    allow_origins=apigw.Cors.
                    ALL_ORIGINS,  # TODO: Temporary for development
                    allow_headers=[
                        "Content-Type", "X-Amz-Date", "Authorization",
                        "X-Api-Key", "X-Amz-Security-Token", "X-Tracing-Id",
                        "x-jeffy-correlation-id", "x-amzn-trace-id"
                    ],
                    allow_methods=apigw.Cors.ALL_METHODS,
                    allow_credentials=True))
            route53.CfnRecordSet(
                self,
                "apiDomainRecord",
                name=domain_name,
                type="A",
                alias_target={
                    "dnsName":
                    api.domain_name.domain_name_alias_domain_name,
                    "hostedZoneId":
                    api.domain_name.domain_name_alias_hosted_zone_id
                },
                hosted_zone_id=hosted_zone,
            )
        else:
            api = apigw.RestApi(
                self,
                'API',
                deploy_options=apigw.StageOptions(metrics_enabled=True),
                policy=api_policy,
                rest_api_name="Serverless TODO API",
                endpoint_types=[apigw.EndpointType.REGIONAL],
                default_cors_preflight_options=apigw.CorsOptions(
                    allow_origins=apigw.Cors.
                    ALL_ORIGINS,  # TODO: Temporary for development
                    allow_headers=[
                        "Content-Type", "X-Amz-Date", "Authorization",
                        "X-Api-Key", "X-Amz-Security-Token", "X-Tracing-Id",
                        "x-jeffy-correlation-id", "x-amzn-trace-id"
                    ],
                    allow_methods=apigw.Cors.ALL_METHODS,
                    allow_credentials=True))

        cognito_authorizer = apigw.CognitoUserPoolsAuthorizer(
            self,
            "CognitoAuthorizer",
            cognito_user_pools=[userpool],
            authorizer_name='todo_cognito_authorizer',
            identity_source='method.request.header.Authorization',
            results_cache_ttl=core.Duration.minutes(60))

        api_role = iam.Role(self,
                            "ApiRole",
                            assumed_by=iam.ServicePrincipal(
                                service="apigateway.amazonaws.com"))
        api_statement = iam.PolicyStatement(
            actions=["lambda:InvokeFunction"],
            resources=[
                "arn:aws:lambda:{}:{}:function:*".format(
                    self.region, self.account)
            ])
        api_role.add_to_policy(api_statement)

        # -----------------------------------
        #      lambda common configure
        # -----------------------------------
        env = {
            "TABLE_NAME": dynamodbTable.table_name,
            "USER_POOL_ID": userpool.user_pool_id,
            "USER_POOL_NAME": userpool.user_pool_provider_name,
            "CLIENT_ID": user_pool_client.user_pool_client_id
        }

        # -----------------------------------
        #           get handler
        # -----------------------------------
        get_resource_base_name = "getTaskFunction"
        get_task_func = lambda_.Function(
            self,
            get_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="get.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        get_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      get_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      get_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        task_path = api.root.add_resource("task")
        task_id_path = task_path.add_resource("{task_id}")
        get_task_integration = apigw.LambdaIntegration(
            get_task_func, credentials_role=api_role)
        task_id_path.add_method(
            "GET",
            integration=get_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           create handler
        # -----------------------------------
        create_resource_base_name = "createTaskFunction"
        create_task_func = lambda_.Function(
            self,
            create_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="create.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        create_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      create_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      create_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        create_task_integration = apigw.LambdaIntegration(
            create_task_func, credentials_role=api_role)
        task_path.add_method(
            "POST",
            integration=create_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           update handler
        # -----------------------------------
        update_resource_base_name = "updateTaskFunction"
        update_task_func = lambda_.Function(
            self,
            update_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="update.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        update_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      update_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      update_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        update_task_integration = apigw.LambdaIntegration(
            update_task_func, credentials_role=api_role)
        task_id_path.add_method(
            "POST",
            integration=update_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           delete handler
        # -----------------------------------
        delete_resource_base_name = "deleteTaskFunction"
        delete_task_func = lambda_.Function(
            self,
            delete_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="delete.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        delete_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      delete_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      delete_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        delete_task_integration = apigw.LambdaIntegration(
            delete_task_func, credentials_role=api_role)
        task_id_path.add_method(
            "DELETE",
            integration=delete_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           search handler
        # -----------------------------------
        search_resource_base_name = "searchTaskFunction"
        search_task_func = lambda_.Function(
            self,
            search_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="search.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        search_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      search_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      search_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        search_task_integration = apigw.LambdaIntegration(
            search_task_func, credentials_role=api_role)
        tasks_path = api.root.add_resource("tasks")
        tasks_path.add_method(
            "GET",
            integration=search_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           login handler
        # -----------------------------------
        login_resource_base_name = "loginFunction"
        login_task_func = lambda_.Function(
            self,
            login_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/user',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="login.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        login_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['cognito-idp:AdminInitiateAuth'],
            resources=[userpool.user_pool_arn]))
        logs.LogGroup(self,
                      login_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      login_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        login_task_integration = apigw.LambdaIntegration(login_task_func)
        auth_path = api.root.add_resource("auth")
        auth_login_path = auth_path.add_resource("login")
        auth_login_path.add_method("POST", integration=login_task_integration)
Ejemplo n.º 6
0
def base_cognito_user_pool(construct, **kwargs):
    """
    Function that generates a Cognito User Pool.
    :param construct: Custom construct that will use this function. From the external construct is usually 'self'.
    :param kwargs: Consist of required 'queue_name' and optionals 'queue_delivery_delay' and 'queue_visibility_timeout'.
    :return: DynamoDB Table Construct.
    """
    user_pool_name = construct.prefix + "_" + kwargs[
        "pool_name"] + "_pool_" + construct.environment_

    if kwargs.get("email") is not None:
        email_settings = cognito.EmailSettings(
            from_=kwargs["email"]["from"],
            reply_to=kwargs["email"].get("reply_to"))
    else:
        email_settings = None

    password_policy_settings = kwargs.get("password_policy")
    temporary_password_validation_time = (
        core.Duration.days(
            password_policy_settings.get("temporary_password_duration"))
        if password_policy_settings.get("temporary_password_duration")
        is not None else None)
    password_policy = cognito.PasswordPolicy(
        min_length=password_policy_settings.get("minimum_length"),
        temp_password_validity=temporary_password_validation_time,
        require_lowercase=password_policy_settings.get("require",
                                                       {}).get("lower_case"),
        require_uppercase=password_policy_settings.get("require",
                                                       {}).get("upper_case"),
        require_digits=password_policy_settings.get("require",
                                                    {}).get("digits"),
        require_symbols=password_policy_settings.get("require",
                                                     {}).get("symbols"),
    )

    sign_up_info = kwargs["sign_up"]
    self_sing_up = sign_up_info["enabled"]
    user_verification_info = base_user_verification(
        sign_up_info=sign_up_info["user_verification"])

    user_invitation = kwargs.get("invitation")
    user_invitation_configuration = cognito.UserInvitationConfig(
        email_subject=user_invitation.get("email", {}).get("subject"),
        email_body=user_invitation.get("email", {}).get("body"),
        sms_message=user_invitation.get("sms", {}).get("body"),
    )

    trigger_functions = kwargs.get("triggers", {})
    lambda_triggers = base_lambda_triggers(construct,
                                           trigger_functions=trigger_functions)

    sign_in_list = kwargs.get("sign_in").get("order", list())
    sing_in_kwargs = dict()
    for element in sign_in_list:
        sing_in_kwargs[element] = True
    sign_in_aliases = cognito.SignInAliases(**sing_in_kwargs)

    attributes = kwargs.get("attributes")
    standard_attributes_list = attributes.get("standard", list())
    standard_attributes_dict = dict()
    for element in standard_attributes_list:
        standard_attributes_dict[element["name"]] = cognito.StandardAttribute(
            mutable=element.get("mutable"), required=element.get("required"))
    standard_attributes = cognito.StandardAttributes(
        **standard_attributes_dict)

    custom_attributes_list = attributes.get("custom", list())
    if len(custom_attributes_list) > 0:
        custom_attributes = base_custom_attributes(
            custom_attributes_list=custom_attributes_list)
    else:
        custom_attributes = None

    user_pool = cognito.UserPool(
        construct,
        id=user_pool_name,
        user_pool_name=user_pool_name,
        email_settings=email_settings,
        password_policy=password_policy,
        self_sign_up_enabled=self_sing_up,
        user_verification=user_verification_info,
        user_invitation=user_invitation_configuration,
        sign_in_aliases=sign_in_aliases,
        standard_attributes=standard_attributes,
        custom_attributes=custom_attributes,
        lambda_triggers=lambda_triggers,
    )

    user_pool_client = None
    if kwargs.get("app_client", {}).get("enabled") is True:
        client_name = kwargs["app_client"]["client_name"]
        generate_secret = kwargs["app_client"]["generate_secret"]
        user_pool_client_name = construct.prefix + "_" + client_name + "_client_" + construct.environment_

        auth_flows = None
        auth_flows_configuration = kwargs["app_client"].get("auth_flows")
        if auth_flows_configuration is not None:
            auth_flows = cognito.AuthFlow(**auth_flows_configuration)

        user_pool_client = cognito.UserPoolClient(
            construct,
            id=user_pool_client_name,
            user_pool_client_name=user_pool_client_name,
            generate_secret=generate_secret,
            auth_flows=auth_flows,
            user_pool=user_pool,
        )

    return user_pool, user_pool_client
    def add_cognito(self):
        """
        Sets up the cognito infrastructure with the user pool, custom domain
        and app client for use by the ALB.
        """
        # Create the user pool that holds our users
        self.user_pool = cognito.UserPool(
            self,
            "user-pool",
            account_recovery=cognito.AccountRecovery.
            EMAIL_AND_PHONE_WITHOUT_MFA,
            auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=True),
            self_sign_up_enabled=True,
            standard_attributes=cognito.StandardAttributes(
                email=cognito.StandardAttribute(mutable=True, required=True),
                given_name=cognito.StandardAttribute(mutable=True,
                                                     required=True),
                family_name=cognito.StandardAttribute(mutable=True,
                                                      required=True)))

        # Add a lambda function that automatically confirms new users without
        # email/phone verification, just for this demo
        auto_confirm_function = _lambda.Function(
            self,
            "auto-confirm-function",
            code=_lambda.Code.from_asset(path=os.path.join(
                os.path.dirname(__file__), "..", "auto_confirm_function")),
            handler="lambda_handler.lambda_handler",
            runtime=_lambda.Runtime.PYTHON_3_8,
        )

        self.user_pool.add_trigger(
            operation=cognito.UserPoolOperation.PRE_SIGN_UP,
            fn=auto_confirm_function)

        # Add a custom domain for the hosted UI
        self.user_pool_custom_domain = self.user_pool.add_domain(
            "user-pool-domain",
            cognito_domain=cognito.CognitoDomainOptions(
                domain_prefix=self.config.cognito_custom_domain))

        # Create an app client that the ALB can use for authentication
        self.user_pool_client = self.user_pool.add_client(
            "alb-app-client",
            user_pool_client_name="AlbAuthentication",
            generate_secret=True,
            o_auth=cognito.OAuthSettings(
                callback_urls=[
                    # This is the endpoint where the ALB accepts the
                    # response from Cognito
                    f"https://{self.config.application_dns_name}/oauth2/idpresponse",

                    # This is here to allow a redirect to the login page
                    # after the logout has been completed
                    f"https://{self.config.application_dns_name}"
                ],
                flows=cognito.OAuthFlows(authorization_code_grant=True),
                scopes=[cognito.OAuthScope.OPENID]),
            supported_identity_providers=[
                cognito.UserPoolClientIdentityProvider.COGNITO
            ])

        # Logout URLs and redirect URIs can't be set in CDK constructs natively ...yet
        user_pool_client_cf: cognito.CfnUserPoolClient = self.user_pool_client.node.default_child
        user_pool_client_cf.logout_ur_ls = [
            # This is here to allow a redirect to the login page
            # after the logout has been completed
            f"https://{self.config.application_dns_name}"
        ]

        self.user_pool_full_domain = self.user_pool_custom_domain.base_url()
        redirect_uri = urllib.parse.quote('https://' +
                                          self.config.application_dns_name)
        self.user_pool_logout_url = f"{self.user_pool_full_domain}/logout?" \
                                    + f"client_id={self.user_pool_client.user_pool_client_id}&" \
                                    + f"logout_uri={redirect_uri}"

        self.user_pool_user_info_url = f"{self.user_pool_full_domain}/oauth2/userInfo"