def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) self.user_pool = cognito.UserPool( self, 'UserPool', auto_verify=cognito.AutoVerifiedAttrs(email=True), self_sign_up_enabled=True, user_pool_name='MysfitsUserPool') user_pool_client = cognito.UserPoolClient( self, 'UserPoolClient', user_pool=self.user_pool, user_pool_client_name='MysfitsUserPoolClient') core.CfnOutput(self, 'CognitoUserPool', description='The Cognito User Pool', value=self.user_pool.user_pool_id) # self.user_pool_id = core.CfnOutput( # self, "CognitoUserPool", # description='The Cognito User Pool', # value=user_pool.user_pool_id, # export_name="user-pool-id" # ).import_value core.CfnOutput(self, 'CognitoUserPoolClient', description='The Cognito User Pool Client', value=user_pool_client.user_pool_client_id)
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))
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
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)
def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Image Bucket image_bucket = s3.Bucket(self, IMG_BUCKET_NAME, removal_policy=cdk.RemovalPolicy.DESTROY) cdk.CfnOutput(self, "imageBucket", value=image_bucket.bucket_name) image_bucket.add_cors_rule( allowed_methods=[s3.HttpMethods.GET, s3.HttpMethods.PUT], allowed_origins=["*"], allowed_headers=["*"], max_age=3000, ) # Thumbnail Bucket resized_image_bucket = s3.Bucket( self, RESIZED_IMG_BUCKET_NAME, removal_policy=cdk.RemovalPolicy.DESTROY) cdk.CfnOutput(self, "resizedBucket", value=resized_image_bucket.bucket_name) resized_image_bucket.add_cors_rule( allowed_methods=[s3.HttpMethods.GET, s3.HttpMethods.PUT], allowed_origins=["*"], allowed_headers=["*"], max_age=3000, ) # S3 Static bucket for website code web_bucket = s3.Bucket( self, WEBSITE_BUCKET_NAME, website_index_document="index.html", website_error_document="index.html", removal_policy=cdk.RemovalPolicy.DESTROY, # uncomment this and delete the policy statement below to allow public access to our # static website # public_read_access=true ) web_policy_statement = iam.PolicyStatement( actions=["s3:GetObject"], resources=[web_bucket.arn_for_objects("*")], principals=[iam.AnyPrincipal()], conditions={"IpAddress": { "aws:SourceIp": ["139.138.203.36"] }}, ) web_bucket.add_to_resource_policy(web_policy_statement) cdk.CfnOutput(self, "bucketURL", value=web_bucket.bucket_website_domain_name) # Deploy site contents to S3 Bucket s3_dep.BucketDeployment( self, "DeployWebsite", sources=[s3_dep.Source.asset("./public")], destination_bucket=web_bucket, ) # DynamoDB to store image labels partition_key = dynamodb.Attribute(name="image", type=dynamodb.AttributeType.STRING) table = dynamodb.Table( self, "ImageLabels", partition_key=partition_key, removal_policy=cdk.RemovalPolicy.DESTROY, ) cdk.CfnOutput(self, "ddbTable", value=table.table_name) # Lambda layer for Pillow library layer = lb.LayerVersion( self, "pil", code=lb.Code.from_asset("reklayer"), compatible_runtimes=[lb.Runtime.PYTHON_3_7], license="Apache-2.0", description= "A layer to enable the PIL library in our Rekognition Lambda", ) # Lambda function rek_fn = lb.Function( self, "rekognitionFunction", code=lb.Code.from_asset("rekognitionFunction"), runtime=lb.Runtime.PYTHON_3_7, handler="index.handler", timeout=cdk.Duration.seconds(30), memory_size=1024, layers=[layer], environment={ "TABLE": table.table_name, "BUCKET": image_bucket.bucket_name, "THUMBBUCKET": resized_image_bucket.bucket_name, }, ) image_bucket.grant_read(rek_fn) resized_image_bucket.grant_write(rek_fn) table.grant_write_data(rek_fn) rek_fn.add_to_role_policy( iam.PolicyStatement(effect=iam.Effect.ALLOW, actions=["rekognition:DetectLabels"], resources=["*"])) # Lambda for Synchronous front end serviceFn = lb.Function( self, "serviceFunction", code=lb.Code.from_asset("servicelambda"), runtime=lb.Runtime.PYTHON_3_7, handler="index.handler", environment={ "TABLE": table.table_name, "BUCKET": image_bucket.bucket_name, "RESIZEDBUCKET": resized_image_bucket.bucket_name, }, ) image_bucket.grant_write(serviceFn) resized_image_bucket.grant_write(serviceFn) table.grant_read_write_data(serviceFn) # Cognito User Pool Auth auto_verified_attrs = cognito.AutoVerifiedAttrs(email=True) sign_in_aliases = cognito.SignInAliases(email=True, username=True) user_pool = cognito.UserPool( self, "UserPool", self_sign_up_enabled=True, auto_verify=auto_verified_attrs, sign_in_aliases=sign_in_aliases, ) user_pool_client = cognito.UserPoolClient(self, "UserPoolClient", user_pool=user_pool, generate_secret=False) identity_pool = cognito.CfnIdentityPool( self, "ImageRekognitionIdentityPool", allow_unauthenticated_identities=False, cognito_identity_providers=[{ "clientId": user_pool_client.user_pool_client_id, "providerName": user_pool.user_pool_provider_name, }], ) # API Gateway cors_options = apigw.CorsOptions(allow_origins=apigw.Cors.ALL_ORIGINS, allow_methods=apigw.Cors.ALL_METHODS) api = apigw.LambdaRestApi( self, "imageAPI", default_cors_preflight_options=cors_options, handler=serviceFn, proxy=False, ) auth = apigw.CfnAuthorizer( self, "ApiGatewayAuthorizer", name="customer-authorizer", identity_source="method.request.header.Authorization", provider_arns=[user_pool.user_pool_arn], rest_api_id=api.rest_api_id, # type=apigw.AuthorizationType.COGNITO, type="COGNITO_USER_POOLS", ) assumed_by = iam.FederatedPrincipal( "cognito-identity.amazon.com", conditions={ "StringEquals": { "cognito-identity.amazonaws.com:aud": identity_pool.ref }, "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }, }, assume_role_action="sts:AssumeRoleWithWebIdentity", ) authenticated_role = iam.Role( self, "ImageRekognitionAuthenticatedRole", assumed_by=assumed_by, ) # IAM policy granting users permission to get and put their pictures policy_statement = iam.PolicyStatement( actions=["s3:GetObject", "s3:PutObject"], effect=iam.Effect.ALLOW, resources=[ image_bucket.bucket_arn + "/private/${cognito-identity.amazonaws.com:sub}/*", image_bucket.bucket_arn + "/private/${cognito-identity.amazonaws.com:sub}/", resized_image_bucket.bucket_arn + "/private/${cognito-identity.amazonaws.com:sub}/*", resized_image_bucket.bucket_arn + "/private/${cognito-identity.amazonaws.com:sub}/", ], ) # IAM policy granting users permission to list their pictures list_policy_statement = iam.PolicyStatement( actions=["s3:ListBucket"], effect=iam.Effect.ALLOW, resources=[ image_bucket.bucket_arn, resized_image_bucket.bucket_arn ], conditions={ "StringLike": { "s3:prefix": ["private/${cognito-identity.amazonaws.com:sub}/*"] } }, ) authenticated_role.add_to_policy(policy_statement) authenticated_role.add_to_policy(list_policy_statement) # Attach role to our Identity Pool cognito.CfnIdentityPoolRoleAttachment( self, "IdentityPoolRoleAttachment", identity_pool_id=identity_pool.ref, roles={"authenticated": authenticated_role.role_arn}, ) # Get some outputs from cognito cdk.CfnOutput(self, "UserPoolId", value=user_pool.user_pool_id) cdk.CfnOutput(self, "AppClientId", value=user_pool_client.user_pool_client_id) cdk.CfnOutput(self, "IdentityPoolId", value=identity_pool.ref) # New Amazon API Gateway with AWS Lambda Integration success_response = apigw.IntegrationResponse( status_code="200", response_parameters={ "method.response.header.Access-Control-Allow-Origin": "'*'" }, ) error_response = apigw.IntegrationResponse( selection_pattern="(\n|.)+", status_code="500", response_parameters={ "method.response.header.Access-Control-Allow-Origin": "'*'" }, ) request_template = json.dumps({ "action": "$util.escapeJavaScript($input.params('action'))", "key": "$util.escapeJavaScript($input.params('key'))", }) lambda_integration = apigw.LambdaIntegration( serviceFn, proxy=False, request_parameters={ "integration.request.querystring.action": "method.request.querystring.action", "integration.request.querystring.key": "method.request.querystring.key", }, request_templates={"application/json": request_template}, passthrough_behavior=apigw.PassthroughBehavior.WHEN_NO_TEMPLATES, integration_responses=[success_response, error_response], ) imageAPI = api.root.add_resource("images") success_resp = apigw.MethodResponse( status_code="200", response_parameters={ "method.response.header.Access-Control-Allow-Origin": True }, ) error_resp = apigw.MethodResponse( status_code="500", response_parameters={ "method.response.header.Access-Control-Allow-Origin": True }, ) # GET /images get_method = imageAPI.add_method( "GET", lambda_integration, authorization_type=apigw.AuthorizationType.COGNITO, request_parameters={ "method.request.querystring.action": True, "method.request.querystring.key": True, }, method_responses=[success_resp, error_resp], ) # DELETE /images delete_method = imageAPI.add_method( "DELETE", lambda_integration, authorization_type=apigw.AuthorizationType.COGNITO, request_parameters={ "method.request.querystring.action": True, "method.request.querystring.key": True, }, method_responses=[success_resp, error_resp], ) # Override the authorizer id because it doesn't work when defininting it as a param # in add_method get_method_resource = get_method.node.find_child("Resource") get_method_resource.add_property_override("AuthorizerId", auth.ref) delete_method_resource = delete_method.node.find_child("Resource") delete_method_resource.add_property_override("AuthorizerId", auth.ref) # Building SQS queue and DeadLetter Queue dl_queue = sqs.Queue( self, "ImageDLQueue", queue_name="ImageDLQueue", ) dl_queue_opts = sqs.DeadLetterQueue(max_receive_count=2, queue=dl_queue) queue = sqs.Queue( self, "ImageQueue", queue_name="ImageQueue", visibility_timeout=cdk.Duration.seconds(30), receive_message_wait_time=cdk.Duration.seconds(20), dead_letter_queue=dl_queue_opts, ) # S3 Bucket Create Notification to SQS # Whenever an image is uploaded add it to the queue image_bucket.add_object_created_notification( s3n.SqsDestination(queue), s3.NotificationKeyFilter(prefix="private/"))
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # S3: Create a Bucket for Unicorn Pursuit web page, and grant public read: bucket = s3.Bucket( self, "www.unicornpursuit.com", bucket_name="www.unicornpursuit.com", access_control=s3.BucketAccessControl.PUBLIC_READ, ) # Grant public read access to the bucket bucket.grant_public_access() # DynamoDB: Create Table for Project Info (ID, Owner, Content, Photo and Votes) voting = ddb.CfnTable( self, "UnicornDynamoDBVoting", table_name="UnicornDynamoDBVoting", key_schema=[ ddb.CfnTable.KeySchemaProperty(attribute_name="id", key_type="HASH"), ddb.CfnTable.KeySchemaProperty(attribute_name="owner", key_type="RANGE"), ], # In the new DynamoDB, you can't create AttDefProperty for non-key attributes. attribute_definitions=[ ddb.CfnTable.AttributeDefinitionProperty(attribute_name="id", attribute_type="N"), ddb.CfnTable.AttributeDefinitionProperty( attribute_name="owner", attribute_type="S"), ], provisioned_throughput=ddb.CfnTable.ProvisionedThroughputProperty( read_capacity_units=5, write_capacity_units=5)) # Grant RW writes for Unicorn App in Fargate and relevant Lambdas invoked from API Gateway # voting.grant_read_write_data(user) # Second DynamoDB table called "users" for storing who voted for whom # Example: [email protected] gave 5 points to project 323, 4 points to 111 etc. users = ddb.Table(self, "UnicornDynamoDBUsers", table_name="UnicornDynamoDBUsers", partition_key=ddb.Attribute( name="owner", type=ddb.AttributeType.STRING)) # Cognito: Create User Pool userpool = cognito.UserPool( self, "CognitoUnicornUserPool", user_pool_name="CognitoUnicornUserPool", self_sign_up_enabled=True, ## Require username or email for users to sign in sign_in_aliases=cognito.SignInAliases( username=False, email=True, ), # Require users to give their full name when signing up required_attributes=cognito.RequiredAttributes(fullname=True, email=True, phone_number=True), # Verify new sign ups using email auto_verify=cognito.AutoVerifiedAttrs( email=False, phone=True, ), # Configure OTP Settings () user_verification=cognito.UserVerificationConfig( sms_message= "Hey Unicorn Hunter, welcome to Unicorn Pursuit! Your OTP is {####}", ), # Set up required password policy password_policy=cognito.PasswordPolicy( min_length=12, require_symbols=True, require_lowercase=True, require_uppercase=True, require_digits=True, )) ## Cognito: Create App Client & create Authentication Flow with User and Password client = userpool.add_client( "UnicornAppClient", user_pool_client_name="UnicornAppClient", generate_secret=False, ## We'll allow both Flows, Implicit and Authorization Code, and decide in the app which to use. auth_flows=cognito.AuthFlow(admin_user_password=False, custom=False, refresh_token=True, user_password=True, user_srp=False), )
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Let's start with creating an IAM Service Role, later to be assumed by our ECS Fargate Container # After creating any resource, we'll be attaching IAM policies to this role using the `fargate_role`. fargate_role = iam.Role( self, "ecsTaskExecutionRole", role_name="ecsTaskExecutionRole", assumed_by=iam.ServicePrincipal("ecs-tasks.amazonaws.com"), description="Custom Role assumed by ECS Fargate (container)" ) # S3: Create a Bucket for Unicorn Pursuit web page, and grant public read: bucket = s3.Bucket(self, "www.unicornpursuit.com", bucket_name="www.unicornpursuit.com", access_control=s3.BucketAccessControl.PUBLIC_READ, ) # Grant public read access to the bucket bucket.grant_public_access() # Grant S3 Read/Write access to our Fargate Container fargate_role.add_to_policy(statement=iam.PolicyStatement( resources=[bucket.bucket_arn], actions=["s3:*"] )) # DynamoDB: Create Table for Project Info (ID, Owner, Content, Photo and Votes) voting_ddb = ddb.CfnTable( self, "UnicornDynamoDBVoting", table_name="UnicornDynamoDBVoting", key_schema=[ ddb.CfnTable.KeySchemaProperty(attribute_name="id",key_type="HASH"), ddb.CfnTable.KeySchemaProperty(attribute_name="owner",key_type="RANGE"), ], # In the new DynamoDB, you can't create AttDefProperty for non-key attributes. attribute_definitions=[ ddb.CfnTable.AttributeDefinitionProperty(attribute_name="id",attribute_type="N"), ddb.CfnTable.AttributeDefinitionProperty(attribute_name="owner",attribute_type="S"), ], provisioned_throughput=ddb.CfnTable.ProvisionedThroughputProperty( read_capacity_units=5, write_capacity_units=5 ) ) # Second DynamoDB table called "users" for storing who voted for whom # Example: [email protected] gave 5 points to project 323, 4 points to 111 etc. users_ddb = ddb.Table( self, "UnicornDynamoDBUsers", table_name="UnicornDynamoDBUsers", partition_key=ddb.Attribute( name="Owner", type=ddb.AttributeType.STRING ) ) # Grant RW writes for Unicorn App in Fargate fargate_role.add_to_policy(statement=iam.PolicyStatement( resources=[voting_ddb.attr_arn, users_ddb.table_arn], actions=["dynamodb:*"] )) # Cognito: Create User Pool userpool = cognito.UserPool( self, "CognitoUnicornUserPool", user_pool_name="CognitoUnicornUserPool", self_sign_up_enabled=True, ## Require username or email for users to sign in sign_in_aliases=cognito.SignInAliases( username=False, email=True, ), # Require users to give their full name when signing up required_attributes=cognito.RequiredAttributes( fullname=True, email=True, phone_number=True ), # Verify new sign ups using email auto_verify=cognito.AutoVerifiedAttrs( email=False, phone=True, ), # Configure OTP Settings () user_verification=cognito.UserVerificationConfig( sms_message="Hey Unicorn Hunter, welcome to Unicorn Pursuit! Your OTP is {####}", ), # Set up required password policy password_policy=cognito.PasswordPolicy( min_length=12, require_symbols=True, require_lowercase=True, require_uppercase=True, require_digits=True, ) ) ## Cognito: Create App Client & create Authentication Flow with User and Password userpool.add_client( "UnicornAppClient", user_pool_client_name="UnicornAppClient", generate_secret=False, ## We'll allow both Flows, Implicit and Authorization Code, and decide in the app which to use. auth_flows=cognito.AuthFlow( admin_user_password=False, custom=False, refresh_token=True, user_password=True, user_srp=False ), ) # Grant Cognito Access to Fargate. Include SSM, so Client App ID can be retrived. fargate_role.add_to_policy(statement=iam.PolicyStatement( resources=["*"], actions=["ssm:*"] )) fargate_role.add_to_policy(statement=iam.PolicyStatement( resources=[userpool.user_pool_arn], actions=["cognito-identity:*","cognito-idp:*","cognito-sync:*"] )) ## Fargate: Create ECS:Fargate with ECR uploaded image vpc = ec2.Vpc(self, "UnicornVPC", max_azs=2, nat_gateways=None) """ VPC with optimal NAT GW usage vpc_lowcost = ec2.Vpc(self, "LowCostVPC", max_azs=2, cidr="10.7.0.0/16", nat_gateways=None, subnet_configuration=[ec2.SubnetConfiguration( subnet_type=ec2.SubnetType.PUBLIC, name="Public", cidr_mask=24, ), ec2.SubnetConfiguration( subnet_type=ec2.SubnetType.ISOLATED, name="Private", cidr_mask=24, ) ], ) """ linux_ami = ec2.AmazonLinuxImage(generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX, edition=ec2.AmazonLinuxEdition.STANDARD, virtualization=ec2.AmazonLinuxVirt.HVM, storage=ec2.AmazonLinuxStorage.GENERAL_PURPOSE ) nat_ec2 = ec2.Instance(self, "NAT", instance_name="NAT", vpc=vpc, vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC), instance_type=ec2.InstanceType(instance_type_identifier="t3.nano"), machine_image=linux_ami, user_data=ec2.UserData.for_linux(), source_dest_check=False, ) # Configure Linux Instance to act as NAT Instance nat_ec2.user_data.add_commands("sysctl -w net.ipv4.ip_forward=1") nat_ec2.user_data.add_commands("/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE") # Add a static route to the ISOLATED subnet, pointing 0.0.0.0/0 to a NAT EC2 selection = vpc.select_subnets( subnet_type=ec2.SubnetType.PRIVATE ) for subnet in selection.subnets: subnet.add_route("DefaultNAT", router_id=nat_ec2.instance_id, router_type=ec2.RouterType.INSTANCE, destination_cidr_block="0.0.0.0/0") # Create ECS Cluster cluster = ecs.Cluster(self, "UnicornCluster", vpc=vpc) ecr.Repository(self, "unicorn", repository_name="unicorn") fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService(self, "UnicornFargateService", cluster=cluster, cpu=512, desired_count=1, task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_registry("057097267726.dkr.ecr.eu-west-1.amazonaws.com/unicorn"), # image=ecs.ContainerImage.from_registry(repo.repository_uri_for_tag()), container_port=8080, container_name="unicorn", execution_role=fargate_role, ), memory_limit_mib=1024, public_load_balancer=True ) fargate_service.service.connections.security_groups[0].add_ingress_rule( peer = ec2.Peer.ipv4(vpc.vpc_cidr_block), connection = ec2.Port.tcp(8080), description="Allow http inbound from VPC" ) # Update NAT EC2 Security Group, to allow only HTTPS from Fargate Service Security Group. nat_ec2.connections.security_groups[0].add_ingress_rule( peer = fargate_service.service.connections.security_groups[0], connection = ec2.Port.tcp(443), description="Allow https from Fargate Service" ) # Grant ECR Access to Fargate by attaching an existing ReadOnly policy. so that Unicorn Docker Image can be pulled. #fargate_role.add_managed_policy(iam.ManagedPolicy("AmazonEC2ContainerRegistryReadOnly")) fargate_role.add_to_policy(statement=iam.PolicyStatement( resources=["*"], actions=["ecr:*"] ))
def __init__(self, scope: core.Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) prj_name = self.node.try_get_context('project_name') env_name = self.node.try_get_context('env') user_pool2 = cognito.UserPool( self, id=f'{env_name}-precog', auto_verify=cognito.AutoVerifiedAttrs(email=True), sign_in_aliases=cognito.SignInAliases(email=True, phone=True), self_sign_up_enabled=True, user_pool_name=f'{env_name}-cdk-2-user-pool', custom_attributes={ "param1": cognito.StringAttribute(mutable=True) }, password_policy=cognito.PasswordPolicy(min_length=10, require_lowercase=True, require_digits=True, require_symbols=False, require_uppercase=True)) user_pool = cognito.CfnUserPool( self, id=f'{env_name}-cognito-user-pool', auto_verified_attributes=['email'], username_attributes=['email', 'phone_number'], user_pool_name=f'{env_name}-cdk-user-pool', schema=[{ "attributeDataType": "String", "name": "param1", "mutable": True }], policies=cognito.CfnUserPool.PoliciesProperty( password_policy=cognito.CfnUserPool.PasswordPolicyProperty( minimum_length=10, require_lowercase=True, require_numbers=True, require_symbols=False, require_uppercase=True))) user_pool_client2 = cognito.UserPoolClient( self, id=f'{env_name}-pool-client2', user_pool=user_pool2, user_pool_client_name=f'{env_name}-cdk-app-client2') identity_pool2 = cognito.CfnIdentityPool( self, id=f'{env_name}-identify-pool-2', allow_unauthenticated_identities=False, cognito_identity_providers=[ cognito.CfnIdentityPool.CognitoIdentityProviderProperty( client_id=user_pool_client2.user_pool_client_id, provider_name=user_pool.attr_provider_name) ], identity_pool_name=f'{env_name}-cdk-identity-pool2') user_pool_client = cognito.CfnUserPoolClient( self, id=f'{env_name}-pool-client', user_pool_id=user_pool.ref, client_name=f'{env_name}-cdk-app-client') identity_pool = cognito.CfnIdentityPool( self, id=f'{env_name}-identify-pool', allow_unauthenticated_identities=False, cognito_identity_providers=[ cognito.CfnIdentityPool.CognitoIdentityProviderProperty( client_id=user_pool_client.ref, provider_name=user_pool.attr_provider_name) ], identity_pool_name=f'{env_name}-cdk-identity-pool') ssm.StringParameter( self, id='app-id', parameter_name=f"/{env_name}/cognito-app-client-id", string_value=user_pool_client.ref) ssm.StringParameter(self, id='user-pool-id', parameter_name=f"/{env_name}/cognito-user-pool-id", string_value=user_pool_client.user_pool_id) ssm.StringParameter( self, id='identity-pool-id', parameter_name=f"/{env_name}/cognito-identity-pool-id", string_value=identity_pool.ref # ref returns the id )
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"
def __init__(self, scope: core.Construct, id: str, cognito_domain_name: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # The code that defines your stack goes here): svc_name = self.node.try_get_context("service_name") url_name = core.CfnParameter( self, id="cognitoDomainName", description="Enter the name the domain you want to use", type="String", default="mystique") # Create an Identity Pool self.unicorn_user_pool = _cognito.UserPool( self, "unicornUserPool", user_pool_name="Miztiik-Unicorn-App-User-Pool", # sign_in_aliases={"username": True,"email": True} sign_in_aliases=_cognito.SignInAliases(username=True, email=True), standard_attributes={ "email": { "required": True, "mutable": False }, "fullname": { "required": False, "mutable": True } }, auto_verify=_cognito.AutoVerifiedAttrs(email=True)) # OAuth Scopes self.unicorn_user_pool_res_srv_identifier = f"premium_api" self.unicorn_read_scope = f"read" self.unicorn_write_scope = f"write" unicorn_users_auth_domain = _cognito.UserPoolDomain( self, "userPoolDomain", user_pool=self.unicorn_user_pool, cognito_domain=_cognito.CognitoDomainOptions( domain_prefix=f"{cognito_domain_name}")) self.unicorn_user_pool.user_pool_arn user_pool_res_srv = _cognito.CfnUserPoolResourceServer( self, "ResourceServer", # Having URL format is recommended # identifier=f"{premium_content.url}", identifier=f"{self.unicorn_user_pool_res_srv_identifier}", name=f"premium-api-authorizer", user_pool_id=self.unicorn_user_pool.user_pool_id, scopes=[{ "scopeName": f"{self.unicorn_read_scope}", "scopeDescription": "Get Premium Api Content" }, { "scopeName": f"{self.unicorn_write_scope}", "scopeDescription": "Put Premium Api Content" }]) user_pool_client = _cognito.UserPoolClient( self, "AppClient", user_pool=self.unicorn_user_pool, user_pool_client_name="premium_app_users", generate_secret=True, # We'll allow both Flows, Implicit and Authorization Code, and decide in the app which to use. auth_flows=_cognito.AuthFlow(admin_user_password=False, custom=True, refresh_token=True, user_password=False, user_srp=True), prevent_user_existence_errors=True, o_auth=_cognito.OAuthSettings( flows=_cognito.OAuthFlows(authorization_code_grant=False, implicit_code_grant=False, client_credentials=True), scopes=[ # _cognito.OAuthScope.EMAIL, # _cognito.OAuthScope.OPENID, # _cognito.OAuthScope.COGNITO_ADMIN, # _cognito.OAuthScope.PROFILE, # https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_cognito/OAuthScope.html#aws_cdk.aws_cognito.OAuthScope _cognito.OAuthScope.custom( f"{self.unicorn_user_pool_res_srv_identifier}/{self.unicorn_read_scope}" ), _cognito.OAuthScope.custom( f"{self.unicorn_user_pool_res_srv_identifier}/{self.unicorn_write_scope}" ) ])) # Add dependency so that ResourceServer is deployed before App Client user_pool_client.node.add_dependency(user_pool_res_srv) # Retrieve Cognito App Client Secret and Add to Secrets Manager app_secrets = CognitoAppClientSecretRetrieverStack( self, "appClientSecrets", user_pool_id=self.unicorn_user_pool.user_pool_id, user_pool_client_id=user_pool_client.user_pool_client_id, user_pool_oauth2_endpoint= f"https://{unicorn_users_auth_domain.domain_name}.auth.{core.Aws.REGION}.amazoncognito.com/oauth2/token", unicorn_user_pool_res_srv_identifier= f"{self.unicorn_user_pool_res_srv_identifier}", unicorn_read_scope=f"{self.unicorn_read_scope}", unicorn_write_scope=f"{self.unicorn_write_scope}") # Export Value self.unicorn_user_pool_secrets_arn = app_secrets.response ########################################### ################# OUTPUTS ################# ########################################### output_0 = core.CfnOutput( self, "AutomationFrom", value=f"{global_args.SOURCE_INFO}", description= "To know more about this automation stack, check out our github page." ) # https://AUTH_DOMAIN/oauth2/token output_1 = core.CfnOutput( self, "UnicornIdentityAuthDomain", value= f"https://{unicorn_users_auth_domain.domain_name}.auth.{core.Aws.REGION}.amazoncognito.com/oauth2/token", description="Authenticate Against this endpoint") output_2 = core.CfnOutput(self, "AppPoolSecretsArn", value=f"{app_secrets.response}", description="AppPoolSecretsArn")
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Create a VPC for Aurora (required for serverless Aurora) aurora_vpc = ec2.Vpc(self, 'AuroraVpc') # Create serverless MySQL Aurora cluster aurora_cluster = rds.ServerlessCluster( self, 'AuroraCluster', vpc=aurora_vpc, engine=rds.DatabaseClusterEngine.AURORA_MYSQL, default_database_name='core') # Handler for the hello endpoint hello_lambda = _lambda.Function( self, 'HelloHandler', runtime=_lambda.Runtime.PYTHON_3_8, code=_lambda.Code.asset('lambda'), handler='hello.handler', ) # Handler for the GET /user_accounts endpoint get_user_account_lambda = _lambda.Function( self, 'GetUserAccount', runtime=_lambda.Runtime.PYTHON_3_8, code=_lambda.Code.asset('lambda'), handler='get_user_account.handler', environment={ 'CLUSTER_ARN': aurora_cluster.cluster_arn, 'SECRET_ARN': aurora_cluster.secret.secret_arn, 'DB_NAME': 'core', }) # Grant the lambda function the required permissions to access the database aurora_cluster.grant_data_api_access( get_user_account_lambda) # This also enables the data api # Cognito User Pool for sign up and authorization user_pool = cognito.UserPool( self, 'user-pool', self_sign_up_enabled=True, auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=False), sign_in_aliases=cognito.SignInAliases(email=True, username=True)) # User pool client - used for sign up and sign in and password recovery user_pool.add_client('app-client', auth_flows=cognito.AuthFlow(user_password=True, user_srp=True)) # Create the API Gateway REST API api = apigw.RestApi(self, 'test-api') # Create custom authorizer, modeled after work around detailed here: # https://github.com/aws/aws-cdk/issues/723#issuecomment-504753280 cognito_authorizer = apigw.CfnAuthorizer( self, 'CognitoAuthorizer', type='COGNITO_USER_POOLS', provider_arns=[user_pool.user_pool_arn], identity_source='method.request.header.Authorization', rest_api_id=api.rest_api_id, name='CognitoAuthorizer') # Hello resource and endpoint, handled by the hello lambda hello_resource = api.root.add_resource('hello') hello_method = hello_resource.add_method( 'GET', integration=apigw.LambdaIntegration(hello_lambda)) hello_method_resource = hello_method.node.find_child('Resource') hello_method_resource.add_property_override( 'AuthorizationType', apigw.AuthorizationType.COGNITO) hello_method_resource.add_property_override( 'AuthorizerId', {"Ref": cognito_authorizer.logical_id}) # Add user_account resource user_account_resource = api.root.add_resource('user_account') # Add GET handler for user_account get_user_account_method = user_account_resource.add_method( 'GET', integration=apigw.LambdaIntegration(get_user_account_lambda)) get_user_account_method_resource = get_user_account_method.node.find_child( 'Resource') get_user_account_method_resource.add_property_override( 'AuthorizationType', apigw.AuthorizationType.COGNITO) get_user_account_method_resource.add_property_override( 'AuthorizerId', {"Ref": cognito_authorizer.logical_id})
def __init__(self, scope: core.Construct, construct_id: str, cognito_prefix: str, stack_log_level: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # The code that defines your stack goes here # Cognito User Pool for Kibana Access self.es_user_pool = _cognito.UserPool( self, "esCognitoUserPool", user_pool_name=f"{cognito_prefix}-log-parser-es-users", self_sign_up_enabled=False, sign_in_aliases=_cognito.SignInAliases(username=True, email=True), sign_in_case_sensitive=False, standard_attributes={ "email": { "required": True, "mutable": False }, "fullname": { "required": False, "mutable": True } }, password_policy=_cognito.PasswordPolicy(min_length=8), auto_verify=_cognito.AutoVerifiedAttrs(email=True)) # Create User Pool Domain to enable SignUp, SignIn, Auth & Callback Urls es_auth_domain = _cognito.UserPoolDomain( self, "esCognitoUserPoolDomain", user_pool=self.es_user_pool, cognito_domain=_cognito.CognitoDomainOptions( domain_prefix=f"{cognito_prefix}-{core.Aws.ACCOUNT_ID}")) # Role Authenticated Cognito Users will assume in ES Service self.es_role = _iam.Role( self, "esKibanaIamRole", assumed_by=_iam.ServicePrincipal("es.amazonaws.com"), managed_policies=[ _iam.ManagedPolicy.from_aws_managed_policy_name( managed_policy_name="AmazonESCognitoAccess") ], ) # Create Cognito Federated Identity Pool to exchange Cognito auth token for AWS Token self.es_id_pool = _cognito.CfnIdentityPool( self, "esIdentityPool", allow_unauthenticated_identities=False, cognito_identity_providers=[], ) # Role For Cognito Federated Idenity Pool Authenticated Users self.es_auth_role = _iam.Role( self, "esAuthIamRole", assumed_by=_iam.FederatedPrincipal( federated="cognito-identity.amazonaws.com", conditions={ "StringEquals": { "cognito-identity.amazonaws.com:aud": self.es_id_pool.ref }, "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }, }, assume_role_action="sts:AssumeRoleWithWebIdentity"), ) # Attach a Role to Cognito Federated Idenity Pool Authenticated Users _cognito.CfnIdentityPoolRoleAttachment( self, "cognitoFederatedIdentityPoolRoleAttachment", identity_pool_id=self.es_id_pool.ref, roles={"authenticated": self.es_auth_role.role_arn}) ########################################### ################# OUTPUTS ################# ########################################### output_0 = core.CfnOutput( self, "AutomationFrom", value=f"{GlobalArgs.SOURCE_INFO}", description= "To know more about this automation stack, check out our github page." ) output_1 = core.CfnOutput( self, "CreateCognitoUserConsole", value= f"https://{core.Aws.REGION}.console.aws.amazon.com/cognito/users?region={core.Aws.REGION}#/pool/{self.es_user_pool.user_pool_id}/users", description="Create a new user in the user pool here.")