@openstax/ts-utils 1.3.1 → 1.4.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/config/envConfig.d.ts +1 -1
- package/dist/cjs/services/launchParams/index.d.ts +2 -0
- package/dist/cjs/services/launchParams/index.js +7 -0
- package/dist/cjs/services/launchParams/signer.d.ts +27 -0
- package/dist/cjs/services/launchParams/signer.js +45 -0
- package/dist/cjs/services/launchParams/verifier.d.ts +21 -0
- package/dist/cjs/services/launchParams/verifier.js +58 -0
- package/dist/cjs/services/searchProvider/index.d.ts +10 -2
- package/dist/cjs/services/searchProvider/memorySearchTheBadWay.js +35 -5
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/config/envConfig.d.ts +1 -1
- package/dist/esm/services/launchParams/index.d.ts +2 -0
- package/dist/esm/services/launchParams/index.js +2 -0
- package/dist/esm/services/launchParams/signer.d.ts +27 -0
- package/dist/esm/services/launchParams/signer.js +38 -0
- package/dist/esm/services/launchParams/verifier.d.ts +21 -0
- package/dist/esm/services/launchParams/verifier.js +51 -0
- package/dist/esm/services/searchProvider/index.d.ts +10 -2
- package/dist/esm/services/searchProvider/memorySearchTheBadWay.js +35 -5
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +4 -1
|
@@ -21,4 +21,4 @@ export declare const ENV_BUILD_CONFIGS: string[];
|
|
|
21
21
|
*
|
|
22
22
|
* @example const config = { configValue: envConfig('environment_variable_name') };
|
|
23
23
|
*/
|
|
24
|
-
export declare const envConfig: (name: string, type?: 'build' | 'runtime', defaultValue?: string | undefined) => ConfigValueProvider<string>;
|
|
24
|
+
export declare const envConfig: (name: string, type?: 'build' | 'runtime', defaultValue?: ConfigValueProvider<string> | undefined) => ConfigValueProvider<string>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLaunchVerifier = exports.createLaunchSigner = void 0;
|
|
4
|
+
var signer_1 = require("./signer");
|
|
5
|
+
Object.defineProperty(exports, "createLaunchSigner", { enumerable: true, get: function () { return signer_1.createLaunchSigner; } });
|
|
6
|
+
var verifier_1 = require("./verifier");
|
|
7
|
+
Object.defineProperty(exports, "createLaunchVerifier", { enumerable: true, get: function () { return verifier_1.createLaunchVerifier; } });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { JWK } from 'node-jose';
|
|
2
|
+
import { ConfigProviderForConfig } from '../../config';
|
|
3
|
+
declare type Config = {
|
|
4
|
+
alg: string;
|
|
5
|
+
expiresIn: string;
|
|
6
|
+
iss: string;
|
|
7
|
+
privateKey: string;
|
|
8
|
+
};
|
|
9
|
+
interface Initializer<C> {
|
|
10
|
+
configSpace?: C;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a class that can sign launch params
|
|
14
|
+
*/
|
|
15
|
+
export declare const createLaunchSigner: <C extends string = "launch">({ configSpace }: Initializer<C>) => (configProvider: { [key in C]: {
|
|
16
|
+
alg: import("../../config").ConfigValueProvider<string>;
|
|
17
|
+
expiresIn: import("../../config").ConfigValueProvider<string>;
|
|
18
|
+
iss: import("../../config").ConfigValueProvider<string>;
|
|
19
|
+
privateKey: import("../../config").ConfigValueProvider<string>;
|
|
20
|
+
}; }) => {
|
|
21
|
+
jwks: () => Promise<{
|
|
22
|
+
keys: JWK.RawKey[];
|
|
23
|
+
}>;
|
|
24
|
+
sign: (subject: string) => Promise<string>;
|
|
25
|
+
};
|
|
26
|
+
export declare type LaunchSigner = ReturnType<ReturnType<typeof createLaunchSigner>>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createLaunchSigner = void 0;
|
|
7
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
|
+
const node_jose_1 = require("node-jose");
|
|
9
|
+
const __1 = require("../..");
|
|
10
|
+
const config_1 = require("../../config");
|
|
11
|
+
const guards_1 = require("../../guards");
|
|
12
|
+
const SUPPORTED_ALGORITHMS = [
|
|
13
|
+
'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512', 'PS256', 'PS384', 'PS512'
|
|
14
|
+
];
|
|
15
|
+
const assertAlg = (alg) => {
|
|
16
|
+
if ((SUPPORTED_ALGORITHMS).includes(alg)) {
|
|
17
|
+
return alg;
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`"${alg}" is not a valid algorithm`);
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Creates a class that can sign launch params
|
|
23
|
+
*/
|
|
24
|
+
const createLaunchSigner = ({ configSpace }) => (configProvider) => {
|
|
25
|
+
const config = configProvider[(0, guards_1.ifDefined)(configSpace, 'launch')];
|
|
26
|
+
const getAlg = (0, __1.once)(async () => assertAlg(await (0, config_1.resolveConfigValue)(config.alg)));
|
|
27
|
+
const getExpiresIn = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.expiresIn));
|
|
28
|
+
const getIss = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.iss));
|
|
29
|
+
const getPrivateKey = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.privateKey));
|
|
30
|
+
const getKeyStore = (0, __1.once)(async () => {
|
|
31
|
+
const keystore = node_jose_1.JWK.createKeyStore();
|
|
32
|
+
await keystore.add(await getPrivateKey(), 'pem');
|
|
33
|
+
return keystore;
|
|
34
|
+
});
|
|
35
|
+
const jwks = async () => (await getKeyStore()).toJSON(false);
|
|
36
|
+
const sign = async (subject) => {
|
|
37
|
+
const alg = await getAlg();
|
|
38
|
+
const expiresIn = await getExpiresIn();
|
|
39
|
+
const iss = await getIss();
|
|
40
|
+
const header = { alg, iss };
|
|
41
|
+
return jsonwebtoken_1.default.sign({}, await getPrivateKey(), { algorithm: alg, expiresIn, header, issuer: iss, subject });
|
|
42
|
+
};
|
|
43
|
+
return { jwks, sign };
|
|
44
|
+
};
|
|
45
|
+
exports.createLaunchSigner = createLaunchSigner;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { JWK } from 'node-jose';
|
|
2
|
+
import { ConfigProviderForConfig } from '../../config';
|
|
3
|
+
declare type Config = {
|
|
4
|
+
trustedDomain: string;
|
|
5
|
+
};
|
|
6
|
+
interface Initializer<C> {
|
|
7
|
+
configSpace?: C;
|
|
8
|
+
fetcher?: (uri: string) => Promise<{
|
|
9
|
+
keys: JWK.RawKey[];
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a class that can verify launch params
|
|
14
|
+
*/
|
|
15
|
+
export declare const createLaunchVerifier: <C extends string = "launch">({ configSpace, fetcher }: Initializer<C>) => (configProvider: { [key in C]: {
|
|
16
|
+
trustedDomain: import("../../config").ConfigValueProvider<string>;
|
|
17
|
+
}; }) => {
|
|
18
|
+
verify: (token: string) => Promise<string>;
|
|
19
|
+
};
|
|
20
|
+
export declare type LaunchVerifier = ReturnType<ReturnType<typeof createLaunchVerifier>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createLaunchVerifier = void 0;
|
|
7
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
|
+
const jwks_rsa_1 = require("jwks-rsa");
|
|
9
|
+
const __1 = require("../..");
|
|
10
|
+
const config_1 = require("../../config");
|
|
11
|
+
const guards_1 = require("../../guards");
|
|
12
|
+
/**
|
|
13
|
+
* Creates a class that can verify launch params
|
|
14
|
+
*/
|
|
15
|
+
const createLaunchVerifier = ({ configSpace, fetcher }) => (configProvider) => {
|
|
16
|
+
const config = configProvider[(0, guards_1.ifDefined)(configSpace, 'launch')];
|
|
17
|
+
const getJwksClient = (0, __1.memoize)((jwksUri) => new jwks_rsa_1.JwksClient({ fetcher, jwksUri }));
|
|
18
|
+
const getJwksKey = (0, __1.memoize)(async (jwksUri, kid) => {
|
|
19
|
+
const client = getJwksClient(jwksUri);
|
|
20
|
+
const key = await client.getSigningKey(kid);
|
|
21
|
+
return key.getPublicKey();
|
|
22
|
+
});
|
|
23
|
+
const getKey = async (header, callback) => {
|
|
24
|
+
// The JWT spec allows iss in the header as a copy of the iss claim, but we require it
|
|
25
|
+
if (!header.iss) {
|
|
26
|
+
return callback(new Error('JWT header missing iss claim'));
|
|
27
|
+
}
|
|
28
|
+
const { iss, kid } = header;
|
|
29
|
+
try {
|
|
30
|
+
const jwksUrl = new URL('/.well-known/jwks.json', iss);
|
|
31
|
+
const launchDomain = jwksUrl.hostname;
|
|
32
|
+
const trustedDomain = await (0, config_1.resolveConfigValue)(config.trustedDomain);
|
|
33
|
+
if (launchDomain !== trustedDomain && !launchDomain.endsWith(`.${trustedDomain}`)) {
|
|
34
|
+
return callback(new Error(`Untrusted launch domain: "${launchDomain}"`));
|
|
35
|
+
}
|
|
36
|
+
callback(null, await getJwksKey(jwksUrl.toString(), kid));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
callback(error);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const verify = (token) => new Promise((resolve, reject) => jsonwebtoken_1.default.verify(token, getKey, {}, (err, payload) => {
|
|
43
|
+
if (err) {
|
|
44
|
+
reject(err);
|
|
45
|
+
}
|
|
46
|
+
else if (typeof payload !== 'object') {
|
|
47
|
+
reject(new Error('received JWT token with unexpected non-JSON payload'));
|
|
48
|
+
}
|
|
49
|
+
else if (!payload.sub) {
|
|
50
|
+
reject(new Error('JWT payload missing sub claim'));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
resolve(payload.sub);
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
return { verify };
|
|
57
|
+
};
|
|
58
|
+
exports.createLaunchVerifier = createLaunchVerifier;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
declare type Filter = {
|
|
1
|
+
export declare type Filter = {
|
|
2
2
|
key: string;
|
|
3
|
-
value: string | string[];
|
|
3
|
+
value: string | string[] | boolean;
|
|
4
4
|
};
|
|
5
5
|
declare type Field = {
|
|
6
6
|
key: string;
|
|
@@ -9,6 +9,9 @@ declare type Field = {
|
|
|
9
9
|
} | {
|
|
10
10
|
key: string;
|
|
11
11
|
type: 'keyword';
|
|
12
|
+
} | {
|
|
13
|
+
key: string;
|
|
14
|
+
type: 'boolean';
|
|
12
15
|
};
|
|
13
16
|
export interface IndexOptions<T> {
|
|
14
17
|
body: T;
|
|
@@ -18,6 +21,11 @@ export interface SearchOptions {
|
|
|
18
21
|
page?: number;
|
|
19
22
|
query: string | undefined;
|
|
20
23
|
fields: Field[];
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated use `must` instead
|
|
26
|
+
*/
|
|
21
27
|
filter?: Filter[];
|
|
28
|
+
must?: Filter[];
|
|
29
|
+
must_not?: Filter[];
|
|
22
30
|
}
|
|
23
31
|
export {};
|
|
@@ -2,18 +2,46 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.memorySearchTheBadWay = void 0;
|
|
4
4
|
const __1 = require("../..");
|
|
5
|
+
const errors_1 = require("../../errors");
|
|
5
6
|
const guards_1 = require("../../guards");
|
|
6
7
|
const MIN_MATCH = 0.5;
|
|
7
8
|
const MAX_RESULTS = 10;
|
|
9
|
+
var MatchType;
|
|
10
|
+
(function (MatchType) {
|
|
11
|
+
MatchType[MatchType["Must"] = 0] = "Must";
|
|
12
|
+
MatchType[MatchType["MustNot"] = 1] = "MustNot";
|
|
13
|
+
})(MatchType || (MatchType = {}));
|
|
8
14
|
const resolveField = (document, field) => field.key.toString().split('.').reduce((result, key) => result[key], document);
|
|
9
15
|
const memorySearchTheBadWay = ({ loadAllDocumentsTheBadWay }) => {
|
|
10
16
|
return {
|
|
11
17
|
ensureIndexCreated: async () => undefined,
|
|
12
18
|
index: async (_options) => undefined,
|
|
13
19
|
search: async (options) => {
|
|
20
|
+
const getFieldType = (field) => { var _a; return (_a = options.fields.find(f => f.key == field.key)) === null || _a === void 0 ? void 0 : _a.type; };
|
|
14
21
|
const results = (await loadAllDocumentsTheBadWay())
|
|
15
22
|
.map(document => {
|
|
16
23
|
let weight = 0;
|
|
24
|
+
const matchFilters = (filters, mustMatch) => {
|
|
25
|
+
for (const field of filters) {
|
|
26
|
+
const docValues = (0, __1.coerceArray)(resolveField(document, field));
|
|
27
|
+
const coerceValue = getFieldType(field) === 'boolean'
|
|
28
|
+
? (input) => {
|
|
29
|
+
if ([true, 'true', '1', 1].includes(input)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if ([false, 'false', '0', 0, ''].includes(input)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
throw new errors_1.InvalidRequestError('input is not a valid boolean filter');
|
|
36
|
+
}
|
|
37
|
+
: (x) => x;
|
|
38
|
+
const hasMatch = (0, __1.coerceArray)(field.value).map(coerceValue).some(v => docValues.includes(v));
|
|
39
|
+
if ((mustMatch === MatchType.Must && !hasMatch) || (mustMatch === MatchType.MustNot && hasMatch)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
17
45
|
if (options.query !== undefined) {
|
|
18
46
|
for (const field of options.fields) {
|
|
19
47
|
if (field.type !== undefined && field.type !== 'text') {
|
|
@@ -33,11 +61,13 @@ const memorySearchTheBadWay = ({ loadAllDocumentsTheBadWay }) => {
|
|
|
33
61
|
}
|
|
34
62
|
}
|
|
35
63
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
const { must_not } = options;
|
|
65
|
+
const must = 'filter' in options ? options.filter : options.must;
|
|
66
|
+
if ((must === null || must === void 0 ? void 0 : must.length) && !matchFilters(must, MatchType.Must)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if ((must_not === null || must_not === void 0 ? void 0 : must_not.length) && !matchFilters(must_not, MatchType.MustNot)) {
|
|
70
|
+
return undefined;
|
|
41
71
|
}
|
|
42
72
|
return { document, weight };
|
|
43
73
|
})
|