@boxyhq/saml-jackson 1.1.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/controller/error.d.ts +5 -0
  2. package/dist/controller/error.js +6 -1
  3. package/dist/controller/utils.d.ts +13 -0
  4. package/dist/controller/utils.js +27 -1
  5. package/dist/db/mem.js +7 -3
  6. package/dist/directory-sync/Base.d.ts +15 -0
  7. package/dist/directory-sync/Base.js +39 -0
  8. package/dist/directory-sync/DirectoryConfig.d.ts +43 -0
  9. package/dist/directory-sync/DirectoryConfig.js +186 -0
  10. package/dist/directory-sync/DirectoryGroups.d.ts +26 -0
  11. package/dist/directory-sync/DirectoryGroups.js +254 -0
  12. package/dist/directory-sync/DirectoryUsers.d.ts +22 -0
  13. package/dist/directory-sync/DirectoryUsers.js +175 -0
  14. package/dist/directory-sync/Groups.d.ts +47 -0
  15. package/dist/directory-sync/Groups.js +195 -0
  16. package/dist/directory-sync/Users.d.ts +47 -0
  17. package/dist/directory-sync/Users.js +135 -0
  18. package/dist/directory-sync/WebhookEventsLogger.d.ts +13 -0
  19. package/dist/directory-sync/WebhookEventsLogger.js +57 -0
  20. package/dist/directory-sync/events.d.ts +7 -0
  21. package/dist/directory-sync/events.js +53 -0
  22. package/dist/directory-sync/index.d.ts +6 -0
  23. package/dist/directory-sync/index.js +42 -0
  24. package/dist/directory-sync/request.d.ts +7 -0
  25. package/dist/directory-sync/request.js +30 -0
  26. package/dist/directory-sync/transform.d.ts +7 -0
  27. package/dist/directory-sync/transform.js +26 -0
  28. package/dist/directory-sync/utils.d.ts +39 -0
  29. package/dist/directory-sync/utils.js +140 -0
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.js +7 -3
  32. package/dist/typings.d.ts +275 -7
  33. package/dist/typings.js +9 -0
  34. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -27,16 +27,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.controllers = void 0;
30
+ const db_1 = __importDefault(require("./db/db"));
31
+ const defaultDb_1 = __importDefault(require("./db/defaultDb"));
32
+ const read_config_1 = __importDefault(require("./read-config"));
30
33
  const admin_1 = require("./controller/admin");
31
34
  const api_1 = require("./controller/api");
32
35
  const oauth_1 = require("./controller/oauth");
33
36
  const health_check_1 = require("./controller/health-check");
34
37
  const logout_1 = require("./controller/logout");
38
+ const directory_sync_1 = __importDefault(require("./directory-sync"));
35
39
  const oidc_discovery_1 = require("./controller/oidc-discovery");
36
40
  const sp_config_1 = require("./controller/sp-config");
37
- const db_1 = __importDefault(require("./db/db"));
38
- const defaultDb_1 = __importDefault(require("./db/defaultDb"));
39
- const read_config_1 = __importDefault(require("./read-config"));
40
41
  const defaultOpts = (opts) => {
41
42
  const newOpts = Object.assign({}, opts);
42
43
  if (!newOpts.externalUrl) {
@@ -45,6 +46,7 @@ const defaultOpts = (opts) => {
45
46
  if (!newOpts.samlPath) {
46
47
  throw new Error('samlPath is required');
47
48
  }
49
+ newOpts.scimPath = newOpts.scimPath || '/api/scim/v2.0';
48
50
  newOpts.samlAudience = newOpts.samlAudience || 'https://saml.boxyhq.com';
49
51
  newOpts.preLoadedConfig = newOpts.preLoadedConfig || ''; // path to folder containing static SAML config that will be preloaded. This is useful for self-hosted deployments that only have to support a single tenant (or small number of known tenants).
50
52
  newOpts.idpEnabled = newOpts.idpEnabled === true;
@@ -79,6 +81,7 @@ const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
79
81
  sessionStore,
80
82
  opts,
81
83
  });
84
+ const directorySync = yield (0, directory_sync_1.default)({ db, opts });
82
85
  const oidcDiscoveryController = new oidc_discovery_1.OidcDiscoveryController({ opts });
83
86
  const spConfig = new sp_config_1.SPSAMLConfig(opts);
84
87
  // write pre-loaded config if present
@@ -98,6 +101,7 @@ const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
98
101
  adminController,
99
102
  logoutController,
100
103
  healthCheckController,
104
+ directorySync,
101
105
  oidcDiscoveryController,
102
106
  };
103
107
  });
package/dist/typings.d.ts CHANGED
@@ -45,6 +45,13 @@ export interface IHealthCheckController {
45
45
  }>;
46
46
  init(): Promise<void>;
47
47
  }
48
+ export interface ILogoutController {
49
+ createRequest(body: SLORequestParams): Promise<{
50
+ logoutUrl: string | null;
51
+ logoutForm: string | null;
52
+ }>;
53
+ handleResponse(body: SAMLResponsePayload): Promise<any>;
54
+ }
48
55
  export interface IOidcDiscoveryController {
49
56
  openidConfig(): {
50
57
  issuer: string;
@@ -123,6 +130,9 @@ export interface Storable {
123
130
  delete(key: string): Promise<any>;
124
131
  getByIndex(idx: Index): Promise<any>;
125
132
  }
133
+ export interface DatabaseStore {
134
+ store(namespace: string): Storable;
135
+ }
126
136
  export interface Encrypted {
127
137
  iv?: string;
128
138
  tag?: string;
@@ -150,6 +160,7 @@ export interface JacksonOption {
150
160
  db: DatabaseOption;
151
161
  clientSecretVerifier?: string;
152
162
  idpDiscoveryPath?: string;
163
+ scimPath?: string;
153
164
  openid: {
154
165
  jwsAlg?: string;
155
166
  jwtSigningKeys?: {
@@ -186,13 +197,6 @@ export interface SAMLConfig {
186
197
  };
187
198
  defaultRedirectUrl: string;
188
199
  }
189
- export interface ILogoutController {
190
- createRequest(body: SLORequestParams): Promise<{
191
- logoutUrl: string | null;
192
- logoutForm: string | null;
193
- }>;
194
- handleResponse(body: SAMLResponsePayload): Promise<any>;
195
- }
196
200
  export interface OAuthErrorHandlerParams {
197
201
  error: 'invalid_request' | 'access_denied' | 'unauthorized_client' | 'unsupported_response_type' | 'invalid_scope' | 'server_error' | 'temporarily_unavailable';
198
202
  error_description: string;
@@ -211,4 +215,268 @@ export interface ISPSAMLConfig {
211
215
  toMarkdown(): string;
212
216
  toHTML(): string;
213
217
  }
218
+ export declare type DirectorySyncEventType = 'user.created' | 'user.updated' | 'user.deleted' | 'group.created' | 'group.updated' | 'group.deleted' | 'group.user_added' | 'group.user_removed';
219
+ export interface Base {
220
+ store(type: 'groups' | 'members' | 'users'): Storable;
221
+ setTenant(tenant: string): this;
222
+ setProduct(product: string): this;
223
+ setTenantAndProduct(tenant: string, product: string): this;
224
+ with(tenant: string, product: string): this;
225
+ createId(): string;
226
+ }
227
+ export interface Users extends Base {
228
+ list({ pageOffset, pageLimit, }: {
229
+ pageOffset?: number;
230
+ pageLimit?: number;
231
+ }): Promise<{
232
+ data: User[] | null;
233
+ error: ApiError | null;
234
+ }>;
235
+ get(id: string): Promise<{
236
+ data: User | null;
237
+ error: ApiError | null;
238
+ }>;
239
+ search(userName: string): Promise<{
240
+ data: User[] | null;
241
+ error: ApiError | null;
242
+ }>;
243
+ delete(id: string): Promise<{
244
+ data: null;
245
+ error: ApiError | null;
246
+ }>;
247
+ clear(): Promise<void>;
248
+ create(param: {
249
+ first_name: string;
250
+ last_name: string;
251
+ email: string;
252
+ active: boolean;
253
+ raw: any;
254
+ }): Promise<{
255
+ data: User | null;
256
+ error: ApiError | null;
257
+ }>;
258
+ update(id: string, param: {
259
+ first_name: string;
260
+ last_name: string;
261
+ email: string;
262
+ active: boolean;
263
+ raw: object;
264
+ }): Promise<{
265
+ data: User | null;
266
+ error: ApiError | null;
267
+ }>;
268
+ }
269
+ export interface Groups extends Base {
270
+ create(param: {
271
+ name: string;
272
+ raw: any;
273
+ }): Promise<{
274
+ data: Group | null;
275
+ error: ApiError | null;
276
+ }>;
277
+ removeAllUsers(groupId: string): Promise<void>;
278
+ list({ pageOffset, pageLimit, }: {
279
+ pageOffset?: number;
280
+ pageLimit?: number;
281
+ }): Promise<{
282
+ data: Group[] | null;
283
+ error: ApiError | null;
284
+ }>;
285
+ get(id: string): Promise<{
286
+ data: Group | null;
287
+ error: ApiError | null;
288
+ }>;
289
+ getAllUsers(groupId: string): Promise<{
290
+ user_id: string;
291
+ }[]>;
292
+ delete(id: string): Promise<{
293
+ data: null;
294
+ error: ApiError | null;
295
+ }>;
296
+ addUserToGroup(groupId: string, userId: string): Promise<void>;
297
+ isUserInGroup(groupId: string, userId: string): Promise<boolean>;
298
+ removeUserFromGroup(groupId: string, userId: string): Promise<void>;
299
+ search(displayName: string): Promise<{
300
+ data: Group[] | null;
301
+ error: ApiError | null;
302
+ }>;
303
+ update(id: string, param: {
304
+ name: string;
305
+ raw: any;
306
+ }): Promise<{
307
+ data: Group | null;
308
+ error: ApiError | null;
309
+ }>;
310
+ }
311
+ export declare type User = {
312
+ id: string;
313
+ email: string;
314
+ first_name: string;
315
+ last_name: string;
316
+ active: boolean;
317
+ raw?: any;
318
+ };
319
+ export declare type Group = {
320
+ id: string;
321
+ name: string;
322
+ raw?: any;
323
+ };
324
+ export declare enum DirectorySyncProviders {
325
+ 'azure-scim-v2' = "Azure SCIM v2.0",
326
+ 'onelogin-scim-v2' = "OneLogin SCIM v2.0",
327
+ 'okta-scim-v2' = "Okta SCIM v2.0",
328
+ 'jumpcloud-scim-v2' = "JumpCloud v2.0",
329
+ 'generic-scim-v2' = "SCIM Generic v2.0"
330
+ }
331
+ export declare type DirectoryType = keyof typeof DirectorySyncProviders;
332
+ export declare type HTTPMethod = 'POST' | 'PUT' | 'DELETE' | 'GET' | 'PATCH';
333
+ export declare type Directory = {
334
+ id: string;
335
+ name: string;
336
+ tenant: string;
337
+ product: string;
338
+ type: DirectoryType;
339
+ log_webhook_events: boolean;
340
+ scim: {
341
+ path: string;
342
+ endpoint?: string;
343
+ secret: string;
344
+ };
345
+ webhook: {
346
+ endpoint: string;
347
+ secret: string;
348
+ };
349
+ };
350
+ export declare type DirectorySyncGroupMember = {
351
+ value: string;
352
+ email?: string;
353
+ };
354
+ export interface DirectoryConfig {
355
+ create({ name, tenant, product, webhook_url, webhook_secret, type, }: {
356
+ name?: string;
357
+ tenant: string;
358
+ product: string;
359
+ webhook_url?: string;
360
+ webhook_secret?: string;
361
+ type?: DirectoryType;
362
+ }): Promise<{
363
+ data: Directory | null;
364
+ error: ApiError | null;
365
+ }>;
366
+ update(id: string, param: Omit<Partial<Directory>, 'id' | 'tenant' | 'prodct' | 'scim'>): Promise<{
367
+ data: Directory | null;
368
+ error: ApiError | null;
369
+ }>;
370
+ get(id: string): Promise<{
371
+ data: Directory | null;
372
+ error: ApiError | null;
373
+ }>;
374
+ getByTenantAndProduct(tenant: string, product: string): Promise<{
375
+ data: Directory | null;
376
+ error: ApiError | null;
377
+ }>;
378
+ list({ pageOffset, pageLimit, }: {
379
+ pageOffset?: number;
380
+ pageLimit?: number;
381
+ }): Promise<{
382
+ data: Directory[] | null;
383
+ error: ApiError | null;
384
+ }>;
385
+ delete(id: string): Promise<void>;
386
+ }
387
+ export interface IDirectoryUsers {
388
+ create(directory: Directory, body: any): Promise<DirectorySyncResponse>;
389
+ get(user: User): Promise<DirectorySyncResponse>;
390
+ update(directory: Directory, user: User, body: any): Promise<DirectorySyncResponse>;
391
+ patch(directory: Directory, user: User, body: any): Promise<DirectorySyncResponse>;
392
+ delete(directory: Directory, user: User, active: boolean): Promise<DirectorySyncResponse>;
393
+ getAll(queryParams: {
394
+ count: number;
395
+ startIndex: number;
396
+ filter?: string;
397
+ }): Promise<DirectorySyncResponse>;
398
+ handleRequest(request: DirectorySyncRequest, eventCallback?: EventCallback): Promise<DirectorySyncResponse>;
399
+ }
400
+ export interface IDirectoryGroups {
401
+ create(directory: Directory, body: any): Promise<DirectorySyncResponse>;
402
+ get(group: Group): Promise<DirectorySyncResponse>;
403
+ updateDisplayName(directory: Directory, group: Group, body: any): Promise<Group>;
404
+ delete(directory: Directory, group: Group): Promise<DirectorySyncResponse>;
405
+ getAll(queryParams: {
406
+ filter?: string;
407
+ }): Promise<DirectorySyncResponse>;
408
+ addGroupMembers(directory: Directory, group: Group, members: DirectorySyncGroupMember[] | undefined, sendWebhookEvent: boolean): Promise<void>;
409
+ removeGroupMembers(directory: Directory, group: Group, members: DirectorySyncGroupMember[], sendWebhookEvent: boolean): Promise<void>;
410
+ addOrRemoveGroupMembers(directory: Directory, group: Group, members: DirectorySyncGroupMember[]): Promise<void>;
411
+ update(directory: Directory, group: Group, body: any): Promise<DirectorySyncResponse>;
412
+ patch(directory: Directory, group: Group, body: any): Promise<DirectorySyncResponse>;
413
+ handleRequest(request: DirectorySyncRequest, eventCallback?: EventCallback): Promise<DirectorySyncResponse>;
414
+ }
415
+ export interface IWebhookEventsLogger extends Base {
416
+ log(directory: Directory, event: DirectorySyncEvent): Promise<WebhookEventLog>;
417
+ getAll(): Promise<WebhookEventLog[]>;
418
+ get(id: string): Promise<WebhookEventLog>;
419
+ clear(): Promise<void>;
420
+ delete(id: string): Promise<void>;
421
+ updateStatus(log: WebhookEventLog, statusCode: number): Promise<WebhookEventLog>;
422
+ }
423
+ export declare type DirectorySyncResponse = {
424
+ status: number;
425
+ data?: any;
426
+ };
427
+ export interface DirectorySyncRequestHandler {
428
+ handle(request: DirectorySyncRequest, callback?: EventCallback): Promise<DirectorySyncResponse>;
429
+ }
430
+ export interface Events {
431
+ handle(event: DirectorySyncEvent): Promise<void>;
432
+ }
433
+ export interface DirectorySyncRequest {
434
+ method: HTTPMethod;
435
+ body: any | undefined;
436
+ directoryId: Directory['id'];
437
+ resourceType: 'users' | 'groups';
438
+ resourceId: string | undefined;
439
+ apiSecret: string | null;
440
+ query: {
441
+ count?: number;
442
+ startIndex?: number;
443
+ filter?: string;
444
+ };
445
+ }
446
+ export declare type DirectorySync = {
447
+ requests: DirectorySyncRequestHandler;
448
+ directories: DirectoryConfig;
449
+ groups: Groups;
450
+ users: Users;
451
+ events: {
452
+ callback: EventCallback;
453
+ };
454
+ webhookLogs: IWebhookEventsLogger;
455
+ providers: () => {
456
+ [K in string]: string;
457
+ };
458
+ };
459
+ export interface ApiError {
460
+ message: string;
461
+ code: number;
462
+ }
463
+ export interface DirectorySyncEvent {
464
+ directory_id: Directory['id'];
465
+ event: DirectorySyncEventType;
466
+ data: User | Group | (User & {
467
+ group: Group;
468
+ });
469
+ tenant: string;
470
+ product: string;
471
+ }
472
+ export interface EventCallback {
473
+ (event: DirectorySyncEvent): Promise<void>;
474
+ }
475
+ export interface WebhookEventLog extends DirectorySyncEvent {
476
+ id: string;
477
+ webhook_endpoint: string;
478
+ created_at: Date;
479
+ status_code?: number;
480
+ delivered?: boolean;
481
+ }
214
482
  export {};
package/dist/typings.js CHANGED
@@ -1,2 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DirectorySyncProviders = void 0;
4
+ var DirectorySyncProviders;
5
+ (function (DirectorySyncProviders) {
6
+ DirectorySyncProviders["azure-scim-v2"] = "Azure SCIM v2.0";
7
+ DirectorySyncProviders["onelogin-scim-v2"] = "OneLogin SCIM v2.0";
8
+ DirectorySyncProviders["okta-scim-v2"] = "Okta SCIM v2.0";
9
+ DirectorySyncProviders["jumpcloud-scim-v2"] = "JumpCloud v2.0";
10
+ DirectorySyncProviders["generic-scim-v2"] = "SCIM Generic v2.0";
11
+ })(DirectorySyncProviders = exports.DirectorySyncProviders || (exports.DirectorySyncProviders = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxyhq/saml-jackson",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
4
4
  "description": "SAML Jackson library",
5
5
  "keywords": [
6
6
  "SAML 2.0"
@@ -42,6 +42,7 @@
42
42
  "@opentelemetry/api": "1.0.4",
43
43
  "@opentelemetry/api-metrics": "0.27.0",
44
44
  "@peculiar/webcrypto": "1.4.0",
45
+ "axios": "^0.27.2",
45
46
  "@peculiar/x509": "1.8.3",
46
47
  "jose": "4.9.2",
47
48
  "marked": "4.1.0",
@@ -56,6 +57,7 @@
56
57
  "xmlbuilder": "15.1.1"
57
58
  },
58
59
  "devDependencies": {
60
+ "@faker-js/faker": "7.2.0",
59
61
  "@types/node": "18.7.16",
60
62
  "@types/sinon": "10.0.13",
61
63
  "@types/tap": "15.0.7",