Skip to content

Commit f924be4

Browse files
committed
Merge branch 'main' into eeen17/lead-screen
* kinda messed up rebase, so merging to clean it all up, won't touch rebase after pushing to remote again lol
2 parents 7918366 + 2781411 commit f924be4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2437
-2294
lines changed

.github/workflows/deploy-dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ jobs:
9292
run: make dev_health_check
9393
- name: Run live testing
9494
run: make test_live_integration
95+
env:
96+
JWT_KEY: ${{ secrets.JWT_KEY }}
9597
- name: Run E2E testing
9698
run: make test_e2e
9799
env:

.github/workflows/deploy-prod.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ jobs:
9090
python-version: 3.11
9191
- name: Run live testing
9292
run: make test_live_integration
93+
env:
94+
JWT_KEY: ${{ secrets.JWT_KEY }}
9395
- name: Run E2E testing
9496
run: make test_e2e
9597
env:

cloudformation/iam.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Resources:
106106
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles/*
107107
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles
108108
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles/*
109+
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links
110+
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/*
109111

110112
PolicyName: lambda-dynamo
111113
Outputs:

cloudformation/main.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,34 @@ Resources:
262262
Projection:
263263
ProjectionType: ALL
264264

265+
StripeLinksTable:
266+
Type: 'AWS::DynamoDB::Table'
267+
DeletionPolicy: "Retain"
268+
UpdateReplacePolicy: "Retain"
269+
Properties:
270+
BillingMode: 'PAY_PER_REQUEST'
271+
TableName: infra-core-api-stripe-links
272+
DeletionProtectionEnabled: true
273+
PointInTimeRecoverySpecification:
274+
PointInTimeRecoveryEnabled: false
275+
AttributeDefinitions:
276+
- AttributeName: userId
277+
AttributeType: S
278+
- AttributeName: linkId
279+
AttributeType: S
280+
KeySchema:
281+
- AttributeName: userId
282+
KeyType: "HASH"
283+
- AttributeName: linkId
284+
KeyType: "RANGE"
285+
GlobalSecondaryIndexes:
286+
- IndexName: LinkIdIndex
287+
KeySchema:
288+
- AttributeName: linkId
289+
KeyType: "HASH"
290+
Projection:
291+
ProjectionType: "ALL"
292+
265293
CacheRecordsTable:
266294
Type: 'AWS::DynamoDB::Table'
267295
DeletionPolicy: "Retain"

generate_jwt.js

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
1-
import jwt from 'jsonwebtoken';
1+
import jwt from "jsonwebtoken";
22
import * as dotenv from "dotenv";
33
dotenv.config();
44

5-
const username = process.env.JWTGEN_USERNAME || '[email protected]'
5+
const username = process.env.JWTGEN_USERNAME || "[email protected]"
66
const payload = {
7-
aud: "custom_jwt",
8-
iss: "custom_jwt",
9-
iat: Math.floor(Date.now() / 1000),
10-
nbf: Math.floor(Date.now() / 1000),
11-
exp: Math.floor(Date.now() / 1000) + (3600 * 24), // Token expires after 24 hour
12-
acr: "1",
13-
aio: "AXQAi/8TAAAA",
14-
amr: ["pwd"],
15-
appid: "your-app-id",
16-
appidacr: "1",
17-
email: username,
18-
groups: ["0"],
19-
idp: "https://login.microsoftonline.com",
20-
ipaddr: "192.168.1.1",
21-
name: "Doe, John",
22-
oid: "00000000-0000-0000-0000-000000000000",
23-
rh: "rh-value",
24-
scp: "user_impersonation",
25-
sub: "subject",
26-
tid: "tenant-id",
27-
unique_name: username,
28-
uti: "uti-value",
29-
ver: "1.0"
7+
aud: "custom_jwt",
8+
iss: "custom_jwt",
9+
iat: Math.floor(Date.now() / 1000),
10+
nbf: Math.floor(Date.now() / 1000),
11+
exp: Math.floor(Date.now() / 1000) + 3600 * 24, // Token expires after 24 hour
12+
acr: "1",
13+
aio: "AXQAi/8TAAAA",
14+
amr: ["pwd"],
15+
appid: "your-app-id",
16+
appidacr: "1",
17+
email: username,
18+
groups: ["0"],
19+
idp: "https://login.microsoftonline.com",
20+
ipaddr: "192.168.1.1",
21+
name: "Doe, John",
22+
oid: "00000000-0000-0000-0000-000000000000",
23+
rh: "rh-value",
24+
scp: "user_impersonation",
25+
sub: "subject",
26+
tid: "tenant-id",
27+
unique_name: username,
28+
uti: "uti-value",
29+
ver: "1.0",
3030
};
3131

3232
const secretKey = process.env.JwtSigningKey;
33-
const token = jwt.sign(payload, secretKey, { algorithm: 'HS256' });
34-
console.log(`USERNAME=${username}`)
35-
console.log('=====================')
36-
console.log(token)
33+
const token = jwt.sign(payload, secretKey, { algorithm: "HS256" });
34+
console.log(`USERNAME=${username}`);
35+
console.log("=====================");
36+
console.log(token);

src/api/functions/authorization.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
DynamoDBClient,
3-
GetItemCommand,
4-
QueryCommand,
5-
} from "@aws-sdk/client-dynamodb";
1+
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
62
import { unmarshall } from "@aws-sdk/util-dynamodb";
73
import { genericConfig } from "../../common/config.js";
84
import { DatabaseFetchError } from "../../common/errors/index.js";

src/api/functions/entraId.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import {
66
officersGroupTestingId,
77
} from "../../common/config.js";
88
import {
9-
BaseError,
109
EntraFetchError,
1110
EntraGroupError,
1211
EntraInvitationError,
12+
EntraPatchError,
1313
InternalServerError,
1414
} from "../../common/errors/index.js";
1515
import { getSecretValue } from "../plugins/auth.js";
@@ -18,8 +18,8 @@ import { getItemFromCache, insertItemIntoCache } from "./cache.js";
1818
import {
1919
EntraGroupActions,
2020
EntraInvitationResponse,
21+
ProfilePatchRequest,
2122
} from "../../common/types/iam.js";
22-
import { FastifyInstance } from "fastify";
2323
import { UserProfileDataBase } from "common/types/msGraphApi.js";
2424
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
2525
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
@@ -397,3 +397,49 @@ export async function getUserProfile(
397397
});
398398
}
399399
}
400+
401+
/**
402+
* Patches the profile of a user from Entra ID.
403+
* @param token - Entra ID token authorized to perform this action.
404+
* @param userId - The user ID to patch the profile for.
405+
* @throws {EntraUserError} If setting the user profile fails.
406+
* @returns {Promise<void>} nothing
407+
*/
408+
export async function patchUserProfile(
409+
token: string,
410+
email: string,
411+
userId: string,
412+
data: ProfilePatchRequest,
413+
): Promise<void> {
414+
try {
415+
const url = `https://graph.microsoft.com/v1.0/users/${userId}`;
416+
const response = await fetch(url, {
417+
method: "PATCH",
418+
headers: {
419+
Authorization: `Bearer ${token}`,
420+
"Content-Type": "application/json",
421+
},
422+
body: JSON.stringify(data),
423+
});
424+
425+
if (!response.ok) {
426+
const errorData = (await response.json()) as {
427+
error?: { message?: string };
428+
};
429+
throw new EntraPatchError({
430+
message: errorData?.error?.message ?? response.statusText,
431+
email,
432+
});
433+
}
434+
return;
435+
} catch (error) {
436+
if (error instanceof EntraPatchError) {
437+
throw error;
438+
}
439+
440+
throw new EntraPatchError({
441+
message: error instanceof Error ? error.message : String(error),
442+
email,
443+
});
444+
}
445+
}

src/api/functions/membership.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FastifyBaseLogger, FastifyInstance } from "fastify";
1+
import { FastifyBaseLogger } from "fastify";
22

33
export async function checkPaidMembership(
44
endpoint: string,
@@ -11,7 +11,13 @@ export async function checkPaidMembership(
1111
log.trace(`Got Membership API Payload for ${netId}: ${membershipApiPayload}`);
1212
try {
1313
return membershipApiPayload["isPaidMember"];
14-
} catch (e: any) {
14+
} catch (e: unknown) {
15+
if (!(e instanceof Error)) {
16+
log.error(
17+
"Failed to get response from membership API (unknown error type.)",
18+
);
19+
throw e;
20+
}
1521
log.error(`Failed to get response from membership API: ${e.toString()}`);
1622
throw e;
1723
}

src/api/functions/mobileWallet.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ import { getSecretValue } from "../plugins/auth.js";
22
import {
33
ConfigType,
44
genericConfig,
5-
GenericConfigType,
65
SecretConfig,
76
} from "../../common/config.js";
87
import {
98
InternalServerError,
109
UnauthorizedError,
1110
} from "../../common/errors/index.js";
12-
import { FastifyInstance, FastifyRequest } from "fastify";
13-
// these make sure that esbuild includes the files
1411
import icon from "../resources/MembershipPass.pkpass/icon.png";
1512
import logo from "../resources/MembershipPass.pkpass/logo.png";
1613
import strip from "../resources/MembershipPass.pkpass/strip.png";

src/api/functions/stripe.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import Stripe from "stripe";
2+
3+
export type StripeLinkCreateParams = {
4+
invoiceId: string;
5+
invoiceAmountUsd: number;
6+
contactName: string;
7+
contactEmail: string;
8+
createdBy: string;
9+
stripeApiKey: string;
10+
};
11+
12+
/**
13+
* Create a Stripe payment link for an invoice. Note that invoiceAmountUsd MUST IN CENTS!!
14+
* @param {StripeLinkCreateParams} options
15+
* @returns {string} A stripe link that can be used to pay the invoice
16+
*/
17+
export const createStripeLink = async ({
18+
invoiceId,
19+
invoiceAmountUsd,
20+
contactName,
21+
contactEmail,
22+
createdBy,
23+
stripeApiKey,
24+
}: StripeLinkCreateParams): Promise<{
25+
linkId: string;
26+
priceId: string;
27+
productId: string;
28+
url: string;
29+
}> => {
30+
const stripe = new Stripe(stripeApiKey);
31+
const description = `Created for ${contactName} (${contactEmail}) by ${createdBy}.`;
32+
const product = await stripe.products.create({
33+
name: `Payment for Invoice: ${invoiceId}`,
34+
description,
35+
});
36+
const price = await stripe.prices.create({
37+
currency: "usd",
38+
unit_amount: invoiceAmountUsd,
39+
product: product.id,
40+
});
41+
const paymentLink = await stripe.paymentLinks.create({
42+
line_items: [
43+
{
44+
price: price.id,
45+
quantity: 1,
46+
},
47+
],
48+
});
49+
return {
50+
url: paymentLink.url,
51+
linkId: paymentLink.id,
52+
productId: product.id,
53+
priceId: price.id,
54+
};
55+
};

0 commit comments

Comments
 (0)