@boxyhq/saml-jackson 0.3.7-beta.759 → 0.3.8-beta.762

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,24 @@ 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
+ * required: true
26
+ * example: cal-saml-config
27
+ * - name: description
28
+ * description: A short description for the config not more than 100 characters
29
+ * type: string
30
+ * in: formData
31
+ * example: SAML login for cal.com app
21
32
  * - name: encodedRawMetadata
22
33
  * description: Base64 encoding of the XML metadata
23
34
  * in: formData
24
- * required: true
35
+ * type: string
36
+ * - name: rawMetadata
37
+ * description: Raw XML metadata
38
+ * in: formData
25
39
  * type: string
26
40
  * - name: defaultRedirectUrl
27
41
  * description: The redirect URL to use in the IdP login flow
@@ -63,10 +77,86 @@ export declare class APIController implements IAPIController {
63
77
  * client_id: 8958e13053832b5af58fdf2ee83f35f5d013dc74
64
78
  * client_secret: 13f01f4df5b01770c616e682d14d3ba23f20948cfa89b1d7
65
79
  * type: accounts.google.com
80
+ * 400:
81
+ * 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
82
  * 401:
67
83
  * description: Unauthorized
68
84
  */
69
85
  config(body: IdPConfig): Promise<OAuth>;
86
+ /**
87
+ * @swagger
88
+ *
89
+ * /api/v1/saml/config:
90
+ * patch:
91
+ * summary: Update SAML configuration
92
+ * operationId: update-saml-config
93
+ * tags: [SAML Config]
94
+ * consumes:
95
+ * - application/json
96
+ * - application/x-www-form-urlencoded
97
+ * parameters:
98
+ * - name: clientID
99
+ * description: Client ID for the config
100
+ * type: string
101
+ * in: formData
102
+ * required: true
103
+ * - name: clientSecret
104
+ * description: Client Secret for the config
105
+ * type: string
106
+ * in: formData
107
+ * required: true
108
+ * - name: name
109
+ * description: Name/identifier for the config
110
+ * type: string
111
+ * in: formData
112
+ * required: true
113
+ * example: cal-saml-config
114
+ * - name: description
115
+ * description: A short description for the config not more than 100 characters
116
+ * type: string
117
+ * in: formData
118
+ * example: SAML login for cal.com app
119
+ * - name: encodedRawMetadata
120
+ * description: Base64 encoding of the XML metadata
121
+ * in: formData
122
+ * type: string
123
+ * - name: rawMetadata
124
+ * description: Raw XML metadata
125
+ * in: formData
126
+ * type: string
127
+ * - name: defaultRedirectUrl
128
+ * description: The redirect URL to use in the IdP login flow
129
+ * in: formData
130
+ * required: true
131
+ * type: string
132
+ * example: http://localhost:3000/login/saml
133
+ * - name: redirectUrl
134
+ * description: JSON encoded array containing a list of allowed redirect URLs
135
+ * in: formData
136
+ * required: true
137
+ * type: string
138
+ * example: '["http://localhost:3000/*"]'
139
+ * - name: tenant
140
+ * description: Tenant
141
+ * in: formData
142
+ * required: true
143
+ * type: string
144
+ * example: boxyhq.com
145
+ * - name: product
146
+ * description: Product
147
+ * in: formData
148
+ * required: true
149
+ * type: string
150
+ * example: demo
151
+ * responses:
152
+ * 204:
153
+ * description: Success
154
+ * 400:
155
+ * description: Please provide clientID | Please provide clientSecret | clientSecret mismatch | Tenant/Product config mismatch with IdP metadata | Description should not exceed 100 characters
156
+ * 401:
157
+ * description: Unauthorized
158
+ */
159
+ updateConfig(body: any): Promise<void>;
70
160
  /**
71
161
  * @swagger
72
162
  *
@@ -96,11 +186,35 @@ export declare class APIController implements IAPIController {
96
186
  * description: Success
97
187
  * schema:
98
188
  * type: object
99
- * properties:
100
- * provider:
101
- * type: string
102
189
  * example:
103
- * type: accounts.google.com
190
+ * {
191
+ * "config": {
192
+ * "idpMetadata": {
193
+ * "sso": {
194
+ * "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml",
195
+ * "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml"
196
+ * },
197
+ * "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
198
+ * "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
199
+ * "loginType": "idp",
200
+ * "provider": "okta.com"
201
+ * },
202
+ * "defaultRedirectUrl": "https://hoppscotch.io/",
203
+ * "redirectUrl": ["https://hoppscotch.io/"],
204
+ * "tenant": "hoppscotch.io",
205
+ * "product": "API Engine",
206
+ * "name": "Hoppscotch-SP",
207
+ * "description": "SP for hoppscotch.io",
208
+ * "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
209
+ * "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
210
+ * "certs": {
211
+ * "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
212
+ * "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
213
+ * }
214
+ * }
215
+ * }
216
+ * '400':
217
+ * description: Please provide `clientID` or `tenant` and `product`.
104
218
  * '401':
105
219
  * description: Unauthorized
106
220
  */
@@ -108,7 +222,7 @@ export declare class APIController implements IAPIController {
108
222
  clientID: string;
109
223
  tenant: string;
110
224
  product: string;
111
- }): Promise<Partial<OAuth>>;
225
+ }): Promise<any>;
112
226
  /**
113
227
  * @swagger
114
228
  * /api/v1/saml/config:
@@ -143,6 +257,8 @@ export declare class APIController implements IAPIController {
143
257
  * responses:
144
258
  * '200':
145
259
  * description: Success
260
+ * '400':
261
+ * description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.'
146
262
  * '401':
147
263
  * description: Unauthorized
148
264
  */
@@ -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
  };
@@ -44,7 +55,7 @@ class APIController {
44
55
  this.configStore = configStore;
45
56
  }
46
57
  _validateIdPConfig(body) {
47
- const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product } = body;
58
+ const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product, name, description, } = body;
48
59
  if (!rawMetadata && !encodedRawMetadata) {
49
60
  throw new error_1.JacksonError('Please provide rawMetadata or encodedRawMetadata', 400);
50
61
  }
@@ -60,6 +71,12 @@ class APIController {
60
71
  if (!product) {
61
72
  throw new error_1.JacksonError('Please provide product', 400);
62
73
  }
74
+ if (!name) {
75
+ throw new error_1.JacksonError('Please provide a friendly name', 400);
76
+ }
77
+ if (description && description.length > 100) {
78
+ throw new error_1.JacksonError('Description should not exceed 100 characters', 400);
79
+ }
63
80
  }
64
81
  /**
65
82
  * @swagger
@@ -74,10 +91,24 @@ class APIController {
74
91
  * consumes:
75
92
  * - application/x-www-form-urlencoded
76
93
  * parameters:
94
+ * - name: name
95
+ * description: Name/identifier for the config
96
+ * type: string
97
+ * in: formData
98
+ * required: true
99
+ * example: cal-saml-config
100
+ * - name: description
101
+ * description: A short description for the config not more than 100 characters
102
+ * type: string
103
+ * in: formData
104
+ * example: SAML login for cal.com app
77
105
  * - name: encodedRawMetadata
78
106
  * description: Base64 encoding of the XML metadata
79
107
  * in: formData
80
- * required: true
108
+ * type: string
109
+ * - name: rawMetadata
110
+ * description: Raw XML metadata
111
+ * in: formData
81
112
  * type: string
82
113
  * - name: defaultRedirectUrl
83
114
  * description: The redirect URL to use in the IdP login flow
@@ -119,12 +150,14 @@ class APIController {
119
150
  * client_id: 8958e13053832b5af58fdf2ee83f35f5d013dc74
120
151
  * client_secret: 13f01f4df5b01770c616e682d14d3ba23f20948cfa89b1d7
121
152
  * type: accounts.google.com
153
+ * 400:
154
+ * 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
122
155
  * 401:
123
156
  * description: Unauthorized
124
157
  */
125
158
  config(body) {
126
159
  return __awaiter(this, void 0, void 0, function* () {
127
- const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product } = body;
160
+ const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product, name, description, } = body;
128
161
  metrics.increment('createConfig');
129
162
  this._validateIdPConfig(body);
130
163
  let metaData = rawMetadata;
@@ -157,6 +190,8 @@ class APIController {
157
190
  redirectUrl: JSON.parse(redirectUrl),
158
191
  tenant,
159
192
  product,
193
+ name,
194
+ description,
160
195
  clientID,
161
196
  clientSecret,
162
197
  certs,
@@ -176,6 +211,130 @@ class APIController {
176
211
  };
177
212
  });
178
213
  }
214
+ /**
215
+ * @swagger
216
+ *
217
+ * /api/v1/saml/config:
218
+ * patch:
219
+ * summary: Update SAML configuration
220
+ * operationId: update-saml-config
221
+ * tags: [SAML Config]
222
+ * consumes:
223
+ * - application/json
224
+ * - application/x-www-form-urlencoded
225
+ * parameters:
226
+ * - name: clientID
227
+ * description: Client ID for the config
228
+ * type: string
229
+ * in: formData
230
+ * required: true
231
+ * - name: clientSecret
232
+ * description: Client Secret for the config
233
+ * type: string
234
+ * in: formData
235
+ * required: true
236
+ * - name: name
237
+ * description: Name/identifier for the config
238
+ * type: string
239
+ * in: formData
240
+ * required: true
241
+ * example: cal-saml-config
242
+ * - name: description
243
+ * description: A short description for the config not more than 100 characters
244
+ * type: string
245
+ * in: formData
246
+ * example: SAML login for cal.com app
247
+ * - name: encodedRawMetadata
248
+ * description: Base64 encoding of the XML metadata
249
+ * in: formData
250
+ * type: string
251
+ * - name: rawMetadata
252
+ * description: Raw XML metadata
253
+ * in: formData
254
+ * type: string
255
+ * - name: defaultRedirectUrl
256
+ * description: The redirect URL to use in the IdP login flow
257
+ * in: formData
258
+ * required: true
259
+ * type: string
260
+ * example: http://localhost:3000/login/saml
261
+ * - name: redirectUrl
262
+ * description: JSON encoded array containing a list of allowed redirect URLs
263
+ * in: formData
264
+ * required: true
265
+ * type: string
266
+ * example: '["http://localhost:3000/*"]'
267
+ * - name: tenant
268
+ * description: Tenant
269
+ * in: formData
270
+ * required: true
271
+ * type: string
272
+ * example: boxyhq.com
273
+ * - name: product
274
+ * description: Product
275
+ * in: formData
276
+ * required: true
277
+ * type: string
278
+ * example: demo
279
+ * responses:
280
+ * 204:
281
+ * description: Success
282
+ * 400:
283
+ * description: Please provide clientID | Please provide clientSecret | clientSecret mismatch | Tenant/Product config mismatch with IdP metadata | Description should not exceed 100 characters
284
+ * 401:
285
+ * description: Unauthorized
286
+ */
287
+ updateConfig(body) {
288
+ var _a;
289
+ return __awaiter(this, void 0, void 0, function* () {
290
+ const { encodedRawMetadata, // could be empty
291
+ rawMetadata, // could be empty
292
+ defaultRedirectUrl, redirectUrl, name, description } = body, clientInfo = __rest(body, ["encodedRawMetadata", "rawMetadata", "defaultRedirectUrl", "redirectUrl", "name", "description"]);
293
+ if (!(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID)) {
294
+ throw new error_1.JacksonError('Please provide clientID', 400);
295
+ }
296
+ if (!(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientSecret)) {
297
+ throw new error_1.JacksonError('Please provide clientSecret', 400);
298
+ }
299
+ if (description && description.length > 100) {
300
+ throw new error_1.JacksonError('Description should not exceed 100 characters', 400);
301
+ }
302
+ const _currentConfig = (_a = (yield this.getConfig(clientInfo))) === null || _a === void 0 ? void 0 : _a.config;
303
+ if (_currentConfig.clientSecret !== (clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientSecret)) {
304
+ throw new error_1.JacksonError('clientSecret mismatch', 400);
305
+ }
306
+ let metaData = rawMetadata;
307
+ if (encodedRawMetadata) {
308
+ metaData = Buffer.from(encodedRawMetadata, 'base64').toString();
309
+ }
310
+ let newMetadata;
311
+ if (metaData) {
312
+ newMetadata = yield saml_1.default.parseMetadataAsync(metaData);
313
+ // extract provider
314
+ let providerName = extractHostName(newMetadata.entityID);
315
+ if (!providerName) {
316
+ providerName = extractHostName(newMetadata.sso.redirectUrl || newMetadata.sso.postUrl);
317
+ }
318
+ newMetadata.provider = providerName ? providerName : 'Unknown';
319
+ }
320
+ if (newMetadata) {
321
+ // check if clientID matches with new metadata payload
322
+ const clientID = dbutils.keyDigest(dbutils.keyFromParts(clientInfo.tenant, clientInfo.product, newMetadata.entityID));
323
+ if (clientID !== (clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID)) {
324
+ throw new error_1.JacksonError('Tenant/Product config mismatch with IdP metadata', 400);
325
+ }
326
+ }
327
+ 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 }), {
328
+ // secondary index on entityID
329
+ name: utils_1.IndexNames.EntityID,
330
+ value: _currentConfig.idpMetadata.entityID,
331
+ }, {
332
+ // secondary index on tenant + product
333
+ name: utils_1.IndexNames.TenantProduct,
334
+ value: dbutils.keyFromParts(_currentConfig.tenant, _currentConfig.product),
335
+ });
336
+ });
337
+ }
179
338
  /**
180
339
  * @swagger
181
340
  *
@@ -205,11 +364,35 @@ class APIController {
205
364
  * description: Success
206
365
  * schema:
207
366
  * type: object
208
- * properties:
209
- * provider:
210
- * type: string
211
367
  * example:
212
- * type: accounts.google.com
368
+ * {
369
+ * "config": {
370
+ * "idpMetadata": {
371
+ * "sso": {
372
+ * "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml",
373
+ * "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml"
374
+ * },
375
+ * "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
376
+ * "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
377
+ * "loginType": "idp",
378
+ * "provider": "okta.com"
379
+ * },
380
+ * "defaultRedirectUrl": "https://hoppscotch.io/",
381
+ * "redirectUrl": ["https://hoppscotch.io/"],
382
+ * "tenant": "hoppscotch.io",
383
+ * "product": "API Engine",
384
+ * "name": "Hoppscotch-SP",
385
+ * "description": "SP for hoppscotch.io",
386
+ * "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
387
+ * "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
388
+ * "certs": {
389
+ * "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
390
+ * "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
391
+ * }
392
+ * }
393
+ * }
394
+ * '400':
395
+ * description: Please provide `clientID` or `tenant` and `product`.
213
396
  * '401':
214
397
  * description: Unauthorized
215
398
  */
@@ -219,7 +402,7 @@ class APIController {
219
402
  metrics.increment('getConfig');
220
403
  if (clientID) {
221
404
  const samlConfig = yield this.configStore.get(clientID);
222
- return samlConfig ? { provider: samlConfig.idpMetadata.provider } : {};
405
+ return samlConfig ? { config: samlConfig } : {};
223
406
  }
224
407
  if (tenant && product) {
225
408
  const samlConfigs = yield this.configStore.getByIndex({
@@ -229,7 +412,7 @@ class APIController {
229
412
  if (!samlConfigs || !samlConfigs.length) {
230
413
  return {};
231
414
  }
232
- return { provider: samlConfigs[0].idpMetadata.provider };
415
+ return { config: samlConfigs[0] };
233
416
  }
234
417
  throw new error_1.JacksonError('Please provide `clientID` or `tenant` and `product`.', 400);
235
418
  });
@@ -268,6 +451,8 @@ class APIController {
268
451
  * responses:
269
452
  * '200':
270
453
  * description: Success
454
+ * '400':
455
+ * description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.'
271
456
  * '401':
272
457
  * description: Unauthorized
273
458
  */
@@ -284,7 +469,7 @@ class APIController {
284
469
  yield this.configStore.delete(clientID);
285
470
  }
286
471
  else {
287
- throw new error_1.JacksonError('clientSecret mismatch.', 400);
472
+ throw new error_1.JacksonError('clientSecret mismatch', 400);
288
473
  }
289
474
  return;
290
475
  }
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
1
  import { APIController } from './controller/api';
2
2
  import { OAuthController } from './controller/oauth';
3
+ import { AdminController } from './controller/admin';
3
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) => {
@@ -55,6 +56,7 @@ const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
55
56
  const codeStore = db.store('oauth:code', opts.db.ttl);
56
57
  const tokenStore = db.store('oauth:token', opts.db.ttl);
57
58
  const apiController = new api_1.APIController({ configStore });
59
+ const adminController = new admin_1.AdminController({ configStore });
58
60
  const oauthController = new oauth_1.OAuthController({
59
61
  configStore,
60
62
  sessionStore,
@@ -75,6 +77,7 @@ const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
75
77
  return {
76
78
  apiController,
77
79
  oauthController,
80
+ adminController,
78
81
  };
79
82
  });
80
83
  exports.controllers = controllers;
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;
@@ -36,6 +39,9 @@ export interface IOAuthController {
36
39
  token(body: OAuthTokenReq): Promise<OAuthTokenRes>;
37
40
  userInfo(token: string): Promise<Profile>;
38
41
  }
42
+ export interface IAdminController {
43
+ getAllConfig(): any;
44
+ }
39
45
  export interface OAuthReqBody {
40
46
  response_type: 'code';
41
47
  client_id: string;
@@ -74,12 +80,14 @@ export interface Index {
74
80
  value: string;
75
81
  }
76
82
  export interface DatabaseDriver {
83
+ getAll(namespace: string): Promise<unknown[]>;
77
84
  get(namespace: string, key: string): Promise<any>;
78
85
  put(namespace: string, key: string, val: any, ttl: number, ...indexes: Index[]): Promise<any>;
79
86
  delete(namespace: string, key: string): Promise<any>;
80
87
  getByIndex(namespace: string, idx: Index): Promise<any>;
81
88
  }
82
89
  export interface Storable {
90
+ getAll(): Promise<unknown[]>;
83
91
  get(key: string): Promise<any>;
84
92
  put(key: string, val: any, ...indexes: Index[]): Promise<any>;
85
93
  delete(key: string): Promise<any>;
@@ -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-beta.759",
3
+ "version": "0.3.8-beta.762",
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",