@boxyhq/saml-jackson 0.3.7 → 0.3.8-beta.763

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.
@@ -0,0 +1,8 @@
1
+ import { IAdminController, Storable, OAuth } from '../typings';
2
+ export declare class AdminController implements IAdminController {
3
+ configStore: Storable;
4
+ constructor({ configStore }: {
5
+ configStore: any;
6
+ });
7
+ getAllConfig(): Promise<Partial<OAuth>[]>;
8
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AdminController = void 0;
13
+ class AdminController {
14
+ constructor({ configStore }) {
15
+ this.configStore = configStore;
16
+ }
17
+ getAllConfig() {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ const configList = (yield this.configStore.getAll());
20
+ if (!configList || !configList.length) {
21
+ return [];
22
+ }
23
+ return configList;
24
+ });
25
+ }
26
+ }
27
+ exports.AdminController = AdminController;
@@ -18,10 +18,23 @@ export declare class APIController implements IAPIController {
18
18
  * consumes:
19
19
  * - application/x-www-form-urlencoded
20
20
  * parameters:
21
+ * - name: name
22
+ * description: Name/identifier for the config
23
+ * type: string
24
+ * in: formData
25
+ * example: cal-saml-config
26
+ * - name: description
27
+ * description: A short description for the config not more than 100 characters
28
+ * type: string
29
+ * in: formData
30
+ * example: SAML login for cal.com app
21
31
  * - name: encodedRawMetadata
22
32
  * description: Base64 encoding of the XML metadata
23
33
  * in: formData
24
- * required: true
34
+ * type: string
35
+ * - name: rawMetadata
36
+ * description: Raw XML metadata
37
+ * in: formData
25
38
  * type: string
26
39
  * - name: defaultRedirectUrl
27
40
  * description: The redirect URL to use in the IdP login flow
@@ -63,10 +76,85 @@ export declare class APIController implements IAPIController {
63
76
  * client_id: 8958e13053832b5af58fdf2ee83f35f5d013dc74
64
77
  * client_secret: 13f01f4df5b01770c616e682d14d3ba23f20948cfa89b1d7
65
78
  * type: accounts.google.com
79
+ * 400:
80
+ * description: Please provide rawMetadata or encodedRawMetadata | Please provide a defaultRedirectUrl | Please provide redirectUrl | Please provide tenant | Please provide product | Please provide a friendly name | Description should not exceed 100 characters
66
81
  * 401:
67
82
  * description: Unauthorized
68
83
  */
69
84
  config(body: IdPConfig): Promise<OAuth>;
85
+ /**
86
+ * @swagger
87
+ *
88
+ * /api/v1/saml/config:
89
+ * patch:
90
+ * summary: Update SAML configuration
91
+ * operationId: update-saml-config
92
+ * tags: [SAML Config]
93
+ * consumes:
94
+ * - application/json
95
+ * - application/x-www-form-urlencoded
96
+ * parameters:
97
+ * - name: clientID
98
+ * description: Client ID for the config
99
+ * type: string
100
+ * in: formData
101
+ * required: true
102
+ * - name: clientSecret
103
+ * description: Client Secret for the config
104
+ * type: string
105
+ * in: formData
106
+ * required: true
107
+ * - name: name
108
+ * description: Name/identifier for the config
109
+ * type: string
110
+ * in: formData
111
+ * example: cal-saml-config
112
+ * - name: description
113
+ * description: A short description for the config not more than 100 characters
114
+ * type: string
115
+ * in: formData
116
+ * example: SAML login for cal.com app
117
+ * - name: encodedRawMetadata
118
+ * description: Base64 encoding of the XML metadata
119
+ * in: formData
120
+ * type: string
121
+ * - name: rawMetadata
122
+ * description: Raw XML metadata
123
+ * in: formData
124
+ * type: string
125
+ * - name: defaultRedirectUrl
126
+ * description: The redirect URL to use in the IdP login flow
127
+ * in: formData
128
+ * required: true
129
+ * type: string
130
+ * example: http://localhost:3000/login/saml
131
+ * - name: redirectUrl
132
+ * description: JSON encoded array containing a list of allowed redirect URLs
133
+ * in: formData
134
+ * required: true
135
+ * type: string
136
+ * example: '["http://localhost:3000/*"]'
137
+ * - name: tenant
138
+ * description: Tenant
139
+ * in: formData
140
+ * required: true
141
+ * type: string
142
+ * example: boxyhq.com
143
+ * - name: product
144
+ * description: Product
145
+ * in: formData
146
+ * required: true
147
+ * type: string
148
+ * example: demo
149
+ * responses:
150
+ * 204:
151
+ * description: Success
152
+ * 400:
153
+ * description: Please provide clientID | Please provide clientSecret | clientSecret mismatch | Tenant/Product config mismatch with IdP metadata | Description should not exceed 100 characters
154
+ * 401:
155
+ * description: Unauthorized
156
+ */
157
+ updateConfig(body: any): Promise<void>;
70
158
  /**
71
159
  * @swagger
72
160
  *
@@ -96,11 +184,35 @@ export declare class APIController implements IAPIController {
96
184
  * description: Success
97
185
  * schema:
98
186
  * type: object
99
- * properties:
100
- * provider:
101
- * type: string
102
187
  * example:
103
- * type: accounts.google.com
188
+ * {
189
+ * "config": {
190
+ * "idpMetadata": {
191
+ * "sso": {
192
+ * "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml",
193
+ * "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml"
194
+ * },
195
+ * "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
196
+ * "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
197
+ * "loginType": "idp",
198
+ * "provider": "okta.com"
199
+ * },
200
+ * "defaultRedirectUrl": "https://hoppscotch.io/",
201
+ * "redirectUrl": ["https://hoppscotch.io/"],
202
+ * "tenant": "hoppscotch.io",
203
+ * "product": "API Engine",
204
+ * "name": "Hoppscotch-SP",
205
+ * "description": "SP for hoppscotch.io",
206
+ * "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
207
+ * "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
208
+ * "certs": {
209
+ * "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
210
+ * "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
211
+ * }
212
+ * }
213
+ * }
214
+ * '400':
215
+ * description: Please provide `clientID` or `tenant` and `product`.
104
216
  * '401':
105
217
  * description: Unauthorized
106
218
  */
@@ -108,7 +220,7 @@ export declare class APIController implements IAPIController {
108
220
  clientID: string;
109
221
  tenant: string;
110
222
  product: string;
111
- }): Promise<Partial<OAuth>>;
223
+ }): Promise<any>;
112
224
  /**
113
225
  * @swagger
114
226
  * /api/v1/saml/config:
@@ -143,6 +255,8 @@ export declare class APIController implements IAPIController {
143
255
  * responses:
144
256
  * '200':
145
257
  * description: Success
258
+ * '400':
259
+ * description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.'
146
260
  * '401':
147
261
  * description: Unauthorized
148
262
  */
@@ -27,6 +27,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
27
27
  step((generator = generator.apply(thisArg, _arguments || [])).next());
28
28
  });
29
29
  };
30
+ var __rest = (this && this.__rest) || function (s, e) {
31
+ var t = {};
32
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
33
+ t[p] = s[p];
34
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
35
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
36
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
37
+ t[p[i]] = s[p[i]];
38
+ }
39
+ return t;
40
+ };
30
41
  var __importDefault = (this && this.__importDefault) || function (mod) {
31
42
  return (mod && mod.__esModule) ? mod : { "default": mod };
32
43
  };
@@ -34,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
34
45
  exports.APIController = void 0;
35
46
  const crypto_1 = __importDefault(require("crypto"));
36
47
  const dbutils = __importStar(require("../db/utils"));
48
+ const metrics = __importStar(require("../opentelemetry/metrics"));
37
49
  const saml_1 = __importDefault(require("../saml/saml"));
38
50
  const x509_1 = __importDefault(require("../saml/x509"));
39
51
  const error_1 = require("./error");
@@ -43,7 +55,7 @@ class APIController {
43
55
  this.configStore = configStore;
44
56
  }
45
57
  _validateIdPConfig(body) {
46
- const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product } = body;
58
+ const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product, description } = body;
47
59
  if (!rawMetadata && !encodedRawMetadata) {
48
60
  throw new error_1.JacksonError('Please provide rawMetadata or encodedRawMetadata', 400);
49
61
  }
@@ -59,6 +71,9 @@ class APIController {
59
71
  if (!product) {
60
72
  throw new error_1.JacksonError('Please provide product', 400);
61
73
  }
74
+ if (description && description.length > 100) {
75
+ throw new error_1.JacksonError('Description should not exceed 100 characters', 400);
76
+ }
62
77
  }
63
78
  /**
64
79
  * @swagger
@@ -73,10 +88,23 @@ class APIController {
73
88
  * consumes:
74
89
  * - application/x-www-form-urlencoded
75
90
  * parameters:
91
+ * - name: name
92
+ * description: Name/identifier for the config
93
+ * type: string
94
+ * in: formData
95
+ * example: cal-saml-config
96
+ * - name: description
97
+ * description: A short description for the config not more than 100 characters
98
+ * type: string
99
+ * in: formData
100
+ * example: SAML login for cal.com app
76
101
  * - name: encodedRawMetadata
77
102
  * description: Base64 encoding of the XML metadata
78
103
  * in: formData
79
- * required: true
104
+ * type: string
105
+ * - name: rawMetadata
106
+ * description: Raw XML metadata
107
+ * in: formData
80
108
  * type: string
81
109
  * - name: defaultRedirectUrl
82
110
  * description: The redirect URL to use in the IdP login flow
@@ -118,12 +146,15 @@ class APIController {
118
146
  * client_id: 8958e13053832b5af58fdf2ee83f35f5d013dc74
119
147
  * client_secret: 13f01f4df5b01770c616e682d14d3ba23f20948cfa89b1d7
120
148
  * type: accounts.google.com
149
+ * 400:
150
+ * description: Please provide rawMetadata or encodedRawMetadata | Please provide a defaultRedirectUrl | Please provide redirectUrl | Please provide tenant | Please provide product | Please provide a friendly name | Description should not exceed 100 characters
121
151
  * 401:
122
152
  * description: Unauthorized
123
153
  */
124
154
  config(body) {
125
155
  return __awaiter(this, void 0, void 0, function* () {
126
- const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product } = body;
156
+ const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product, name, description, } = body;
157
+ metrics.increment('createConfig');
127
158
  this._validateIdPConfig(body);
128
159
  let metaData = rawMetadata;
129
160
  if (encodedRawMetadata) {
@@ -155,6 +186,8 @@ class APIController {
155
186
  redirectUrl: JSON.parse(redirectUrl),
156
187
  tenant,
157
188
  product,
189
+ name,
190
+ description,
158
191
  clientID,
159
192
  clientSecret,
160
193
  certs,
@@ -174,6 +207,129 @@ class APIController {
174
207
  };
175
208
  });
176
209
  }
210
+ /**
211
+ * @swagger
212
+ *
213
+ * /api/v1/saml/config:
214
+ * patch:
215
+ * summary: Update SAML configuration
216
+ * operationId: update-saml-config
217
+ * tags: [SAML Config]
218
+ * consumes:
219
+ * - application/json
220
+ * - application/x-www-form-urlencoded
221
+ * parameters:
222
+ * - name: clientID
223
+ * description: Client ID for the config
224
+ * type: string
225
+ * in: formData
226
+ * required: true
227
+ * - name: clientSecret
228
+ * description: Client Secret for the config
229
+ * type: string
230
+ * in: formData
231
+ * required: true
232
+ * - name: name
233
+ * description: Name/identifier for the config
234
+ * type: string
235
+ * in: formData
236
+ * example: cal-saml-config
237
+ * - name: description
238
+ * description: A short description for the config not more than 100 characters
239
+ * type: string
240
+ * in: formData
241
+ * example: SAML login for cal.com app
242
+ * - name: encodedRawMetadata
243
+ * description: Base64 encoding of the XML metadata
244
+ * in: formData
245
+ * type: string
246
+ * - name: rawMetadata
247
+ * description: Raw XML metadata
248
+ * in: formData
249
+ * type: string
250
+ * - name: defaultRedirectUrl
251
+ * description: The redirect URL to use in the IdP login flow
252
+ * in: formData
253
+ * required: true
254
+ * type: string
255
+ * example: http://localhost:3000/login/saml
256
+ * - name: redirectUrl
257
+ * description: JSON encoded array containing a list of allowed redirect URLs
258
+ * in: formData
259
+ * required: true
260
+ * type: string
261
+ * example: '["http://localhost:3000/*"]'
262
+ * - name: tenant
263
+ * description: Tenant
264
+ * in: formData
265
+ * required: true
266
+ * type: string
267
+ * example: boxyhq.com
268
+ * - name: product
269
+ * description: Product
270
+ * in: formData
271
+ * required: true
272
+ * type: string
273
+ * example: demo
274
+ * responses:
275
+ * 204:
276
+ * description: Success
277
+ * 400:
278
+ * description: Please provide clientID | Please provide clientSecret | clientSecret mismatch | Tenant/Product config mismatch with IdP metadata | Description should not exceed 100 characters
279
+ * 401:
280
+ * description: Unauthorized
281
+ */
282
+ updateConfig(body) {
283
+ var _a;
284
+ return __awaiter(this, void 0, void 0, function* () {
285
+ const { encodedRawMetadata, // could be empty
286
+ rawMetadata, // could be empty
287
+ defaultRedirectUrl, redirectUrl, name, description } = body, clientInfo = __rest(body, ["encodedRawMetadata", "rawMetadata", "defaultRedirectUrl", "redirectUrl", "name", "description"]);
288
+ if (!(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID)) {
289
+ throw new error_1.JacksonError('Please provide clientID', 400);
290
+ }
291
+ if (!(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientSecret)) {
292
+ throw new error_1.JacksonError('Please provide clientSecret', 400);
293
+ }
294
+ if (description && description.length > 100) {
295
+ throw new error_1.JacksonError('Description should not exceed 100 characters', 400);
296
+ }
297
+ const _currentConfig = (_a = (yield this.getConfig(clientInfo))) === null || _a === void 0 ? void 0 : _a.config;
298
+ if (_currentConfig.clientSecret !== (clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientSecret)) {
299
+ throw new error_1.JacksonError('clientSecret mismatch', 400);
300
+ }
301
+ let metaData = rawMetadata;
302
+ if (encodedRawMetadata) {
303
+ metaData = Buffer.from(encodedRawMetadata, 'base64').toString();
304
+ }
305
+ let newMetadata;
306
+ if (metaData) {
307
+ newMetadata = yield saml_1.default.parseMetadataAsync(metaData);
308
+ // extract provider
309
+ let providerName = extractHostName(newMetadata.entityID);
310
+ if (!providerName) {
311
+ providerName = extractHostName(newMetadata.sso.redirectUrl || newMetadata.sso.postUrl);
312
+ }
313
+ newMetadata.provider = providerName ? providerName : 'Unknown';
314
+ }
315
+ if (newMetadata) {
316
+ // check if clientID matches with new metadata payload
317
+ const clientID = dbutils.keyDigest(dbutils.keyFromParts(clientInfo.tenant, clientInfo.product, newMetadata.entityID));
318
+ if (clientID !== (clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID)) {
319
+ throw new error_1.JacksonError('Tenant/Product config mismatch with IdP metadata', 400);
320
+ }
321
+ }
322
+ yield this.configStore.put(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID, Object.assign(Object.assign({}, _currentConfig), { name: name ? name : _currentConfig.name, description: description ? description : _currentConfig.description, idpMetadata: newMetadata ? newMetadata : _currentConfig.idpMetadata, defaultRedirectUrl: defaultRedirectUrl ? defaultRedirectUrl : _currentConfig.defaultRedirectUrl, redirectUrl: redirectUrl ? JSON.parse(redirectUrl) : _currentConfig.redirectUrl }), {
323
+ // secondary index on entityID
324
+ name: utils_1.IndexNames.EntityID,
325
+ value: _currentConfig.idpMetadata.entityID,
326
+ }, {
327
+ // secondary index on tenant + product
328
+ name: utils_1.IndexNames.TenantProduct,
329
+ value: dbutils.keyFromParts(_currentConfig.tenant, _currentConfig.product),
330
+ });
331
+ });
332
+ }
177
333
  /**
178
334
  * @swagger
179
335
  *
@@ -203,20 +359,45 @@ class APIController {
203
359
  * description: Success
204
360
  * schema:
205
361
  * type: object
206
- * properties:
207
- * provider:
208
- * type: string
209
362
  * example:
210
- * type: accounts.google.com
363
+ * {
364
+ * "config": {
365
+ * "idpMetadata": {
366
+ * "sso": {
367
+ * "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml",
368
+ * "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml"
369
+ * },
370
+ * "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
371
+ * "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
372
+ * "loginType": "idp",
373
+ * "provider": "okta.com"
374
+ * },
375
+ * "defaultRedirectUrl": "https://hoppscotch.io/",
376
+ * "redirectUrl": ["https://hoppscotch.io/"],
377
+ * "tenant": "hoppscotch.io",
378
+ * "product": "API Engine",
379
+ * "name": "Hoppscotch-SP",
380
+ * "description": "SP for hoppscotch.io",
381
+ * "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
382
+ * "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
383
+ * "certs": {
384
+ * "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
385
+ * "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
386
+ * }
387
+ * }
388
+ * }
389
+ * '400':
390
+ * description: Please provide `clientID` or `tenant` and `product`.
211
391
  * '401':
212
392
  * description: Unauthorized
213
393
  */
214
394
  getConfig(body) {
215
395
  return __awaiter(this, void 0, void 0, function* () {
216
396
  const { clientID, tenant, product } = body;
397
+ metrics.increment('getConfig');
217
398
  if (clientID) {
218
399
  const samlConfig = yield this.configStore.get(clientID);
219
- return samlConfig ? { provider: samlConfig.idpMetadata.provider } : {};
400
+ return samlConfig ? { config: samlConfig } : {};
220
401
  }
221
402
  if (tenant && product) {
222
403
  const samlConfigs = yield this.configStore.getByIndex({
@@ -226,7 +407,7 @@ class APIController {
226
407
  if (!samlConfigs || !samlConfigs.length) {
227
408
  return {};
228
409
  }
229
- return { provider: samlConfigs[0].idpMetadata.provider };
410
+ return { config: samlConfigs[0] };
230
411
  }
231
412
  throw new error_1.JacksonError('Please provide `clientID` or `tenant` and `product`.', 400);
232
413
  });
@@ -265,12 +446,15 @@ class APIController {
265
446
  * responses:
266
447
  * '200':
267
448
  * description: Success
449
+ * '400':
450
+ * description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.'
268
451
  * '401':
269
452
  * description: Unauthorized
270
453
  */
271
454
  deleteConfig(body) {
272
455
  return __awaiter(this, void 0, void 0, function* () {
273
456
  const { clientID, clientSecret, tenant, product } = body;
457
+ metrics.increment('deleteConfig');
274
458
  if (clientID && clientSecret) {
275
459
  const samlConfig = yield this.configStore.get(clientID);
276
460
  if (!samlConfig) {
@@ -280,7 +464,7 @@ class APIController {
280
464
  yield this.configStore.delete(clientID);
281
465
  }
282
466
  else {
283
- throw new error_1.JacksonError('clientSecret mismatch.', 400);
467
+ throw new error_1.JacksonError('clientSecret mismatch', 400);
284
468
  }
285
469
  return;
286
470
  }
@@ -14,6 +14,7 @@ export declare class OAuthController implements IOAuthController {
14
14
  });
15
15
  authorize(body: OAuthReqBody): Promise<{
16
16
  redirect_url: string;
17
+ authorize_form: string;
17
18
  }>;
18
19
  samlResponse(body: SAMLResponsePayload): Promise<{
19
20
  redirect_url: string;
@@ -33,15 +33,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.OAuthController = void 0;
35
35
  const crypto_1 = __importDefault(require("crypto"));
36
+ const util_1 = require("util");
37
+ const zlib_1 = require("zlib");
36
38
  const dbutils = __importStar(require("../db/utils"));
39
+ const metrics = __importStar(require("../opentelemetry/metrics"));
37
40
  const saml_1 = __importDefault(require("../saml/saml"));
38
41
  const error_1 = require("./error");
39
42
  const allowed = __importStar(require("./oauth/allowed"));
40
43
  const codeVerifier = __importStar(require("./oauth/code-verifier"));
41
44
  const redirect = __importStar(require("./oauth/redirect"));
42
45
  const utils_1 = require("./utils");
43
- const util_1 = require("util");
44
- const zlib_1 = require("zlib");
45
46
  const deflateRawAsync = (0, util_1.promisify)(zlib_1.deflateRaw);
46
47
  const relayStatePrefix = 'boxyhq_jackson_';
47
48
  function getEncodedClientId(client_id) {
@@ -74,6 +75,7 @@ class OAuthController {
74
75
  const { response_type = 'code', client_id, redirect_uri, state, tenant, product, code_challenge, code_challenge_method = '',
75
76
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
77
  provider = 'saml', } = body;
78
+ metrics.increment('oauthAuthorize');
77
79
  if (!redirect_uri) {
78
80
  throw new error_1.JacksonError('Please specify a redirect URL.', 400);
79
81
  }
@@ -119,7 +121,20 @@ class OAuthController {
119
121
  if (!allowed.redirect(redirect_uri, samlConfig.redirectUrl)) {
120
122
  throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
121
123
  }
124
+ let ssoUrl;
125
+ let post = false;
126
+ const { sso } = samlConfig.idpMetadata;
127
+ if ('redirectUrl' in sso) {
128
+ // HTTP Redirect binding
129
+ ssoUrl = sso.redirectUrl;
130
+ }
131
+ else if ('postUrl' in sso) {
132
+ // HTTP-POST binding
133
+ ssoUrl = sso.postUrl;
134
+ post = true;
135
+ }
122
136
  const samlReq = saml_1.default.request({
137
+ ssoUrl,
123
138
  entityID: this.opts.samlAudience,
124
139
  callbackUrl: this.opts.externalUrl + this.opts.samlPath,
125
140
  signingKey: samlConfig.certs.privateKey,
@@ -133,13 +148,24 @@ class OAuthController {
133
148
  code_challenge,
134
149
  code_challenge_method,
135
150
  });
136
- // deepak: When supporting HTTP-POST skip deflate
137
- const samlReqEnc = yield deflateRawAsync(samlReq.request);
138
- const redirectUrl = redirect.success(samlConfig.idpMetadata.sso.redirectUrl, {
139
- RelayState: relayStatePrefix + sessionId,
140
- SAMLRequest: Buffer.from(samlReqEnc).toString('base64'),
141
- });
142
- return { redirect_url: redirectUrl };
151
+ const relayState = relayStatePrefix + sessionId;
152
+ let redirectUrl;
153
+ let authorizeForm;
154
+ if (!post) {
155
+ // HTTP Redirect binding
156
+ redirectUrl = redirect.success(ssoUrl, {
157
+ RelayState: relayState,
158
+ SAMLRequest: Buffer.from(yield deflateRawAsync(samlReq.request)).toString('base64'),
159
+ });
160
+ }
161
+ else {
162
+ // HTTP POST binding
163
+ authorizeForm = (0, utils_1.createAuthorizeForm)(relayState, encodeURI(Buffer.from(samlReq.request).toString('base64')), ssoUrl);
164
+ }
165
+ return {
166
+ redirect_url: redirectUrl,
167
+ authorize_form: authorizeForm,
168
+ };
143
169
  });
144
170
  }
145
171
  samlResponse(body) {
@@ -262,6 +288,7 @@ class OAuthController {
262
288
  token(body) {
263
289
  return __awaiter(this, void 0, void 0, function* () {
264
290
  const { client_id, client_secret, code_verifier, code, grant_type = 'authorization_code' } = body;
291
+ metrics.increment('oauthToken');
265
292
  if (grant_type !== 'authorization_code') {
266
293
  throw new error_1.JacksonError('Unsupported grant_type', 400);
267
294
  }
@@ -292,6 +319,12 @@ class OAuthController {
292
319
  throw new error_1.JacksonError('Invalid client_id or client_secret', 401);
293
320
  }
294
321
  }
322
+ else {
323
+ // encoded client_id, verify client_secret
324
+ if (client_secret !== this.opts.clientSecretVerifier) {
325
+ throw new error_1.JacksonError('Invalid client_secret', 401);
326
+ }
327
+ }
295
328
  }
296
329
  }
297
330
  else if (codeVal && codeVal.session) {
@@ -339,6 +372,7 @@ class OAuthController {
339
372
  userInfo(token) {
340
373
  return __awaiter(this, void 0, void 0, function* () {
341
374
  const rsp = yield this.tokenStore.get(token);
375
+ metrics.increment('oauthUserInfo');
342
376
  if (!rsp || !rsp.claims) {
343
377
  throw new error_1.JacksonError('Invalid token', 403);
344
378
  }
@@ -2,3 +2,4 @@ export declare enum IndexNames {
2
2
  EntityID = "entityID",
3
3
  TenantProduct = "tenantProduct"
4
4
  }
5
+ export declare const createAuthorizeForm: (relayState: string, samlReqEnc: string, postUrl: string) => string;
@@ -1,8 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IndexNames = void 0;
3
+ exports.createAuthorizeForm = exports.IndexNames = void 0;
4
4
  var IndexNames;
5
5
  (function (IndexNames) {
6
6
  IndexNames["EntityID"] = "entityID";
7
7
  IndexNames["TenantProduct"] = "tenantProduct";
8
8
  })(IndexNames = exports.IndexNames || (exports.IndexNames = {}));
9
+ const createAuthorizeForm = (relayState, samlReqEnc, postUrl) => {
10
+ const formElements = [
11
+ '<!DOCTYPE html>',
12
+ '<html>',
13
+ '<head>',
14
+ '<meta charset="utf-8">',
15
+ '<meta http-equiv="x-ua-compatible" content="ie=edge">',
16
+ '</head>',
17
+ '<body onload="document.forms[0].submit()">',
18
+ '<noscript>',
19
+ '<p>Note: Since your browser does not support JavaScript, you must press the Continue button once to proceed.</p>',
20
+ '</noscript>',
21
+ '<form method="post" action="' + encodeURI(postUrl) + '">',
22
+ '<input type="hidden" name="RelayState" value="' + relayState + '"/>',
23
+ '<input type="hidden" name="SAMLRequest" value="' + samlReqEnc + '"/>',
24
+ '<input type="submit" value="Continue" />',
25
+ '</form>',
26
+ '<script>document.forms[0].style.display="none";</script>',
27
+ '</body>',
28
+ '</html>',
29
+ ];
30
+ return formElements.join('');
31
+ };
32
+ exports.createAuthorizeForm = createAuthorizeForm;
package/dist/db/db.d.ts CHANGED
@@ -4,6 +4,7 @@ declare class DB implements DatabaseDriver {
4
4
  private encryptionKey;
5
5
  constructor(db: DatabaseDriver, encryptionKey: EncryptionKey);
6
6
  get(namespace: string, key: string): Promise<unknown>;
7
+ getAll(namespace: any): Promise<unknown[]>;
7
8
  getByIndex(namespace: string, idx: Index): Promise<unknown[]>;
8
9
  put(namespace: string, key: string, val: unknown, ttl?: number, ...indexes: Index[]): Promise<unknown>;
9
10
  delete(namespace: string, key: string): Promise<unknown>;
package/dist/db/db.js CHANGED
@@ -57,6 +57,15 @@ class DB {
57
57
  return decrypt(res, this.encryptionKey);
58
58
  });
59
59
  }
60
+ getAll(namespace) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const res = (yield this.db.getAll(namespace));
63
+ const encryptionKey = this.encryptionKey;
64
+ return res.map((r) => {
65
+ return decrypt(r, encryptionKey);
66
+ });
67
+ });
68
+ }
60
69
  getByIndex(namespace, idx) {
61
70
  return __awaiter(this, void 0, void 0, function* () {
62
71
  const res = yield this.db.getByIndex(namespace, idx);
package/dist/db/mem.d.ts CHANGED
@@ -10,6 +10,7 @@ declare class Mem implements DatabaseDriver {
10
10
  constructor(options: DatabaseOption);
11
11
  init(): Promise<Mem>;
12
12
  get(namespace: string, key: string): Promise<any>;
13
+ getAll(namespace: string): Promise<unknown[]>;
13
14
  getByIndex(namespace: string, idx: Index): Promise<any>;
14
15
  put(namespace: string, key: string, val: Encrypted, ttl?: number, ...indexes: any[]): Promise<any>;
15
16
  delete(namespace: string, key: string): Promise<any>;
package/dist/db/mem.js CHANGED
@@ -66,6 +66,21 @@ class Mem {
66
66
  return null;
67
67
  });
68
68
  }
69
+ getAll(namespace) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ const returnValue = [];
72
+ if (namespace) {
73
+ for (const key in this.store) {
74
+ if (key.startsWith(namespace)) {
75
+ returnValue.push(this.store[key]);
76
+ }
77
+ }
78
+ }
79
+ if (returnValue)
80
+ return returnValue;
81
+ return [];
82
+ });
83
+ }
69
84
  getByIndex(namespace, idx) {
70
85
  return __awaiter(this, void 0, void 0, function* () {
71
86
  const dbKeys = yield this.indexes[dbutils.keyForIndex(namespace, idx)];
@@ -80,6 +95,10 @@ class Mem {
80
95
  return __awaiter(this, void 0, void 0, function* () {
81
96
  const k = dbutils.key(namespace, key);
82
97
  this.store[k] = val;
98
+ if (!Date.parse(this.store['createdAt']))
99
+ this.store['createdAt'] = new Date().toISOString();
100
+ this.store['modifiedAt'] = new Date().toISOString();
101
+ // console.log(this.store)
83
102
  if (ttl) {
84
103
  this.ttlStore[k] = {
85
104
  namespace,
@@ -7,6 +7,7 @@ declare class Mongo implements DatabaseDriver {
7
7
  constructor(options: DatabaseOption);
8
8
  init(): Promise<Mongo>;
9
9
  get(namespace: string, key: string): Promise<any>;
10
+ getAll(namespace: string): Promise<unknown[]>;
10
11
  getByIndex(namespace: string, idx: Index): Promise<any>;
11
12
  put(namespace: string, key: string, val: Encrypted, ttl?: number, ...indexes: any[]): Promise<void>;
12
13
  delete(namespace: string, key: string): Promise<any>;
package/dist/db/mongo.js CHANGED
@@ -36,8 +36,16 @@ class Mongo {
36
36
  }
37
37
  init() {
38
38
  return __awaiter(this, void 0, void 0, function* () {
39
- this.client = new mongodb_1.MongoClient(this.options.url);
40
- yield this.client.connect();
39
+ try {
40
+ if (!this.options.url) {
41
+ throw Error('Please specify a db url');
42
+ }
43
+ this.client = new mongodb_1.MongoClient(this.options.url);
44
+ yield this.client.connect();
45
+ }
46
+ catch (err) {
47
+ console.error(`error connecting to ${this.options.type} db: ${err}`);
48
+ }
41
49
  this.db = this.client.db();
42
50
  this.collection = this.db.collection('jacksonStore');
43
51
  yield this.collection.createIndex({ indexes: 1 });
@@ -56,6 +64,15 @@ class Mongo {
56
64
  return null;
57
65
  });
58
66
  }
67
+ getAll(namespace) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ const _namespaceMatch = new RegExp(`^${namespace}:.*`);
70
+ const docs = yield this.collection.find({ _id: _namespaceMatch }).toArray();
71
+ if (docs)
72
+ return docs.map(({ value }) => value);
73
+ return [];
74
+ });
75
+ }
59
76
  getByIndex(namespace, idx) {
60
77
  return __awaiter(this, void 0, void 0, function* () {
61
78
  const docs = yield this.collection
@@ -86,8 +103,12 @@ class Mongo {
86
103
  }
87
104
  doc.indexes.push(idxKey);
88
105
  }
106
+ doc.modifiedAt = new Date().toISOString();
89
107
  yield this.collection.updateOne({ _id: dbutils.key(namespace, key) }, {
90
108
  $set: doc,
109
+ $setOnInsert: {
110
+ createdAt: new Date().toISOString(),
111
+ },
91
112
  }, { upsert: true });
92
113
  });
93
114
  }
@@ -5,6 +5,7 @@ declare class Redis implements DatabaseDriver {
5
5
  constructor(options: DatabaseOption);
6
6
  init(): Promise<Redis>;
7
7
  get(namespace: string, key: string): Promise<any>;
8
+ getAll(namespace: string): Promise<unknown[]>;
8
9
  getByIndex(namespace: string, idx: Index): Promise<any>;
9
10
  put(namespace: string, key: string, val: Encrypted, ttl?: number, ...indexes: any[]): Promise<void>;
10
11
  delete(namespace: string, key: string): Promise<any>;
package/dist/db/redis.js CHANGED
@@ -57,6 +57,26 @@ class Redis {
57
57
  return null;
58
58
  });
59
59
  }
60
+ getAll(namespace) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const keys = yield this.client.sendCommand(['keys', namespace + ':*']);
63
+ const returnValue = [];
64
+ for (let i = 0; i < keys.length; i++) {
65
+ try {
66
+ if (this.client.get(keys[i])) {
67
+ const value = yield this.client.get(keys[i]);
68
+ returnValue.push(JSON.parse(value));
69
+ }
70
+ }
71
+ catch (error) {
72
+ console.error(error);
73
+ }
74
+ }
75
+ if (returnValue)
76
+ return returnValue;
77
+ return [];
78
+ });
79
+ }
60
80
  getByIndex(namespace, idx) {
61
81
  return __awaiter(this, void 0, void 0, function* () {
62
82
  const dbKeys = yield this.client.sMembers(dbutils.keyForIndex(namespace, idx));
@@ -3,4 +3,6 @@ export declare class JacksonStore {
3
3
  value: string;
4
4
  iv?: string;
5
5
  tag?: string;
6
+ createdAt?: Date;
7
+ modifiedAt?: string;
6
8
  }
@@ -36,6 +36,19 @@ __decorate([
36
36
  nullable: true,
37
37
  })
38
38
  ], JacksonStore.prototype, "tag", void 0);
39
+ __decorate([
40
+ (0, typeorm_1.Column)({
41
+ type: 'timestamp',
42
+ default: () => 'CURRENT_TIMESTAMP',
43
+ nullable: false,
44
+ })
45
+ ], JacksonStore.prototype, "createdAt", void 0);
46
+ __decorate([
47
+ (0, typeorm_1.Column)({
48
+ type: 'timestamp',
49
+ nullable: true,
50
+ })
51
+ ], JacksonStore.prototype, "modifiedAt", void 0);
39
52
  JacksonStore = __decorate([
40
53
  (0, typeorm_1.Entity)()
41
54
  ], JacksonStore);
@@ -10,6 +10,7 @@ declare class Sql implements DatabaseDriver {
10
10
  constructor(options: DatabaseOption);
11
11
  init(): Promise<Sql>;
12
12
  get(namespace: string, key: string): Promise<any>;
13
+ getAll(namespace: string): Promise<unknown[]>;
13
14
  getByIndex(namespace: string, idx: Index): Promise<any>;
14
15
  put(namespace: string, key: string, val: Encrypted, ttl?: number, ...indexes: any[]): Promise<void>;
15
16
  delete(namespace: string, key: string): Promise<any>;
@@ -95,7 +95,7 @@ class Sql {
95
95
  }
96
96
  get(namespace, key) {
97
97
  return __awaiter(this, void 0, void 0, function* () {
98
- let res = yield this.storeRepository.findOne({
98
+ const res = yield this.storeRepository.findOne({
99
99
  key: dbutils.key(namespace, key),
100
100
  });
101
101
  if (res && res.value) {
@@ -108,6 +108,22 @@ class Sql {
108
108
  return null;
109
109
  });
110
110
  }
111
+ getAll(namespace) {
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ const response = yield this.storeRepository.find({
114
+ where: { key: (0, typeorm_1.Like)(`%${namespace}%`) },
115
+ select: ['value', 'iv', 'tag'],
116
+ order: {
117
+ ['createdAt']: 'DESC',
118
+ // ['createdAt']: 'ASC',
119
+ },
120
+ });
121
+ const returnValue = JSON.parse(JSON.stringify(response));
122
+ if (returnValue)
123
+ return returnValue;
124
+ return [];
125
+ });
126
+ }
111
127
  getByIndex(namespace, idx) {
112
128
  return __awaiter(this, void 0, void 0, function* () {
113
129
  const res = yield this.indexRepository.find({
@@ -135,6 +151,7 @@ class Sql {
135
151
  store.value = val.value;
136
152
  store.iv = val.iv;
137
153
  store.tag = val.tag;
154
+ store.modifiedAt = new Date().toISOString();
138
155
  yield transactionalEntityManager.save(store);
139
156
  if (ttl) {
140
157
  const ttlRec = new JacksonTTL_1.JacksonTTL();
package/dist/db/store.js CHANGED
@@ -40,6 +40,11 @@ class Store {
40
40
  return yield this.db.get(this.namespace, dbutils.keyDigest(key));
41
41
  });
42
42
  }
43
+ getAll() {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ return yield this.db.getAll(this.namespace);
46
+ });
47
+ }
43
48
  getByIndex(idx) {
44
49
  return __awaiter(this, void 0, void 0, function* () {
45
50
  idx.value = dbutils.keyDigest(idx.value);
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- import { JacksonOption } from './typings';
2
1
  import { APIController } from './controller/api';
3
2
  import { OAuthController } from './controller/oauth';
3
+ import { AdminController } from './controller/admin';
4
+ import { JacksonOption } from './typings';
4
5
  export declare const controllers: (opts: JacksonOption) => Promise<{
5
6
  apiController: APIController;
6
7
  oauthController: OAuthController;
8
+ adminController: AdminController;
7
9
  }>;
8
10
  export default controllers;
9
11
  export * from './typings';
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.controllers = void 0;
26
26
  const api_1 = require("./controller/api");
27
27
  const oauth_1 = require("./controller/oauth");
28
+ const admin_1 = require("./controller/admin");
28
29
  const db_1 = __importDefault(require("./db/db"));
29
30
  const read_config_1 = __importDefault(require("./read-config"));
30
31
  const defaultOpts = (opts) => {
@@ -44,6 +45,7 @@ const defaultOpts = (opts) => {
44
45
  newOpts.db.type = newOpts.db.type || 'postgres'; // Only needed if DB_ENGINE is sql.
45
46
  newOpts.db.ttl = (newOpts.db.ttl || 300) * 1; // TTL for the code, session and token stores (in seconds)
46
47
  newOpts.db.cleanupLimit = (newOpts.db.cleanupLimit || 1000) * 1; // Limit cleanup of TTL entries to this many items at a time
48
+ newOpts.clientSecretVerifier = newOpts.clientSecretVerifier || 'dummy';
47
49
  return newOpts;
48
50
  };
49
51
  const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
@@ -54,6 +56,7 @@ const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
54
56
  const codeStore = db.store('oauth:code', opts.db.ttl);
55
57
  const tokenStore = db.store('oauth:token', opts.db.ttl);
56
58
  const apiController = new api_1.APIController({ configStore });
59
+ const adminController = new admin_1.AdminController({ configStore });
57
60
  const oauthController = new oauth_1.OAuthController({
58
61
  configStore,
59
62
  sessionStore,
@@ -74,6 +77,7 @@ const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
74
77
  return {
75
78
  apiController,
76
79
  oauthController,
80
+ adminController,
77
81
  };
78
82
  });
79
83
  exports.controllers = controllers;
@@ -0,0 +1,2 @@
1
+ declare const increment: (action: string) => void;
2
+ export { increment };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.increment = void 0;
4
+ const api_metrics_1 = require("@opentelemetry/api-metrics");
5
+ const counters = {
6
+ createConfig: {
7
+ name: 'jackson.config.create',
8
+ description: 'Number of SAML config create requests',
9
+ },
10
+ getConfig: {
11
+ name: 'jackson.config.get',
12
+ description: 'Number of SAML config get requests',
13
+ },
14
+ deleteConfig: {
15
+ name: 'jackson.config.delete',
16
+ description: 'Number of SAML config delete requests',
17
+ },
18
+ oauthAuthorize: {
19
+ name: 'jackson.oauth.authorize',
20
+ description: 'Number of SAML oauth authorize requests',
21
+ },
22
+ oauthToken: {
23
+ name: 'jackson.oauth.token',
24
+ description: 'Number of SAML oauth token requests',
25
+ },
26
+ oauthUserInfo: {
27
+ name: 'jackson.oauth.userinfo',
28
+ description: 'Number of SAML oauth user info requests',
29
+ },
30
+ };
31
+ const createCounter = (action) => {
32
+ const meter = api_metrics_1.metrics.getMeterProvider().getMeter('jackson');
33
+ const counter = counters[action];
34
+ return meter.createCounter(counter.name, {
35
+ description: counter.description,
36
+ });
37
+ };
38
+ const increment = (action) => {
39
+ const counter = createCounter(action);
40
+ counter.add(1, { provider: 'saml' });
41
+ };
42
+ exports.increment = increment;
package/dist/saml/saml.js CHANGED
@@ -68,7 +68,7 @@ const request = ({ ssoUrl, entityID, callbackUrl, isPassive = false, forceAuthn
68
68
  '@ID': id,
69
69
  '@Version': '2.0',
70
70
  '@IssueInstant': date,
71
- '@ProtocolBinding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
71
+ '@ProtocolBinding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
72
72
  '@Destination': ssoUrl,
73
73
  'saml:Issuer': {
74
74
  '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion',
package/dist/typings.d.ts CHANGED
@@ -3,6 +3,8 @@ export declare type IdPConfig = {
3
3
  redirectUrl: string;
4
4
  tenant: string;
5
5
  product: string;
6
+ name: string;
7
+ description: string;
6
8
  rawMetadata?: string;
7
9
  encodedRawMetadata?: string;
8
10
  };
@@ -13,11 +15,12 @@ export interface OAuth {
13
15
  }
14
16
  export interface IAPIController {
15
17
  config(body: IdPConfig): Promise<OAuth>;
18
+ updateConfig(body: any): Promise<void>;
16
19
  getConfig(body: {
17
20
  clientID?: string;
18
21
  tenant?: string;
19
22
  product?: string;
20
- }): Promise<Partial<OAuth>>;
23
+ }): Promise<any>;
21
24
  deleteConfig(body: {
22
25
  clientID?: string;
23
26
  clientSecret?: string;
@@ -27,7 +30,8 @@ export interface IAPIController {
27
30
  }
28
31
  export interface IOAuthController {
29
32
  authorize(body: OAuthReqBody): Promise<{
30
- redirect_url: string;
33
+ redirect_url?: string;
34
+ authorize_form?: string;
31
35
  }>;
32
36
  samlResponse(body: SAMLResponsePayload): Promise<{
33
37
  redirect_url: string;
@@ -35,6 +39,9 @@ export interface IOAuthController {
35
39
  token(body: OAuthTokenReq): Promise<OAuthTokenRes>;
36
40
  userInfo(token: string): Promise<Profile>;
37
41
  }
42
+ export interface IAdminController {
43
+ getAllConfig(): any;
44
+ }
38
45
  export interface OAuthReqBody {
39
46
  response_type: 'code';
40
47
  client_id: string;
@@ -73,12 +80,14 @@ export interface Index {
73
80
  value: string;
74
81
  }
75
82
  export interface DatabaseDriver {
83
+ getAll(namespace: string): Promise<unknown[]>;
76
84
  get(namespace: string, key: string): Promise<any>;
77
85
  put(namespace: string, key: string, val: any, ttl: number, ...indexes: Index[]): Promise<any>;
78
86
  delete(namespace: string, key: string): Promise<any>;
79
87
  getByIndex(namespace: string, idx: Index): Promise<any>;
80
88
  }
81
89
  export interface Storable {
90
+ getAll(): Promise<unknown[]>;
82
91
  get(key: string): Promise<any>;
83
92
  put(key: string, val: any, ...indexes: Index[]): Promise<any>;
84
93
  delete(key: string): Promise<any>;
@@ -123,4 +132,5 @@ export interface JacksonOption {
123
132
  preLoadedConfig?: string;
124
133
  idpEnabled?: boolean;
125
134
  db: DatabaseOption;
135
+ clientSecretVerifier?: string;
126
136
  }
@@ -0,0 +1,16 @@
1
+ import {MigrationInterface, QueryRunner} from "typeorm";
2
+
3
+ export class createdAt1644332636666 implements MigrationInterface {
4
+ name = 'createdAt1644332636666'
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` ADD \`createdAt\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP()`);
8
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` ADD \`modifiedAt\` timestamp NULL`);
9
+ }
10
+
11
+ public async down(queryRunner: QueryRunner): Promise<void> {
12
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` DROP COLUMN \`modifiedAt\``);
13
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` DROP COLUMN \`createdAt\``);
14
+ }
15
+
16
+ }
@@ -0,0 +1,16 @@
1
+ import {MigrationInterface, QueryRunner} from "typeorm";
2
+
3
+ export class createdAt1644332641078 implements MigrationInterface {
4
+ name = 'createdAt1644332641078'
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` ADD \`createdAt\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP`);
8
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` ADD \`modifiedAt\` timestamp NULL`);
9
+ }
10
+
11
+ public async down(queryRunner: QueryRunner): Promise<void> {
12
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` DROP COLUMN \`modifiedAt\``);
13
+ await queryRunner.query(`ALTER TABLE \`jackson_store\` DROP COLUMN \`createdAt\``);
14
+ }
15
+
16
+ }
@@ -0,0 +1,16 @@
1
+ import {MigrationInterface, QueryRunner} from "typeorm";
2
+
3
+ export class createdAt1644332647279 implements MigrationInterface {
4
+ name = 'createdAt1644332647279'
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(`ALTER TABLE "jackson_store" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT now()`);
8
+ await queryRunner.query(`ALTER TABLE "jackson_store" ADD "modifiedAt" TIMESTAMP`);
9
+ }
10
+
11
+ public async down(queryRunner: QueryRunner): Promise<void> {
12
+ await queryRunner.query(`ALTER TABLE "jackson_store" DROP COLUMN "modifiedAt"`);
13
+ await queryRunner.query(`ALTER TABLE "jackson_store" DROP COLUMN "createdAt"`);
14
+ }
15
+
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxyhq/saml-jackson",
3
- "version": "0.3.7",
3
+ "version": "0.3.8-beta.763",
4
4
  "description": "SAML 2.0 service",
5
5
  "keywords": [
6
6
  "SAML 2.0"
@@ -18,9 +18,9 @@
18
18
  ],
19
19
  "scripts": {
20
20
  "build": "tsc -p tsconfig.build.json",
21
- "db:migration:generate:postgres": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate --config ormconfig.js -n Initial",
22
- "db:migration:generate:mysql": "cross-env DB_TYPE=mysql DB_URL=mysql://root:mysql@localhost:3307/mysql ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate --config ormconfig.js -n Initial",
23
- "db:migration:generate:mariadb": "cross-env DB_TYPE=mariadb DB_URL=mariadb://root@localhost:3306/mysql ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate --config ormconfig.js -n Initial",
21
+ "db:migration:generate:postgres": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate --config ormconfig.js -n createdAt",
22
+ "db:migration:generate:mysql": "cross-env DB_TYPE=mysql DB_URL=mysql://root:mysql@localhost:3307/mysql ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate --config ormconfig.js -n createdAt",
23
+ "db:migration:generate:mariadb": "cross-env DB_TYPE=mariadb DB_URL=mariadb://root@localhost:3306/mysql ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate --config ormconfig.js -n createdAt",
24
24
  "db:migration:run:postgres": "ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run",
25
25
  "db:migration:run:mysql": "cross-env DB_TYPE=mysql DB_URL=mysql://root:mysql@localhost:3307/mysql ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run",
26
26
  "db:migration:run:mariadb": "cross-env DB_TYPE=mariadb DB_URL=mariadb://root@localhost:3306/mysql ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run",
@@ -37,10 +37,9 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@boxyhq/saml20": "0.2.0",
40
+ "@opentelemetry/api-metrics": "0.27.0",
40
41
  "@peculiar/webcrypto": "1.2.3",
41
42
  "@peculiar/x509": "1.6.1",
42
- "cors": "2.8.5",
43
- "express": "4.17.2",
44
43
  "mongodb": "4.3.1",
45
44
  "mysql2": "2.3.3",
46
45
  "pg": "8.7.3",
@@ -55,7 +54,6 @@
55
54
  "xmlbuilder": "15.1.1"
56
55
  },
57
56
  "devDependencies": {
58
- "@types/express": "4.17.13",
59
57
  "@types/node": "17.0.17",
60
58
  "@types/sinon": "10.0.11",
61
59
  "@types/tap": "15.0.5",