Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cloudformation/iam.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Resources:
Effect: Allow
Resource:
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config*
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:427040638965:secret:infra-core-api-testing-credentials* # this secret only exists in awsdev account

- Action:
- dynamodb:DescribeLimits
Expand Down
2 changes: 1 addition & 1 deletion generate_jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const getSecretValue = async (secretId) => {
}
};

const secrets = await getSecretValue("infra-core-api-config");
const secrets = await getSecretValue("infra-core-api-testing-credentials");
const client = new STSClient({ region: "us-east-1" });
const command = new GetCallerIdentityCommand({});
let data;
Expand Down
10 changes: 9 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
environmentConfig,
genericConfig,
SecretConfig,
SecretTesting,
} from "../common/config.js";
import organizationsPlugin from "./routes/organizations.js";
import authorizeFromSchemaPlugin from "./plugins/authorizeFromSchema.js";
Expand Down Expand Up @@ -252,13 +253,20 @@ async function init(prettyPrint: boolean = false) {
app.dynamoClient = dynamoClient;
app.secretsManagerClient = secretsManagerClient;
app.redisClient = redisClient;
app.secretConfig = secret;
app.refreshSecretConfig = async () => {
app.secretConfig = (await getSecretValue(
app.secretsManagerClient,
genericConfig.ConfigSecretName,
)) as SecretConfig;
if (app.environmentConfig.TestingCredentialsSecret) {
const temp = (await getSecretValue(
app.secretsManagerClient,
app.environmentConfig.TestingCredentialsSecret,
)) as SecretTesting;
app.secretConfig = { ...app.secretConfig, ...temp };
}
};
app.refreshSecretConfig();
app.addHook("onRequest", (req, _, done) => {
req.startTime = now();
const hostname = req.hostname;
Expand Down
19 changes: 17 additions & 2 deletions src/api/lambda.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import "zod-openapi/extend";
import awsLambdaFastify from "@fastify/aws-lambda";
import awsLambdaFastify, { LambdaResponse } from "@fastify/aws-lambda";
import init from "./index.js";
import warmer from "lambda-warmer";
import { type APIGatewayEvent, type Context } from "aws-lambda";
import { InternalServerError } from "common/errors/index.js";

const app = await init();
const realHandler = awsLambdaFastify(app, {
Expand All @@ -16,7 +17,21 @@ const handler = async (event: APIGatewayEvent, context: Context) => {
return "warmed";
}
// else proceed with handler logic
return realHandler(event, context);
return realHandler(event, context).catch((e) => {
console.error(e);
const newError = new InternalServerError({
message: "Failed to initialize application.",
});
const json = JSON.stringify(newError.toJson());
return {
statusCode: newError.httpStatusCode,
body: json,
headers: {
"Content-Type": "application/json",
},
isBase64Encoded: false,
};
});
};

await app.ready(); // needs to be placed after awsLambdaFastify call because of the decoration: https://github.com/fastify/aws-lambda-fastify/blob/master/index.js#L9
Expand Down
4 changes: 2 additions & 2 deletions src/api/plugins/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
UnauthenticatedError,
UnauthorizedError,
} from "../../common/errors/index.js";
import { SecretConfig } from "../../common/config.js";
import { SecretConfig, SecretTesting } from "../../common/config.js";
import {
AUTH_DECISION_CACHE_SECONDS,
getGroupRoles,
Expand Down Expand Up @@ -195,7 +195,7 @@ const authPlugin: FastifyPluginAsync = async (fastify, _options) => {
}
signingKey =
process.env.JwtSigningKey ||
(fastify.secretConfig.jwt_key as string) ||
((fastify.secretConfig as SecretTesting).jwt_key as string) ||
"";
if (signingKey === "") {
throw new UnauthenticatedError({
Expand Down
4 changes: 2 additions & 2 deletions src/api/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { FastifyRequest, FastifyInstance, FastifyReply } from "fastify";
import { AppRoles, RunEnvironment } from "../common/roles.js";
import { AadToken } from "./plugins/auth.js";
import { ConfigType, SecretConfig } from "../common/config.js";
import { ConfigType, SecretConfig, SecretTesting } from "../common/config.js";
import NodeCache from "node-cache";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
Expand Down Expand Up @@ -36,7 +36,7 @@ declare module "fastify" {
redisClient: Redis;
secretsManagerClient: SecretsManagerClient;
cloudfrontKvClient: CloudFrontKeyValueStoreClient;
secretConfig: SecretConfig;
secretConfig: SecretConfig | (SecretConfig & SecretTesting);
refreshSecretConfig: CallableFunction;
}
interface FastifyRequest {
Expand Down
7 changes: 6 additions & 1 deletion src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ConfigType = {
PaidMemberPriceId: string;
AadValidReadOnlyClientId: string;
LinkryCloudfrontKvArn?: string;
TestingCredentialsSecret?: string;
};

export type GenericConfigType = {
Expand Down Expand Up @@ -98,6 +99,7 @@ const environmentConfig: EnvironmentConfigType = {
/^https:\/\/(?:.*\.)?acmuiuc\.pages\.dev$/,
/http:\/\/localhost:\d+$/,
],
TestingCredentialsSecret: "infra-core-api-testing-credentials",
AadValidClientId: "39c28870-94e4-47ee-b4fb-affe0bf96c9f",
LinkryBaseUrl: "https://core.aws.qa.acmuiuc.org",
PasskitIdentifier: "pass.org.acmuiuc.qa.membership",
Expand Down Expand Up @@ -137,7 +139,6 @@ const environmentConfig: EnvironmentConfigType = {
};

export type SecretConfig = {
jwt_key?: string;
discord_guild_id: string;
discord_bot_token: string;
entra_id_private_key?: string;
Expand All @@ -151,6 +152,10 @@ export type SecretConfig = {
redis_url: string;
};

export type SecretTesting = {
jwt_key: string;
}

const roleArns = {
Entra: process.env.EntraRoleArn,
};
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async function getSecrets() {
let response = { PLAYWRIGHT_USERNAME: "", PLAYWRIGHT_PASSWORD: "" };
let keyData;
if (!process.env.PLAYWRIGHT_USERNAME || !process.env.PLAYWRIGHT_PASSWORD) {
keyData = await getSecretValue("infra-core-api-config");
keyData = await getSecretValue("infra-core-api-testing-credentials");
}
response["PLAYWRIGHT_USERNAME"] =
process.env.PLAYWRIGHT_USERNAME ||
Expand Down
2 changes: 1 addition & 1 deletion tests/live/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async function getSecrets() {
const response = { JWTKEY: "" };
let keyData;
if (!process.env.JWT_KEY) {
keyData = await getSecretValue("infra-core-api-config");
keyData = await getSecretValue("infra-core-api-testing-credentials");
}
response["JWTKEY"] =
process.env.JWT_KEY || ((keyData ? keyData["jwt_key"] : "") as string);
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/apiKey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { afterAll, expect, test, beforeEach, vi, describe } from "vitest";
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/api/index.js";
import { createJwt } from "./auth.test.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import {
ConditionalCheckFailedException,
Expand Down Expand Up @@ -31,7 +31,7 @@ vi.mock("../../src/api/functions/apiKey.js", () => {
// Mock DynamoDB client
const dynamoMock = mockClient(DynamoDBClient);
const sqsMock = mockClient(SQSClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];

vi.stubEnv("JwtSigningKey", jwt_secret);

Expand Down
3 changes: 2 additions & 1 deletion tests/unit/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import {
secretObject,
jwtPayload,
jwtPayloadNoGroups,
testSecretObject,
} from "./secret.testdata.js";
import jwt from "jsonwebtoken";
import { allAppRoles, AppRoles } from "../../src/common/roles.js";
import { beforeEach, describe } from "node:test";

const app = await init();
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
export function createJwt(date?: Date, groups?: string[], email?: string) {
let modifiedPayload = {
...jwtPayload,
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/discordEvent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/api/index.js";
import { createJwt } from "./auth.test.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import { describe } from "node:test";
import { updateDiscord } from "../../src/api/functions/discord.js";

const ddbMock = mockClient(DynamoDBClient);

const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

vi.mock("../../src/api/functions/discord.js", () => {
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/entraInviteUser.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterAll, expect, test, beforeEach, vi } from "vitest";
import init from "../../src/api/index.js";
import { createJwt } from "./auth.test.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import { describe } from "node:test";

Expand All @@ -23,7 +23,7 @@ import {
} from "../../src/api/functions/entraId.js";
import { EntraInvitationError } from "../../src/common/errors/index.js";

const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];

vi.stubEnv("JwtSigningKey", jwt_secret);

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/eventPost.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/api/index.js";
import { createJwt } from "./auth.test.js";
import { secretJson, secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import { marshall } from "@aws-sdk/util-dynamodb";

const ddbMock = mockClient(DynamoDBClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

const app = await init();
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/api/index.js";
import { createJwt } from "./auth.test.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import { marshall } from "@aws-sdk/util-dynamodb";

const ddbMock = mockClient(DynamoDBClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

// Mock the Discord client to prevent the actual Discord API call
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/ical.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import {
dynamoEventWithRepeatExclusion,
dynamoTableData,
} from "./mockEventData.testdata.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import { readFile } from "fs/promises";

const ddbMock = mockClient(DynamoDBClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

const app = await init();
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/linkry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import {
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/api/index.js";
import { createJwt } from "./auth.test.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import { dynamoTableData } from "./mockLinkryData.testdata.js";
import { genericConfig } from "../../src/common/config.js";

const ddbMock = mockClient(DynamoDBClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

// Mock the Cloudfront KV client to prevent the actual Cloudfront KV call
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import init from "../../src/api/index.js";
import { describe } from "node:test";
import { mockClient } from "aws-sdk-client-mock";
import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import supertest from "supertest";
import { createJwt } from "./auth.test.js";
import { genericConfig } from "../../src/common/config.js";
import { marshall } from "@aws-sdk/util-dynamodb";
import { Modules } from "../../src/common/modules.js";

const ddbMock = mockClient(DynamoDBClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

const app = await init();
Expand Down
18 changes: 15 additions & 3 deletions tests/unit/secret.testdata.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SecretConfig } from "../../src/common/config.js";

const secretObject = {
jwt_key: "somethingreallysecret",
discord_guild_id: "12345",
discord_bot_token: "12345",
entra_id_private_key: "",
Expand All @@ -13,10 +12,16 @@ const secretObject = {
acm_passkit_signerKey_base64: "",
apple_signing_cert_base64: "",
redis_url: "",
} as SecretConfig & { jwt_key: string };
} as SecretConfig;

const testSecretObject = {
jwt_key: "somethingreallysecret",
};

const secretJson = JSON.stringify(secretObject);

const testSecretJson = JSON.stringify(testSecretObject);

const jwtPayload = {
aud: "custom_jwt",
iss: "custom_jwt",
Expand Down Expand Up @@ -69,4 +74,11 @@ const jwtPayloadNoGroups = {
ver: "1.0",
};

export { secretJson, secretObject, jwtPayload, jwtPayloadNoGroups };
export {
secretJson,
secretObject,
testSecretJson,
testSecretObject,
jwtPayload,
jwtPayloadNoGroups,
};
4 changes: 2 additions & 2 deletions tests/unit/tickets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "@aws-sdk/client-dynamodb";
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/api/index.js";
import { secretObject } from "./secret.testdata.js";
import { testSecretObject } from "./secret.testdata.js";
import {
dynamoTableData,
fulfilledMerchItem1,
Expand All @@ -28,7 +28,7 @@ import {
} from "./data/mockTIcketsMerchMetadata.testdata.js";

const ddbMock = mockClient(DynamoDBClient);
const jwt_secret = secretObject["jwt_key"];
const jwt_secret = testSecretObject["jwt_key"];
vi.stubEnv("JwtSigningKey", jwt_secret);

const app = await init();
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/vitest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
} from "@aws-sdk/client-secrets-manager";
import { mockClient } from "aws-sdk-client-mock";
import { marshall } from "@aws-sdk/util-dynamodb";
import { genericConfig } from "../../src/common/config.js";
import { secretJson } from "./secret.testdata.js";
import { environmentConfig, genericConfig } from "../../src/common/config.js";
import { secretJson, testSecretJson } from "./secret.testdata.js";

const ddbMock = mockClient(DynamoDBClient);
const smMock = mockClient(SecretsManagerClient);
Expand Down Expand Up @@ -114,6 +114,9 @@ smMock.on(GetSecretValueCommand).callsFake((command) => {
if (command.SecretId == genericConfig.ConfigSecretName) {
return Promise.resolve({ SecretString: secretJson });
}
if (command.SecretId == environmentConfig["dev"].TestingCredentialsSecret) {
return Promise.resolve({ SecretString: testSecretJson });
}
return Promise.reject(new Error("Secret ID not mocked"));
});

Expand Down
Loading