@openstax/ts-utils 1.47.0 → 1.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/misc/hashValue.js +2 -2
- package/dist/cjs/misc/jwks.d.ts +5 -2
- package/dist/cjs/services/apiGateway/index.js +1 -2
- package/dist/cjs/services/authProvider/index.js +3 -6
- package/dist/cjs/services/authProvider/utils/userSubrequest.js +2 -5
- package/dist/cjs/services/fileServer/localFileServer.js +2 -3
- package/dist/cjs/services/fileServer/s3FileServer.js +2 -2
- package/dist/cjs/services/httpMessage/verifier.d.ts +2 -2
- package/dist/cjs/services/keyStore/index.d.ts +3 -6
- package/dist/cjs/services/keyStore/index.js +20 -9
- package/dist/cjs/services/launchParams/verifier.d.ts +2 -2
- package/dist/cjs/services/lrsGateway/addStatementDefaultFields.js +1 -2
- package/dist/cjs/services/lrsGateway/file-system.js +2 -2
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/misc/hashValue.js +2 -2
- package/dist/esm/misc/jwks.d.ts +5 -2
- package/dist/esm/services/apiGateway/index.js +1 -2
- package/dist/esm/services/authProvider/index.js +3 -3
- package/dist/esm/services/authProvider/utils/userSubrequest.js +2 -2
- package/dist/esm/services/fileServer/localFileServer.js +2 -3
- package/dist/esm/services/fileServer/s3FileServer.js +2 -2
- package/dist/esm/services/httpMessage/verifier.d.ts +2 -2
- package/dist/esm/services/keyStore/index.d.ts +3 -6
- package/dist/esm/services/keyStore/index.js +20 -9
- package/dist/esm/services/launchParams/verifier.d.ts +2 -2
- package/dist/esm/services/lrsGateway/addStatementDefaultFields.js +1 -2
- package/dist/esm/services/lrsGateway/file-system.js +2 -2
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +11 -15
- package/script/bin/delete-stack.bash +2 -2
- package/script/bin/deploy.bash +14 -3
- package/script/bin/destroy-deployment.bash +4 -4
- package/script/bin/get-deployed-environments.bash +1 -1
- package/script/bin/get-env-param.bash +4 -4
- package/script/bin/init-constants-script.bash +1 -1
- package/script/bin/init-params-script.bash +1 -1
- package/script/bin/update-utils.bash +3 -3
- package/script/bin/upload-pager-duty-endpoints.bash +3 -3
- package/script/bin/upload-params.bash +5 -5
- package/script/bin-entry.bash +1 -1
- package/script/build.bash +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sha1 } from 'js-sha1';
|
|
2
2
|
/**
|
|
3
3
|
* creates a string hash of lots of different kinds of things.
|
|
4
4
|
*
|
|
@@ -10,5 +10,5 @@ export const hashValue = (value) => {
|
|
|
10
10
|
const allKeys = new Set();
|
|
11
11
|
JSON.stringify(value, (k, v) => (allKeys.add(k), v));
|
|
12
12
|
const strValue = (_a = JSON.stringify(value, Array.from(allKeys).sort())) !== null && _a !== void 0 ? _a : 'undefined';
|
|
13
|
-
return
|
|
13
|
+
return sha1(strValue);
|
|
14
14
|
};
|
package/dist/esm/misc/jwks.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { webcrypto } from 'crypto';
|
|
1
2
|
import { JwksClient } from 'jwks-rsa';
|
|
2
|
-
|
|
3
|
+
export type PublicJsonWebKey = webcrypto.JsonWebKey & {
|
|
4
|
+
kid: string;
|
|
5
|
+
};
|
|
3
6
|
export type JwksFetcher = (uri: string) => Promise<{
|
|
4
|
-
keys:
|
|
7
|
+
keys: PublicJsonWebKey[];
|
|
5
8
|
}>;
|
|
6
9
|
export declare const getJwksClient: (jwksUri: string, fetcher?: JwksFetcher) => JwksClient;
|
|
7
10
|
export declare const getJwksKey: (iss: string, kid?: string, fetcher?: JwksFetcher) => Promise<import("jwks-rsa").SigningKey>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as pathToRegexp from 'path-to-regexp';
|
|
2
2
|
import queryString from 'query-string';
|
|
3
|
-
import { v4 as uuid } from 'uuid';
|
|
4
3
|
import { resolveConfigValue } from '../../config/index.js';
|
|
5
4
|
import { SessionExpiredError, UnauthorizedError } from '../../errors/index.js';
|
|
6
5
|
import { fetchStatusRetry } from '../../fetch/fetchStatusRetry.js';
|
|
@@ -32,7 +31,7 @@ const makeRouteClient = (initializer, config, route, app) => {
|
|
|
32
31
|
const url = await renderUrl({ params, query });
|
|
33
32
|
const body = payload ? JSON.stringify(payload) : undefined;
|
|
34
33
|
const baseOptions = merge((await ((_a = app === null || app === void 0 ? void 0 : app.authProvider) === null || _a === void 0 ? void 0 : _a.getAuthorizedFetchConfig())) || {}, fetchConfig || {});
|
|
35
|
-
const requestId =
|
|
34
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
36
35
|
const requestLogger = (_b = app === null || app === void 0 ? void 0 : app.logger) === null || _b === void 0 ? void 0 : _b.createSubContext();
|
|
37
36
|
requestLogger === null || requestLogger === void 0 ? void 0 : requestLogger.setContext({ requestId, url, timeStamp: new Date().getTime() });
|
|
38
37
|
const fetcher = fetchStatusRetry(fetch, { retries: 1, status: [502], logger: requestLogger });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { parse as parseCookie, serialize as serializeCookie } from 'cookie';
|
|
2
2
|
import { tuple } from '../../misc/helpers.js';
|
|
3
3
|
import { getHeader } from '../../routing/helpers.js';
|
|
4
4
|
export const stubAuthProvider = (user) => {
|
|
@@ -15,12 +15,12 @@ export const getAuthTokenOrCookie = (request, cookieName, queryKey = 'auth') =>
|
|
|
15
15
|
var _a, _b;
|
|
16
16
|
const authParam = request.queryStringParameters ? request.queryStringParameters[queryKey] : undefined;
|
|
17
17
|
const authHeader = getHeader(request.headers, 'authorization');
|
|
18
|
-
const cookieValue =
|
|
18
|
+
const cookieValue = parseCookie((_b = (_a = request.cookies) === null || _a === void 0 ? void 0 : _a.join('; ')) !== null && _b !== void 0 ? _b : '')[cookieName];
|
|
19
19
|
return typeof authParam === 'string'
|
|
20
20
|
? tuple(authParam, { Authorization: `Bearer ${authParam}` })
|
|
21
21
|
: authHeader && authHeader.length >= 8 && authHeader.startsWith('Bearer ')
|
|
22
22
|
? tuple(authHeader.slice(7), { Authorization: authHeader })
|
|
23
23
|
: cookieValue
|
|
24
|
-
? tuple(cookieValue, { cookie:
|
|
24
|
+
? tuple(cookieValue, { cookie: serializeCookie(cookieName, cookieValue) })
|
|
25
25
|
: tuple(null, {});
|
|
26
26
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { serialize as serializeCookie } from 'cookie';
|
|
2
2
|
export const loadUserData = (fetch, accountsBase, cookieName, token) => {
|
|
3
|
-
const headers = { cookie:
|
|
3
|
+
const headers = { cookie: serializeCookie(cookieName, token) };
|
|
4
4
|
// this returns `{"error_id":null}` when the token is invalid
|
|
5
5
|
return fetch(accountsBase.replace(/\/+$/, '') + '/api/user', { headers })
|
|
6
6
|
.then(response => response.json())
|
|
@@ -5,7 +5,6 @@ import https from 'https';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { pipeline } from 'stream/promises';
|
|
7
7
|
import Busboy from 'busboy';
|
|
8
|
-
import { v4 as uuid } from 'uuid';
|
|
9
8
|
import { assertString } from '../../assertions/index.js';
|
|
10
9
|
import { resolveConfigValue } from '../../config/index.js';
|
|
11
10
|
import { ifDefined } from '../../guards/index.js';
|
|
@@ -39,7 +38,7 @@ const handleUpload = (req, res, uploadDir) => {
|
|
|
39
38
|
});
|
|
40
39
|
busboy.on('file', (_name, file, info) => {
|
|
41
40
|
originalName = info.filename;
|
|
42
|
-
tempPath = path.join(uploadDir,
|
|
41
|
+
tempPath = path.join(uploadDir, crypto.randomUUID());
|
|
43
42
|
file.pipe(fs.createWriteStream(tempPath));
|
|
44
43
|
});
|
|
45
44
|
busboy.on('finish', async () => {
|
|
@@ -133,7 +132,7 @@ export const localFileServer = (initializer) => (configProvider) => {
|
|
|
133
132
|
return source;
|
|
134
133
|
};
|
|
135
134
|
const getSignedFileUploadConfig = async () => {
|
|
136
|
-
const prefix = 'uploads/' +
|
|
135
|
+
const prefix = 'uploads/' + crypto.randomUUID();
|
|
137
136
|
return {
|
|
138
137
|
url: `https://${await host}:${await port}/`,
|
|
139
138
|
payload: {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* spell-checker: ignore presigner */
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { CopyObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
4
5
|
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
5
6
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
6
|
-
import { v4 as uuid } from 'uuid';
|
|
7
7
|
import { assertDefined } from '../../assertions/index.js';
|
|
8
8
|
import { resolveConfigValue } from '../../config/index.js';
|
|
9
9
|
import { ifDefined } from '../../guards/index.js';
|
|
@@ -57,7 +57,7 @@ export const s3FileServer = (initializer) => (configProvider) => {
|
|
|
57
57
|
* https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
|
|
58
58
|
*/
|
|
59
59
|
const getSignedFileUploadConfig = async () => {
|
|
60
|
-
const prefix = 'uploads/' +
|
|
60
|
+
const prefix = 'uploads/' + randomUUID();
|
|
61
61
|
const bucket = (await bucketName());
|
|
62
62
|
const Conditions = [
|
|
63
63
|
{ acl: 'private' },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { APIGatewayProxyEventV2 } from 'aws-lambda';
|
|
2
2
|
import { VerifyConfig } from 'http-message-signatures';
|
|
3
|
-
import { JWK } from 'node-jose';
|
|
4
3
|
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
4
|
+
import { type PublicJsonWebKey } from '../../misc/jwks.js';
|
|
5
5
|
type Config = {
|
|
6
6
|
apiHost: string;
|
|
7
7
|
bypassSignatureVerification: string;
|
|
@@ -9,7 +9,7 @@ type Config = {
|
|
|
9
9
|
interface Initializer<C> {
|
|
10
10
|
configSpace?: C;
|
|
11
11
|
fetcher?: (uri: string) => Promise<{
|
|
12
|
-
keys:
|
|
12
|
+
keys: PublicJsonWebKey[];
|
|
13
13
|
}>;
|
|
14
14
|
}
|
|
15
15
|
export type SignatureAgentVerifier = (signatureAgent: string) => boolean | Promise<boolean>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { JWK } from 'node-jose';
|
|
2
1
|
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
2
|
+
import type { PublicJsonWebKey } from '../../misc/jwks.js';
|
|
3
3
|
type Config = {
|
|
4
4
|
privateKey: string;
|
|
5
5
|
};
|
|
@@ -8,12 +8,9 @@ export type PrivateKey = {
|
|
|
8
8
|
pem: string;
|
|
9
9
|
};
|
|
10
10
|
export declare const createKeyStore: (config: ConfigProviderForConfig<Config>) => {
|
|
11
|
-
getPrivateKey: () => Promise<
|
|
12
|
-
kid: string;
|
|
13
|
-
pem: string;
|
|
14
|
-
}>;
|
|
11
|
+
getPrivateKey: () => Promise<PrivateKey>;
|
|
15
12
|
jwks: () => Promise<{
|
|
16
|
-
keys:
|
|
13
|
+
keys: PublicJsonWebKey[];
|
|
17
14
|
}>;
|
|
18
15
|
};
|
|
19
16
|
export type KeyStore = ReturnType<typeof createKeyStore>;
|
|
@@ -1,23 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash, createPublicKey } from 'crypto';
|
|
2
2
|
import { resolveConfigValue } from '../../config/index.js';
|
|
3
3
|
import { once } from '../../misc/helpers.js';
|
|
4
|
+
// RFC 7638 required members per key type, in lexicographic order
|
|
5
|
+
const thumbprintMembers = {
|
|
6
|
+
RSA: ['e', 'kty', 'n'],
|
|
7
|
+
EC: ['crv', 'kty', 'x', 'y'],
|
|
8
|
+
OKP: ['crv', 'kty', 'x'],
|
|
9
|
+
oct: ['k', 'kty'],
|
|
10
|
+
};
|
|
11
|
+
const jwkThumbprint = (jwk) => {
|
|
12
|
+
const members = thumbprintMembers[jwk.kty];
|
|
13
|
+
const canonical = Object.fromEntries(members.map((m) => [m, jwk[m]]));
|
|
14
|
+
return createHash('sha256').update(JSON.stringify(canonical)).digest('base64url');
|
|
15
|
+
};
|
|
4
16
|
export const createKeyStore = (config) => {
|
|
5
17
|
const store = once(async () => {
|
|
6
18
|
const pem = await resolveConfigValue(config.privateKey);
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
return { keyStore, keys };
|
|
19
|
+
const publicJwk = createPublicKey(pem).export({ format: 'jwk' });
|
|
20
|
+
const kid = jwkThumbprint(publicJwk);
|
|
21
|
+
return { pem, publicJwk, kid };
|
|
11
22
|
});
|
|
12
23
|
return {
|
|
13
24
|
// this method only supports one key for now (always returns the first key)
|
|
14
25
|
getPrivateKey: async () => {
|
|
15
|
-
const {
|
|
16
|
-
return
|
|
26
|
+
const { pem, kid } = await store();
|
|
27
|
+
return { kid, pem };
|
|
17
28
|
},
|
|
18
29
|
jwks: async () => {
|
|
19
|
-
const {
|
|
20
|
-
return
|
|
30
|
+
const { publicJwk, kid } = await store();
|
|
31
|
+
return { keys: [{ ...publicJwk, kid }] };
|
|
21
32
|
},
|
|
22
33
|
};
|
|
23
34
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import jwt from 'jsonwebtoken';
|
|
2
|
-
import type { JWK } from 'node-jose';
|
|
3
2
|
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
3
|
+
import { type PublicJsonWebKey } from '../../misc/jwks.js';
|
|
4
4
|
type Config = {
|
|
5
5
|
trustedDomain: string;
|
|
6
6
|
bypassSignatureVerification: string;
|
|
@@ -8,7 +8,7 @@ type Config = {
|
|
|
8
8
|
interface Initializer<C> {
|
|
9
9
|
configSpace?: C;
|
|
10
10
|
fetcher?: (uri: string) => Promise<{
|
|
11
|
-
keys:
|
|
11
|
+
keys: PublicJsonWebKey[];
|
|
12
12
|
}>;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import formatISO from 'date-fns/formatISO';
|
|
2
|
-
import { v4 as uuid } from 'uuid';
|
|
3
2
|
import { formatAgent } from './attempt-utils.js';
|
|
4
3
|
export const addStatementDefaultFields = (statement, user) => ({
|
|
5
|
-
id:
|
|
4
|
+
id: globalThis.crypto.randomUUID(),
|
|
6
5
|
actor: formatAgent(user.uuid),
|
|
7
6
|
timestamp: formatISO(new Date()),
|
|
8
7
|
...statement,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
1
2
|
import * as fsModule from 'fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import formatISO from 'date-fns/formatISO';
|
|
4
|
-
import { v4 as uuid } from 'uuid';
|
|
5
5
|
import { assertDefined } from '../../assertions/index.js';
|
|
6
6
|
import { resolveConfigValue } from '../../config/index.js';
|
|
7
7
|
import { UnauthorizedError } from '../../errors/index.js';
|
|
@@ -69,7 +69,7 @@ export const fileSystemLrsGateway = (initializer) => (configProvider) => ({ auth
|
|
|
69
69
|
: assertDefined(await authProvider.getUser(), new UnauthorizedError);
|
|
70
70
|
const statementsWithDefaults = statements.map(statement => ({
|
|
71
71
|
...statement,
|
|
72
|
-
id:
|
|
72
|
+
id: randomUUID(),
|
|
73
73
|
actor: formatAgent(author.uuid),
|
|
74
74
|
timestamp: formatISO(new Date()),
|
|
75
75
|
}));
|