From 67eb04d13fd1b902679ff890ce2d09e31c3f8a8f Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sun, 26 Jan 2025 01:27:47 -0600 Subject: [PATCH 1/6] a super basic pass issuer --- .gitignore | 1 + Makefile | 1 + cloudformation/main.yml | 4 + generate_jwt.js | 2 +- src/api/esbuild.config.js | 34 ++ src/api/functions/discord.ts | 16 +- src/api/functions/entraId.ts | 10 +- src/api/functions/mobileWallet.ts | 117 ++++++ src/api/index.ts | 3 + src/api/package.json | 6 +- src/api/plugins/auth.ts | 7 +- .../resources/MembershipPass.pkpass/icon.png | Bin 0 -> 8318 bytes .../resources/MembershipPass.pkpass/logo.png | Bin 0 -> 28139 bytes .../resources/MembershipPass.pkpass/pass.ts | 18 + .../resources/MembershipPass.pkpass/strip.png | Bin 0 -> 44831 bytes src/api/resources/types.d.ts | 9 + src/api/routes/mobileWallet.ts | 29 ++ src/api/tsconfig.json | 1 - src/api/types.d.ts | 1 + src/common/config.ts | 9 + yarn.lock | 375 ++++++++++++++++-- 21 files changed, 602 insertions(+), 41 deletions(-) create mode 100644 src/api/esbuild.config.js create mode 100644 src/api/functions/mobileWallet.ts create mode 100644 src/api/resources/MembershipPass.pkpass/icon.png create mode 100644 src/api/resources/MembershipPass.pkpass/logo.png create mode 100644 src/api/resources/MembershipPass.pkpass/pass.ts create mode 100644 src/api/resources/MembershipPass.pkpass/strip.png create mode 100644 src/api/resources/types.d.ts create mode 100644 src/api/routes/mobileWallet.ts diff --git a/.gitignore b/.gitignore index 4be0d91e..4ce03d78 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,4 @@ __pycache__ /playwright-report/ /blob-report/ /playwright/.cache/ +dist_devel/ diff --git a/Makefile b/Makefile index af53f386..0b2c9ae0 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,7 @@ clean: build: src/ cloudformation/ docs/ yarn -D VITE_BUILD_HASH=$(GIT_HASH) yarn build + cp -r src/api/resources/ dist/api/resources sam build --template-file cloudformation/main.yml local: diff --git a/cloudformation/main.yml b/cloudformation/main.yml index 5edf7e32..57872f63 100644 --- a/cloudformation/main.yml +++ b/cloudformation/main.yml @@ -127,6 +127,10 @@ Resources: Minify: true OutExtension: - .js=.mjs + Loader: + - .png=file + - .pkpass=file + - .json=file Target: "es2022" Sourcemap: false EntryPoints: diff --git a/generate_jwt.js b/generate_jwt.js index 02f58a07..9b9b51c0 100644 --- a/generate_jwt.js +++ b/generate_jwt.js @@ -18,7 +18,7 @@ const payload = { groups: ["0"], idp: "https://login.microsoftonline.com", ipaddr: "192.168.1.1", - name: "John Doe", + name: "Singh, Dev", oid: "00000000-0000-0000-0000-000000000000", rh: "rh-value", scp: "user_impersonation", diff --git a/src/api/esbuild.config.js b/src/api/esbuild.config.js new file mode 100644 index 00000000..3cc15627 --- /dev/null +++ b/src/api/esbuild.config.js @@ -0,0 +1,34 @@ +import { build, context } from 'esbuild'; +import { readFileSync } from 'fs'; +import { resolve } from 'path'; + +const isWatching = !!process.argv.includes('--watch') +const nodePackage = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf8')); + +const buildOptions = { + entryPoints: [resolve(process.cwd(), 'index.ts')], + outfile: resolve(process.cwd(), '../', '../', 'dist_devel', 'index.js'), + bundle: true, + platform: 'node', + format: 'esm', + external: [ + Object.keys(nodePackage.dependencies ?? {}), + Object.keys(nodePackage.peerDependencies ?? {}), + Object.keys(nodePackage.devDependencies ?? {}), + ].flat(), + loader: { + '.png': 'file', // Add this line to specify a loader for .png files + }, +}; + +if (isWatching) { + context(buildOptions).then(ctx => { + if (isWatching) { + ctx.watch(); + } else { + ctx.rebuild(); + } + }); +} else { + build(buildOptions) +} diff --git a/src/api/functions/discord.ts b/src/api/functions/discord.ts index 5fb92680..e3886e94 100644 --- a/src/api/functions/discord.ts +++ b/src/api/functions/discord.ts @@ -12,7 +12,10 @@ import { type EventPostRequest } from "../routes/events.js"; import moment from "moment-timezone"; import { FastifyBaseLogger } from "fastify"; -import { DiscordEventError } from "../../common/errors/index.js"; +import { + DiscordEventError, + InternalServerError, +} from "../../common/errors/index.js"; import { getSecretValue } from "../plugins/auth.js"; import { genericConfig } from "../../common/config.js"; import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; @@ -30,8 +33,15 @@ export const updateDiscord = async ( isDelete: boolean = false, logger: FastifyBaseLogger, ): Promise => { - const secretApiConfig = - (await getSecretValue(smClient, genericConfig.ConfigSecretName)) || {}; + const secretApiConfig = await getSecretValue( + smClient, + genericConfig.ConfigSecretName, + ); + if (!secretApiConfig) { + throw new InternalServerError({ + message: "Could not find credentials for Discord.", + }); + } const client = new Client({ intents: [GatewayIntentBits.Guilds] }); let payload: GuildScheduledEventCreateOptions | null = null; diff --git a/src/api/functions/entraId.ts b/src/api/functions/entraId.ts index 30c0331a..434a8d62 100644 --- a/src/api/functions/entraId.ts +++ b/src/api/functions/entraId.ts @@ -30,12 +30,12 @@ export async function getEntraIdToken( clientId: string, scopes: string[] = ["https://graph.microsoft.com/.default"], ) { - const secretApiConfig = - (await getSecretValue( - fastify.secretsManagerClient, - genericConfig.ConfigSecretName, - )) || {}; + const secretApiConfig = await getSecretValue( + fastify.secretsManagerClient, + genericConfig.ConfigSecretName, + ); if ( + !secretApiConfig || !secretApiConfig.entra_id_private_key || !secretApiConfig.entra_id_thumbprint ) { diff --git a/src/api/functions/mobileWallet.ts b/src/api/functions/mobileWallet.ts new file mode 100644 index 00000000..df5acd9a --- /dev/null +++ b/src/api/functions/mobileWallet.ts @@ -0,0 +1,117 @@ +import { getSecretValue } from "../plugins/auth.js"; +import { genericConfig } from "../../common/config.js"; +import { + InternalServerError, + UnauthorizedError, +} from "../../common/errors/index.js"; +import { FastifyInstance, FastifyRequest } from "fastify"; +// these make sure that esbuild includes the files +import icon from "../resources/MembershipPass.pkpass/icon.png"; +import logo from "../resources/MembershipPass.pkpass/logo.png"; +import strip from "../resources/MembershipPass.pkpass/strip.png"; +import pass from "../resources/MembershipPass.pkpass/pass.js"; +import { PKPass } from "passkit-generator"; +import { promises as fs } from "fs"; + +function trim(s: string) { + return (s || "").replace(/^\s+|\s+$/g, ""); +} + +function convertName(name: string): string { + if (!name.includes(",")) { + return name; + } + return `${trim(name.split(",")[1])} ${name.split(",")[0]}`; +} + +export async function issueAppleWalletMembershipCard( + app: FastifyInstance, + request: FastifyRequest, + email: string, + name: string, +) { + if (!email.endsWith("@illinois.edu")) { + throw new UnauthorizedError({ + message: + "Cannot issue membership pass for emails not on the illinois.edu domain.", + }); + } + const secretApiConfig = await getSecretValue( + app.secretsManagerClient, + genericConfig.ConfigSecretName, + ); + if (!secretApiConfig) { + throw new InternalServerError({ + message: "Could not retrieve signing data", + }); + } + const signerCert = Buffer.from( + secretApiConfig.acm_passkit_signerCert_base64, + "base64", + ).toString("utf-8"); + const signerKey = Buffer.from( + secretApiConfig.acm_passkit_signerKey_base64, + "base64", + ).toString("utf-8"); + const wwdr = Buffer.from( + secretApiConfig.apple_signing_cert_base64, + "base64", + ).toString("utf-8"); + pass["passTypeIdentifier"] = app.environmentConfig["PasskitIdentifier"]; + + const pkpass = new PKPass( + { + "icon.png": await fs.readFile(icon), + "logo.png": await fs.readFile(logo), + "strip.png": await fs.readFile(strip), + "pass.json": Buffer.from(JSON.stringify(pass)), + }, + { + wwdr, + signerCert, + signerKey, + }, + { + // logoText: app.runEnvironment === "dev" ? "INVALID Membership Pass" : "Membership Pass", + serialNumber: app.environmentConfig["PasskitSerialNumber"], + }, + ); + pkpass.setBarcodes({ + altText: email.split("@")[0], + format: "PKBarcodeFormatPDF417", + message: app.runEnvironment === "dev" ? `INVALID${email}INVALID` : email, + }); + const iat = new Date().toLocaleDateString("en-US", { + day: "2-digit", + month: "2-digit", + year: "numeric", + }); + if (name && name !== "") { + pkpass.secondaryFields.push({ + label: "Member Name", + key: "name", + value: convertName(name), + }); + } + if (app.runEnvironment === "prod") { + pkpass.backFields.push({ + label: "Verification URL", + key: "iss", + value: `https://membership.acm.illinois.edu/verify/${email.split("@")[0]}`, + }); + } else { + pkpass.backFields.push({ + label: "TESTING ONLY Pass", + key: "iss", + value: `Do not honor!`, + }); + } + pkpass.backFields.push({ label: "Pass Created On", key: "iat", value: iat }); + pkpass.backFields.push({ label: "Membership ID", key: "id", value: email }); + const buffer = pkpass.getAsBuffer(); + request.log.info( + { type: "audit", actor: email, target: email }, + "Created membership verification pass", + ); + return buffer; +} diff --git a/src/api/index.ts b/src/api/index.ts index ed7f3497..9ab71053 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -21,6 +21,7 @@ import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts"; import NodeCache from "node-cache"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; +import mobileWalletRoute from "./routes/mobileWallet.js"; dotenv.config(); @@ -82,6 +83,7 @@ async function init() { app.nodeCache = new NodeCache({ checkperiod: 30 }); app.dynamoClient = dynamoClient; app.secretsManagerClient = secretsManagerClient; + app.secretsManagerData = null; app.addHook("onRequest", (req, _, done) => { req.startTime = now(); const hostname = req.hostname; @@ -110,6 +112,7 @@ async function init() { api.register(organizationsPlugin, { prefix: "/organizations" }); api.register(icalPlugin, { prefix: "/ical" }); api.register(iamRoutes, { prefix: "/iam" }); + api.register(mobileWalletRoute, { prefix: "/mobile" }); api.register(ticketsPlugin, { prefix: "/tickets" }); if (app.runEnvironment === "dev") { api.register(vendingPlugin, { prefix: "/vending" }); diff --git a/src/api/package.json b/src/api/package.json index 9f0467c2..532847cb 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -8,7 +8,7 @@ "type": "module", "scripts": { "build": "tsc", - "dev": "cross-env LOG_LEVEL=debug tsx watch index.ts", + "dev": "cross-env LOG_LEVEL=debug node esbuild.config.js --watch & nodemon ../../dist/index.js", "typecheck": "tsc --noEmit", "lint": "eslint . --ext .ts --cache", "prettier": "prettier --check *.ts **/*.ts", @@ -36,12 +36,14 @@ "moment": "^2.30.1", "moment-timezone": "^0.5.45", "node-cache": "^5.1.2", + "passkit-generator": "^3.3.1", "pluralize": "^8.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.2", "zod-validation-error": "^3.3.1" }, "devDependencies": { - "@tsconfig/node22": "^22.0.0" + "@tsconfig/node22": "^22.0.0", + "nodemon": "^3.1.9" } } \ No newline at end of file diff --git a/src/api/plugins/auth.ts b/src/api/plugins/auth.ts index 1e5fa54c..ca312873 100644 --- a/src/api/plugins/auth.ts +++ b/src/api/plugins/auth.ts @@ -57,7 +57,7 @@ export type AadToken = { export const getSecretValue = async ( smClient: SecretsManagerClient, secretId: string, -): Promise | null | SecretConfig> => { +): Promise => { const data = await smClient.send( new GetSecretValueCommand({ SecretId: secretId }), ); @@ -65,10 +65,7 @@ export const getSecretValue = async ( return null; } try { - return JSON.parse(data.SecretString) as Record< - string, - string | number | boolean - >; + return JSON.parse(data.SecretString) as SecretConfig; } catch { return null; } diff --git a/src/api/resources/MembershipPass.pkpass/icon.png b/src/api/resources/MembershipPass.pkpass/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3800ba0836799d62a56431c62333e1a9256b3317 GIT binary patch literal 8318 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 u*Z!8*OteY~7=ZrcJ);_*HEBRi^C^-gFmtBTU8q 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 + export default value; +} diff --git a/src/api/routes/mobileWallet.ts b/src/api/routes/mobileWallet.ts new file mode 100644 index 00000000..b35ac858 --- /dev/null +++ b/src/api/routes/mobileWallet.ts @@ -0,0 +1,29 @@ +import { FastifyPluginAsync } from "fastify"; +import { issueAppleWalletMembershipCard } from "../functions/mobileWallet.js"; +import { UnauthenticatedError } from "../../common/errors/index.js"; + +const mobileWalletRoute: FastifyPluginAsync = async (fastify, _options) => { + fastify.get( + "/apple", + { + onRequest: async (request, reply) => { + await fastify.authorize(request, reply, []); + }, + }, + async (request, reply) => { + if (!request.username || !request.tokenPayload) { + throw new UnauthenticatedError({ message: "Could not find user." }); + } + const item = await issueAppleWalletMembershipCard( + fastify, + request, + request.username, + request.tokenPayload.name, + ); + reply.type("application/vnd.apple.pkpass"); + reply.send(item); + }, + ); +}; + +export default mobileWalletRoute; diff --git a/src/api/tsconfig.json b/src/api/tsconfig.json index 7a90b5c6..05a40a05 100644 --- a/src/api/tsconfig.json +++ b/src/api/tsconfig.json @@ -14,7 +14,6 @@ "../common/**/*.ts" ], "exclude": [ - "../../node_modules", "../../dist" ] } diff --git a/src/api/types.d.ts b/src/api/types.d.ts index 7f0e498f..be970ffa 100644 --- a/src/api/types.d.ts +++ b/src/api/types.d.ts @@ -26,6 +26,7 @@ declare module "fastify" { nodeCache: NodeCache; dynamoClient: DynamoDBClient; secretsManagerClient: SecretsManagerClient; + secretsManagerData: Record | null; } interface FastifyRequest { startTime: number; diff --git a/src/common/config.ts b/src/common/config.ts index b0609d60..f5641d5c 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -12,6 +12,8 @@ export type ConfigType = { AzureRoleMapping: AzureRoleMapping; ValidCorsOrigins: ValueOrArray | OriginFunction; AadValidClientId: string; + PasskitIdentifier: string; + PasskitSerialNumber: string; }; type GenericConfigType = { @@ -66,6 +68,8 @@ const environmentConfig: EnvironmentConfigType = { /^https:\/\/(?:.*\.)?acmuiuc\.pages\.dev$/, ], AadValidClientId: "39c28870-94e4-47ee-b4fb-affe0bf96c9f", + PasskitIdentifier: "pass.org.acmuiuc.qa.membership", + PasskitSerialNumber: "0" }, prod: { AzureRoleMapping: { AutonomousWriters: [AppRoles.EVENTS_MANAGER] }, @@ -76,6 +80,8 @@ const environmentConfig: EnvironmentConfigType = { /^https:\/\/(?:.*\.)?acmuiuc\.pages\.dev$/, ], AadValidClientId: "5e08cf0f-53bb-4e09-9df2-e9bdc3467296", + PasskitIdentifier: "pass.edu.illinois.acm.membership", + PasskitSerialNumber: "0" } }; @@ -85,6 +91,9 @@ export type SecretConfig = { discord_bot_token: string; entra_id_private_key: string; entra_id_thumbprint: string; + acm_passkit_signerCert_base64: string; + acm_passkit_signerKey_base64: string; + apple_signing_cert_base64: string; }; export { genericConfig, environmentConfig }; diff --git a/yarn.lock b/yarn.lock index 26a9a553..203b4117 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1705,6 +1705,18 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429" integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg== +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" @@ -2062,6 +2074,23 @@ resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.5.tgz#33a60ab4231e3cab29e8a0077f342125f2c8d1bd" integrity sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ== +"@sideway/address@^4.1.0": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" + integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sinonjs/commons@^3.0.0", "@sinonjs/commons@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" @@ -3817,6 +3846,14 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + arch@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -4080,6 +4117,11 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -4094,6 +4136,20 @@ better-opn@^3.0.2: dependencies: open "^8.0.4" +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bowser@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -4128,7 +4184,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.3: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -4155,6 +4211,14 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -4211,6 +4275,14 @@ caniuse-lite@^1.0.30001688: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz#f2d15e3aaf8e18f76b2b8c1481abde063b8104c8" integrity sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w== +canvas@^3.0.0-rc2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-3.1.0.tgz#6cdf094b859fef8e39b0e2c386728a376f1727b2" + integrity sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg== + dependencies: + node-addon-api "^7.0.0" + prebuild-install "^7.1.1" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -4279,6 +4351,26 @@ check-error@^2.1.1: resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== +chokidar@^3.5.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + cli-boxes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" @@ -4584,7 +4676,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: +debug@4, debug@^4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -4603,6 +4695,13 @@ decimal.js@^10.4.3: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" @@ -4675,6 +4774,11 @@ dequal@^2.0.2, dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== +detect-libc@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -4723,6 +4827,11 @@ discord.js@^14.15.3: tslib "^2.6.3" undici "6.19.8" +do-not-zip@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/do-not-zip/-/do-not-zip-1.0.0.tgz#cdced6c6352664ecb368f9fe7a15e1cb40c50c42" + integrity sha512-Pgd81ET43bhAGaN2Hq1zluSX1FmD7kl7KcV9ER/lawiLsRUB9pRA5y8r6us29Xk6BrINZETO8TjhYwtwafWUww== + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -4824,6 +4933,13 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + enhanced-resolve@^5.15.0: version "5.18.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz#91eb1db193896b9801251eeff1c6980278b1e404" @@ -5483,6 +5599,11 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + expect-type@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" @@ -5785,6 +5906,11 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5894,7 +6020,12 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.2: +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -6151,6 +6282,16 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -6192,7 +6333,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6274,6 +6415,13 @@ is-bigint@^1.1.0: dependencies: has-bigints "^1.0.2" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.1.tgz#c20d0c654be05da4fbc23c562635c019e93daf89" @@ -6355,7 +6503,7 @@ is-generator-function@^1.0.10, is-generator-function@^1.0.7: has-tostringtag "^1.0.2" safe-regex-test "^1.1.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -6515,6 +6663,17 @@ iterator.prototype@^1.1.4: has-symbols "^1.1.0" set-function-name "^2.0.2" +joi@17.4.2: + version "17.4.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.2.tgz#02f4eb5cf88e515e614830239379dcbbe28ce7f7" + integrity sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.0" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + jose@^4.14.6: version "4.15.9" resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" @@ -7036,6 +7195,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -7055,11 +7219,16 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -7118,6 +7287,11 @@ nanoid@^3.3.7, nanoid@^3.3.8: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== +napi-build-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" + integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -7151,6 +7325,18 @@ nmtree@^1.0.6: dependencies: commander "^2.11.0" +node-abi@^3.3.0: + version "3.73.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.73.0.tgz#4459ea77e71969edba8588387eecb05e2c2cff3b" + integrity sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg== + dependencies: + semver "^7.3.5" + +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" @@ -7158,6 +7344,11 @@ node-cache@^5.1.2: dependencies: clone "2.x" +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + node-ical@^0.20.1: version "0.20.1" resolved "https://registry.yarnpkg.com/node-ical/-/node-ical-0.20.1.tgz#3a67319af9be956b3cc81cdf6716d1352eaefaca" @@ -7173,7 +7364,23 @@ node-releases@^2.0.19: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== -normalize-path@^3.0.0: +nodemon@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.9.tgz#df502cdc3b120e1c3c0c6e4152349019efa7387b" + integrity sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg== + dependencies: + chokidar "^3.5.2" + debug "^4" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^7.5.3" + simple-update-notifier "^2.0.0" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -7288,7 +7495,7 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -7387,6 +7594,16 @@ parse5@^7.1.2: dependencies: entities "^4.5.0" +passkit-generator@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/passkit-generator/-/passkit-generator-3.3.1.tgz#8e711973b40ff09316367823030bd404731b3a7c" + integrity sha512-Kme0BjZTiBNSnCMMM0Vndjq1EzSIePU1Pne9jHY08WctmHlA8CEgbRRjzh1S9oBHO7qkDVOGek5OW4oJWTe8Ug== + dependencies: + do-not-zip "^1.0.0" + joi "17.4.2" + node-forge "^1.3.1" + tslib "^2.7.0" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -7432,6 +7649,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path2d@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/path2d/-/path2d-0.2.2.tgz#cc85d61ed7827e7863a2ee36713d4b5315a3d85d" + integrity sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ== + pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -7442,7 +7664,15 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -pdfjs-dist@4.8.69, pdfjs-dist@^4.5.136, pdfjs-dist@^4.6.82, pdfjs-dist@^4.8.69: +pdfjs-dist@4.8.69: + version "4.8.69" + resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.8.69.tgz#61ea5d66863d49b40e5eacbd4070341175bdda2e" + integrity sha512-IHZsA4T7YElCKNNXtiLgqScw4zPd3pG9do8UrznC757gMd7UPeHSL2qwNNMJo4r79fl8oj1Xx+1nh2YkzdMpLQ== + optionalDependencies: + canvas "^3.0.0-rc2" + path2d "^0.2.1" + +pdfjs-dist@^4.5.136, pdfjs-dist@^4.6.82: version "4.10.38" resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz#3ee698003790dc266cc8b55c0e662ccb9ae18f53" integrity sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ== @@ -7459,7 +7689,7 @@ picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -7616,14 +7846,23 @@ postcss@^8.4.41, postcss@^8.4.49: picocolors "^1.1.1" source-map-js "^1.2.1" -postcss@^8.4.43: - version "8.5.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" - integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" +prebuild-install@^7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" + integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^2.0.0" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" prelude-ls@^1.2.1: version "1.2.1" @@ -7700,6 +7939,19 @@ psl@^1.1.28, psl@^1.1.33: dependencies: punycode "^2.3.1" +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +pump@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -7742,7 +7994,7 @@ range-parser@1.2.0: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== -rc@^1.0.1, rc@^1.1.6: +rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7900,6 +8152,22 @@ react-transition-group@4.4.5: dependencies: loose-envify "^1.1.0" +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + real-require@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" @@ -8175,7 +8443,7 @@ safe-buffer@5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2: +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8243,7 +8511,7 @@ semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3: +semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -8398,6 +8666,27 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" + sinon@^18.0.1: version "18.0.1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-18.0.1.tgz#464334cdfea2cddc5eda9a4ea7e2e3f0c7a91c5e" @@ -8628,6 +8917,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -8803,7 +9099,7 @@ supertest@^7.0.0: methods "^1.1.2" superagent "^9.0.1" -supports-color@^5.3.0: +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -8901,6 +9197,27 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +tar-fs@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.2.tgz#425f154f3404cb16cb8ff6e671d45ab2ed9596c5" + integrity sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -8980,6 +9297,11 @@ totalist@^3.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== +touch@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== + tough-cookie@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" @@ -9059,7 +9381,7 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.6.3: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.6.3, tslib@^2.7.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -9211,6 +9533,11 @@ unbox-primitive@^1.1.0: has-symbols "^1.1.0" which-boxed-primitive "^1.1.1" +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + undici-types@~6.20.0: version "6.20.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" @@ -9297,7 +9624,7 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -util-deprecate@^1.0.2: +util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== From 3b872584980f4f406c9dab5f627f110c6b181b2b Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Tue, 28 Jan 2025 04:14:38 +0000 Subject: [PATCH 2/6] route emails you the pass instead --- cloudformation/iam.yml | 20 +- cloudformation/main.yml | 3 + src/api/functions/entraId.ts | 46 +++ src/api/functions/mobileWallet.ts | 2 +- src/api/functions/ses.ts | 130 ++++++++ src/api/index.ts | 8 +- src/api/package.json | 7 +- src/api/plugins/auth.ts | 1 - src/api/routes/mobileWallet.ts | 78 ++++- src/api/types.d.ts | 2 + src/common/config.ts | 10 +- src/common/errors/index.ts | 14 + yarn.lock | 511 +++++++++++++++++++++++++++++- 13 files changed, 809 insertions(+), 23 deletions(-) create mode 100644 src/api/functions/ses.ts diff --git a/cloudformation/iam.yml b/cloudformation/iam.yml index 756c9706..c110fde1 100644 --- a/cloudformation/iam.yml +++ b/cloudformation/iam.yml @@ -10,6 +10,9 @@ Parameters: LambdaFunctionName: Type: String AllowedPattern: ^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$ + SesEmailDomain: + Type: String + Resources: ApiLambdaIAMRole: Type: AWS::IAM::Role @@ -24,6 +27,21 @@ Resources: Service: - lambda.amazonaws.com Policies: + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - ses:SendEmail + - ses:SendRawEmail + Effect: Allow + Resource: "*" + Condition: + StringEquals: + ses:FromAddress: !Sub "membership@${SesEmailDomain}" + ForAllValues:StringLike: + ses:Recipients: + - "*@illinois.edu" + PolicyName: ses-membership - PolicyDocument: Version: '2012-10-17' Statement: @@ -85,4 +103,4 @@ Outputs: Value: Fn::GetAtt: - ApiLambdaIAMRole - - Arn \ No newline at end of file + - Arn diff --git a/cloudformation/main.yml b/cloudformation/main.yml index 57872f63..30a9a886 100644 --- a/cloudformation/main.yml +++ b/cloudformation/main.yml @@ -40,12 +40,14 @@ Mappings: HostedZoneId: Z04502822NVIA85WM2SML ApiDomainName: "aws.qa.acmuiuc.org" LinkryApiDomainName: "aws.qa.acmuiuc.org" + SesDomain: "aws.qa.acmuiuc.org" LinkryApiCertificateArn: arn:aws:acm:us-east-1:427040638965:certificate/63ccdf0b-d2b5-44f0-b589-eceffb935c23 prod: ApiCertificateArn: arn:aws:acm:us-east-1:298118738376:certificate/6142a0e2-d62f-478e-bf15-5bdb616fe705 HostedZoneId: Z05246633460N5MEB9DBF ApiDomainName: "aws.acmuiuc.org" LinkryApiDomainName: "acm.illinois.edu" + SesDomain: "acm.illinois.edu" LinkryApiCertificateArn: arn:aws:acm:us-east-1:298118738376:certificate/aeb93d9e-b0b7-4272-9c12-24ca5058c77e EnvironmentToCidr: dev: @@ -71,6 +73,7 @@ Resources: Parameters: RunEnvironment: !Ref RunEnvironment LambdaFunctionName: !Sub ${ApplicationPrefix}-lambda + SesEmailDomain: !FindInMap [General, !Ref RunEnvironment, SesDomain] AppLogGroups: Type: AWS::Serverless::Application diff --git a/src/api/functions/entraId.ts b/src/api/functions/entraId.ts index 434a8d62..785cc719 100644 --- a/src/api/functions/entraId.ts +++ b/src/api/functions/entraId.ts @@ -7,6 +7,7 @@ import { } from "../../common/config.js"; import { BaseError, + EntraFetchError, EntraGroupError, EntraInvitationError, InternalServerError, @@ -19,6 +20,7 @@ import { EntraInvitationResponse, } from "../../common/types/iam.js"; import { FastifyInstance } from "fastify"; +import { UserProfileDataBase } from "common/types/msGraphApi.js"; function validateGroupId(groupId: string): boolean { const groupIdPattern = /^[a-zA-Z0-9-]+$/; // Adjust the pattern as needed @@ -351,3 +353,47 @@ export async function listGroupMembers( }); } } + +/** + * Retrieves the profile of a user from Entra ID. + * @param token - Entra ID token authorized to perform this action. + * @param userId - The user ID to fetch the profile for. + * @throws {EntraUserError} If fetching the user profile fails. + * @returns {Promise} The user's profile information. + */ +export async function getUserProfile( + token: string, + email: string, +): Promise { + const userId = await resolveEmailToOid(token, email); + try { + const url = `https://graph.microsoft.com/v1.0/users/${userId}?$select=userPrincipalName,givenName,surname,displayName,otherMails,mail`; + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData = (await response.json()) as { + error?: { message?: string }; + }; + throw new EntraFetchError({ + message: errorData?.error?.message ?? response.statusText, + email, + }); + } + return (await response.json()) as UserProfileDataBase; + } catch (error) { + if (error instanceof EntraFetchError) { + throw error; + } + + throw new EntraFetchError({ + message: error instanceof Error ? error.message : String(error), + email, + }); + } +} diff --git a/src/api/functions/mobileWallet.ts b/src/api/functions/mobileWallet.ts index df5acd9a..e4503327 100644 --- a/src/api/functions/mobileWallet.ts +++ b/src/api/functions/mobileWallet.ts @@ -28,7 +28,7 @@ export async function issueAppleWalletMembershipCard( app: FastifyInstance, request: FastifyRequest, email: string, - name: string, + name?: string, ) { if (!email.endsWith("@illinois.edu")) { throw new UnauthorizedError({ diff --git a/src/api/functions/ses.ts b/src/api/functions/ses.ts new file mode 100644 index 00000000..0ce5ff22 --- /dev/null +++ b/src/api/functions/ses.ts @@ -0,0 +1,130 @@ +import { SendRawEmailCommand } from "@aws-sdk/client-ses"; +import { encode } from "base64-arraybuffer"; + +/** + * Generates a SendRawEmailCommand for SES to send an email with an attached membership pass. + * + * @param recipientEmail - The email address of the recipient. + * * @param recipientEmail - The email address of the sender with a verified identity in SES. + * @param attachmentBuffer - The membership pass in ArrayBufferLike format. + * @returns The command to send the email via SES. + */ +export function generateMembershipEmailCommand( + recipientEmail: string, + senderEmail: string, + attachmentBuffer: ArrayBufferLike, +): SendRawEmailCommand { + const encodedAttachment = encode(attachmentBuffer); + const boundary = "----BoundaryForEmail"; + + const emailTemplate = ` + + + + Your ACM @ UIUC Membership + + + + + + +

 
+ +
+
+

Welcome

+

+ Thank you for becoming a member of ACM @ UIUC! Attached is your membership pass. + You can add it to your Apple or Google Wallet for easy access. +

+

+ If you have any questions, feel free to contact us at + infra@acm.illinois.edu. +

+ +
+ + + + `; + + const rawEmail = ` +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="${boundary}" +From: ACM @ UIUC <${senderEmail}> +To: ${recipientEmail} +Subject: Your ACM @ UIUC Membership + +--${boundary} +Content-Type: text/html; charset="UTF-8" + +${emailTemplate} + +--${boundary} +Content-Type: application/vnd.apple.pkpass +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="membership.pkpass" + +${encodedAttachment} +--${boundary}--`.trim(); + return new SendRawEmailCommand({ + RawMessage: { + Data: new TextEncoder().encode(rawEmail), + }, + }); +} diff --git a/src/api/index.ts b/src/api/index.ts index 9ab71053..a84d3d76 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -22,6 +22,7 @@ import NodeCache from "node-cache"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; import mobileWalletRoute from "./routes/mobileWallet.js"; +import { SESClient } from "@aws-sdk/client-ses"; dotenv.config(); @@ -36,6 +37,10 @@ async function init() { region: genericConfig.AwsRegion, }); + const sesClient = new SESClient({ + region: genericConfig.AwsRegion, + }); + const app: FastifyInstance = fastify({ logger: { level: process.env.LOG_LEVEL || "info", @@ -84,6 +89,7 @@ async function init() { app.dynamoClient = dynamoClient; app.secretsManagerClient = secretsManagerClient; app.secretsManagerData = null; + app.sesClient = sesClient; app.addHook("onRequest", (req, _, done) => { req.startTime = now(); const hostname = req.hostname; @@ -112,7 +118,7 @@ async function init() { api.register(organizationsPlugin, { prefix: "/organizations" }); api.register(icalPlugin, { prefix: "/ical" }); api.register(iamRoutes, { prefix: "/iam" }); - api.register(mobileWalletRoute, { prefix: "/mobile" }); + api.register(mobileWalletRoute, { prefix: "/mobileWallet" }); api.register(ticketsPlugin, { prefix: "/tickets" }); if (app.runEnvironment === "dev") { api.register(vendingPlugin, { prefix: "/vending" }); diff --git a/src/api/package.json b/src/api/package.json index 532847cb..307f3c0f 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -8,7 +8,7 @@ "type": "module", "scripts": { "build": "tsc", - "dev": "cross-env LOG_LEVEL=debug node esbuild.config.js --watch & nodemon ../../dist/index.js", + "dev": "cross-env LOG_LEVEL=debug concurrently --names 'esbuild,server' 'node esbuild.config.js --watch' 'cd ../../dist_devel && nodemon index.js'", "typecheck": "tsc --noEmit", "lint": "eslint . --ext .ts --cache", "prettier": "prettier --check *.ts **/*.ts", @@ -17,6 +17,7 @@ "dependencies": { "@aws-sdk/client-dynamodb": "^3.624.0", "@aws-sdk/client-secrets-manager": "^3.624.0", + "@aws-sdk/client-ses": "^3.734.0", "@aws-sdk/client-sts": "^3.726.0", "@aws-sdk/util-dynamodb": "^3.624.0", "@azure/msal-node": "^2.16.1", @@ -25,6 +26,7 @@ "@fastify/caching": "^9.0.1", "@fastify/cors": "^10.0.1", "@touch4it/ical-timezones": "^1.9.0", + "base64-arraybuffer": "^1.0.2", "discord.js": "^14.15.3", "dotenv": "^16.4.5", "esbuild": "^0.24.2", @@ -44,6 +46,7 @@ }, "devDependencies": { "@tsconfig/node22": "^22.0.0", + "@types/base64-arraybuffer": "^0.2.4", "nodemon": "^3.1.9" } -} \ No newline at end of file +} diff --git a/src/api/plugins/auth.ts b/src/api/plugins/auth.ts index ca312873..9eeba555 100644 --- a/src/api/plugins/auth.ts +++ b/src/api/plugins/auth.ts @@ -15,7 +15,6 @@ import { } from "../../common/errors/index.js"; import { genericConfig, SecretConfig } from "../../common/config.js"; import { getGroupRoles, getUserRoles } from "../functions/authorization.js"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; function intersection(setA: Set, setB: Set): Set { const _intersection = new Set(); diff --git a/src/api/routes/mobileWallet.ts b/src/api/routes/mobileWallet.ts index b35ac858..ead63984 100644 --- a/src/api/routes/mobileWallet.ts +++ b/src/api/routes/mobileWallet.ts @@ -1,27 +1,85 @@ import { FastifyPluginAsync } from "fastify"; import { issueAppleWalletMembershipCard } from "../functions/mobileWallet.js"; -import { UnauthenticatedError } from "../../common/errors/index.js"; +import { + UnauthenticatedError, + UnauthorizedError, + ValidationError, +} from "../../common/errors/index.js"; +import { generateMembershipEmailCommand } from "api/functions/ses.js"; +import { z } from "zod"; +import { getEntraIdToken, getUserProfile } from "api/functions/entraId.js"; const mobileWalletRoute: FastifyPluginAsync = async (fastify, _options) => { - fastify.get( - "/apple", + fastify.get<{ Querystring: { email: string } }>( + "/membership", { - onRequest: async (request, reply) => { - await fastify.authorize(request, reply, []); + schema: { + querystring: { + type: "object", + properties: { + email: { type: "string", format: "email" }, + }, + required: ["email"], + }, }, }, async (request, reply) => { - if (!request.username || !request.tokenPayload) { + if (!request.query.email) { throw new UnauthenticatedError({ message: "Could not find user." }); } + try { + await z + .string() + .email() + .refine( + (email) => email.endsWith("@illinois.edu"), + "Email must be on the illinois.edu domain.", + ) + .parseAsync(request.query.email); + } catch { + throw new ValidationError({ + message: "Email query parameter is not a valid email", + }); + } + + const membershipApiPayload = (await ( + await fetch( + `${fastify.environmentConfig.MembershipApiEndpoint}?netId=${request.query.email.replace("@illinois.edu", "")}`, + ) + ).json()) as { netId: string; isPaidMember: boolean }; + try { + if (!membershipApiPayload["isPaidMember"]) { + throw new UnauthorizedError({ + message: "User is not a paid member.", + }); + } + } catch (e: any) { + request.log.error( + `Failed to get response from membership API: ${e.toString()}`, + ); + throw e; + } + const entraIdToken = await getEntraIdToken( + fastify, + fastify.environmentConfig.AadValidClientId, + ); + const userProfile = await getUserProfile( + entraIdToken, + request.query.email, + ); const item = await issueAppleWalletMembershipCard( fastify, request, - request.username, - request.tokenPayload.name, + request.query.email, + userProfile.displayName, + ); + const emailCommand = generateMembershipEmailCommand( + request.query.email, + `membership@${fastify.environmentConfig.EmailDomain}`, + item, ); - reply.type("application/vnd.apple.pkpass"); - reply.send(item); + await fastify.sesClient.send(emailCommand); + reply.status(202).send({ message: "OK" }); }, ); }; diff --git a/src/api/types.d.ts b/src/api/types.d.ts index be970ffa..a3b4af9e 100644 --- a/src/api/types.d.ts +++ b/src/api/types.d.ts @@ -5,6 +5,7 @@ import { ConfigType } from "../common/config.js"; import NodeCache from "node-cache"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; +import { SESClient } from "@aws-sdk/client-ses"; declare module "fastify" { interface FastifyInstance { authenticate: ( @@ -26,6 +27,7 @@ declare module "fastify" { nodeCache: NodeCache; dynamoClient: DynamoDBClient; secretsManagerClient: SecretsManagerClient; + sesClient: SESClient; secretsManagerData: Record | null; } interface FastifyRequest { diff --git a/src/common/config.ts b/src/common/config.ts index f5641d5c..3800c37f 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -14,6 +14,8 @@ export type ConfigType = { AadValidClientId: string; PasskitIdentifier: string; PasskitSerialNumber: string; + MembershipApiEndpoint: string; + EmailDomain: string; }; type GenericConfigType = { @@ -69,7 +71,9 @@ const environmentConfig: EnvironmentConfigType = { ], AadValidClientId: "39c28870-94e4-47ee-b4fb-affe0bf96c9f", PasskitIdentifier: "pass.org.acmuiuc.qa.membership", - PasskitSerialNumber: "0" + PasskitSerialNumber: "0", + MembershipApiEndpoint: "https://infra-membership-api.aws.qa.acmuiuc.org/api/v1/checkMembership", + EmailDomain: "aws.qa.acmuiuc.org", }, prod: { AzureRoleMapping: { AutonomousWriters: [AppRoles.EVENTS_MANAGER] }, @@ -81,7 +85,9 @@ const environmentConfig: EnvironmentConfigType = { ], AadValidClientId: "5e08cf0f-53bb-4e09-9df2-e9bdc3467296", PasskitIdentifier: "pass.edu.illinois.acm.membership", - PasskitSerialNumber: "0" + PasskitSerialNumber: "0", + MembershipApiEndpoint: "https://infra-membership-api.aws.acmuiuc.org/api/v1/checkMembership", + EmailDomain: "acm.illinois.edu", } }; diff --git a/src/common/errors/index.ts b/src/common/errors/index.ts index 632669f4..a68726a5 100644 --- a/src/common/errors/index.ts +++ b/src/common/errors/index.ts @@ -134,6 +134,20 @@ export class DiscordEventError extends BaseError<"DiscordEventError"> { } } + +export class EntraFetchError extends BaseError<"EntraFetchError"> { + email: string; + constructor({ message, email }: { message?: string; email: string }) { + super({ + name: "EntraFetchError", + id: 509, + message: message || "Could not get data from Entra ID.", + httpStatusCode: 500, + }); + this.email = email; + } +} + export class EntraInvitationError extends BaseError<"EntraInvitationError"> { email: string; constructor({ message, email }: { message?: string; email: string }) { diff --git a/yarn.lock b/yarn.lock index 203b4117..db04a3d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -153,6 +153,52 @@ tslib "^2.6.2" uuid "^9.0.1" +"@aws-sdk/client-ses@^3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-ses/-/client-ses-3.734.0.tgz#4546c9a0c11d63ba390b52d30b942fcc8bcb83db" + integrity sha512-UivyDyjEriXtBin5KMjqFVUZiTJkibLT6Td62GgABKuAItJh19kQU3EcJ6nyYCAqEmpAapwTZWJzFLf27QyauQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.734.0" + "@aws-sdk/credential-provider-node" "3.734.0" + "@aws-sdk/middleware-host-header" "3.734.0" + "@aws-sdk/middleware-logger" "3.734.0" + "@aws-sdk/middleware-recursion-detection" "3.734.0" + "@aws-sdk/middleware-user-agent" "3.734.0" + "@aws-sdk/region-config-resolver" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@aws-sdk/util-endpoints" "3.734.0" + "@aws-sdk/util-user-agent-browser" "3.734.0" + "@aws-sdk/util-user-agent-node" "3.734.0" + "@smithy/config-resolver" "^4.0.1" + "@smithy/core" "^3.1.1" + "@smithy/fetch-http-handler" "^5.0.1" + "@smithy/hash-node" "^4.0.1" + "@smithy/invalid-dependency" "^4.0.1" + "@smithy/middleware-content-length" "^4.0.1" + "@smithy/middleware-endpoint" "^4.0.2" + "@smithy/middleware-retry" "^4.0.3" + "@smithy/middleware-serde" "^4.0.1" + "@smithy/middleware-stack" "^4.0.1" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/node-http-handler" "^4.0.2" + "@smithy/protocol-http" "^5.0.1" + "@smithy/smithy-client" "^4.1.2" + "@smithy/types" "^4.1.0" + "@smithy/url-parser" "^4.0.1" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.3" + "@smithy/util-defaults-mode-node" "^4.0.3" + "@smithy/util-endpoints" "^3.0.1" + "@smithy/util-middleware" "^4.0.1" + "@smithy/util-retry" "^4.0.1" + "@smithy/util-utf8" "^4.0.0" + "@smithy/util-waiter" "^4.0.2" + tslib "^2.6.2" + "@aws-sdk/client-sso-oidc@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.721.0.tgz#a53b954e5b0112cd253d82b0f68264827e7d36ca" @@ -331,6 +377,50 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/client-sso@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.734.0.tgz#789c98267f07aaa7155b404d0bfd4059c4b4deb9" + integrity sha512-oerepp0mut9VlgTwnG5Ds/lb0C0b2/rQ+hL/rF6q+HGKPfGsCuPvFx1GtwGKCXd49ase88/jVgrhcA9OQbz3kg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.734.0" + "@aws-sdk/middleware-host-header" "3.734.0" + "@aws-sdk/middleware-logger" "3.734.0" + "@aws-sdk/middleware-recursion-detection" "3.734.0" + "@aws-sdk/middleware-user-agent" "3.734.0" + "@aws-sdk/region-config-resolver" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@aws-sdk/util-endpoints" "3.734.0" + "@aws-sdk/util-user-agent-browser" "3.734.0" + "@aws-sdk/util-user-agent-node" "3.734.0" + "@smithy/config-resolver" "^4.0.1" + "@smithy/core" "^3.1.1" + "@smithy/fetch-http-handler" "^5.0.1" + "@smithy/hash-node" "^4.0.1" + "@smithy/invalid-dependency" "^4.0.1" + "@smithy/middleware-content-length" "^4.0.1" + "@smithy/middleware-endpoint" "^4.0.2" + "@smithy/middleware-retry" "^4.0.3" + "@smithy/middleware-serde" "^4.0.1" + "@smithy/middleware-stack" "^4.0.1" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/node-http-handler" "^4.0.2" + "@smithy/protocol-http" "^5.0.1" + "@smithy/smithy-client" "^4.1.2" + "@smithy/types" "^4.1.0" + "@smithy/url-parser" "^4.0.1" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.3" + "@smithy/util-defaults-mode-node" "^4.0.3" + "@smithy/util-endpoints" "^3.0.1" + "@smithy/util-middleware" "^4.0.1" + "@smithy/util-retry" "^4.0.1" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/client-sts@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.721.0.tgz#701de8e0877aec3974291e19cd1361feda742680" @@ -457,6 +547,23 @@ fast-xml-parser "4.4.1" tslib "^2.6.2" +"@aws-sdk/core@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.734.0.tgz#fa2289750efd75f4fb8c45719a4a4ea7e7755160" + integrity sha512-SxnDqf3vobdm50OLyAKfqZetv6zzwnSqwIwd3jrbopxxHKqNIM/I0xcYjD6Tn+mPig+u7iRKb9q3QnEooFTlmg== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/core" "^3.1.1" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/property-provider" "^4.0.1" + "@smithy/protocol-http" "^5.0.1" + "@smithy/signature-v4" "^5.0.1" + "@smithy/smithy-client" "^4.1.2" + "@smithy/types" "^4.1.0" + "@smithy/util-middleware" "^4.0.1" + fast-xml-parser "4.4.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-env@3.716.0": version "3.716.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.716.0.tgz#10ab93c5806f5e1b29dde8dae38307c766b99197" @@ -479,6 +586,17 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-env@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.734.0.tgz#6c0b1734764a7fb1616455836b1c3dacd99e50a3" + integrity sha512-gtRkzYTGafnm1FPpiNO8VBmJrYMoxhDlGPYDVcijzx3DlF8dhWnowuSBCxLSi+MJMx5hvwrX2A+e/q0QAeHqmw== + dependencies: + "@aws-sdk/core" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/property-provider" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-http@3.716.0": version "3.716.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.716.0.tgz#6d02e3c8b67069a30f51cd3fa761a1e939940da4" @@ -511,6 +629,22 @@ "@smithy/util-stream" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-http@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.734.0.tgz#21c5fbb380d1dd503491897b346e1e0b1d06ae41" + integrity sha512-JFSL6xhONsq+hKM8xroIPhM5/FOhiQ1cov0lZxhzZWj6Ai3UAjucy3zyIFDr9MgP1KfCYNdvyaUq9/o+HWvEDg== + dependencies: + "@aws-sdk/core" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/fetch-http-handler" "^5.0.1" + "@smithy/node-http-handler" "^4.0.2" + "@smithy/property-provider" "^4.0.1" + "@smithy/protocol-http" "^5.0.1" + "@smithy/smithy-client" "^4.1.2" + "@smithy/types" "^4.1.0" + "@smithy/util-stream" "^4.0.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-ini@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.721.0.tgz#6b28d36fb3409099eb2f8e6222a6b8064516ab32" @@ -547,6 +681,25 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-ini@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.734.0.tgz#5769ae28cd255d4fc946799c0273b4af6f2f12bb" + integrity sha512-HEyaM/hWI7dNmb4NhdlcDLcgJvrilk8G4DQX6qz0i4pBZGC2l4iffuqP8K6ZQjUfz5/6894PzeFuhTORAMd+cg== + dependencies: + "@aws-sdk/core" "3.734.0" + "@aws-sdk/credential-provider-env" "3.734.0" + "@aws-sdk/credential-provider-http" "3.734.0" + "@aws-sdk/credential-provider-process" "3.734.0" + "@aws-sdk/credential-provider-sso" "3.734.0" + "@aws-sdk/credential-provider-web-identity" "3.734.0" + "@aws-sdk/nested-clients" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/credential-provider-imds" "^4.0.1" + "@smithy/property-provider" "^4.0.1" + "@smithy/shared-ini-file-loader" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-node@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.721.0.tgz#a52dc78efebfa566711e12b53e01a9e7216cba8a" @@ -583,6 +736,24 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-node@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.734.0.tgz#86d54171c11cab5b64bfa55ab0def5e807440ad2" + integrity sha512-9NOSNbkPVb91JwaXOhyfahkzAwWdMsbWHL6fh5/PHlXYpsDjfIfT23I++toepNF2nODAJNLnOEHGYIxgNgf6jQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.734.0" + "@aws-sdk/credential-provider-http" "3.734.0" + "@aws-sdk/credential-provider-ini" "3.734.0" + "@aws-sdk/credential-provider-process" "3.734.0" + "@aws-sdk/credential-provider-sso" "3.734.0" + "@aws-sdk/credential-provider-web-identity" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/credential-provider-imds" "^4.0.1" + "@smithy/property-provider" "^4.0.1" + "@smithy/shared-ini-file-loader" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-process@3.716.0": version "3.716.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.716.0.tgz#a8a7b9416cb28c0e2ef601a2713342533619ce4c" @@ -607,6 +778,18 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-process@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.734.0.tgz#eb1de678a9c3d2d7b382e74a670fa283327f9c45" + integrity sha512-zvjsUo+bkYn2vjT+EtLWu3eD6me+uun+Hws1IyWej/fKFAqiBPwyeyCgU7qjkiPQSXqk1U9+/HG9IQ6Iiz+eBw== + dependencies: + "@aws-sdk/core" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/property-provider" "^4.0.1" + "@smithy/shared-ini-file-loader" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-sso@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.721.0.tgz#14350ec1ccdb612af36f35e4383067ecfb99f8e6" @@ -635,6 +818,20 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-sso@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.734.0.tgz#68a9d678319e9743d65cf59e2d29c0c440d8975c" + integrity sha512-cCwwcgUBJOsV/ddyh1OGb4gKYWEaTeTsqaAK19hiNINfYV/DO9r4RMlnWAo84sSBfJuj9shUNsxzyoe6K7R92Q== + dependencies: + "@aws-sdk/client-sso" "3.734.0" + "@aws-sdk/core" "3.734.0" + "@aws-sdk/token-providers" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/property-provider" "^4.0.1" + "@smithy/shared-ini-file-loader" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-web-identity@3.716.0": version "3.716.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.716.0.tgz#dfde14b78a311c0d5ef974f42049c41bef604a83" @@ -657,6 +854,18 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-web-identity@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.734.0.tgz#666b61cc9f498a3aaecd8e38c9ae34aef37e2e64" + integrity sha512-t4OSOerc+ppK541/Iyn1AS40+2vT/qE+MFMotFkhCgCJbApeRF2ozEdnDN6tGmnl4ybcUuxnp9JWLjwDVlR/4g== + dependencies: + "@aws-sdk/core" "3.734.0" + "@aws-sdk/nested-clients" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/property-provider" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/endpoint-cache@3.693.0": version "3.693.0" resolved "https://registry.yarnpkg.com/@aws-sdk/endpoint-cache/-/endpoint-cache-3.693.0.tgz#4b3f0bbc16dc2907e1b977e3d8ddfc7ba008fd12" @@ -697,6 +906,16 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-host-header@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz#a9a02c055352f5c435cc925a4e1e79b7ba41b1b5" + integrity sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/protocol-http" "^5.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/middleware-logger@3.714.0": version "3.714.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.714.0.tgz#c059e1aabf28fdfc647db6a3dba625a9813787cd" @@ -715,6 +934,15 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-logger@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz#d31e141ae7a78667e372953a3b86905bc6124664" + integrity sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/middleware-recursion-detection@3.714.0": version "3.714.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.714.0.tgz#c2d20d335c035196ac1cd5cdf3f58c5f31b01bdb" @@ -735,6 +963,16 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-recursion-detection@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz#4fa1deb9887455afbb39130f7d9bc89ccee17168" + integrity sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/protocol-http" "^5.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/middleware-user-agent@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.721.0.tgz#2a5fbfb63d42a79b4f4b9d94e5aefa66b4e57ddd" @@ -761,6 +999,63 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-user-agent@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.734.0.tgz#12d400ccb98593f2b02e4fb08239cb9835d41d3a" + integrity sha512-MFVzLWRkfFz02GqGPjqSOteLe5kPfElUrXZft1eElnqulqs6RJfVSpOV7mO90gu293tNAeggMWAVSGRPKIYVMg== + dependencies: + "@aws-sdk/core" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@aws-sdk/util-endpoints" "3.734.0" + "@smithy/core" "^3.1.1" + "@smithy/protocol-http" "^5.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + +"@aws-sdk/nested-clients@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.734.0.tgz#10a116d141522341c446b11783551ef863aabd27" + integrity sha512-iph2XUy8UzIfdJFWo1r0Zng9uWj3253yvW9gljhtu+y/LNmNvSnJxQk1f3D2BC5WmcoPZqTS3UsycT3mLPSzWA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.734.0" + "@aws-sdk/middleware-host-header" "3.734.0" + "@aws-sdk/middleware-logger" "3.734.0" + "@aws-sdk/middleware-recursion-detection" "3.734.0" + "@aws-sdk/middleware-user-agent" "3.734.0" + "@aws-sdk/region-config-resolver" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@aws-sdk/util-endpoints" "3.734.0" + "@aws-sdk/util-user-agent-browser" "3.734.0" + "@aws-sdk/util-user-agent-node" "3.734.0" + "@smithy/config-resolver" "^4.0.1" + "@smithy/core" "^3.1.1" + "@smithy/fetch-http-handler" "^5.0.1" + "@smithy/hash-node" "^4.0.1" + "@smithy/invalid-dependency" "^4.0.1" + "@smithy/middleware-content-length" "^4.0.1" + "@smithy/middleware-endpoint" "^4.0.2" + "@smithy/middleware-retry" "^4.0.3" + "@smithy/middleware-serde" "^4.0.1" + "@smithy/middleware-stack" "^4.0.1" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/node-http-handler" "^4.0.2" + "@smithy/protocol-http" "^5.0.1" + "@smithy/smithy-client" "^4.1.2" + "@smithy/types" "^4.1.0" + "@smithy/url-parser" "^4.0.1" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.3" + "@smithy/util-defaults-mode-node" "^4.0.3" + "@smithy/util-endpoints" "^3.0.1" + "@smithy/util-middleware" "^4.0.1" + "@smithy/util-retry" "^4.0.1" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@aws-sdk/region-config-resolver@3.714.0": version "3.714.0" resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.714.0.tgz#26449aeb67daa00560c69bb80cb6cd187ee18dc9" @@ -785,6 +1080,18 @@ "@smithy/util-middleware" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/region-config-resolver@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz#45ffbc56a3e94cc5c9e0cd596b0fda60f100f70b" + integrity sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/types" "^4.1.0" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.0.1" + tslib "^2.6.2" + "@aws-sdk/token-providers@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.721.0.tgz#7956b8e88fd995b0fed3716a4d33f0e35f76a598" @@ -807,6 +1114,18 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/token-providers@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.734.0.tgz#8880e94f21457fe5dd7074ecc52fdd43180cbb2c" + integrity sha512-2U6yWKrjWjZO8Y5SHQxkFvMVWHQWbS0ufqfAIBROqmIZNubOL7jXCiVdEFekz6MZ9LF2tvYGnOW4jX8OKDGfIw== + dependencies: + "@aws-sdk/nested-clients" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/property-provider" "^4.0.1" + "@smithy/shared-ini-file-loader" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/types@3.714.0", "@aws-sdk/types@^3.222.0": version "3.714.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.714.0.tgz#de6afee1436d2d95364efa0663887f3bf0b1303a" @@ -823,6 +1142,14 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/types@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.734.0.tgz#af5e620b0e761918282aa1c8e53cac6091d169a2" + integrity sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg== + dependencies: + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@aws-sdk/util-dynamodb@^3.624.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-dynamodb/-/util-dynamodb-3.721.0.tgz#781723705f5a6c8dd8b3bd163acb5b0a78b7e33b" @@ -850,6 +1177,16 @@ "@smithy/util-endpoints" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/util-endpoints@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.734.0.tgz#43bac42a21a45477a386ccf398028e7f793bc217" + integrity sha512-w2+/E88NUbqql6uCVAsmMxDQKu7vsKV0KqhlQb0lL+RCq4zy07yXYptVNs13qrnuTfyX7uPXkXrlugvK9R1Ucg== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/types" "^4.1.0" + "@smithy/util-endpoints" "^3.0.1" + tslib "^2.6.2" + "@aws-sdk/util-locate-window@^3.0.0": version "3.693.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz#1160f6d055cf074ca198eb8ecf89b6311537ad6c" @@ -877,6 +1214,16 @@ bowser "^2.11.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-browser@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz#bbf3348b14bd7783f60346e1ce86978999450fe7" + integrity sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng== + dependencies: + "@aws-sdk/types" "3.734.0" + "@smithy/types" "^4.1.0" + bowser "^2.11.0" + tslib "^2.6.2" + "@aws-sdk/util-user-agent-node@3.721.0": version "3.721.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.721.0.tgz#d5336167c753d1bbb749044155cb54aebdf3cf32" @@ -899,6 +1246,17 @@ "@smithy/types" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-node@3.734.0": + version "3.734.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.734.0.tgz#d5c6ee192cea9d53a871178a2669b8b4dea39a68" + integrity sha512-c6Iinh+RVQKs6jYUFQ64htOU2HUXFQ3TVx+8Tu3EDF19+9vzWi9UukhIMH9rqyyEXIAkk9XL7avt8y2Uyw2dGA== + dependencies: + "@aws-sdk/middleware-user-agent" "3.734.0" + "@aws-sdk/types" "3.734.0" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@azure/msal-browser@^3.20.0": version "3.28.0" resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.28.0.tgz#faf955f1debe24ebf24cf8cbfb67246c658c3f11" @@ -2192,6 +2550,20 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/core@^3.1.1", "@smithy/core@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.1.2.tgz#f5b4c89bf054b717781d71c66b4fb594e06cbb62" + integrity sha512-htwQXkbdF13uwwDevz9BEzL5ABK+1sJpVQXywwGSH973AVOvisHNfpcB8A8761G6XgHoS2kHPqc9DqHJ2gp+/Q== + dependencies: + "@smithy/middleware-serde" "^4.0.2" + "@smithy/protocol-http" "^5.0.1" + "@smithy/types" "^4.1.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-middleware" "^4.0.1" + "@smithy/util-stream" "^4.0.2" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@smithy/credential-provider-imds@^3.2.8": version "3.2.8" resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz#27ed2747074c86a7d627a98e56f324a65cba88de" @@ -2246,7 +2618,7 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/hash-node@^4.0.0": +"@smithy/hash-node@^4.0.0", "@smithy/hash-node@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.0.1.tgz#ce78fc11b848a4f47c2e1e7a07fb6b982d2f130c" integrity sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w== @@ -2264,7 +2636,7 @@ "@smithy/types" "^3.7.2" tslib "^2.6.2" -"@smithy/invalid-dependency@^4.0.0": +"@smithy/invalid-dependency@^4.0.0", "@smithy/invalid-dependency@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz#704d1acb6fac105558c17d53f6d55da6b0d6b6fc" integrity sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ== @@ -2302,7 +2674,7 @@ "@smithy/types" "^3.7.2" tslib "^2.6.2" -"@smithy/middleware-content-length@^4.0.0": +"@smithy/middleware-content-length@^4.0.0", "@smithy/middleware-content-length@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz#378bc94ae623f45e412fb4f164b5bb90b9de2ba3" integrity sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ== @@ -2339,6 +2711,20 @@ "@smithy/util-middleware" "^4.0.1" tslib "^2.6.2" +"@smithy/middleware-endpoint@^4.0.2", "@smithy/middleware-endpoint@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.3.tgz#74b64fb2473ae35649a8d22d41708bc5d8d99df2" + integrity sha512-YdbmWhQF5kIxZjWqPIgboVfi8i5XgiYMM7GGKFMTvBei4XjNQfNv8sukT50ITvgnWKKKpOtp0C0h7qixLgb77Q== + dependencies: + "@smithy/core" "^3.1.2" + "@smithy/middleware-serde" "^4.0.2" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/shared-ini-file-loader" "^4.0.1" + "@smithy/types" "^4.1.0" + "@smithy/url-parser" "^4.0.1" + "@smithy/util-middleware" "^4.0.1" + tslib "^2.6.2" + "@smithy/middleware-retry@^3.0.31": version "3.0.34" resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz#136c89fc22d70819fdefc51b0d24952cf98883f1" @@ -2369,6 +2755,21 @@ tslib "^2.6.2" uuid "^9.0.1" +"@smithy/middleware-retry@^4.0.3": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.0.4.tgz#95e55a1b163ff06264f20b4dbbcbd915c8028f60" + integrity sha512-wmxyUBGHaYUqul0wZiset4M39SMtDBOtUr2KpDuftKNN74Do9Y36Go6Eqzj9tL0mIPpr31ulB5UUtxcsCeGXsQ== + dependencies: + "@smithy/node-config-provider" "^4.0.1" + "@smithy/protocol-http" "^5.0.1" + "@smithy/service-error-classification" "^4.0.1" + "@smithy/smithy-client" "^4.1.3" + "@smithy/types" "^4.1.0" + "@smithy/util-middleware" "^4.0.1" + "@smithy/util-retry" "^4.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + "@smithy/middleware-serde@^3.0.11": version "3.0.11" resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz#c7d54e0add4f83e05c6878a011fc664e21022f12" @@ -2385,6 +2786,14 @@ "@smithy/types" "^4.1.0" tslib "^2.6.2" +"@smithy/middleware-serde@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz#f792d72f6ad8fa6b172e3f19c6fe1932a856a56d" + integrity sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ== + dependencies: + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@smithy/middleware-stack@^3.0.11": version "3.0.11" resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz#453af2096924e4064d9da4e053cfdf65d9a36acc" @@ -2443,6 +2852,17 @@ "@smithy/types" "^4.1.0" tslib "^2.6.2" +"@smithy/node-http-handler@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz#48d47a046cf900ab86bfbe7f5fd078b52c82fab6" + integrity sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw== + dependencies: + "@smithy/abort-controller" "^4.0.1" + "@smithy/protocol-http" "^5.0.1" + "@smithy/querystring-builder" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@smithy/property-provider@^3.1.11": version "3.1.11" resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.11.tgz#161cf1c2a2ada361e417382c57f5ba6fbca8acad" @@ -2553,7 +2973,7 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/signature-v4@^5.0.0": +"@smithy/signature-v4@^5.0.0", "@smithy/signature-v4@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.0.1.tgz#f93401b176150286ba246681031b0503ec359270" integrity sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA== @@ -2593,6 +3013,19 @@ "@smithy/util-stream" "^4.0.1" tslib "^2.6.2" +"@smithy/smithy-client@^4.1.2", "@smithy/smithy-client@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.1.3.tgz#2c8f9aff3377e7655cebe84239da6be277ba8554" + integrity sha512-A2Hz85pu8BJJaYFdX8yb1yocqigyqBzn+OVaVgm+Kwi/DkN8vhN2kbDVEfADo6jXf5hPKquMLGA3UINA64UZ7A== + dependencies: + "@smithy/core" "^3.1.2" + "@smithy/middleware-endpoint" "^4.0.3" + "@smithy/middleware-stack" "^4.0.1" + "@smithy/protocol-http" "^5.0.1" + "@smithy/types" "^4.1.0" + "@smithy/util-stream" "^4.0.2" + tslib "^2.6.2" + "@smithy/types@^3.7.2": version "3.7.2" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.7.2.tgz#05cb14840ada6f966de1bf9a9c7dd86027343e10" @@ -2731,6 +3164,17 @@ bowser "^2.11.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-browser@^4.0.3": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.4.tgz#6fa7ba64a80a77f27b9b5c6972918904578b8d5b" + integrity sha512-Ej1bV5sbrIfH++KnWxjjzFNq9nyP3RIUq2c9Iqq7SmMO/idUR24sqvKH2LUQFTSPy/K7G4sB2m8n7YYlEAfZaw== + dependencies: + "@smithy/property-provider" "^4.0.1" + "@smithy/smithy-client" "^4.1.3" + "@smithy/types" "^4.1.0" + bowser "^2.11.0" + tslib "^2.6.2" + "@smithy/util-defaults-mode-node@^3.0.31": version "3.0.34" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz#5eb0d97231a34e137980abfb08ea5e3a8f2156f7" @@ -2757,6 +3201,19 @@ "@smithy/types" "^4.1.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-node@^4.0.3": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.4.tgz#5470fdc96672cee5199620b576d7025de3b17333" + integrity sha512-HE1I7gxa6yP7ZgXPCFfZSDmVmMtY7SHqzFF55gM/GPegzZKaQWZZ+nYn9C2Cc3JltCMyWe63VPR3tSFDEvuGjw== + dependencies: + "@smithy/config-resolver" "^4.0.1" + "@smithy/credential-provider-imds" "^4.0.1" + "@smithy/node-config-provider" "^4.0.1" + "@smithy/property-provider" "^4.0.1" + "@smithy/smithy-client" "^4.1.3" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@smithy/util-endpoints@^2.1.7": version "2.1.7" resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz#a088ebfab946a7219dd4763bfced82709894b82d" @@ -2766,7 +3223,7 @@ "@smithy/types" "^3.7.2" tslib "^2.6.2" -"@smithy/util-endpoints@^3.0.0": +"@smithy/util-endpoints@^3.0.0", "@smithy/util-endpoints@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz#44ccbf1721447966f69496c9003b87daa8f61975" integrity sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA== @@ -2851,6 +3308,20 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/util-stream@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.0.2.tgz#63495d3f7fba9d78748d540921136dc4a8d4c067" + integrity sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA== + dependencies: + "@smithy/fetch-http-handler" "^5.0.1" + "@smithy/node-http-handler" "^4.0.2" + "@smithy/types" "^4.1.0" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-buffer-from" "^4.0.0" + "@smithy/util-hex-encoding" "^4.0.0" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + "@smithy/util-uri-escape@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz#e43358a78bf45d50bb736770077f0f09195b6f54" @@ -2898,6 +3369,15 @@ "@smithy/types" "^3.7.2" tslib "^2.6.2" +"@smithy/util-waiter@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.0.2.tgz#0a73a0fcd30ea7bbc3009cf98ad199f51b8eac51" + integrity sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ== + dependencies: + "@smithy/abort-controller" "^4.0.1" + "@smithy/types" "^4.1.0" + tslib "^2.6.2" + "@storybook/addon-actions@8.4.7": version "8.4.7" resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.4.7.tgz#210c6bb5a7e17c3664c300b4b69b6243ec34b9cd" @@ -3284,6 +3764,13 @@ dependencies: "@babel/types" "^7.20.7" +"@types/base64-arraybuffer@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@types/base64-arraybuffer/-/base64-arraybuffer-0.2.4.tgz#24487cb51beb247d54ce7701d9adb01f0df4b250" + integrity sha512-37eO3t3V0GgVUlk6A9eh7Kc9zgKHS2v4DCJHzSffd6wzp90xhvbQ07lb6qjWh5M56x9AgZlKAeEfwKBEeWQwFA== + dependencies: + base64-arraybuffer "*" + "@types/body-parser@*": version "1.19.5" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" @@ -4117,6 +4604,11 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== +base64-arraybuffer@*, base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -7846,6 +8338,15 @@ postcss@^8.4.41, postcss@^8.4.49: picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.4.43: + version "8.5.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" + integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prebuild-install@^7.1.1: version "7.1.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" From 65ea763bef514543d99676ac4378bc92d418454f Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Tue, 28 Jan 2025 17:13:46 +0000 Subject: [PATCH 3/6] fix cfn --- cloudformation/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudformation/main.yml b/cloudformation/main.yml index 30a9a886..a1d66311 100644 --- a/cloudformation/main.yml +++ b/cloudformation/main.yml @@ -32,22 +32,22 @@ Mappings: General: dev: LogRetentionDays: 7 + SesDomain: "aws.qa.acmuiuc.org" prod: LogRetentionDays: 365 + SesDomain: "acm.illinois.edu" ApiGwConfig: dev: ApiCertificateArn: arn:aws:acm:us-east-1:427040638965:certificate/63ccdf0b-d2b5-44f0-b589-eceffb935c23 HostedZoneId: Z04502822NVIA85WM2SML ApiDomainName: "aws.qa.acmuiuc.org" LinkryApiDomainName: "aws.qa.acmuiuc.org" - SesDomain: "aws.qa.acmuiuc.org" LinkryApiCertificateArn: arn:aws:acm:us-east-1:427040638965:certificate/63ccdf0b-d2b5-44f0-b589-eceffb935c23 prod: ApiCertificateArn: arn:aws:acm:us-east-1:298118738376:certificate/6142a0e2-d62f-478e-bf15-5bdb616fe705 HostedZoneId: Z05246633460N5MEB9DBF ApiDomainName: "aws.acmuiuc.org" LinkryApiDomainName: "acm.illinois.edu" - SesDomain: "acm.illinois.edu" LinkryApiCertificateArn: arn:aws:acm:us-east-1:298118738376:certificate/aeb93d9e-b0b7-4272-9c12-24ca5058c77e EnvironmentToCidr: dev: From ad93bbe90f37325388d3101db00b47631b0d57f1 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Tue, 28 Jan 2025 20:39:47 +0000 Subject: [PATCH 4/6] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 88055351..b200bf1f 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,5 @@ This repository is split into multiple parts: You will need node>=22 installed, as well as the AWS CLI and the AWS SAM CLI. The best way to work with all of this is to open the environment in a container within your IDE (VS Code should prompt you to do so: use "Clone in Container" for best performance). This container will have all needed software installed. Then, run `make install` to install all packages, and `make local` to start the UI and API servers! The UI will be accessible on `http://localhost:5173/` and the API on `http://localhost:8080/`. + +See the [README for the API server](src/api/README.md) as well. From d24c864bbef871e240830f00c1e5ee393832681a Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Tue, 28 Jan 2025 21:56:41 +0000 Subject: [PATCH 5/6] write tests --- src/api/functions/entraId.ts | 2 +- src/api/functions/membership.ts | 18 +++ src/api/routes/mobileWallet.ts | 34 +++--- tests/unit/mobileWallet.test.ts | 102 ++++++++++++++++ tests/unit/secret.testdata.ts | 3 + yarn.lock | 207 ++------------------------------ 6 files changed, 147 insertions(+), 219 deletions(-) create mode 100644 src/api/functions/membership.ts create mode 100644 tests/unit/mobileWallet.test.ts diff --git a/src/api/functions/entraId.ts b/src/api/functions/entraId.ts index 785cc719..03863490 100644 --- a/src/api/functions/entraId.ts +++ b/src/api/functions/entraId.ts @@ -180,7 +180,7 @@ export async function resolveEmailToOid( }; if (!data.value || data.value.length === 0) { - throw new Error(`No user found with email: ${email}`); + throw new EntraFetchError({ message: "No user found with email", email }); } return data.value[0].id; diff --git a/src/api/functions/membership.ts b/src/api/functions/membership.ts new file mode 100644 index 00000000..1f309b3f --- /dev/null +++ b/src/api/functions/membership.ts @@ -0,0 +1,18 @@ +import { FastifyBaseLogger, FastifyInstance } from "fastify"; + +export async function checkPaidMembership( + endpoint: string, + log: FastifyBaseLogger, + netId: string, +) { + const membershipApiPayload = (await ( + await fetch(`${endpoint}?netId=${netId}`) + ).json()) as { netId: string; isPaidMember: boolean }; + log.trace(`Got Membership API Payload for ${netId}: ${membershipApiPayload}`); + try { + return membershipApiPayload["isPaidMember"]; + } catch (e: any) { + log.error(`Failed to get response from membership API: ${e.toString()}`); + throw e; + } +} diff --git a/src/api/routes/mobileWallet.ts b/src/api/routes/mobileWallet.ts index ead63984..4c797957 100644 --- a/src/api/routes/mobileWallet.ts +++ b/src/api/routes/mobileWallet.ts @@ -1,13 +1,15 @@ import { FastifyPluginAsync } from "fastify"; import { issueAppleWalletMembershipCard } from "../functions/mobileWallet.js"; import { + EntraFetchError, UnauthenticatedError, UnauthorizedError, ValidationError, } from "../../common/errors/index.js"; -import { generateMembershipEmailCommand } from "api/functions/ses.js"; +import { generateMembershipEmailCommand } from "../functions/ses.js"; import { z } from "zod"; -import { getEntraIdToken, getUserProfile } from "api/functions/entraId.js"; +import { getEntraIdToken, getUserProfile } from "../functions/entraId.js"; +import { checkPaidMembership } from "../functions/membership.js"; const mobileWalletRoute: FastifyPluginAsync = async (fastify, _options) => { fastify.get<{ Querystring: { email: string } }>( @@ -41,32 +43,26 @@ const mobileWalletRoute: FastifyPluginAsync = async (fastify, _options) => { message: "Email query parameter is not a valid email", }); } - - const membershipApiPayload = (await ( - await fetch( - `${fastify.environmentConfig.MembershipApiEndpoint}?netId=${request.query.email.replace("@illinois.edu", "")}`, - ) - ).json()) as { netId: string; isPaidMember: boolean }; - try { - if (!membershipApiPayload["isPaidMember"]) { - throw new UnauthorizedError({ - message: "User is not a paid member.", - }); - } - } catch (e: any) { - request.log.error( - `Failed to get response from membership API: ${e.toString()}`, - ); - throw e; + const isPaidMember = await checkPaidMembership( + fastify.environmentConfig.MembershipApiEndpoint, + request.log, + request.query.email.replace("@illinois.edu", ""), + ); + if (!isPaidMember) { + throw new UnauthenticatedError({ + message: `${request.query.email} is not a paid member.`, + }); } const entraIdToken = await getEntraIdToken( fastify, fastify.environmentConfig.AadValidClientId, ); + const userProfile = await getUserProfile( entraIdToken, request.query.email, ); + const item = await issueAppleWalletMembershipCard( fastify, request, diff --git a/tests/unit/mobileWallet.test.ts b/tests/unit/mobileWallet.test.ts new file mode 100644 index 00000000..9075b0c5 --- /dev/null +++ b/tests/unit/mobileWallet.test.ts @@ -0,0 +1,102 @@ +import { afterAll, expect, test, beforeEach, vi } from "vitest"; +import { SendRawEmailCommand, SESClient } from "@aws-sdk/client-ses"; +import { mockClient } from "aws-sdk-client-mock"; +import { secretObject } from "./secret.testdata.js"; +import init from "../../src/api/index.js"; +import { describe } from "node:test"; +import { EntraFetchError } from "../../src/common/errors/index.js"; + +const sesMock = mockClient(SESClient); +const jwt_secret = secretObject["jwt_key"]; +vi.stubEnv("JwtSigningKey", jwt_secret); + +vi.mock("fs", () => { + return { + ...vi.importActual("fs"), + promises: { + readFile: vi.fn(() => { + return ""; + }), + }, + }; +}); + +vi.mock("../../src/api/functions/membership.js", () => { + return { + ...vi.importActual("../../src/api/functions/membership.js"), + checkPaidMembership: vi.fn( + (_endpoint: string, _log: any, netId: string) => { + if (netId === "valid") { + return true; + } + return false; + }, + ), + }; +}); + +vi.mock("../../src/api/functions/entraId.js", () => { + return { + ...vi.importActual("../../src/api/functions/entraId.js"), + getEntraIdToken: vi.fn().mockImplementation(async () => { + return "atokenofalltime"; + }), + getUserProfile: vi + .fn() + .mockImplementation(async (_token: string, email: string) => { + if (email === "valid@illinois.edu") { + return { displayName: "John Doe" }; + } + throw new EntraFetchError({ + message: "User not found", + email, + }); + }), + resolveEmailToOid: vi.fn().mockImplementation(async () => { + return "12345"; + }), + }; +}); + +const app = await init(); +describe("Mobile wallet pass issuance", async () => { + test("Test that passes will not be issued for non-emails", async () => { + const response = await app.inject({ + method: "GET", + url: "/api/v1/mobileWallet/membership?email=notanemail", + }); + expect(response.statusCode).toBe(400); + await response.json(); + }); + test("Test that passes will not be issued for non-members", async () => { + const response = await app.inject({ + method: "GET", + url: "/api/v1/mobileWallet/membership?email=notamember@illinois.edu", + }); + expect(response.statusCode).toBe(403); + await response.json(); + }); + test("Test that passes will be issued for members", async () => { + sesMock.on(SendRawEmailCommand).resolves({}); + const response = await app.inject({ + method: "GET", + url: "/api/v1/mobileWallet/membership?email=valid@illinois.edu", + }); + expect(response.statusCode).toBe(202); + }); + test("Test that SES errors result in a server error", async () => { + sesMock.on(SendRawEmailCommand).rejects({}); + const response = await app.inject({ + method: "GET", + url: "/api/v1/mobileWallet/membership?email=valid@illinois.edu", + }); + expect(response.statusCode).toBe(500); + }); + afterAll(async () => { + await app.close(); + }); + beforeEach(() => { + (app as any).nodeCache.flushAll(); + vi.clearAllMocks(); + }); +}); diff --git a/tests/unit/secret.testdata.ts b/tests/unit/secret.testdata.ts index 33efb5c8..978bc25f 100644 --- a/tests/unit/secret.testdata.ts +++ b/tests/unit/secret.testdata.ts @@ -6,6 +6,9 @@ const secretObject = { discord_bot_token: "12345", entra_id_private_key: "", entra_id_thumbprint: "", + acm_passkit_signerCert_base64: "", + acm_passkit_signerKey_base64: "", + apple_signing_cert_base64: "", } as SecretConfig & { jwt_key: string }; const secretJson = JSON.stringify(secretObject); diff --git a/yarn.lock b/yarn.lock index db04a3d2..92761f8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4609,11 +4609,6 @@ base64-arraybuffer@*, base64-arraybuffer@^1.0.2: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -4633,15 +4628,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - bowser@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -4703,14 +4689,6 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -4767,14 +4745,6 @@ caniuse-lite@^1.0.30001688: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz#f2d15e3aaf8e18f76b2b8c1481abde063b8104c8" integrity sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w== -canvas@^3.0.0-rc2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/canvas/-/canvas-3.1.0.tgz#6cdf094b859fef8e39b0e2c386728a376f1727b2" - integrity sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg== - dependencies: - node-addon-api "^7.0.0" - prebuild-install "^7.1.1" - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -4858,11 +4828,6 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - cli-boxes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" @@ -5187,13 +5152,6 @@ decimal.js@^10.4.3: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" @@ -5266,11 +5224,6 @@ dequal@^2.0.2, dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -detect-libc@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" - integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== - detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -5425,13 +5378,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - enhanced-resolve@^5.15.0: version "5.18.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz#91eb1db193896b9801251eeff1c6980278b1e404" @@ -6091,11 +6037,6 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -expand-template@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" - integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - expect-type@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" @@ -6398,11 +6339,6 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -6512,11 +6448,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== - glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -6774,11 +6705,6 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -6825,7 +6751,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@2, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -7687,11 +7613,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -7711,16 +7632,11 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -7779,11 +7695,6 @@ nanoid@^3.3.7, nanoid@^3.3.8: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== -napi-build-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" - integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -7817,18 +7728,6 @@ nmtree@^1.0.6: dependencies: commander "^2.11.0" -node-abi@^3.3.0: - version "3.73.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.73.0.tgz#4459ea77e71969edba8588387eecb05e2c2cff3b" - integrity sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg== - dependencies: - semver "^7.3.5" - -node-addon-api@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" - integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== - node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" @@ -7987,7 +7886,7 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -8141,11 +8040,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path2d@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/path2d/-/path2d-0.2.2.tgz#cc85d61ed7827e7863a2ee36713d4b5315a3d85d" - integrity sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ== - pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -8156,15 +8050,7 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -pdfjs-dist@4.8.69: - version "4.8.69" - resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.8.69.tgz#61ea5d66863d49b40e5eacbd4070341175bdda2e" - integrity sha512-IHZsA4T7YElCKNNXtiLgqScw4zPd3pG9do8UrznC757gMd7UPeHSL2qwNNMJo4r79fl8oj1Xx+1nh2YkzdMpLQ== - optionalDependencies: - canvas "^3.0.0-rc2" - path2d "^0.2.1" - -pdfjs-dist@^4.5.136, pdfjs-dist@^4.6.82: +pdfjs-dist@4.8.69, pdfjs-dist@^4.5.136, pdfjs-dist@^4.6.82, pdfjs-dist@^4.8.69: version "4.10.38" resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz#3ee698003790dc266cc8b55c0e662ccb9ae18f53" integrity sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ== @@ -8347,24 +8233,6 @@ postcss@^8.4.43: picocolors "^1.1.1" source-map-js "^1.2.1" -prebuild-install@^7.1.1: - version "7.1.3" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" - integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug== - dependencies: - detect-libc "^2.0.0" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^2.0.0" - node-abi "^3.3.0" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^4.0.0" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8445,14 +8313,6 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== -pump@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" - integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -8495,7 +8355,7 @@ range-parser@1.2.0: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: +rc@^1.0.1, rc@^1.1.6: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -8653,15 +8513,6 @@ react-transition-group@4.4.5: dependencies: loose-envify "^1.1.0" -readable-stream@^3.1.1, readable-stream@^3.4.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -8944,7 +8795,7 @@ safe-buffer@5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.2: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -9012,7 +8863,7 @@ semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3: +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -9167,20 +9018,6 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" - integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== - dependencies: - decompress-response "^6.0.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-update-notifier@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" @@ -9418,13 +9255,6 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -9698,27 +9528,6 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar-fs@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.2.tgz#425f154f3404cb16cb8ff6e671d45ab2ed9596c5" - integrity sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -10125,7 +9934,7 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -util-deprecate@^1.0.1, util-deprecate@^1.0.2: +util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== From ffd49ac9107b6313506ac471610ce9d7e9944d53 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Tue, 28 Jan 2025 22:07:08 +0000 Subject: [PATCH 6/6] super basic live test --- tests/live/mobileWallet.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/live/mobileWallet.test.ts diff --git a/tests/live/mobileWallet.test.ts b/tests/live/mobileWallet.test.ts new file mode 100644 index 00000000..51400a04 --- /dev/null +++ b/tests/live/mobileWallet.test.ts @@ -0,0 +1,12 @@ +import { expect, test, describe } from "vitest"; + +const baseEndpoint = `https://infra-core-api.aws.qa.acmuiuc.org`; + +describe("Mobile pass issuance", async () => { + test("Test that passes will not be issued for non-members", async () => { + const response = await fetch( + `${baseEndpoint}/api/v1/mobileWallet/membership?email=notamemberatall@illinois.edu`, + ); + expect(response.status).toBe(403); + }); +});