@openstax/ts-utils 1.31.2 → 1.32.1
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/routing/index.d.ts +20 -2
- package/dist/cjs/routing/index.js +10 -1
- package/dist/cjs/services/authProvider/browser.d.ts +3 -3
- package/dist/cjs/services/authProvider/decryption.d.ts +5 -1
- package/dist/cjs/services/authProvider/decryption.js +28 -18
- package/dist/cjs/services/authProvider/index.d.ts +6 -6
- package/dist/cjs/services/authProvider/subrequest.d.ts +1 -1
- package/dist/cjs/services/authProvider/subrequest.js +2 -7
- package/dist/cjs/services/authProvider/utils/userSubrequest.d.ts +3 -0
- package/dist/cjs/services/authProvider/utils/userSubrequest.js +13 -0
- package/dist/cjs/services/fileServer/s3FileServer.js +1 -1
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/routing/index.d.ts +20 -2
- package/dist/esm/routing/index.js +8 -0
- package/dist/esm/services/authProvider/browser.d.ts +3 -3
- package/dist/esm/services/authProvider/decryption.d.ts +5 -1
- package/dist/esm/services/authProvider/decryption.js +28 -18
- package/dist/esm/services/authProvider/index.d.ts +6 -6
- package/dist/esm/services/authProvider/subrequest.d.ts +1 -1
- package/dist/esm/services/authProvider/subrequest.js +2 -4
- package/dist/esm/services/authProvider/utils/userSubrequest.d.ts +3 -0
- package/dist/esm/services/authProvider/utils/userSubrequest.js +6 -0
- package/dist/esm/services/fileServer/s3FileServer.js +1 -1
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -25,6 +25,20 @@ export type RouteMatchRecord<R> = R extends AnyRoute<R> ? {
|
|
|
25
25
|
route: R;
|
|
26
26
|
params: ParamsForRoute<R>;
|
|
27
27
|
} : never;
|
|
28
|
+
type Flatten<T> = T extends any ? {
|
|
29
|
+
[K in keyof T]: T[K];
|
|
30
|
+
} : never;
|
|
31
|
+
export type ExternalRoute<R> = R extends Route<infer N, infer P, infer Sa, infer Sr, infer Ri, infer Ro> & infer E ? Route<N, P, Sa, Sr extends {
|
|
32
|
+
payload: any;
|
|
33
|
+
} ? Flatten<Pick<Sr, 'payload'>> : Record<string, never>, Ri, Ro> & Flatten<Omit<E, 'name' | 'path' | 'handler' | 'requestServiceProvider'>> : never;
|
|
34
|
+
/** this utility simplifies the route type to remove stuff that is only
|
|
35
|
+
* relevant internal to the route, like the service types, keeping only
|
|
36
|
+
* the payload type which is necessary for the apiGateway
|
|
37
|
+
*
|
|
38
|
+
* this helps avoid the "type too complicated" error that typescript throws
|
|
39
|
+
* when there are a lot of routes with complex services
|
|
40
|
+
**/
|
|
41
|
+
export declare const routesList: <R>(routes: R[]) => ExternalRoute<R>[];
|
|
28
42
|
/**
|
|
29
43
|
* The conditional type for the payload for a given route, `R`. This isn't a route structure, its
|
|
30
44
|
* a convention based on the request middleware
|
|
@@ -140,7 +154,9 @@ export declare const makeCreateRoute: <Sa, Ri, Ex = {}>() => CreateRoute<Sa, Ri,
|
|
|
140
154
|
*/
|
|
141
155
|
export declare const makeRenderRouteUrl: <Ru extends {
|
|
142
156
|
path: string;
|
|
143
|
-
}>() => <R
|
|
157
|
+
}>() => <R>(route: ExternalRoute<R> extends Ru ? R & {
|
|
158
|
+
path: string;
|
|
159
|
+
} : R extends Ru ? R : never, params: ParamsForRoute<R>, query?: QueryParams) => string;
|
|
144
160
|
/**
|
|
145
161
|
* A pre-made result from `makeRenderRouteUrl`, this function interpolates parameter and query
|
|
146
162
|
* arguments into a route path.
|
|
@@ -153,7 +169,9 @@ export declare const makeRenderRouteUrl: <Ru extends {
|
|
|
153
169
|
* @param query the query parameters to add to the route path
|
|
154
170
|
* @returns the interpolated route path
|
|
155
171
|
*/
|
|
156
|
-
export declare const renderAnyRouteUrl: <R
|
|
172
|
+
export declare const renderAnyRouteUrl: <R>(route: ExternalRoute<R> extends any ? R & {
|
|
173
|
+
path: string;
|
|
174
|
+
} : R extends any ? R : never, params: ParamsForRoute<R>, query?: QueryParams) => string;
|
|
157
175
|
type RequestPathExtractor<Ri> = (request: Ri) => string;
|
|
158
176
|
type RequestLogExtractor<Ri> = (request: Ri) => JsonCompatibleStruct;
|
|
159
177
|
type RequestRouteMatcher<Ri, R> = (request: Ri, route: R) => boolean;
|
|
@@ -2,6 +2,14 @@ import * as pathToRegexp from 'path-to-regexp';
|
|
|
2
2
|
import queryString from 'query-string';
|
|
3
3
|
import { mapFind, memoize } from '../misc/helpers';
|
|
4
4
|
import { createConsoleLogger } from '../services/logger/console';
|
|
5
|
+
/** this utility simplifies the route type to remove stuff that is only
|
|
6
|
+
* relevant internal to the route, like the service types, keeping only
|
|
7
|
+
* the payload type which is necessary for the apiGateway
|
|
8
|
+
*
|
|
9
|
+
* this helps avoid the "type too complicated" error that typescript throws
|
|
10
|
+
* when there are a lot of routes with complex services
|
|
11
|
+
**/
|
|
12
|
+
export const routesList = (routes) => routes;
|
|
5
13
|
/**
|
|
6
14
|
* Makes a createRoute function that can be used to create routes (this is a factory factory). The
|
|
7
15
|
* `makeCreateRoute` function is typically called once in the backend and once in the frontend to
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConfigProviderForConfig } from '../../config';
|
|
2
2
|
import { FetchConfig, GenericFetch } from '../../fetch';
|
|
3
|
-
import { User } from '.';
|
|
3
|
+
import { ApiUser, User } from '.';
|
|
4
4
|
type Config = {
|
|
5
5
|
accountsBase: string;
|
|
6
6
|
};
|
|
@@ -26,7 +26,7 @@ export interface Window {
|
|
|
26
26
|
addEventListener: (event: 'message', callback: EventHandler) => void;
|
|
27
27
|
removeEventListener: (event: 'message', callback: EventHandler) => void;
|
|
28
28
|
}
|
|
29
|
-
export type UpdatableUserFields = Partial<Pick<
|
|
29
|
+
export type UpdatableUserFields = Partial<Pick<ApiUser, 'consent_preferences' | 'first_name' | 'last_name'>>;
|
|
30
30
|
export declare const browserAuthProvider: <C extends string = "auth">({ window, configSpace }: Initializer<C>) => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }) => {
|
|
31
31
|
/**
|
|
32
32
|
* gets the authentication token
|
|
@@ -65,7 +65,7 @@ export declare const browserAuthProvider: <C extends string = "auth">({ window,
|
|
|
65
65
|
* updates user settings, for example the cookie consent preferences
|
|
66
66
|
*/
|
|
67
67
|
updateUser: (updates: UpdatableUserFields) => Promise<{
|
|
68
|
-
user:
|
|
68
|
+
user: ApiUser;
|
|
69
69
|
token: string | null;
|
|
70
70
|
}>;
|
|
71
71
|
};
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import type { ConfigProviderForConfig } from '../../config';
|
|
2
|
-
import {
|
|
2
|
+
import { GenericFetch } from '../../fetch';
|
|
3
|
+
import { ApiUser, AuthProvider, CookieAuthProvider } from '.';
|
|
3
4
|
type Config = {
|
|
5
|
+
accountsBase: string;
|
|
4
6
|
cookieName: string;
|
|
5
7
|
encryptionPrivateKey: string;
|
|
6
8
|
signaturePublicKey: string;
|
|
7
9
|
};
|
|
8
10
|
interface Initializer<C> {
|
|
9
11
|
configSpace?: C;
|
|
12
|
+
fetch: GenericFetch;
|
|
10
13
|
}
|
|
11
14
|
export type DecryptionAuthProvider = AuthProvider & {
|
|
12
15
|
getTokenExpiration: (tokenString?: string) => Promise<number | null | undefined>;
|
|
16
|
+
loadUserData: () => Promise<ApiUser | undefined>;
|
|
13
17
|
};
|
|
14
18
|
export declare const decryptionAuthProvider: <C extends string = "decryption">(initializer: Initializer<C>) => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }) => CookieAuthProvider<DecryptionAuthProvider>;
|
|
15
19
|
export {};
|
|
@@ -3,14 +3,17 @@ import { SessionExpiredError } from '../../errors';
|
|
|
3
3
|
import { ifDefined } from '../../guards';
|
|
4
4
|
import { once } from '../../misc/helpers';
|
|
5
5
|
import { decryptAndVerify } from './utils/decryptAndVerify';
|
|
6
|
+
import { loadUserData } from './utils/userSubrequest';
|
|
6
7
|
import { getAuthTokenOrCookie } from '.';
|
|
7
8
|
export const decryptionAuthProvider = (initializer) => (configProvider) => {
|
|
8
9
|
const config = configProvider[ifDefined(initializer.configSpace, 'decryption')];
|
|
10
|
+
const accountsBase = once(() => resolveConfigValue(config.accountsBase));
|
|
9
11
|
const cookieName = once(() => resolveConfigValue(config.cookieName));
|
|
10
12
|
const encryptionPrivateKey = once(() => resolveConfigValue(config.encryptionPrivateKey));
|
|
11
13
|
const signaturePublicKey = once(() => resolveConfigValue(config.signaturePublicKey));
|
|
12
14
|
return ({ request, logger }) => {
|
|
13
15
|
let user;
|
|
16
|
+
let userData;
|
|
14
17
|
const getAuthToken = async () => getAuthTokenOrCookie(request, await cookieName())[0];
|
|
15
18
|
const getAuthorizedFetchConfig = async () => {
|
|
16
19
|
const [token, headers] = getAuthTokenOrCookie(request, await cookieName());
|
|
@@ -19,40 +22,47 @@ export const decryptionAuthProvider = (initializer) => (configProvider) => {
|
|
|
19
22
|
}
|
|
20
23
|
return { headers };
|
|
21
24
|
};
|
|
22
|
-
const
|
|
25
|
+
const getDecryptedPayload = async (tokenString) => {
|
|
23
26
|
const token = tokenString !== null && tokenString !== void 0 ? tokenString : await getAuthToken();
|
|
24
27
|
if (!token) {
|
|
25
28
|
return undefined;
|
|
26
29
|
}
|
|
27
30
|
return decryptAndVerify(token, await encryptionPrivateKey(), await signaturePublicKey());
|
|
28
31
|
};
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
const getUser = async () => {
|
|
33
|
+
if (!user) {
|
|
34
|
+
const result = await getDecryptedPayload();
|
|
35
|
+
if (!result) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
if ('error' in result && result.error == 'expired token') {
|
|
39
|
+
throw new SessionExpiredError();
|
|
40
|
+
}
|
|
41
|
+
if ('user' in result) {
|
|
42
|
+
logger.setContext({ user: result.user.uuid });
|
|
43
|
+
user = result.user;
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
|
-
return
|
|
46
|
+
return user;
|
|
42
47
|
};
|
|
43
48
|
return {
|
|
44
49
|
getAuthToken,
|
|
45
50
|
getAuthorizedFetchConfig,
|
|
46
51
|
getTokenExpiration: async (tokenString) => {
|
|
47
52
|
var _a;
|
|
48
|
-
const payload = await
|
|
53
|
+
const payload = await getDecryptedPayload(tokenString);
|
|
49
54
|
return payload ? ((_a = payload.exp) !== null && _a !== void 0 ? _a : null) : undefined;
|
|
50
55
|
},
|
|
51
|
-
getUser
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
getUser,
|
|
57
|
+
loadUserData: async () => {
|
|
58
|
+
if (!userData) {
|
|
59
|
+
const token = await getAuthToken();
|
|
60
|
+
if (!token) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
userData = await loadUserData(initializer.fetch, await accountsBase(), await cookieName(), token);
|
|
54
64
|
}
|
|
55
|
-
return
|
|
65
|
+
return userData;
|
|
56
66
|
},
|
|
57
67
|
};
|
|
58
68
|
};
|
|
@@ -10,14 +10,14 @@ export type ConsentPreferences = {
|
|
|
10
10
|
export type TokenUser = {
|
|
11
11
|
id: number;
|
|
12
12
|
name: string;
|
|
13
|
+
uuid: string;
|
|
14
|
+
faculty_status: string;
|
|
15
|
+
is_admin: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type ApiUser = TokenUser & {
|
|
13
18
|
first_name: string;
|
|
14
19
|
last_name: string;
|
|
15
20
|
full_name: string;
|
|
16
|
-
uuid: string;
|
|
17
|
-
faculty_status: string;
|
|
18
|
-
is_administrator: boolean;
|
|
19
|
-
} & Partial<ConsentPreferences>;
|
|
20
|
-
export interface ApiUser extends TokenUser {
|
|
21
21
|
contact_infos: Array<{
|
|
22
22
|
type: string;
|
|
23
23
|
value: string;
|
|
@@ -34,7 +34,7 @@ export interface ApiUser extends TokenUser {
|
|
|
34
34
|
self_reported_role: string;
|
|
35
35
|
signed_contract_names: string[];
|
|
36
36
|
using_openstax: boolean;
|
|
37
|
-
}
|
|
37
|
+
} & Partial<ConsentPreferences>;
|
|
38
38
|
export type User = TokenUser | ApiUser;
|
|
39
39
|
export type AuthProvider = {
|
|
40
40
|
getAuthToken: () => Promise<string | null>;
|
|
@@ -2,8 +2,8 @@ import { ConfigProviderForConfig } from '../../config';
|
|
|
2
2
|
import { GenericFetch } from '../../fetch';
|
|
3
3
|
import { CookieAuthProvider } from '.';
|
|
4
4
|
type Config = {
|
|
5
|
-
cookieName: string;
|
|
6
5
|
accountsBase: string;
|
|
6
|
+
cookieName: string;
|
|
7
7
|
};
|
|
8
8
|
interface Initializer<C> {
|
|
9
9
|
configSpace?: C;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import cookie from 'cookie';
|
|
2
1
|
import { once } from '../..';
|
|
3
2
|
import { resolveConfigValue } from '../../config';
|
|
4
3
|
import { ifDefined } from '../../guards';
|
|
4
|
+
import { loadUserData } from './utils/userSubrequest';
|
|
5
5
|
import { getAuthTokenOrCookie } from '.';
|
|
6
6
|
export const subrequestAuthProvider = (initializer) => (configProvider) => {
|
|
7
7
|
const config = configProvider[ifDefined(initializer.configSpace, 'subrequest')];
|
|
@@ -23,9 +23,7 @@ export const subrequestAuthProvider = (initializer) => (configProvider) => {
|
|
|
23
23
|
if (!token) {
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
|
-
const
|
|
27
|
-
const user = await initializer.fetch((await accountsBase()).replace(/\/+$/, '') + '/api/user', { headers })
|
|
28
|
-
.then(response => response.json());
|
|
26
|
+
const user = await loadUserData(initializer.fetch, await accountsBase(), resolvedCookieName, token);
|
|
29
27
|
if (user) {
|
|
30
28
|
logger.setContext({ user: user.uuid });
|
|
31
29
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import cookie from 'cookie';
|
|
2
|
+
export const loadUserData = (fetch, accountsBase, cookieName, token) => {
|
|
3
|
+
const headers = { cookie: cookie.serialize(cookieName, token) };
|
|
4
|
+
return fetch(accountsBase.replace(/\/+$/, '') + '/api/user', { headers })
|
|
5
|
+
.then(response => response.json());
|
|
6
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* cspell:ignore presigner */
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import { CopyObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
3
4
|
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
4
5
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
5
|
-
import path from 'path';
|
|
6
6
|
import { v4 as uuid } from 'uuid';
|
|
7
7
|
import { once } from '../..';
|
|
8
8
|
import { assertDefined } from '../../assertions';
|