From 462af6eb13850ed34f325d6d1054c1b3c7a5b2dc Mon Sep 17 00:00:00 2001 From: James Gregory Date: Mon, 30 May 2022 15:09:49 +1000 Subject: [PATCH] feat: support for adminEnable/DisableUser --- README.md | 4 +- .../aws-sdk/adminDisableUser.test.ts | 38 +++++++++++++ .../aws-sdk/adminEnableUser.test.ts | 54 ++++++++++++++++++ src/targets/adminDisableUser.test.ts | 55 +++++++++++++++++++ src/targets/adminDisableUser.ts | 32 +++++++++++ src/targets/adminEnableUser.test.ts | 55 +++++++++++++++++++ src/targets/adminEnableUser.ts | 32 +++++++++++ src/targets/targets.ts | 4 ++ 8 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 integration-tests/aws-sdk/adminDisableUser.test.ts create mode 100644 integration-tests/aws-sdk/adminEnableUser.test.ts create mode 100644 src/targets/adminDisableUser.test.ts create mode 100644 src/targets/adminDisableUser.ts create mode 100644 src/targets/adminEnableUser.test.ts create mode 100644 src/targets/adminEnableUser.ts diff --git a/README.md b/README.md index 0102986a..d20a3d23 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ A _Good Enough_ offline emulator for [Amazon Cognito](https://aws.amazon.com/cog | AdminDeleteUser | ✅ | | AdminDeleteUserAttributes | ✅ | | AdminDisableProviderForUser | ❌ | -| AdminDisableUser | ❌ | -| AdminEnableUser | ❌ | +| AdminDisableUser | ✅ | +| AdminEnableUser | ✅ | | AdminForgetDevice | ❌ | | AdminGetDevice | ❌ | | AdminGetUser | ✅ | diff --git a/integration-tests/aws-sdk/adminDisableUser.test.ts b/integration-tests/aws-sdk/adminDisableUser.test.ts new file mode 100644 index 00000000..c959aba2 --- /dev/null +++ b/integration-tests/aws-sdk/adminDisableUser.test.ts @@ -0,0 +1,38 @@ +import { withCognitoSdk } from "./setup"; + +describe( + "CognitoIdentityServiceProvider.adminDisableUser", + withCognitoSdk((Cognito) => { + it("updates a user's attributes", async () => { + const client = Cognito(); + + await client + .adminCreateUser({ + UserAttributes: [ + { Name: "email", Value: "example@example.com" }, + { Name: "custom:example", Value: "1" }, + ], + Username: "abc", + UserPoolId: "test", + DesiredDeliveryMediums: ["EMAIL"], + }) + .promise(); + + await client + .adminDisableUser({ + UserPoolId: "test", + Username: "abc", + }) + .promise(); + + const user = await client + .adminGetUser({ + UserPoolId: "test", + Username: "abc", + }) + .promise(); + + expect(user.Enabled).toEqual(false); + }); + }) +); diff --git a/integration-tests/aws-sdk/adminEnableUser.test.ts b/integration-tests/aws-sdk/adminEnableUser.test.ts new file mode 100644 index 00000000..db1019e4 --- /dev/null +++ b/integration-tests/aws-sdk/adminEnableUser.test.ts @@ -0,0 +1,54 @@ +import { withCognitoSdk } from "./setup"; + +describe( + "CognitoIdentityServiceProvider.adminEnableUser", + withCognitoSdk((Cognito) => { + it("updates a user's attributes", async () => { + const client = Cognito(); + + await client + .adminCreateUser({ + UserAttributes: [ + { Name: "email", Value: "example@example.com" }, + { Name: "custom:example", Value: "1" }, + ], + Username: "abc", + UserPoolId: "test", + DesiredDeliveryMediums: ["EMAIL"], + }) + .promise(); + + await client + .adminDisableUser({ + UserPoolId: "test", + Username: "abc", + }) + .promise(); + + let user = await client + .adminGetUser({ + UserPoolId: "test", + Username: "abc", + }) + .promise(); + + expect(user.Enabled).toEqual(false); + + await client + .adminEnableUser({ + UserPoolId: "test", + Username: "abc", + }) + .promise(); + + user = await client + .adminGetUser({ + UserPoolId: "test", + Username: "abc", + }) + .promise(); + + expect(user.Enabled).toEqual(true); + }); + }) +); diff --git a/src/targets/adminDisableUser.test.ts b/src/targets/adminDisableUser.test.ts new file mode 100644 index 00000000..99429a68 --- /dev/null +++ b/src/targets/adminDisableUser.test.ts @@ -0,0 +1,55 @@ +import { ClockFake } from "../__tests__/clockFake"; +import { newMockCognitoService } from "../__tests__/mockCognitoService"; +import { newMockUserPoolService } from "../__tests__/mockUserPoolService"; +import { TestContext } from "../__tests__/testContext"; +import * as TDB from "../__tests__/testDataBuilder"; +import { UserNotFoundError } from "../errors"; +import { UserPoolService } from "../services"; +import { AdminDisableUser, AdminDisableUserTarget } from "./adminDisableUser"; + +const originalDate = new Date(); + +describe("AdminDisableUser target", () => { + let adminDisableUser: AdminDisableUserTarget; + let mockUserPoolService: jest.Mocked; + let clock: ClockFake; + + beforeEach(() => { + mockUserPoolService = newMockUserPoolService(); + clock = new ClockFake(originalDate); + + adminDisableUser = AdminDisableUser({ + clock, + cognito: newMockCognitoService(mockUserPoolService), + }); + }); + + it("enables the user", async () => { + const existingUser = TDB.user(); + + mockUserPoolService.getUserByUsername.mockResolvedValue(existingUser); + + const newDate = new Date(); + clock.advanceTo(newDate); + + await adminDisableUser(TestContext, { + Username: existingUser.Username, + UserPoolId: "test", + }); + + expect(mockUserPoolService.saveUser).toHaveBeenCalledWith(TestContext, { + ...existingUser, + UserLastModifiedDate: newDate, + Enabled: false, + }); + }); + + it("throws if the user doesn't exist", async () => { + await expect( + adminDisableUser(TestContext, { + Username: "user", + UserPoolId: "test", + }) + ).rejects.toEqual(new UserNotFoundError()); + }); +}); diff --git a/src/targets/adminDisableUser.ts b/src/targets/adminDisableUser.ts new file mode 100644 index 00000000..fcaefabe --- /dev/null +++ b/src/targets/adminDisableUser.ts @@ -0,0 +1,32 @@ +import { + AdminDisableUserRequest, + AdminDisableUserResponse, +} from "aws-sdk/clients/cognitoidentityserviceprovider"; +import { UserNotFoundError } from "../errors"; +import { Services } from "../services"; +import { Target } from "./Target"; + +export type AdminDisableUserTarget = Target< + AdminDisableUserRequest, + AdminDisableUserResponse +>; + +type AdminDisableUserServices = Pick; + +export const AdminDisableUser = + ({ cognito, clock }: AdminDisableUserServices): AdminDisableUserTarget => + async (ctx, req) => { + const userPool = await cognito.getUserPool(ctx, req.UserPoolId); + const user = await userPool.getUserByUsername(ctx, req.Username); + if (!user) { + throw new UserNotFoundError(); + } + + await userPool.saveUser(ctx, { + ...user, + Enabled: false, + UserLastModifiedDate: clock.get(), + }); + + return {}; + }; diff --git a/src/targets/adminEnableUser.test.ts b/src/targets/adminEnableUser.test.ts new file mode 100644 index 00000000..019aa30e --- /dev/null +++ b/src/targets/adminEnableUser.test.ts @@ -0,0 +1,55 @@ +import { ClockFake } from "../__tests__/clockFake"; +import { newMockCognitoService } from "../__tests__/mockCognitoService"; +import { newMockUserPoolService } from "../__tests__/mockUserPoolService"; +import { TestContext } from "../__tests__/testContext"; +import * as TDB from "../__tests__/testDataBuilder"; +import { UserNotFoundError } from "../errors"; +import { UserPoolService } from "../services"; +import { AdminEnableUser, AdminEnableUserTarget } from "./adminEnableUser"; + +const originalDate = new Date(); + +describe("AdminEnableUser target", () => { + let adminEnableUser: AdminEnableUserTarget; + let mockUserPoolService: jest.Mocked; + let clock: ClockFake; + + beforeEach(() => { + mockUserPoolService = newMockUserPoolService(); + clock = new ClockFake(originalDate); + + adminEnableUser = AdminEnableUser({ + clock, + cognito: newMockCognitoService(mockUserPoolService), + }); + }); + + it("enables the user", async () => { + const existingUser = TDB.user(); + + mockUserPoolService.getUserByUsername.mockResolvedValue(existingUser); + + const newDate = new Date(); + clock.advanceTo(newDate); + + await adminEnableUser(TestContext, { + Username: existingUser.Username, + UserPoolId: "test", + }); + + expect(mockUserPoolService.saveUser).toHaveBeenCalledWith(TestContext, { + ...existingUser, + UserLastModifiedDate: newDate, + Enabled: true, + }); + }); + + it("throws if the user doesn't exist", async () => { + await expect( + adminEnableUser(TestContext, { + Username: "user", + UserPoolId: "test", + }) + ).rejects.toEqual(new UserNotFoundError()); + }); +}); diff --git a/src/targets/adminEnableUser.ts b/src/targets/adminEnableUser.ts new file mode 100644 index 00000000..9afa75ee --- /dev/null +++ b/src/targets/adminEnableUser.ts @@ -0,0 +1,32 @@ +import { + AdminEnableUserRequest, + AdminEnableUserResponse, +} from "aws-sdk/clients/cognitoidentityserviceprovider"; +import { UserNotFoundError } from "../errors"; +import { Services } from "../services"; +import { Target } from "./Target"; + +export type AdminEnableUserTarget = Target< + AdminEnableUserRequest, + AdminEnableUserResponse +>; + +type AdminEnableUserServices = Pick; + +export const AdminEnableUser = + ({ cognito, clock }: AdminEnableUserServices): AdminEnableUserTarget => + async (ctx, req) => { + const userPool = await cognito.getUserPool(ctx, req.UserPoolId); + const user = await userPool.getUserByUsername(ctx, req.Username); + if (!user) { + throw new UserNotFoundError(); + } + + await userPool.saveUser(ctx, { + ...user, + Enabled: true, + UserLastModifiedDate: clock.get(), + }); + + return {}; + }; diff --git a/src/targets/targets.ts b/src/targets/targets.ts index 75c4f8b4..fd10810f 100644 --- a/src/targets/targets.ts +++ b/src/targets/targets.ts @@ -4,6 +4,8 @@ import { AdminCreateUser } from "./adminCreateUser"; import { AdminDeleteUser } from "./adminDeleteUser"; import { AdminDeleteUserAttributes } from "./adminDeleteUserAttributes"; +import { AdminDisableUser } from "./adminDisableUser"; +import { AdminEnableUser } from "./adminEnableUser"; import { AdminGetUser } from "./adminGetUser"; import { AdminInitiateAuth } from "./adminInitiateAuth"; import { AdminListGroupsForUser } from "./adminListGroupsForUser"; @@ -48,6 +50,8 @@ export const Targets = { AdminCreateUser, AdminDeleteUser, AdminDeleteUserAttributes, + AdminDisableUser, + AdminEnableUser, AdminGetUser, AdminInitiateAuth, AdminListGroupsForUser,