diff --git a/src/control-plane/auth/cognito-auth.ts b/src/control-plane/auth/cognito-auth.ts index aa9bd75..e69e84b 100644 --- a/src/control-plane/auth/cognito-auth.ts +++ b/src/control-plane/auth/cognito-auth.ts @@ -13,7 +13,7 @@ import { ServicePrincipal, Effect, } from 'aws-cdk-lib/aws-iam'; -import { Runtime, IFunction, LayerVersion, ILayerVersion } from 'aws-cdk-lib/aws-lambda'; +import { Runtime, IFunction, LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { NagSuppressions } from 'cdk-nag'; import { Construct } from 'constructs'; import { CreateAdminUserProps, IAuth } from './auth-interface'; @@ -209,16 +209,17 @@ export class CognitoAuth extends Construct implements IAuth { private readonly userPool: cognito.UserPool; /** - * The Lambda Layer containing the Powertools library. + * The Lambda function for creating a new Admin User. This is used as part of a + * custom resource in CloudFormation to create an admin user. */ - private readonly lambdaPowertoolsLayer: ILayerVersion; + private readonly createAdminUserFunction: IFunction; constructor(scope: Construct, id: string, props?: CognitoAuthProps) { super(scope, id); addTemplateTag(this, 'CognitoAuth'); // https://docs.powertools.aws.dev/lambda/python/2.31.0/#lambda-layer - this.lambdaPowertoolsLayer = LayerVersion.fromLayerVersionArn( + const lambdaPowertoolsLayer = LayerVersion.fromLayerVersionArn( this, 'LambdaPowerTools', `arn:aws:lambda:${Stack.of(this).region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:59` @@ -460,7 +461,7 @@ export class CognitoAuth extends Construct implements IAuth { handler: 'lambda_handler', timeout: Duration.seconds(60), role: userManagementExecRole, - layers: [this.lambdaPowertoolsLayer], + layers: [lambdaPowertoolsLayer], environment: { USER_POOL_ID: this.userPool.userPoolId, }, @@ -495,25 +496,23 @@ export class CognitoAuth extends Construct implements IAuth { }, ] ); - } - createAdminUser(scope: Construct, id: string, props: CreateAdminUserProps) { - const createAdminUserFunction = new PythonFunction(scope, `createAdminUserFunction-${id}`, { + this.createAdminUserFunction = new PythonFunction(scope, 'createAdminUserFunction', { entry: path.join(__dirname, '../../../resources/functions/auth-custom-resource'), runtime: Runtime.PYTHON_3_12, index: 'index.py', handler: 'handler', timeout: Duration.seconds(60), - layers: [this.lambdaPowertoolsLayer], + layers: [lambdaPowertoolsLayer], }); this.userPool.grant( - createAdminUserFunction, + this.createAdminUserFunction, 'cognito-idp:AdminCreateUser', 'cognito-idp:AdminDeleteUser' ); NagSuppressions.addResourceSuppressions( - createAdminUserFunction.role!, + this.createAdminUserFunction.role!, [ { id: 'AwsSolutions-IAM4', @@ -525,9 +524,11 @@ export class CognitoAuth extends Construct implements IAuth { ], true // applyToChildren = true, so that it applies to policies created for the role. ); + } + createAdminUser(scope: Construct, id: string, props: CreateAdminUserProps) { new CustomResource(scope, `createAdminUserCustomResource-${id}`, { - serviceToken: createAdminUserFunction.functionArn, + serviceToken: this.createAdminUserFunction.functionArn, properties: { UserPoolId: this.userPool.userPoolId, Name: props.name, diff --git a/src/control-plane/control-plane.ts b/src/control-plane/control-plane.ts index 12b0363..f7f4f1c 100644 --- a/src/control-plane/control-plane.ts +++ b/src/control-plane/control-plane.ts @@ -81,11 +81,7 @@ export class ControlPlane extends Construct { cdk.Aspects.of(this).add(new DestroyPolicySetter()); - const auth = - props.auth || - new CognitoAuth(this, 'CognitoAuth', { - setAPIGWScopes: false, - }); + const auth = props.auth || new CognitoAuth(this, 'CognitoAuth'); auth.createAdminUser(this, 'adminUser', { name: systemAdminName, diff --git a/src/control-plane/integ.default.ts b/src/control-plane/integ.default.ts index 8cfa240..0a3fe05 100644 --- a/src/control-plane/integ.default.ts +++ b/src/control-plane/integ.default.ts @@ -5,7 +5,7 @@ import * as cdk from 'aws-cdk-lib'; import { CfnRule, EventBus, Rule } from 'aws-cdk-lib/aws-events'; import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs'; import { AwsSolutionsChecks } from 'cdk-nag'; -import { ControlPlane } from '.'; +import { CognitoAuth, ControlPlane } from '.'; import { DestroyPolicySetter } from '../cdk-aspect/destroy-policy-setter'; export interface IntegStackProps extends cdk.StackProps { @@ -16,7 +16,12 @@ export class IntegStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: IntegStackProps) { super(scope, id, props); + const cognitoAuth = new CognitoAuth(this, 'CognitoAuth', { + setAPIGWScopes: false, // only for testing purposes! + }); + const controlPlane = new ControlPlane(this, 'ControlPlane', { + auth: cognitoAuth, systemAdminEmail: props.systemAdminEmail, }); diff --git a/src/utils/event-manager.ts b/src/utils/event-manager.ts index 2e70ee0..19fcee5 100644 --- a/src/utils/event-manager.ts +++ b/src/utils/event-manager.ts @@ -250,6 +250,8 @@ export class EventManager extends Construct implements IEventManager { deactivateRequest: this.controlPlaneEventSource, deactivateSuccess: this.applicationPlaneEventSource, deactivateFailure: this.applicationPlaneEventSource, + tenantUserCreated: this.controlPlaneEventSource, + tenantUserDeleted: this.controlPlaneEventSource, }; for (const key in this.supportedEvents) { diff --git a/test/event-manager.test.ts b/test/event-manager.test.ts index f3809e7..54df91b 100644 --- a/test/event-manager.test.ts +++ b/test/event-manager.test.ts @@ -187,4 +187,21 @@ describe('EventManager', () => { testForRulesInOtherStack(controlPlaneStack); testForRulesInOtherStack(eventManagerStack); }); + + describe('supportedEvents in event-manager', () => { + const app = new cdk.App(); + const eventManagerStack = new cdk.Stack(app, 'EventManagerStack'); + const eventManager = new EventManager(eventManagerStack, 'EventManager'); + + // This ensures that when we try and use supportedEvents to create a rule, + // and key-in using a DetailType, a value exists for the source, + // which is used in the getOrCreateRule(...) function to create + // a new event-manager rule. + // (ex. "source: [this.supportedEvents[eventType]]") + it('should have values for all DetailType enum entries', () => { + Object.values(DetailType).forEach((detailTypeValues) => { + expect(eventManager.supportedEvents[detailTypeValues]).toBeDefined(); + }); + }); + }); });