diff --git a/packages/cognito/src/authorize.ts b/packages/cognito/src/authorize.ts index 29acd90029..cd85b4db63 100644 --- a/packages/cognito/src/authorize.ts +++ b/packages/cognito/src/authorize.ts @@ -2,18 +2,11 @@ import {Logger} from "@aws-lambda-powertools/logger" import {APIGatewayProxyEvent, APIGatewayProxyResult} from "aws-lambda" import {injectLambdaContext} from "@aws-lambda-powertools/logger/middleware" -import {DynamoDBClient} from "@aws-sdk/client-dynamodb" -import {DynamoDBDocumentClient} from "@aws-sdk/lib-dynamodb" - import {MiddyErrorHandler} from "@cpt-ui-common/middyErrorHandler" import middy from "@middy/core" import inputOutputLogger from "@middy/input-output-logger" -import {createHash} from "crypto" - -import {insertStateMapping} from "@cpt-ui-common/dynamoFunctions" - /* * Expects the following environment variables to be set: * @@ -24,8 +17,6 @@ import {insertStateMapping} from "@cpt-ui-common/dynamoFunctions" * * FULL_CLOUDFRONT_DOMAIN * - * StateMappingTableName - * */ // Environment variables @@ -33,15 +24,11 @@ const authorizeEndpoint = process.env["IDP_AUTHORIZE_PATH"] as string const cis2ClientId = process.env["OIDC_CLIENT_ID"] as string const userPoolClientId = process.env["COGNITO_CLIENT_ID"] as string const cloudfrontDomain = process.env["FULL_CLOUDFRONT_DOMAIN"] as string -const stateMappingTableName = process.env["StateMappingTableName"] as string const logger = new Logger({serviceName: "authorize"}) const errorResponseBody = {message: "A system error has occurred"} const middyErrorHandler = new MiddyErrorHandler(errorResponseBody) -const dynamoClient = new DynamoDBClient() -const documentClient = DynamoDBDocumentClient.from(dynamoClient) - const lambdaHandler = async ( event: APIGatewayProxyEvent ): Promise => { @@ -50,14 +37,12 @@ const lambdaHandler = async ( authorizeEndpoint, cis2ClientId, userPoolClientId, - cloudfrontDomain, - stateMappingTableName + cloudfrontDomain }}) // Validate required environment variables if (!authorizeEndpoint) throw new Error("Authorize endpoint environment variable not set") if (!cloudfrontDomain) throw new Error("Cloudfront domain environment variable not set") - if (!stateMappingTableName) throw new Error("State mapping table name environment variable not set") if (!userPoolClientId) throw new Error("Cognito user pool client ID environment variable not set") if (!cis2ClientId) throw new Error("OIDC client ID environment variable not set") @@ -74,34 +59,18 @@ const lambdaHandler = async ( queryParams.scope = "openid profile nhsperson nationalrbacaccess associatedorgs" // Ensure the state parameter is provided - const originalState = queryParams.state - if (!originalState) throw new Error("Missing state parameter") - - // Generate the hashed state value - const cis2State = createHash("sha256").update(originalState).digest("hex") - - // Set TTL for 5 minutes from now - const stateTtl = Math.floor(Date.now() / 1000) + 300 + const state = queryParams.state + if (!state) throw new Error("Missing state parameter") // Build the callback URI for redirection const callbackUri = `https://${cloudfrontDomain}/oauth2/callback` - // Store original state mapping in DynamoDB - const item = { - State: cis2State, - CognitoState: originalState, - ExpiryTime: stateTtl - } - - await insertStateMapping(documentClient, stateMappingTableName, item, logger) - logger.debug("State mapping inserted", {item}) - // Build the redirect parameters for CIS2 const responseParameters = { response_type: queryParams.response_type as string, scope: queryParams.scope as string, client_id: cis2ClientId, - state: cis2State, + state, redirect_uri: callbackUri, prompt: "login" } diff --git a/packages/cognito/src/authorizeMock.ts b/packages/cognito/src/authorizeMock.ts index 91971fc809..0cf377ae15 100644 --- a/packages/cognito/src/authorizeMock.ts +++ b/packages/cognito/src/authorizeMock.ts @@ -2,18 +2,11 @@ import {Logger} from "@aws-lambda-powertools/logger" import {APIGatewayProxyEvent, APIGatewayProxyResult} from "aws-lambda" import {injectLambdaContext} from "@aws-lambda-powertools/logger/middleware" -import {DynamoDBClient} from "@aws-sdk/client-dynamodb" -import {DynamoDBDocumentClient} from "@aws-sdk/lib-dynamodb" - import {MiddyErrorHandler} from "@cpt-ui-common/middyErrorHandler" import middy from "@middy/core" import inputOutputLogger from "@middy/input-output-logger" -import {createHash} from "crypto" - -import {insertStateMapping} from "@cpt-ui-common/dynamoFunctions" - /* * Expects the following environment variables to be set: * @@ -23,8 +16,6 @@ import {insertStateMapping} from "@cpt-ui-common/dynamoFunctions" * * FULL_CLOUDFRONT_DOMAIN * - * StateMappingTableName - * */ // Environment variables @@ -32,16 +23,12 @@ const authorizeEndpoint = process.env["IDP_AUTHORIZE_PATH"] as string const cis2ClientId = process.env["OIDC_CLIENT_ID"] as string const userPoolClientId = process.env["COGNITO_CLIENT_ID"] as string const cloudfrontDomain = process.env["FULL_CLOUDFRONT_DOMAIN"] as string -const stateMappingTableName = process.env["StateMappingTableName"] as string const apigeeApiKey = process.env["APIGEE_API_KEY"] as string const logger = new Logger({serviceName: "authorize"}) const errorResponseBody = {message: "A system error has occurred"} const middyErrorHandler = new MiddyErrorHandler(errorResponseBody) -const dynamoClient = new DynamoDBClient() -const documentClient = DynamoDBDocumentClient.from(dynamoClient) - const lambdaHandler = async ( event: APIGatewayProxyEvent ): Promise => { @@ -56,14 +43,12 @@ const lambdaHandler = async ( cis2ClientId, userPoolClientId, cloudfrontDomain, - stateMappingTableName, apigeeApiKey }}) // Validate required environment variables if (!authorizeEndpoint) throw new Error("Authorize endpoint environment variable not set") if (!cloudfrontDomain) throw new Error("Cloudfront domain environment variable not set") - if (!stateMappingTableName) throw new Error("State mapping table name environment variable not set") if (!userPoolClientId) throw new Error("Cognito user pool client ID environment variable not set") if (!cis2ClientId) throw new Error("OIDC client ID environment variable not set") if (!apigeeApiKey) throw new Error("apigee api key environment variable not set") @@ -81,12 +66,6 @@ const lambdaHandler = async ( const originalState = queryParams.state if (!originalState) throw new Error("Missing state parameter") - // Generate the hashed state value - const cis2State = createHash("sha256").update(originalState).digest("hex") - - // Set TTL for 5 minutes from now - const stateTtl = Math.floor(Date.now() / 1000) + 300 - // Build the callback URI for redirection // for pull requests we pack the real callback url for this pull request into the state // the callback lambda then decodes this and redirects to the callback url for this pull request @@ -96,19 +75,10 @@ const lambdaHandler = async ( const newStateJson = { isPullRequest: true, redirectUri: realCallbackUri, - originalState: cis2State + originalState } const newState = Buffer.from(JSON.stringify(newStateJson)).toString("base64") - // Store original state mapping in DynamoDB - const item = { - State: cis2State, - CognitoState: originalState, - ExpiryTime: stateTtl - } - - await insertStateMapping(documentClient, stateMappingTableName, item, logger) - // Build the redirect parameters for CIS2 const responseParameters = { redirect_uri: callbackUri, diff --git a/packages/cognito/src/callback.ts b/packages/cognito/src/callback.ts index 4c2d8a4de9..5594cc40dd 100644 --- a/packages/cognito/src/callback.ts +++ b/packages/cognito/src/callback.ts @@ -1,17 +1,14 @@ import {Logger} from "@aws-lambda-powertools/logger" import {APIGatewayProxyEvent, APIGatewayProxyResult} from "aws-lambda" import {injectLambdaContext} from "@aws-lambda-powertools/logger/middleware" -import {DynamoDBClient} from "@aws-sdk/client-dynamodb" -import {DynamoDBDocumentClient} from "@aws-sdk/lib-dynamodb" import {MiddyErrorHandler} from "@cpt-ui-common/middyErrorHandler" import middy from "@middy/core" import inputOutputLogger from "@middy/input-output-logger" -import {deleteStateMapping, getStateMapping} from "@cpt-ui-common/dynamoFunctions" +import {buildCallbackRedirect} from "./helpers" /* * Expects the following environment variables to be set: * - * StateMappingTableName * COGNITO_CLIENT_ID * COGNITO_DOMAIN * PRIMARY_OIDC_ISSUER @@ -23,17 +20,12 @@ const errorResponseBody = {message: "A system error has occurred"} const middyErrorHandler = new MiddyErrorHandler(errorResponseBody) // Environment variables -const stateMappingTableName = process.env["StateMappingTableName"] as string const fullCognitoDomain = process.env["COGNITO_DOMAIN"] as string -const dynamoClient = new DynamoDBClient() -const documentClient = DynamoDBDocumentClient.from(dynamoClient) - const lambdaHandler = async (event: APIGatewayProxyEvent): Promise => { logger.appendKeys({"apigw-request-id": event.requestContext?.requestId}) // Destructure and validate required query parameters - // TODO: investigate if session_state is needed at all for this function const {state, code, session_state} = event.queryStringParameters || {} if (!state || !code) { logger.error( @@ -44,27 +36,7 @@ const lambdaHandler = async (event: APIGatewayProxyEvent): Promise => { logger.appendKeys({"apigw-request-id": event.requestContext?.requestId}) @@ -72,56 +63,7 @@ const lambdaHandler = async (event: APIGatewayProxyEvent): Promise { - return { - insertStateMapping: mockInsertStateMapping - } -}) // Import the handler after setting the env variables and mocks. import {mockAPIGatewayProxyEvent, mockContext} from "./mockObjects" @@ -49,20 +39,12 @@ describe("authorize handler", () => { "openid profile nhsperson nationalrbacaccess associatedorgs" ) expect(params.get("client_id")).toBe("cis2Client123") - - // Verify that the state parameter is hashed correctly. - const expectedHash = createHash("sha256") - .update("test-state") - .digest("hex") - expect(params.get("state")).toBe(expectedHash) + expect(params.get("state")).toBe("test-state") expect(params.get("redirect_uri")).toBe( `https://${process.env.FULL_CLOUDFRONT_DOMAIN}/oauth2/callback` ) expect(params.get("prompt")).toBe("login") - - // Ensure the DynamoDB put command was called. - expect(mockInsertStateMapping).toHaveBeenCalledTimes(1) }) it("should throw error if missing state parameter", async () => { diff --git a/packages/cognito/tests/test_authorizeMock.test.ts b/packages/cognito/tests/test_authorizeMock.test.ts index 4d81fc4a8d..775a9b2ea7 100644 --- a/packages/cognito/tests/test_authorizeMock.test.ts +++ b/packages/cognito/tests/test_authorizeMock.test.ts @@ -1,21 +1,11 @@ import {jest} from "@jest/globals" -import {createHash} from "crypto" - // Set environment variables before importing the handler. process.env.IDP_AUTHORIZE_PATH = "https://example.com/authorize" process.env.OIDC_CLIENT_ID = "cis2Client123" process.env.useMock = "true" process.env.COGNITO_CLIENT_ID = "userPoolClient123" process.env.FULL_CLOUDFRONT_DOMAIN = "cpt-ui-pr-854.dev.eps.national.nhs.uk" -process.env.StateMappingTableName = "stateMappingTest" - -const mockInsertStateMapping = jest.fn() -jest.unstable_mockModule("@cpt-ui-common/dynamoFunctions", () => { - return { - insertStateMapping: mockInsertStateMapping - } -}) // Import the handler after setting the env variables and mocks. import {mockAPIGatewayProxyEvent, mockContext} from "./mockObjects" @@ -47,14 +37,10 @@ describe("authorize mock handler", () => { expect(params.get("response_type")).toBe("code") expect(params.get("client_id")).toBe("apigee_api_key") - // Verify that the state parameter is hashed correctly. - const expectedHash = createHash("sha256") - .update("test-state") - .digest("hex") const expectedStateJson = { isPullRequest: true, redirectUri: `https://${process.env.FULL_CLOUDFRONT_DOMAIN}/oauth2/mock-callback`, - originalState: expectedHash + originalState: "test-state" } const expectedState = Buffer.from(JSON.stringify(expectedStateJson)).toString("base64") @@ -64,9 +50,6 @@ describe("authorize mock handler", () => { "https://cpt-ui.dev.eps.national.nhs.uk/oauth2/mock-callback" ) expect(params.get("prompt")).toBe(null) - - // Ensure the DynamoDB put command was called. - expect(mockInsertStateMapping).toHaveBeenCalledTimes(1) }) it("should throw error if missing state parameter", async () => { diff --git a/packages/cognito/tests/test_callback.test.ts b/packages/cognito/tests/test_callback.test.ts index 5f956b94e7..62da4e6274 100644 --- a/packages/cognito/tests/test_callback.test.ts +++ b/packages/cognito/tests/test_callback.test.ts @@ -3,18 +3,8 @@ import {jest} from "@jest/globals" import {APIGatewayProxyEvent} from "aws-lambda" // Set required environment variables before importing the handler. -process.env.StateMappingTableName = "testStateMappingTable" process.env.COGNITO_DOMAIN = "cognito.example.com" -const mockDeleteStateMapping = jest.fn() -const mockGetStateMapping = jest.fn() -jest.unstable_mockModule("@cpt-ui-common/dynamoFunctions", () => { - return { - deleteStateMapping: mockDeleteStateMapping, - getStateMapping: mockGetStateMapping - } -}) - // Import the handler after setting env variables and mocks. import {mockAPIGatewayProxyEvent, mockContext} from "./mockObjects" const {handler} = await import("../src/callback") @@ -29,22 +19,12 @@ describe("callback handler", () => { const event: APIGatewayProxyEvent = { ...mockAPIGatewayProxyEvent, queryStringParameters: { - state: "hashedStateValue", + state: "testState", code: "testCode", session_state: "testSessionState" } } - // Simulate the state item returned from DynamoDB. - const stateItem = { - State: "hashedStateValue", - CognitoState: "originalStateValue", - CodeVerifier: "dummy", - Ttl: 123456, - UseMock: true - } - mockGetStateMapping.mockImplementationOnce(() => Promise.resolve(stateItem)) - const result = await handler(event, mockContext) expect(result.statusCode).toBe(302) expect(result.headers).toHaveProperty("Location") @@ -52,15 +32,11 @@ describe("callback handler", () => { // Verify the redirect URL. const redirectUrl = new URL(result.headers?.Location as string) const params = redirectUrl.searchParams - expect(params.get("state")).toBe("originalStateValue") + expect(params.get("state")).toBe("testState") expect(params.get("session_state")).toBe("testSessionState") expect(params.get("code")).toBe("testCode") expect(redirectUrl.hostname).toBe(process.env.COGNITO_DOMAIN) expect(redirectUrl.pathname).toBe("/oauth2/idpresponse") - - // Ensure DynamoDB commands were executed (one for Get, one for Delete). - expect(mockGetStateMapping).toHaveBeenCalledTimes(1) - expect(mockDeleteStateMapping).toHaveBeenCalledTimes(1) }) it("should throw error if missing required query parameters", async () => { @@ -77,22 +53,4 @@ describe("callback handler", () => { {"message": "A system error has occurred"} ) }) - - it("should throw error if state not found in DynamoDB", async () => { - const event: APIGatewayProxyEvent = { - ...mockAPIGatewayProxyEvent, - queryStringParameters: { - state: "nonexistentState", - code: "testCode", - session_state: "testSessionState" - } - } - - // For GetCommand, simulate that no item is found. - mockGetStateMapping.mockImplementationOnce(() => Promise.reject(new Error("there was a problem"))) - - await expect(handler(event, mockContext)).resolves.toStrictEqual( - {"message": "A system error has occurred"} - ) - }) }) diff --git a/packages/cognito/tests/test_callbackMock.test.ts b/packages/cognito/tests/test_callbackMock.test.ts index e63029a8a5..8808c3588a 100644 --- a/packages/cognito/tests/test_callbackMock.test.ts +++ b/packages/cognito/tests/test_callbackMock.test.ts @@ -2,23 +2,10 @@ import {jest} from "@jest/globals" import {APIGatewayProxyEvent} from "aws-lambda" // Set required environment variables before importing the handler. -process.env.StateMappingTableName = "testStateMappingTable" process.env.COGNITO_DOMAIN = "cognito.example.com" -const mockDeleteStateMapping = jest.fn() -const mockGetStateMapping = jest.fn() -const mockInsertSessionState = jest.fn() -jest.unstable_mockModule("@cpt-ui-common/dynamoFunctions", () => { - return { - deleteStateMapping: mockDeleteStateMapping, - getStateMapping: mockGetStateMapping, - insertSessionState: mockInsertSessionState - } -}) - // Import the handler after setting env variables and mocks. import {mockAPIGatewayProxyEvent, mockContext} from "./mockObjects" -import {createHash} from "crypto" import {Logger} from "@aws-lambda-powertools/logger" const {handler} = await import("../src/callbackMock") @@ -77,14 +64,6 @@ describe("Callback mock handler", () => { test("should handle state not being a JSON object", async () => { const state = "non_json_state" - const stateItem = { - State: "hashedStateValue", - CognitoState: "originalStateValue", - CodeVerifier: "dummy", - Ttl: 123456, - UseMock: true - } - mockGetStateMapping.mockImplementationOnce(() => Promise.resolve(stateItem)) const loggerWarnSpy = jest.spyOn(Logger.prototype, "warn") const event: APIGatewayProxyEvent = { ...mockAPIGatewayProxyEvent, @@ -102,13 +81,10 @@ describe("Callback mock handler", () => { // Verify the redirect URL. const redirectUrl = new URL(result.headers?.Location as string) const params = redirectUrl.searchParams - expect(params.get("state")).toBe("originalStateValue") - const expectedSessionState = createHash("sha256").update(state).digest("hex") - expect(params.get("session_state")).toBe(expectedSessionState) + expect(params.get("state")).toBe("non_json_state") + expect(params.get("session_state")).toBe("testSessionState") expect(redirectUrl.hostname).toBe("cognito.example.com") expect(redirectUrl.pathname).toBe("/oauth2/idpresponse") expect(loggerWarnSpy).toHaveBeenCalled() - expect(mockInsertSessionState).toHaveBeenCalled() - expect(mockDeleteStateMapping).toHaveBeenCalled() }) }) diff --git a/packages/cognito/tests/test_token.mock.test.ts b/packages/cognito/tests/test_token.mock.test.ts index deabf4147c..86e562d14b 100644 --- a/packages/cognito/tests/test_token.mock.test.ts +++ b/packages/cognito/tests/test_token.mock.test.ts @@ -17,7 +17,6 @@ process.env.MOCK_OIDC_CLIENT_ID = "test-client-id" process.env.MOCK_USER_POOL_IDP = "test-idp" process.env.TokenMappingTableName = "test-token-mapping-table" process.env.SessionManagementTableName = "test-session-management-table" -process.env.SessionStateMappingTableName = "test-session-state-table" process.env.FULL_CLOUDFRONT_DOMAIN = "test.cloudfront.net" process.env.jwtPrivateKeyArn = "test-private-key-arn" process.env.jwtKid = "test-kid" @@ -62,13 +61,11 @@ const { }) const mockInsertTokenMapping = jest.fn().mockName("mockInsertTokenMapping") -const mockGetSessionState = jest.fn().mockName("mockGetSessionState") const mockTryGetTokenMapping = jest.fn().mockName("mockTryGetTokenMapping") jest.unstable_mockModule("@cpt-ui-common/dynamoFunctions", () => { return { insertTokenMapping: mockInsertTokenMapping, - getSessionState: mockGetSessionState, tryGetTokenMapping: mockTryGetTokenMapping } }) @@ -155,16 +152,6 @@ describe("token mock handler", () => { }) it("inserts correct details into dynamo table", async () => { - // return some valid data for the get command - - mockGetSessionState.mockImplementationOnce(() => { - return Promise.resolve({ - LocalCode: "test-code", - ApigeeCode: "apigee-code", - SessionState: "test-session-state" - }) - }) - mockExchangeTokenForApigeeAccessToken.mockReturnValue({ accessToken: "new-access-token", refreshToken: "new-refresh-token", @@ -187,7 +174,7 @@ describe("token mock handler", () => { }) const response = await handler({ - body: "code=test-code", + body: "code=test-code&session_state=test-session-state", headers: {}, requestContext: { requestId: "test-id" @@ -244,14 +231,6 @@ describe("token mock handler", () => { }) }) - mockGetSessionState.mockImplementationOnce(() => { - return Promise.resolve({ - LocalCode: "test-code", - ApigeeCode: "apigee-code", - SessionState: "test-session-state" - }) - }) - mockExchangeTokenForApigeeAccessToken.mockReturnValue({ accessToken: "new-access-token", refreshToken: "new-refresh-token", @@ -275,7 +254,7 @@ describe("token mock handler", () => { }) const response = await handler({ - body: "code=test-code", + body: "code=test-code&session_state=test-session-state", headers: {}, requestContext: { requestId: "test-id" @@ -332,14 +311,6 @@ describe("token mock handler", () => { }) }) - mockGetSessionState.mockImplementationOnce(() => { - return Promise.resolve({ - LocalCode: "test-code", - ApigeeCode: "apigee-code", - SessionState: "test-session-state" - }) - }) - mockExchangeTokenForApigeeAccessToken.mockReturnValue({ accessToken: "new-access-token", refreshToken: "new-refresh-token", @@ -363,7 +334,7 @@ describe("token mock handler", () => { }) const response = await handler({ - body: "code=test-code", + body: "code=test-code&session_state=test-session-state", headers: {}, requestContext: { requestId: "test-id" @@ -444,14 +415,6 @@ describe("token mock handler", () => { const originalDomain = process.env["FULL_CLOUDFRONT_DOMAIN"] process.env["FULL_CLOUDFRONT_DOMAIN"] = "test-pr-123.cloudfront.net" - mockGetSessionState.mockImplementationOnce(() => { - return Promise.resolve({ - LocalCode: "test-code", - ApigeeCode: "apigee-code", - SessionState: "test-session-state" - }) - }) - mockExchangeTokenForApigeeAccessToken.mockReturnValue({ accessToken: "new-access-token", refreshToken: "new-refresh-token", @@ -472,7 +435,7 @@ describe("token mock handler", () => { }) await handler({ - body: "code=test-code", + body: "code=test-code&session_state=test-session-state", headers: {}, requestContext: { requestId: "test-id" @@ -498,14 +461,6 @@ describe("token mock handler", () => { return Promise.resolve(undefined) // No existing mapping }) - mockGetSessionState.mockImplementationOnce(() => { - return Promise.resolve({ - LocalCode: "test-code", - ApigeeCode: "apigee-code", - SessionState: "test-session-state" - }) - }) - mockExchangeTokenForApigeeAccessToken.mockReturnValue({ accessToken: "new-access-token", refreshToken: "new-refresh-token", @@ -526,7 +481,7 @@ describe("token mock handler", () => { }) await handler({ - body: "code=test-code", + body: "code=test-code&session_state=test-session-state", headers: {}, requestContext: { requestId: "test-id" diff --git a/packages/common/dynamoFunctions/src/index.ts b/packages/common/dynamoFunctions/src/index.ts index 2620c0141a..56484464b4 100644 --- a/packages/common/dynamoFunctions/src/index.ts +++ b/packages/common/dynamoFunctions/src/index.ts @@ -8,5 +8,3 @@ export { tryGetTokenMapping as tryGetTokenMapping } from "./tokenMapping" export {extractRoleInformation, UserInfoResponse} from "./userUtils" -export {insertStateMapping, deleteStateMapping, getStateMapping} from "./stateMapping" -export {insertSessionState, getSessionState} from "./sessionState" diff --git a/packages/common/dynamoFunctions/src/sessionState.ts b/packages/common/dynamoFunctions/src/sessionState.ts deleted file mode 100644 index 386d7a9fc3..0000000000 --- a/packages/common/dynamoFunctions/src/sessionState.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {Logger} from "@aws-lambda-powertools/logger" -import {DynamoDBDocumentClient, GetCommand, PutCommand} from "@aws-sdk/lib-dynamodb" - -type SessionStateItem = { - LocalCode: string - SessionState: string - ApigeeCode: string - ExpiryTime: number -} - -export const insertSessionState = async ( - documentClient: DynamoDBDocumentClient, - sessionStateTableName: string, - item: SessionStateItem, - logger: Logger -): Promise => { - logger.debug("Inserting into sessionState", {item, sessionStateTableName}) - try { - await documentClient.send( - new PutCommand({ - TableName: sessionStateTableName, - Item: item - }) - ) - logger.info("Successfully inserted into sessionState", {sessionStateTableName}) - } catch (error) { - logger.error("Error inserting into sessionState", {error}) - throw new Error("Error inserting into sessionState") - } -} - -export const getSessionState = async ( - documentClient: DynamoDBDocumentClient, - sessionStateTableName: string, - localCode: string, - logger: Logger -): Promise => { - logger.debug("Retrieving data from sessionState", {localCode, sessionStateTableName}) - try { - const getResult = await documentClient.send( - new GetCommand({ - TableName: sessionStateTableName, - Key: {LocalCode: localCode} - }) - ) - - if (!getResult.Item) { - logger.error("localCode not found in sessionState", {localCode, getResult, sessionStateTableName}) - throw new Error("localCode not found in sessionState") - } - const sessionStateItem = getResult.Item as SessionStateItem - logger.debug("Successfully retrieved data from sessionState", {sessionStateItem, sessionStateTableName}) - return sessionStateItem - - } catch (error) { - logger.error("Error retrieving data from sessionState", {error}) - throw new Error("Error retrieving data from sessionState") - } -} diff --git a/packages/common/dynamoFunctions/src/stateMapping.ts b/packages/common/dynamoFunctions/src/stateMapping.ts deleted file mode 100644 index ddeaaf91e6..0000000000 --- a/packages/common/dynamoFunctions/src/stateMapping.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {Logger} from "@aws-lambda-powertools/logger" -import { - DeleteCommand, - DynamoDBDocumentClient, - GetCommand, - PutCommand -} from "@aws-sdk/lib-dynamodb" - -type StateItem = { - State: string; - CognitoState: string; - ExpiryTime : number; -}; - -export const insertStateMapping = async ( - documentClient: DynamoDBDocumentClient, - stateMappingTableName: string, - item: StateItem, - logger: Logger -): Promise => { - logger.debug("Inserting into stateMapping", {item, stateMappingTableName}) - try { - await documentClient.send( - new PutCommand({ - TableName: stateMappingTableName, - Item: item - }) - ) - logger.debug("Successfully inserted into stateMapping", {stateMappingTableName}) - } catch (error) { - logger.error("Error inserting into stateMapping", {error}) - throw new Error("Error inserting into stateMapping") - } -} - -export const getStateMapping = async ( - documentClient: DynamoDBDocumentClient, - stateMappingTableName: string, - state: string, - logger: Logger -): Promise => { - logger.debug("Retrieving data from stateMapping", {state, stateMappingTableName}) - try { - const getResult = await documentClient.send( - new GetCommand({ - TableName: stateMappingTableName, - Key: {State: state} - }) - ) - - if (!getResult.Item) { - logger.error("state not found in stateMapping", {state, getResult, stateMappingTableName}) - throw new Error("state not found in stateMapping") - } - const stateItem = getResult.Item as StateItem - logger.debug("Successfully retrieved data from stateMapping", {stateItem}) - return stateItem - - } catch (error) { - logger.error("Error retrieving data from stateMapping", {error}) - throw new Error("Error retrieving data from stateMapping") - } -} - -export const deleteStateMapping = async ( - documentClient: DynamoDBDocumentClient, - stateMappingTableName: string, - state: string, - logger: Logger -): Promise => { - logger.debug("Deleting from stateMapping", {state, stateMappingTableName}) - try { - const response = await documentClient.send( - new DeleteCommand({ - TableName: stateMappingTableName, - Key: {State: state} - }) - ) - if (response.$metadata.httpStatusCode !== 200) { - logger.error("Failed to delete from stateMapping", {response}) - throw new Error("Failed to delete from stateMapping") - } - logger.debug("Successfully deleted from stateMapping", {stateMappingTableName}) - } catch(error) { - logger.error("Error deleting data from stateMapping", {error}) - throw new Error("Error deleting data from stateMapping") - } -} diff --git a/packages/common/dynamoFunctions/tests/sessionState.test.ts b/packages/common/dynamoFunctions/tests/sessionState.test.ts deleted file mode 100644 index edc475c948..0000000000 --- a/packages/common/dynamoFunctions/tests/sessionState.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import {jest} from "@jest/globals" - -import {DynamoDBDocumentClient, PutCommand, GetCommand} from "@aws-sdk/lib-dynamodb" -import {Logger} from "@aws-lambda-powertools/logger" -import {insertSessionState, getSessionState} from "../src/sessionState" - -const mockLogger: Partial = { - info: jest.fn(), - debug: jest.fn(), - error: jest.fn() -} - -describe("insert sessionState", () => { - let mockDocumentClient: jest.Mocked - - beforeEach(() => { - jest.clearAllMocks() - mockDocumentClient = { - send: jest.fn() - } as unknown as jest.Mocked - }) - - it("should insert into DynamoDB successfully", async () => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - mockDocumentClient.send.mockResolvedValueOnce({} as never) - - const mockTableName = "mockTable" - await insertSessionState( - mockDocumentClient, - mockTableName, - { - LocalCode: "foo", - SessionState: "bar", - ApigeeCode: "baz", - ExpiryTime: 10 - }, - mockLogger as Logger - ) - - expect(mockDocumentClient.send).toHaveBeenCalledWith( - expect.any(PutCommand) - ) - }) - - it("should log and throw an error on failure", async () => { - const mockError = new Error("DynamoDB error") as never - - mockDocumentClient.send.mockRejectedValueOnce(mockError) - const mockTableName = "mockTable" - await expect( - insertSessionState( - mockDocumentClient, - mockTableName, - { - LocalCode: "foo", - SessionState: "bar", - ApigeeCode: "baz", - ExpiryTime: 10 - }, - mockLogger as Logger - ) - ).rejects.toThrow("Error inserting into sessionState") - - expect(mockLogger.error).toHaveBeenCalledWith( - "Error inserting into sessionState", - {error: mockError} - ) - expect(mockDocumentClient.send).toHaveBeenCalledTimes(1) - - }) -}) - -describe("get sessionState", () => { - let mockDocumentClient: jest.Mocked - - beforeEach(() => { - jest.clearAllMocks() - mockDocumentClient = { - send: jest.fn() - } as unknown as jest.Mocked - }) - - it("should get data from DynamoDB successfully", async () => { - const mockItem = { - LocalCode: "foo", - SessionState: "bar", - ApigeeCode: "baz", - ExpiryTime: 10 - } - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - mockDocumentClient.send.mockResolvedValueOnce({"Item": mockItem} as never) - - const mockUsername = "testUser" - const mockTableName = "mockTable" - const result = await getSessionState( - mockDocumentClient, - mockTableName, - mockUsername, - mockLogger as Logger - ) - - expect(mockDocumentClient.send).toHaveBeenCalledWith( - expect.any(GetCommand) - ) - expect(result).toBe(mockItem) - }) - - it("should log and throw an error on failure", async () => { - const mockError = new Error("DynamoDB error") as never - - mockDocumentClient.send.mockRejectedValueOnce(mockError) - const mockUsername = "testUser" - const mockTableName = "mockTable" - await expect( - getSessionState( - mockDocumentClient, - mockTableName, - mockUsername, - mockLogger as Logger - ) - ).rejects.toThrow("Error retrieving data from sessionState") - - expect(mockLogger.error).toHaveBeenCalledWith( - "Error retrieving data from sessionState", - {error: mockError} - ) - expect(mockDocumentClient.send).toHaveBeenCalledTimes(1) - }) -}) diff --git a/packages/common/dynamoFunctions/tests/stateMapping.test.ts b/packages/common/dynamoFunctions/tests/stateMapping.test.ts deleted file mode 100644 index 2a5ed4a4bd..0000000000 --- a/packages/common/dynamoFunctions/tests/stateMapping.test.ts +++ /dev/null @@ -1,182 +0,0 @@ -import {jest} from "@jest/globals" - -import { - DynamoDBDocumentClient, - PutCommand, - GetCommand, - DeleteCommand -} from "@aws-sdk/lib-dynamodb" -import {Logger} from "@aws-lambda-powertools/logger" -import {insertStateMapping, getStateMapping, deleteStateMapping} from "../src/stateMapping" - -const mockLogger: Partial = { - info: jest.fn(), - debug: jest.fn(), - error: jest.fn() -} - -describe("insert stateMapping", () => { - let mockDocumentClient: jest.Mocked - - beforeEach(() => { - jest.clearAllMocks() - mockDocumentClient = { - send: jest.fn() - } as unknown as jest.Mocked - }) - - it("should insert into DynamoDB successfully", async () => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - mockDocumentClient.send.mockResolvedValueOnce({} as never) - - const mockTableName = "mockTable" - await insertStateMapping( - mockDocumentClient, - mockTableName, - { - State: "foo", - CognitoState: "bar", - ExpiryTime: 10 - }, - mockLogger as Logger - ) - - expect(mockDocumentClient.send).toHaveBeenCalledWith( - expect.any(PutCommand) - ) - }) - - it("should log and throw an error on failure", async () => { - const mockError = new Error("DynamoDB error") as never - - mockDocumentClient.send.mockRejectedValueOnce(mockError) - const mockTableName = "mockTable" - await expect( - insertStateMapping( - mockDocumentClient, - mockTableName, - { - State: "foo", - CognitoState: "bar", - ExpiryTime: 10 - }, - mockLogger as Logger - ) - ).rejects.toThrow("Error inserting into stateMapping") - - expect(mockLogger.error).toHaveBeenCalledWith( - "Error inserting into stateMapping", - {error: mockError} - ) - expect(mockDocumentClient.send).toHaveBeenCalledTimes(1) - - }) -}) - -describe("get stateMapping", () => { - let mockDocumentClient: jest.Mocked - - beforeEach(() => { - jest.clearAllMocks() - mockDocumentClient = { - send: jest.fn() - } as unknown as jest.Mocked - }) - - it("should get data from DynamoDB successfully", async () => { - const mockItem = { - State: "foo", - CognitoState: "baz", - ExpiryTime: 10 - } - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - mockDocumentClient.send.mockResolvedValueOnce({"Item": mockItem} as never) - - const mockUsername = "testUser" - const mockTableName = "mockTable" - const result = await getStateMapping( - mockDocumentClient, - mockTableName, - mockUsername, - mockLogger as Logger - ) - - expect(mockDocumentClient.send).toHaveBeenCalledWith( - expect.any(GetCommand) - ) - expect(result).toBe(mockItem) - }) - - it("should log and throw an error on failure", async () => { - const mockError = new Error("DynamoDB error") as never - - mockDocumentClient.send.mockRejectedValueOnce(mockError) - const mockUsername = "testUser" - const mockTableName = "mockTable" - await expect( - getStateMapping( - mockDocumentClient, - mockTableName, - mockUsername, - mockLogger as Logger - ) - ).rejects.toThrow("Error retrieving data from stateMapping") - - expect(mockLogger.error).toHaveBeenCalledWith( - "Error retrieving data from stateMapping", - {error: mockError} - ) - expect(mockDocumentClient.send).toHaveBeenCalledTimes(1) - }) -}) - -describe("delete stateMapping", () => { - let mockDocumentClient: jest.Mocked - - beforeEach(() => { - jest.clearAllMocks() - mockDocumentClient = { - send: jest.fn() - } as unknown as jest.Mocked - }) - - it("should insert into DynamoDB successfully", async () => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - mockDocumentClient.send.mockResolvedValueOnce({"$metadata": {httpStatusCode: 200}} as never) - - const mockUsername = "testUser" - const mockTableName = "mockTable" - await deleteStateMapping( - mockDocumentClient, - mockTableName, - mockUsername, - mockLogger as Logger - ) - - expect(mockDocumentClient.send).toHaveBeenCalledWith( - expect.any(DeleteCommand) - ) - }) - - it("should log and throw an error on failure", async () => { - const mockError = new Error("DynamoDB error") as never - - mockDocumentClient.send.mockRejectedValueOnce(mockError) - const mockUsername = "testUser" - const mockTableName = "mockTable" - await expect( - deleteStateMapping( - mockDocumentClient, - mockTableName, - mockUsername, - mockLogger as Logger - ) - ).rejects.toThrow("Error deleting data from stateMapping") - - expect(mockLogger.error).toHaveBeenCalledWith( - "Error deleting data from stateMapping", - {error: mockError} - ) - expect(mockDocumentClient.send).toHaveBeenCalledTimes(1) - }) -})