Skip to content

Commit 8b5e2c1

Browse files
committed
CCM-12614: simplify the pdm mock authentication
1 parent 6a1a08f commit 8b5e2c1

File tree

8 files changed

+22
-374
lines changed

8 files changed

+22
-374
lines changed

infrastructure/terraform/components/dl/module_lambda_pdm_mock.tf

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,6 @@ module "pdm_mock" {
3535
send_to_firehose = true
3636
log_destination_arn = local.log_destination_arn
3737
log_subscription_role_arn = local.acct.log_subscription_role_arn
38-
39-
lambda_env_vars = {
40-
MOCK_ACCESS_TOKEN = var.pdm_mock_access_token
41-
ACCESS_TOKEN_SSM_PATH = local.apim_access_token_ssm_parameter_name
42-
USE_NON_MOCK_TOKEN = var.pdm_use_non_mock_token
43-
}
4438
}
4539

4640
data "aws_iam_policy_document" "pdm_mock" {

lambdas/pdm-mock-lambda/README.md

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ curl -X POST https://<api-gateway-url>/patient-data-manager/FHIR/R4/DocumentRefe
2727

2828
**Headers:**
2929

30-
- `Authorization: Bearer <token>` - Required authentication token (default: `mock-pdm-token`)
31-
- `Content-Type: application/fhir+json` - Required content type
30+
- `Authorization: Bearer <token>` - Authentication token is not validated and can be any string value.
31+
- `Content-Type: application/fhir+json` - Required content type.
3232
- `X-Request-ID: <UUID>` - This uuid will be used as the DocumentReference `id` in the response.
3333

3434
**Response (201 Created):**
@@ -76,8 +76,8 @@ curl https://<api-gateway-url>/patient-data-manager/FHIR/R4/DocumentReference/te
7676

7777
**Headers:**
7878

79-
- `Authorization: Bearer <token>` - Required authentication token (default: `mock-pdm-token`)
80-
- `Content-Type: application/fhir+json` - Required content type
79+
- `Authorization: Bearer <token>` - Authentication token is not validated and can be any string value.
80+
- `Content-Type: application/fhir+json` - Required content type.
8181
- `X-Request-ID: <uuid>` - Used for request tracking and correlation. This isn't part of the ID or response that gets returned.
8282

8383
**Response (200 OK):**
@@ -136,17 +136,18 @@ Both GET and POST endpoints require the `X-Request-ID` header. If it's missing,
136136

137137
The mock API supports triggering specific error responses for testing in both endpoints. Use these special resource IDs:
138138

139-
| Resource ID | Status Code | Error Code | Description |
140-
| ------------------------ | ----------- | ------------------- | ------------------------------- |
141-
| `error-400-invalid` | 400 | INVALID_VALUE | Invalid resource value |
142-
| `error-401-unauthorized` | 401 | UNAUTHORISED | Unauthorized access |
143-
| `error-403-forbidden` | 403 | FORBIDDEN | Access forbidden |
144-
| `error-404-notfound` | 404 | RESOURCE_NOT_FOUND | Resource not found |
145-
| `error-409-conflict` | 409 | CONFLICT | Resource already exists |
146-
| `error-429-ratelimit` | 429 | TOO_MANY_REQUESTS | Rate limit exceeded |
147-
| `error-500-internal` | 500 | INTERNAL_ERROR | Internal server error |
148-
| `error-503-unavailable` | 503 | SERVICE_UNAVAILABLE | Service temporarily unavailable |
149-
| `empty-response` | 200 | - | Empty success response |
139+
| Resource ID | Status Code | Error Code | Description |
140+
| ------------------------ | ----------- | ------------------- | ---------------------------------------- |
141+
| `error-400-invalid` | 400 | INVALID_VALUE | Invalid resource value |
142+
| `error-401-unauthorized` | 401 | UNAUTHORISED | Unauthorized access |
143+
| `error-403-forbidden` | 403 | FORBIDDEN | Access forbidden |
144+
| `error-404-notfound` | 404 | RESOURCE_NOT_FOUND | Resource not found |
145+
| `error-409-conflict` | 409 | CONFLICT | Resource already exists |
146+
| `error-429-ratelimit` | 429 | TOO_MANY_REQUESTS | Rate limit exceeded |
147+
| `error-500-internal` | 500 | INTERNAL_ERROR | Internal server error |
148+
| `error-503-unavailable` | 503 | SERVICE_UNAVAILABLE | Service temporarily unavailable |
149+
| `empty-response` | 200 | - | Empty success response |
150+
| `unavailable-response` | 200 | - | Success response with no attachment.data |
150151

151152
**Example - Trigger 404 Error:**
152153

@@ -177,9 +178,6 @@ curl https://<api-gateway-url>/resource/error-404-notfound \
177178

178179
The lambda is configured via environment variables:
179180

180-
| Variable | Description | Default |
181-
| ----------------------- | ---------------------------------------- | -------------------------- |
182-
| `MOCK_ACCESS_TOKEN` | Token to use in local/dev environments | `mock-pdm-token` |
183-
| `ACCESS_TOKEN_SSM_PATH` | SSM parameter path for the access token | `/dl/main/apim/access_token`|
184-
| `USE_NON_MOCK_TOKEN` | Use SSM token instead of mock token | `false` |
185-
| `LOG_LEVEL` | Logging level (DEBUG, INFO, WARN, ERROR) | `INFO` |
181+
| Variable | Description | Default |
182+
| ----------------------- | ---------------------------------------- | ------------------------ |
183+
| `LOG_LEVEL` | Logging level (DEBUG, INFO, WARN, ERROR) | `INFO` |

lambdas/pdm-mock-lambda/src/__tests__/authenticator.test.ts

Lines changed: 0 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,14 @@ const mockLogger: Logger = {
1010
} as any;
1111

1212
describe('Authenticator', () => {
13-
let mockGetAccessToken: jest.Mock;
1413

1514
beforeEach(() => {
1615
jest.clearAllMocks();
17-
mockGetAccessToken = jest.fn();
1816
});
1917

2018
describe('with mock token', () => {
2119
it('should authenticate successfully with valid Bearer token', async () => {
2220
const authenticator = createAuthenticator(
23-
{
24-
mockAccessToken: 'test-token',
25-
useNonMockToken: false,
26-
getAccessToken: mockGetAccessToken,
27-
},
2821
mockLogger,
2922
);
3023

@@ -33,16 +26,10 @@ describe('Authenticator', () => {
3326
});
3427

3528
expect(result.isValid).toBe(true);
36-
expect(mockGetAccessToken).not.toHaveBeenCalled();
3729
});
3830

3931
it('should reject request with missing Authorization header', async () => {
4032
const authenticator = createAuthenticator(
41-
{
42-
mockAccessToken: 'test-token',
43-
useNonMockToken: false,
44-
getAccessToken: mockGetAccessToken,
45-
},
4633
mockLogger,
4734
);
4835

@@ -64,11 +51,6 @@ describe('Authenticator', () => {
6451

6552
it('should reject request with invalid token type', async () => {
6653
const authenticator = createAuthenticator(
67-
{
68-
mockAccessToken: 'test-token',
69-
useNonMockToken: false,
70-
getAccessToken: mockGetAccessToken,
71-
},
7254
mockLogger,
7355
);
7456

@@ -86,37 +68,8 @@ describe('Authenticator', () => {
8668
);
8769
});
8870

89-
it('should reject request with invalid token value', async () => {
90-
const authenticator = createAuthenticator(
91-
{
92-
mockAccessToken: 'test-token',
93-
useNonMockToken: false,
94-
getAccessToken: mockGetAccessToken,
95-
},
96-
mockLogger,
97-
);
98-
99-
const result = await authenticator({
100-
headers: { Authorization: 'Bearer wrong-token' },
101-
});
102-
103-
expect(result.isValid).toBe(false);
104-
expect(result).toHaveProperty('error');
105-
expect((result as { isValid: false; error: any }).error.statusCode).toBe(
106-
401,
107-
);
108-
expect((result as { isValid: false; error: any }).error.body).toContain(
109-
'Invalid Access Token',
110-
);
111-
});
112-
11371
it('should handle lowercase authorization header', async () => {
11472
const authenticator = createAuthenticator(
115-
{
116-
mockAccessToken: 'test-token',
117-
useNonMockToken: false,
118-
getAccessToken: mockGetAccessToken,
119-
},
12073
mockLogger,
12174
);
12275

@@ -127,68 +80,4 @@ describe('Authenticator', () => {
12780
expect(result.isValid).toBe(true);
12881
});
12982
});
130-
131-
describe('with non-mock token', () => {
132-
it('should authenticate successfully with SSM token', async () => {
133-
mockGetAccessToken.mockResolvedValue('ssm-token');
134-
135-
const authenticator = createAuthenticator(
136-
{
137-
mockAccessToken: 'test-token',
138-
useNonMockToken: true,
139-
getAccessToken: mockGetAccessToken,
140-
},
141-
mockLogger,
142-
);
143-
144-
const result = await authenticator({
145-
headers: { Authorization: 'Bearer ssm-token' },
146-
});
147-
148-
expect(result.isValid).toBe(true);
149-
expect(mockGetAccessToken).toHaveBeenCalledTimes(1);
150-
});
151-
152-
it('should reject request with mock token when non-mock token is required', async () => {
153-
mockGetAccessToken.mockResolvedValue('ssm-token');
154-
155-
const authenticator = createAuthenticator(
156-
{
157-
mockAccessToken: 'test-token',
158-
useNonMockToken: true,
159-
getAccessToken: mockGetAccessToken,
160-
},
161-
mockLogger,
162-
);
163-
164-
const result = await authenticator({
165-
headers: { Authorization: 'Bearer test-token' },
166-
});
167-
168-
expect(result.isValid).toBe(false);
169-
expect(result).toHaveProperty('error');
170-
expect((result as { isValid: false; error: any }).error.statusCode).toBe(
171-
401,
172-
);
173-
});
174-
175-
it('should handle SSM token retrieval errors gracefully', async () => {
176-
mockGetAccessToken.mockRejectedValue(new Error('SSM error'));
177-
178-
const authenticator = createAuthenticator(
179-
{
180-
mockAccessToken: 'test-token',
181-
useNonMockToken: true,
182-
getAccessToken: mockGetAccessToken,
183-
},
184-
mockLogger,
185-
);
186-
187-
await expect(
188-
authenticator({
189-
headers: { Authorization: 'Bearer test-token' },
190-
}),
191-
).rejects.toThrow('SSM error');
192-
});
193-
});
19483
});

lambdas/pdm-mock-lambda/src/__tests__/config.test.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.
Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createContainer } from 'container';
2-
import { parameterStore } from 'utils';
32

43
jest.mock('utils', () => {
54
const actual = jest.requireActual('utils');
@@ -25,9 +24,6 @@ describe('Container', () => {
2524
beforeEach(() => {
2625
jest.clearAllMocks();
2726
process.env = { ...originalEnv };
28-
process.env.MOCK_ACCESS_TOKEN = 'test-token';
29-
process.env.ACCESS_TOKEN_SSM_PATH = '/test/path';
30-
process.env.USE_NON_MOCK_TOKEN = 'false';
3127
process.env.LOG_LEVEL = 'INFO';
3228

3329
container = createContainer();
@@ -88,76 +84,4 @@ describe('Container', () => {
8884
expect(result).toBeDefined();
8985
expect(result.isValid).toBeDefined();
9086
});
91-
92-
it('should handle USE_NON_MOCK_TOKEN configuration', () => {
93-
process.env.USE_NON_MOCK_TOKEN = 'true';
94-
const containerWithSSM = createContainer();
95-
96-
expect(containerWithSSM).toBeDefined();
97-
expect(containerWithSSM.authenticator).toBeDefined();
98-
expect(typeof containerWithSSM.authenticator).toBe('function');
99-
});
100-
101-
it('should wire getAccessToken to authenticator when using SSM token', async () => {
102-
const mockTokenValue = JSON.stringify({
103-
access_token: 'ssm-stored-token',
104-
expires_at: 1_765_187_843,
105-
token_type: 'Bearer',
106-
});
107-
108-
(parameterStore.getParameter as jest.Mock).mockResolvedValue({
109-
Value: mockTokenValue,
110-
});
111-
112-
process.env.USE_NON_MOCK_TOKEN = 'true';
113-
process.env.ACCESS_TOKEN_SSM_PATH = '/test/token/path';
114-
process.env.MOCK_ACCESS_TOKEN = 'unused-mock-token';
115-
116-
const testContainer = createContainer();
117-
118-
const result = await testContainer.authenticator({
119-
headers: { Authorization: 'Bearer ssm-stored-token' },
120-
});
121-
122-
expect(result.isValid).toBe(true);
123-
expect(parameterStore.getParameter).toHaveBeenCalledWith(
124-
'/test/token/path',
125-
);
126-
});
127-
128-
it('should handle invalid JSON format in SSM parameter', async () => {
129-
(parameterStore.getParameter as jest.Mock).mockResolvedValue({
130-
Value: 'invalid-json',
131-
});
132-
133-
process.env.USE_NON_MOCK_TOKEN = 'true';
134-
process.env.ACCESS_TOKEN_SSM_PATH = '/test/token/path';
135-
136-
const testContainer = createContainer();
137-
138-
await expect(
139-
testContainer.authenticator({
140-
headers: { Authorization: 'Bearer any-token' },
141-
}),
142-
).rejects.toThrow('Invalid access token format in SSM parameter');
143-
});
144-
145-
it('should handle missing SSM parameter', async () => {
146-
(parameterStore.getParameter as jest.Mock).mockResolvedValue({
147-
Value: undefined,
148-
});
149-
150-
process.env.USE_NON_MOCK_TOKEN = 'true';
151-
process.env.ACCESS_TOKEN_SSM_PATH = '/test/token/path';
152-
153-
const testContainer = createContainer();
154-
155-
await expect(
156-
testContainer.authenticator({
157-
headers: { Authorization: 'Bearer any-token' },
158-
}),
159-
).rejects.toThrow(
160-
'Access token parameter "/test/token/path" not found in SSM',
161-
);
162-
});
16387
});

0 commit comments

Comments
 (0)