scimgateway 5.1.5 → 5.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -196,7 +196,8 @@ For Node.js (and also Bun), we might set the property `scimgateway_postinstall_s
196
196
  }
197
197
 
198
198
 
199
- Each endpoint plugin needs a TypeScript file (.ts) and a configuration file (.json). **They both must have the same naming prefix**. For SAP Hana endpoint we have:
199
+ Each endpoint plugin needs a TypeScript file (.ts) and a configuration file (.json).
200
+ **They both must have the same naming prefix**. For SAP Hana endpoint we have:
200
201
  >lib\plugin-saphana.ts
201
202
  >config\plugin-saphana.json
202
203
 
@@ -410,7 +411,7 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
410
411
  "certificate": {
411
412
  "key": "key.pem",
412
413
  "cert": "cert.pem",
413
- "ca": null
414
+ "ca": "ca.pem" // if several: "ca": ["ca1.pem", "ca2.pem"]
414
415
  }
415
416
 
416
417
  Example of how to make a self signed certificate:
@@ -465,7 +466,7 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
465
466
 
466
467
  - **endpoint** - Contains endpoint specific configuration according to customized **plugin code**.
467
468
 
468
- #### Configuration notes
469
+ ### Configuration notes - general
469
470
 
470
471
  - Custom Schemas, ServiceProviderConfig and ResourceType can be used if `./lib/scimdef-v2.json or scimdef-v1.json` exists. Original scimdef-v2.json/scimdef-v1.json can be copied from node_modules/scimgateway/lib to your plugin/lib and customized.
471
472
  - Using reverse proxy and we want ipAllowList and correct meta.location response, following headers must be set by proxy: `X-Forwarded-For`, `X-Forwarded-Proto` and `X-Forwarded-Host`
@@ -521,88 +522,204 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
521
522
  "plugin-soap.endpoint.password": "secret"
522
523
  }
523
524
 
525
+ ### Configuration notes - Email, using Microsoft Exchange Online (ExO)
524
526
 
525
- - Email, using Microsoft Exchange Online (ExO)
526
-
527
- - Entra ID application must have application permissions `Mail.Send`
528
- - To prevent the sending of emails from any defined mailboxes, an ExO `ApplicationAccessPolicy` must be defined through PowerShell.
529
-
530
- First create a mail-enabled security-group that only includes those users (mailboxes) the application is allowed to send from
531
- Note, `mail enabled security group` cannot be created from portal, only from admin or admin.exchange console
532
-
533
- ##Connect to Exchange
534
- Install-Module -Name ExchangeOnlineManagement
535
- Connect-ExchangeOnline
536
-
537
- ##Create ApplicationAccessPolicy
538
- New-ApplicationAccessPolicy -AppId <AppClientID> -PolicyScopeGroupId <MailEnabledSecurityGrpId> -AccessRight RestrictAccess -Description "Restrict app to specific mailboxes"
539
-
540
- - Email, using Google Workspace Gmail
541
-
542
- - https://console.cloud.google.com
543
- - IAM & Admin > Service Accounts > Create Service Account
544
- - Name=email-sender
545
- - Create and Continue
546
- - Grant this service account access to project - not needed
547
- - Grant users access to this service - not needed
548
- - IAM & Admin > Service Accounts > "email-sender" account > Keys
549
- - Add Key > Create new key > JSON
550
- - download json Service Account Key file, refere to configuration `email.auth.options.serviceAccountKeyFile`
551
-
552
- - https://admin.google.com
553
- - Security > Access and data control > API controls
554
- - Manage Domain Wide Delegation > Add new
555
- - Client ID = id of service account created
556
- - OAuth scope = `https://www.googleapis.com/auth/gmail.send`
557
-
558
- - https://admin.google.com
559
- - Billing > Subscriptions - verify Google Workspace license
560
- - Directory > Users > "user"
561
- - Licenses > Edit > enable Google Workspace license
562
- `email.emailOnError.from` mail address must have Google Workspace license
563
-
564
- - Gateway chainging and chainingBaseUrl configuration
565
-
566
- By configuring the `chainingBaseUrl`, it is possible to chain multiple gateways in sequence, such as `gateway1->gateway2->gateway3->endpoint`. In this setup, gateway behave much like a reverse proxy, validating authorization at each step unless PassThrough mode is enabled. Chaining is also supported in stream subscriber mode
527
+ - Entra ID application must have application permissions `Mail.Send`
528
+ - To prevent the sending of emails from any defined mailboxes, an ExO `ApplicationAccessPolicy` must be defined through PowerShell.
567
529
 
568
- {
569
- "scimgateway": {
570
- ...
571
- "chainingBaseUrl": "https:\\gateway2:8880",
572
- ...
573
- "auth": {
574
- ...
575
- "passThrough": {
576
- "enabled": false,
577
- "readOnly": false,
578
- "baseEntities": []
579
- }
580
- ...
581
- }
582
- },
530
+ First create a mail-enabled security-group that only includes those users (mailboxes) the application is allowed to send from
531
+ Note, `mail enabled security group` cannot be created from portal, only from admin or admin.exchange console
532
+
533
+ ##Connect to Exchange
534
+ Install-Module -Name ExchangeOnlineManagement
535
+ Connect-ExchangeOnline
536
+
537
+ ##Create ApplicationAccessPolicy
538
+ New-ApplicationAccessPolicy -AppId <AppClientID> -PolicyScopeGroupId <MailEnabledSecurityGrpId> -AccessRight RestrictAccess -Description "Restrict app to specific mailboxes"
539
+
540
+ ### Configuration notes - Email, using Google Workspace Gmail
541
+
542
+ - https://console.cloud.google.com
543
+ - IAM & Admin > Service Accounts > Create Service Account
544
+ - Name=email-sender
545
+ - Create and Continue
546
+ - Grant this service account access to project - not needed
547
+ - Grant users access to this service - not needed
548
+ - IAM & Admin > Service Accounts > "email-sender" account > Keys
549
+ - Add Key > Create new key > JSON
550
+ - download json Service Account Key file, refere to configuration `email.auth.options.serviceAccountKeyFile`
551
+
552
+ - https://admin.google.com
553
+ - Security > Access and data control > API controls
554
+ - Manage Domain Wide Delegation > Add new
555
+ - Client ID = id of service account created
556
+ - OAuth scope = `https://www.googleapis.com/auth/gmail.send`
557
+
558
+ - https://admin.google.com
559
+ - Billing > Subscriptions - verify Google Workspace license
560
+ - Directory > Users > "user"
561
+ - Licenses > Edit > enable Google Workspace license
562
+ `email.emailOnError.from` mail address must have Google Workspace license
563
+
564
+ ### Configuration notes - Gateway chainging and chainingBaseUrl
565
+
566
+ By configuring the `chainingBaseUrl`, it is possible to chain multiple gateways in sequence, such as `gateway1->gateway2->gateway3->endpoint`. In this setup, gateway behave much like a reverse proxy, validating authorization at each step unless PassThrough mode is enabled. Chaining is also supported in stream subscriber mode
567
+
568
+ {
569
+ "scimgateway": {
570
+ ...
571
+ "chainingBaseUrl": "https:\\gateway2:8880",
572
+ ...
573
+ "auth": {
574
+ ...
575
+ "passThrough": {
576
+ "enabled": false,
577
+ "readOnly": false,
578
+ "baseEntities": []
579
+ }
583
580
  ...
584
- }
585
-
586
-
587
- Using above configuration example on gateway1, incoming requests will be routed to `https:\\gateway2:8880`
581
+ }
582
+ },
583
+ ...
584
+ }
585
+
586
+
587
+ Using above configuration example on gateway1, incoming requests will be routed to `https:\\gateway2:8880`
588
+
589
+ The plugin and its associated authentication configuration can mirror the setup running on the final gateway. However, in chaining mode, the plugin binary is used solely for initializing and configuring the gateway. This allows for the use of a simplified `plugin-<name>.ts` binary containing only the essential mandatory components:
588
590
 
589
- The plugin and its associated authentication configuration can mirror the setup running on the final gateway. However, in chaining mode, the plugin binary is used solely for initializing and configuring the gateway. This allows for the use of a simplified `plugin-<name>.ts` binary containing only the essential mandatory components:
590
-
591
- // start - mandatory plugin initialization
592
- const ScimGateway: typeof import('scimgateway').ScimGateway = await (async () => {
593
- try {
594
- return (await import('scimgateway')).ScimGateway
595
- } catch (err) {
596
- const source = './scimgateway.ts'
597
- return (await import(source)).ScimGateway
598
- }
599
- })()
600
- const scimgateway = new ScimGateway()
601
- const config = scimgateway.getConfig()
602
- scimgateway.authPassThroughAllowed = false
603
- // end - mandatory plugin initialization
591
+ // start - mandatory plugin initialization
592
+ const ScimGateway: typeof import('scimgateway').ScimGateway = await (async () => {
593
+ try {
594
+ return (await import('scimgateway')).ScimGateway
595
+ } catch (err) {
596
+ const source = './scimgateway.ts'
597
+ return (await import(source)).ScimGateway
598
+ }
599
+ })()
600
+ const scimgateway = new ScimGateway()
601
+ const config = scimgateway.getConfig()
602
+ scimgateway.authPassThroughAllowed = false
603
+ // end - mandatory plugin initialization
604
+
605
+ Using `scimgateway.authPassThroughAllowed = true` and `plugin-<name>.json` configuration `scimgateway.auth.passThrough=true` enables Authentication PassTrhough
606
+
607
+ ### Configuration notes - HelperRest used by plugins
608
+ For REST endpoints, plugins may use HelperRest to simplify authentication and communication
609
+ doRequest() executes REST request and return response
610
+ `doRequest(<baseEntity>, <method>, <path>, <body>, <ctx>, <options>)`
611
+
612
+ * baseEntity - 'undefined' if not used and must correspond with endpoint configuration that defines baseUrls and connection options.
613
+ * method - GET, PATCH, PUT, DELETE
614
+ * path - either full url or just the path that will be added to baseUrl. Using full url will override baseUrl. Using path is preferred because of auth caching logic and simplicity
615
+ * body - optional body to be used
616
+ * ctx - optional, passing authorization header if Auth PassThrough is enabled
617
+ * opt - optional, connection options that will extend/override any endpoint.entity.undefined.connection definitions
618
+
619
+ Configuration showing connection settings:
620
+
621
+ {
622
+ "scimgateway": {
623
+ ...
624
+ }
625
+ "endpoint": {
626
+ "entity": {
627
+ "undefined": {
628
+ "connection": {
629
+ "baseUrls": [],
630
+ "auth": {
631
+ "type": "xxx",
632
+ "options": {
633
+ ...
634
+ "jwtPayload": {},
635
+ "samlPayload": {},
636
+ "tls": {} // files located in ./config/certs
637
+ }
638
+ },
639
+ "options": {
640
+ "headers": {},
641
+ "tls": {} // files located in ./config/certs
642
+ },
643
+ "proxy": {}
644
+ }
645
+ }
646
+ }
647
+ }
648
+ }
649
+
650
+
651
+ * baseUrls - Endpoint URL. Several may be defined for failower. There are retry logic on connection failures
652
+ * auth.type - defines authentication being used: `basic`, `oauth`, `token`, `bearer`, `oauthSamlBearer` or `oauthJwtBearer`
653
+ * auth.options - for each valid type there are different options. tenantIdGUID is special for Entra ID and serviceAccountKeyFile is special for Google. Using these will simplify and reduce options to be included. Also note we do not need to include baseUrls when using tenantIdGUID/serviceAccountKeyFile as long as endpoint is Entra ID (Microsoft Graph) or Google.
654
+
655
+ Example using basic auth:
656
+
657
+ "connection": {
658
+ "baseUrls": [
659
+ "https://localhost:8880"
660
+ ],
661
+ "auth": {
662
+ "type": "basic",
663
+ "options": {
664
+ "username": "gwadmin",
665
+ "password": "password"
666
+ }
667
+ },
668
+ "options": {
669
+ "tls": {
670
+ "rejectUnauthorized": false,
671
+ "ca": "ca.pem"
672
+ }
673
+ }
674
+ }
675
+
676
+ Example Entra ID (plugin-entra-id) using clientId/clientSecret:
677
+
678
+ "connection": {
679
+ "baseUrls": [],
680
+ "auth": {
681
+ "type": "oauth",
682
+ "options": {
683
+ "tenantIdGUID": "<tenantId>",
684
+ "clientId": "<clientId",
685
+ "clientSecret": "<clientSecret>"
686
+ }
687
+ }
688
+ }
689
+
690
+ Example Entra ID (plugin-entra-id) using certificate secret:
691
+
692
+ "connection": {
693
+ "baseUrls": [],
694
+ "auth": {
695
+ "type": "oauth",
696
+ "options": {
697
+ "tenantIdGUID": "<tenantId>",
698
+ "clientId": "<clientId",
699
+ "tls": {
700
+ "key": "key.pem",
701
+ "cert": "cert.pem"
702
+ }
703
+ }
704
+ }
705
+ }
706
+
707
+ Example using general OAuth:
708
+
709
+ "connection": {
710
+ "baseUrls": [<"endpointUrl">],
711
+ "auth": {
712
+ "type": "oauth",
713
+ "options": {
714
+ "tokenUrl": "<tokenUrl>"
715
+ "clientId": "<clientId",
716
+ "clientSecret": "<clientSecret>"
717
+ }
718
+ }
719
+ }
720
+
721
+ Please see code editor method HelperRest doRequest() IntelliSense for type and option details
604
722
 
605
- Using `scimgateway.authPassThroughAllowed = true` and `plugin-<name>.json` configuration `scimgateway.auth.passThrough=true` enables Authentication PassTrhough
606
723
 
607
724
  ## Manual startup
608
725
 
@@ -1175,6 +1292,23 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1175
1292
 
1176
1293
  ## Change log
1177
1294
 
1295
+ ### v5.1.7
1296
+
1297
+ [Fixed]
1298
+
1299
+ - Using gateway certificate CA, the CA did not load correctly. It now also supports an array of multiple CAs.
1300
+
1301
+ [Improved]
1302
+
1303
+ - Dependencies bump
1304
+
1305
+ ### v5.1.6
1306
+
1307
+ [Improved]
1308
+
1309
+ - HelperRest, payload/claims configuration now defined in auth.options.jwtPayload and auth.options.samlPayload. Previously all was defiend in auth.options
1310
+ - README configuration notes updated
1311
+
1178
1312
  ### v5.1.5
1179
1313
 
1180
1314
  [Improved]
@@ -1205,7 +1339,7 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1205
1339
  "options": {
1206
1340
  "tenantIdGUID": "Entra ID Tenant ID (GUID)",
1207
1341
  "clientId": "<application clientId>",
1208
- "certificate": { // files located in ./config/certs
1342
+ "tls": { // files located in ./config/certs
1209
1343
  "key": "key.pem",
1210
1344
  "cert": "cert.pem"
1211
1345
  }
package/bun.lock CHANGED
@@ -5,9 +5,9 @@
5
5
  "name": "scimgateway",
6
6
  "dependencies": {
7
7
  "@ldapjs/asn1": "^2.0.0",
8
- "@nats-io/jetstream": "^3.0.0-35",
9
- "@nats-io/nats-core": "^3.0.0-48",
10
- "@nats-io/transport-node": "^3.0.0-33",
8
+ "@nats-io/jetstream": "^3.0.0-37",
9
+ "@nats-io/nats-core": "^3.0.0-50",
10
+ "@nats-io/transport-node": "^3.0.0-35",
11
11
  "@types/ldapjs": "^3.0.6",
12
12
  "@types/lokijs": "^1.5.14",
13
13
  "@types/tedious": "^4.0.14",
@@ -18,16 +18,16 @@
18
18
  "jsonwebtoken": "^9.0.2",
19
19
  "ldapjs": "^3.0.7",
20
20
  "lokijs": "^1.5.12",
21
- "mongodb": "^6.12.0",
21
+ "mongodb": "^6.13.0",
22
22
  "node-machine-id": "1.1.12",
23
- "nodemailer": "^6.9.16",
23
+ "nodemailer": "^6.10.0",
24
24
  "passport": "^0.7.0",
25
25
  "passport-azure-ad": "^4.3.5",
26
26
  "saml": "^3.0.1",
27
27
  "winston": "^3.17.0",
28
28
  },
29
29
  "devDependencies": {
30
- "@stylistic/eslint-plugin": "^2.10.1",
30
+ "@stylistic/eslint-plugin": "^3.1.0",
31
31
  "@types/bun": "latest",
32
32
  "@types/dot-object": "^2.1.6",
33
33
  "@types/jsonwebtoken": "^9.0.6",
@@ -35,9 +35,9 @@
35
35
  "@types/nodemailer": "^6.4.14",
36
36
  "@types/passport": "^1.0.16",
37
37
  "@types/passport-azure-ad": "^4.3.6",
38
- "@typescript-eslint/eslint-plugin": "^8.12.2",
39
- "@typescript-eslint/parser": "^8.12.2",
40
- "eslint": "^9.13.0",
38
+ "@typescript-eslint/eslint-plugin": "^8.23.0",
39
+ "@typescript-eslint/parser": "^8.23.0",
40
+ "eslint": "^9.20.0",
41
41
  },
42
42
  "peerDependencies": {
43
43
  "typescript": "^5.0.0",
@@ -53,17 +53,17 @@
53
53
 
54
54
  "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
55
55
 
56
- "@eslint/config-array": ["@eslint/config-array@0.18.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw=="],
56
+ "@eslint/config-array": ["@eslint/config-array@0.19.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
57
57
 
58
- "@eslint/core": ["@eslint/core@0.7.0", "", {}, "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw=="],
58
+ "@eslint/core": ["@eslint/core@0.11.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA=="],
59
59
 
60
- "@eslint/eslintrc": ["@eslint/eslintrc@3.1.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ=="],
60
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.2.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w=="],
61
61
 
62
- "@eslint/js": ["@eslint/js@9.14.0", "", {}, "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg=="],
62
+ "@eslint/js": ["@eslint/js@9.20.0", "", {}, "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ=="],
63
63
 
64
- "@eslint/object-schema": ["@eslint/object-schema@2.1.4", "", {}, "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ=="],
64
+ "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
65
65
 
66
- "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.2", "", { "dependencies": { "levn": "^0.4.1" } }, "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw=="],
66
+ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.5", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="],
67
67
 
68
68
  "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
69
69
 
@@ -91,15 +91,15 @@
91
91
 
92
92
  "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.1.9", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw=="],
93
93
 
94
- "@nats-io/jetstream": ["@nats-io/jetstream@3.0.0-35", "", { "dependencies": { "@nats-io/nats-core": "3.0.0-48" } }, "sha512-oJdNge4IoNoA3BqZPU9hdX+4okLW2Tn+JAvxeRAV+Nh6J78/8+3O/moyhbz1AszqjakgdeEVTUF3GUa1F/fCZw=="],
94
+ "@nats-io/jetstream": ["@nats-io/jetstream@3.0.0-37", "", { "dependencies": { "@nats-io/nats-core": "3.0.0-50" } }, "sha512-hnYON3MrHWnEXAqGEoxoQ7TcGp7McqKPtZGKgga2I/R87c5QVOQOxDzP6y5YNiqO+y+nayGUXb3cM4qcYwLBMQ=="],
95
95
 
96
- "@nats-io/nats-core": ["@nats-io/nats-core@3.0.0-48", "", { "dependencies": { "@nats-io/nkeys": "2.0.0-3", "@nats-io/nuid": "2.0.1-2" } }, "sha512-IGT2vGgDxPAXrGQqzpD27EYMoIUjU+Y3AOgecLIIouews0qv9Wh2vJ10QmRSdElVaDL6VZTY+pyU3BJUFhD4kQ=="],
96
+ "@nats-io/nats-core": ["@nats-io/nats-core@3.0.0-50", "", { "dependencies": { "@nats-io/nkeys": "2.0.2", "@nats-io/nuid": "2.0.3" } }, "sha512-Kur1/yhzNrpcpu+OhsQ89k9Ge1woEWJd5FV3tpp0BtpRWMlIth3StBiADguynbKSQCkBAOUQ+C0kRnFW8zOIeg=="],
97
97
 
98
- "@nats-io/nkeys": ["@nats-io/nkeys@2.0.0-3", "", { "dependencies": { "tweetnacl": "^1.0.3" } }, "sha512-ars24TseiM1v5JTdSl9lgFV5ayoq1nqYD5apDn3LoWZM5BFGve11cam2N6F/VVOENCZzuAkLtw/uTN5HDutkmA=="],
98
+ "@nats-io/nkeys": ["@nats-io/nkeys@2.0.2", "", { "dependencies": { "tweetnacl": "^1.0.3" } }, "sha512-0JTyVl9P+UJyjUBDWP9589TuUKXJQ8tDkVRgi02X/MMzW997+4FykirvZEkIe6ZAhiLIBN+NpN8ULMMt6mDrbA=="],
99
99
 
100
- "@nats-io/nuid": ["@nats-io/nuid@2.0.1-2", "", {}, "sha512-oXJiWXH87FjcPAXVVkoY+o7z9YFuOvsVlUlvB4h/QsoC1hp63lB18YOMHmKxVNO7BzvwzJh3SKLlIlsQrEwaxg=="],
100
+ "@nats-io/nuid": ["@nats-io/nuid@2.0.3", "", {}, "sha512-TpA3HEBna/qMVudy+3HZr5M3mo/L1JPofpVT4t0HkFGkz2Cn9wrlrQC8tvR8Md5Oa9//GtGG26eN0qEWF5Vqew=="],
101
101
 
102
- "@nats-io/transport-node": ["@nats-io/transport-node@3.0.0-33", "", { "dependencies": { "@nats-io/nats-core": "3.0.0-48", "@nats-io/nkeys": "2.0.0-3", "@nats-io/nuid": "2.0.1-2" } }, "sha512-eq3X6Z1pcdIWUZ57jKZay1J+RTQmvaNts2+GywPZVDX6uE/c8CQkdlqY+0SCUfbtiPjEt4TDtFs00IBvV84Q8w=="],
102
+ "@nats-io/transport-node": ["@nats-io/transport-node@3.0.0-35", "", { "dependencies": { "@nats-io/nats-core": "3.0.0-50", "@nats-io/nkeys": "2.0.2", "@nats-io/nuid": "2.0.3" } }, "sha512-Mx3y2/m7KAUFQtWx2MO0+kxw5JdtUOpdQMSTuAVYJbT8fXlAawH8It7CESoFaeJjhmQ58Li/gOAf+rDGJAOKGQ=="],
103
103
 
104
104
  "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
105
105
 
@@ -107,7 +107,7 @@
107
107
 
108
108
  "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
109
109
 
110
- "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@2.10.1", "", { "dependencies": { "@typescript-eslint/utils": "^8.12.2", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ=="],
110
+ "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@3.1.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.13.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g=="],
111
111
 
112
112
  "@types/body-parser": ["@types/body-parser@1.19.5", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg=="],
113
113
 
@@ -161,21 +161,21 @@
161
161
 
162
162
  "@types/ws": ["@types/ws@8.5.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA=="],
163
163
 
164
- "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.13.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.13.0", "@typescript-eslint/type-utils": "8.13.0", "@typescript-eslint/utils": "8.13.0", "@typescript-eslint/visitor-keys": "8.13.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg=="],
164
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.23.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.23.0", "@typescript-eslint/type-utils": "8.23.0", "@typescript-eslint/utils": "8.23.0", "@typescript-eslint/visitor-keys": "8.23.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w=="],
165
165
 
166
- "@typescript-eslint/parser": ["@typescript-eslint/parser@8.13.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.13.0", "@typescript-eslint/types": "8.13.0", "@typescript-eslint/typescript-estree": "8.13.0", "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ=="],
166
+ "@typescript-eslint/parser": ["@typescript-eslint/parser@8.23.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.23.0", "@typescript-eslint/types": "8.23.0", "@typescript-eslint/typescript-estree": "8.23.0", "@typescript-eslint/visitor-keys": "8.23.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q=="],
167
167
 
168
- "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "@typescript-eslint/visitor-keys": "8.13.0" } }, "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA=="],
168
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.23.0", "", { "dependencies": { "@typescript-eslint/types": "8.23.0", "@typescript-eslint/visitor-keys": "8.23.0" } }, "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw=="],
169
169
 
170
- "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.13.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.13.0", "@typescript-eslint/utils": "8.13.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" } }, "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA=="],
170
+ "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.23.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.23.0", "@typescript-eslint/utils": "8.23.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA=="],
171
171
 
172
- "@typescript-eslint/types": ["@typescript-eslint/types@8.13.0", "", {}, "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng=="],
172
+ "@typescript-eslint/types": ["@typescript-eslint/types@8.23.0", "", {}, "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ=="],
173
173
 
174
- "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" } }, "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g=="],
174
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.23.0", "", { "dependencies": { "@typescript-eslint/types": "8.23.0", "@typescript-eslint/visitor-keys": "8.23.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ=="],
175
175
 
176
176
  "@typescript-eslint/utils": ["@typescript-eslint/utils@8.13.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.13.0", "@typescript-eslint/types": "8.13.0", "@typescript-eslint/typescript-estree": "8.13.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ=="],
177
177
 
178
- "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "eslint-visitor-keys": "^3.4.3" } }, "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw=="],
178
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.23.0", "", { "dependencies": { "@typescript-eslint/types": "8.23.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ=="],
179
179
 
180
180
  "@xmldom/xmldom": ["@xmldom/xmldom@0.7.13", "", {}, "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g=="],
181
181
 
@@ -241,7 +241,7 @@
241
241
 
242
242
  "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
243
243
 
244
- "cross-spawn": ["cross-spawn@7.0.5", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug=="],
244
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
245
245
 
246
246
  "debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
247
247
 
@@ -261,7 +261,7 @@
261
261
 
262
262
  "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
263
263
 
264
- "eslint": ["eslint@9.14.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.18.0", "@eslint/core": "^0.7.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "9.14.0", "@eslint/plugin-kit": "^0.2.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.0", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "text-table": "^0.2.0" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g=="],
264
+ "eslint": ["eslint@9.20.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA=="],
265
265
 
266
266
  "eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="],
267
267
 
@@ -411,7 +411,7 @@
411
411
 
412
412
  "moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
413
413
 
414
- "mongodb": ["mongodb@6.12.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.9", "bson": "^6.10.1", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA=="],
414
+ "mongodb": ["mongodb@6.13.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.9", "bson": "^6.10.1", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q=="],
415
415
 
416
416
  "mongodb-connection-string-url": ["mongodb-connection-string-url@3.0.1", "", { "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^13.0.0" } }, "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg=="],
417
417
 
@@ -431,7 +431,7 @@
431
431
 
432
432
  "node-machine-id": ["node-machine-id@1.1.12", "", {}, "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ=="],
433
433
 
434
- "nodemailer": ["nodemailer@6.9.16", "", {}, "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ=="],
434
+ "nodemailer": ["nodemailer@6.10.0", "", {}, "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA=="],
435
435
 
436
436
  "oauth": ["oauth@0.9.15", "", {}, "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="],
437
437
 
@@ -515,15 +515,13 @@
515
515
 
516
516
  "text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="],
517
517
 
518
- "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="],
519
-
520
518
  "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
521
519
 
522
520
  "tr46": ["tr46@4.1.1", "", { "dependencies": { "punycode": "^2.3.0" } }, "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw=="],
523
521
 
524
522
  "triple-beam": ["triple-beam@1.4.1", "", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="],
525
523
 
526
- "ts-api-utils": ["ts-api-utils@1.4.0", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ=="],
524
+ "ts-api-utils": ["ts-api-utils@2.0.1", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w=="],
527
525
 
528
526
  "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="],
529
527
 
@@ -575,13 +573,23 @@
575
573
 
576
574
  "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
577
575
 
576
+ "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.10.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw=="],
577
+
578
578
  "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
579
579
 
580
580
  "@ldapjs/controls/@ldapjs/asn1": ["@ldapjs/asn1@1.2.0", "", {}, "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw=="],
581
581
 
582
+ "@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.23.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.23.0", "@typescript-eslint/types": "8.23.0", "@typescript-eslint/typescript-estree": "8.23.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA=="],
583
+
584
+ "@typescript-eslint/type-utils/@typescript-eslint/utils": ["@typescript-eslint/utils@8.23.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.23.0", "@typescript-eslint/types": "8.23.0", "@typescript-eslint/typescript-estree": "8.23.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA=="],
585
+
582
586
  "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
583
587
 
584
- "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
588
+ "@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "@typescript-eslint/visitor-keys": "8.13.0" } }, "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA=="],
589
+
590
+ "@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.13.0", "", {}, "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng=="],
591
+
592
+ "@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" } }, "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g=="],
585
593
 
586
594
  "bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
587
595
 
@@ -607,10 +615,24 @@
607
615
 
608
616
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
609
617
 
618
+ "@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "eslint-visitor-keys": "^3.4.3" } }, "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw=="],
619
+
620
+ "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.13.0", "", { "dependencies": { "@typescript-eslint/types": "8.13.0", "eslint-visitor-keys": "^3.4.3" } }, "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw=="],
621
+
622
+ "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
623
+
624
+ "@typescript-eslint/utils/@typescript-eslint/typescript-estree/ts-api-utils": ["ts-api-utils@1.4.0", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ=="],
625
+
610
626
  "bun-types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
611
627
 
612
628
  "color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
613
629
 
614
630
  "passport-azure-ad/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
631
+
632
+ "@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
633
+
634
+ "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
635
+
636
+ "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
615
637
  }
616
638
  }
@@ -42,7 +42,7 @@ export class HelperRest {
42
42
  if (this.config_entity[baseEntity]?.connection) {
43
43
  connectionFound = true
44
44
  const type = this.config_entity[baseEntity].connection?.auth?.type
45
- if (type === 'oauthJwtBearer' || type === 'oauth') { // includes oauth because of email.auth.type
45
+ if (type === 'oauthJwtBearer' || type === 'oauth') {
46
46
  // set default baseUrls for Entra ID and Google if not already defined
47
47
  if (this.config_entity[baseEntity]?.connection?.auth?.options?.tenantIdGUID) { // Entra ID, setting baseUrls to graph
48
48
  if (!this.config_entity[baseEntity].connection.baseUrls) {
@@ -83,29 +83,32 @@ export class HelperRest {
83
83
 
84
84
  const action = 'getAccessToken'
85
85
 
86
+ const serviceAccountKeyFile = this.config_entity[baseEntity]?.connection?.auth?.options?.serviceAccountKeyFile
87
+ const tenantIdGUID = this.config_entity[baseEntity]?.connection?.auth?.options?.tenantIdGUID
86
88
  let tokenUrl: string
87
- let form: object
89
+ let form: Record<string, any>
88
90
  let resource = ''
89
91
 
92
+ try {
93
+ const urlObj = new URL(this.config_entity[baseEntity].connection.baseUrls[0])
94
+ resource = urlObj.origin
95
+ } catch (err) { void 0 }
96
+ if (tenantIdGUID) {
97
+ tokenUrl = `https://login.microsoftonline.com/${tenantIdGUID}/oauth2/v2.0/token`
98
+ if (resource) this.config_entity[baseEntity].connection.auth.options.scope = resource + '/.default' // "https://graph.microsoft.com/.default"
99
+ } else tokenUrl = this.config_entity[baseEntity].connection.auth.options.tokenUrl
100
+
90
101
  try {
91
102
  switch (this.config_entity[baseEntity]?.connection?.auth?.type) {
92
103
  case 'oauth':
93
- try {
94
- const urlObj = new URL(this.config_entity[baseEntity].connection.baseUrls[0])
95
- resource = urlObj.origin
96
- } catch (err) { void 0 }
97
- if (this.config_entity[baseEntity].connection.auth?.options?.tenantIdGUID) { // Azure
98
- tokenUrl = `https://login.microsoftonline.com/${this.config_entity[baseEntity].connection.auth.options.tenantIdGUID}/oauth2/token`
99
- } else {
100
- tokenUrl = this.config_entity[baseEntity].connection.auth.options.tokenUrl
101
- }
102
104
  form = {
103
105
  grant_type: 'client_credentials',
104
106
  client_id: this.config_entity[baseEntity].connection.auth.options.clientId,
105
107
  client_secret: this.config_entity[baseEntity].connection.auth.options.clientSecret,
106
- scope: this.config_entity[baseEntity].connection.auth.options.scope || null,
107
- resource: resource || null, // "https://graph.microsoft.com"
108
108
  }
109
+ if (this.config_entity[baseEntity].connection.auth.options.scope) form.scope = this.config_entity[baseEntity].connection.auth.options.scope // required using Entra ID /oauth2/v2.0/token
110
+ if (this.config_entity[baseEntity].connection.auth.options.resource) resource = this.config_entity[baseEntity].connection.auth.options.resource // required using Entra ID /oauth2/token
111
+
109
112
  break
110
113
 
111
114
  case 'token':
@@ -119,23 +122,26 @@ export class HelperRest {
119
122
  case 'oauthSamlBearer':
120
123
  tokenUrl = this.config_entity[baseEntity].connection.auth.options.tokenUrl
121
124
  const context = null
122
- const cert = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.certificate.cert).toString()
123
- const key = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.certificate.key).toString()
124
-
125
- const issuer = `scimgateway/${this.scimgateway.pluginName}`
126
- const lifetime = 3600
127
- const clientId = this.config_entity[baseEntity].connection.auth.options.clientId
128
- const nameId = this.config_entity[baseEntity].connection.auth.options.userId
129
- const userIdentifierFormat = 'userName'
125
+ const cert = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.tls.cert).toString()
126
+ const key = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.tls.key).toString()
127
+
130
128
  const tokenEndpoint = tokenUrl
131
- const audience = `scimgateway/${this.scimgateway.pluginName}`
132
129
  const delay = 1
133
130
 
131
+ // mandatory: clientId, companyId and nameId
132
+ const clientId = this.config_entity[baseEntity].connection.auth.options.samlPayload.clientId
133
+ const companyId = this.config_entity[baseEntity].connection.auth.options.samlPayload.companyId
134
+ const nameId = this.config_entity[baseEntity].connection.auth.options.samlPayload.nameId
135
+ const userIdentifierFormat = this.config_entity[baseEntity].connection.auth.options.samlPayload.userIdentifierFormat || 'userName'
136
+ const lifetime = this.config_entity[baseEntity].connection.auth.options.samlPayload.lifetime || 3600
137
+ const issuer = this.config_entity[baseEntity].connection.auth.options.samlPayload.clientId || `https://scimgateway.${this.scimgateway.pluginName}.com`
138
+ const audience = this.config_entity[baseEntity].connection.auth.options.samlPayload.audience || `scimgateway/${this.scimgateway.pluginName}`
139
+
134
140
  form = {
135
141
  token_url: tokenUrl,
136
142
  grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer',
137
143
  client_id: clientId,
138
- company_id: this.config_entity[baseEntity].connection.auth.options.companyId,
144
+ company_id: companyId,
139
145
  assertion: await samlAssertion.run(context, cert, key, issuer, lifetime, clientId, nameId, userIdentifierFormat, tokenEndpoint, audience, delay),
140
146
  }
141
147
  break
@@ -143,25 +149,21 @@ export class HelperRest {
143
149
  case 'oauthJwtBearer':
144
150
  let jwtClaims: jsonwebtoken.JwtPayload | Record<string, any> = {}
145
151
  let jwtOpts: jsonwebtoken.SignOptions = {}
146
- const serviceAccountKeyFile = this.config_entity[baseEntity]?.connection?.auth?.options?.serviceAccountKeyFile
147
- const tenantIdGUID = this.config_entity[baseEntity]?.connection?.auth?.options?.tenantIdGUID
148
152
 
149
- if (tenantIdGUID) {
150
- // Microsoft Entra ID
151
- if (!this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?.key || !this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?.cert) {
152
- throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - missing options.certificate.key/cert configuration`)
153
+ if (tenantIdGUID) { // Microsoft Entra ID
154
+ if (!this.config_entity[baseEntity]?.connection?.auth?.options?.tls?.cert) {
155
+ throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - missing options.tls.key/cert configuration`)
153
156
  }
154
- tokenUrl = `https://login.microsoftonline.com/${tenantIdGUID}/oauth2/v2.0/token` // `https://login.microsoftonline.com/${tenantIdGUID}/oauth2/token`
155
- let privateKey = this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?._key || ''
156
- let cert = this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?._cert || ''
157
+ let privateKey = this.config_entity[baseEntity]?.connection?.auth?.options?.tls?._key || ''
158
+ let cert = this.config_entity[baseEntity]?.connection?.auth?.options?.tls?._cert || ''
157
159
  if (!privateKey || !cert) {
158
- privateKey = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.certificate.key, 'utf-8') || ''
159
- cert = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.certificate.cert, 'utf-8') || ''
160
- if (privateKey) this.config_entity[baseEntity].connection.auth.options.certificate._key = privateKey
161
- if (cert) this.config_entity[baseEntity].connection.auth.options.certificate._cert = cert
160
+ privateKey = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.tls.key, 'utf-8') || ''
161
+ cert = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.tls.cert, 'utf-8') || ''
162
+ if (privateKey) this.config_entity[baseEntity].connection.auth.options.tls._key = privateKey
163
+ if (cert) this.config_entity[baseEntity].connection.auth.options.tls._cert = cert
162
164
  }
163
165
  if (!privateKey || !cert) {
164
- throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - missing options.certificate.key/cert file content`)
166
+ throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - missing options.tls.key/cert file content`)
165
167
  }
166
168
 
167
169
  const jwtPayload: jsonwebtoken.JwtPayload = {
@@ -199,23 +201,16 @@ export class HelperRest {
199
201
  }
200
202
  */
201
203
 
202
- let scope = 'https://graph.microsoft.com/.default'
203
- try {
204
- const urlObj = new URL(this.config_entity[baseEntity].connection.baseUrls[0])
205
- scope = urlObj.origin + '/.default' // for application exposed api's and included permissions use: api://${this.config_entity[baseEntity]?.connection?.auth?.options?.clientId}/.default
206
- } catch (err) { void 0 }
207
-
208
204
  form = {
209
- scope,
210
205
  grant_type: 'client_credentials',
206
+ scope: this.config_entity[baseEntity].connection.auth.options.scope, // "https://graph.microsoft.com/.default"
211
207
  client_id: this.config_entity[baseEntity]?.connection?.auth?.options?.clientId,
212
208
  client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
213
209
  client_assertion: jsonwebtoken.sign(jwtClaims, privateKey, jwtOpts),
214
210
  }
215
- } else if (serviceAccountKeyFile) {
216
- // Google - using Service Account key json-file
217
- if (!this.config_entity[baseEntity]?.connection?.auth?.options?.scope || !this.config_entity[baseEntity]?.connection?.auth?.options?.subject) {
218
- const err = new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - using auth.options 'serviceAccountKeyFile' requires mandatory configuration entity.${baseEntity}.connection.auth.options.scope/subject`)
211
+ } else if (serviceAccountKeyFile) { // Google - using Service Account key json-file
212
+ if (!this.config_entity[baseEntity]?.connection?.auth?.options?.jwtPayload?.scope || !this.config_entity[baseEntity]?.connection?.auth?.options?.jwtPayload?.subject) {
213
+ const err = new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - using auth.options 'serviceAccountKeyFile' requires mandatory configuration entity.${baseEntity}.connection.auth.options.jwtPayload.scope/subject`)
219
214
  throw err
220
215
  }
221
216
  let gkey: Record<string, any> = this.config_entity[baseEntity]?.connection?.auth?.options?._gkey
@@ -234,7 +229,7 @@ export class HelperRest {
234
229
  tokenUrl = gkey.token_uri // https://oauth2.googleapis.com/token
235
230
  const privateKey = gkey.private_key
236
231
  const jwtPayload: jsonwebtoken.JwtPayload = {
237
- sub: this.config_entity[baseEntity]?.connection?.auth?.options?.subject, // firstname.lastname@mycompany.com
232
+ sub: this.config_entity[baseEntity]?.connection?.auth?.options?.jwtPayload?.subject, // gmail sender mail-address: noreply@mycompany.com
238
233
  iss: gkey.client_email, // service account email/user
239
234
  aud: gkey.token_uri,
240
235
  iat: Math.floor(Date.now() / 1000) - 60, // issued at
@@ -242,7 +237,7 @@ export class HelperRest {
242
237
  }
243
238
  jwtClaims = {
244
239
  ...jwtPayload,
245
- scope: this.config_entity[baseEntity]?.connection?.auth?.options?.scope, // https://www.googleapis.com/auth/gmail.send
240
+ scope: this.config_entity[baseEntity]?.connection?.auth?.options?.jwtPayload?.scope, // https://www.googleapis.com/auth/gmail.send
246
241
  }
247
242
  jwtOpts = {
248
243
  algorithm: 'RS256',
@@ -257,23 +252,23 @@ export class HelperRest {
257
252
  assertion: jsonwebtoken.sign(jwtClaims, privateKey, jwtOpts),
258
253
  }
259
254
  } else {
260
- // standard JWT - requires all configuation: tokenUrl, rawJwtPayload and certificate.key
255
+ // standard JWT - requires all configuation: tokenUrl, jwtPayload and tls.key
261
256
  if (!this.config_entity[baseEntity]?.connection?.auth?.options?.tokenUrl
262
- || !this.config_entity[baseEntity]?.connection?.auth?.options?.rawJwtPayload
263
- || typeof this.config_entity[baseEntity]?.connection?.auth?.options?.rawJwtPayload !== 'object') {
264
- throw new Error(`auth.type '${this.config_entity[baseEntity]?.connection?.auth?.type}' (no tenantIdGUID/serviceAccountKeyFile using raw) - missing configuration entity.${baseEntity}.connection.auth.options.tokenUrl/rawJwtPayload`)
257
+ || !this.config_entity[baseEntity]?.connection?.auth?.options?.jwtPayload
258
+ || typeof this.config_entity[baseEntity]?.connection?.auth?.options?.jwtPayload !== 'object') {
259
+ throw new Error(`auth.type '${this.config_entity[baseEntity]?.connection?.auth?.type}' (no tenantIdGUID/serviceAccountKeyFile using raw) - missing configuration entity.${baseEntity}.connection.auth.options.tokenUrl/jwtPayload`)
265
260
  }
266
- if (!this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?.key) {
267
- throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' (no tenantIdGUID/serviceAccountKeyFile using raw) - missing options.certificate.key configuration`)
261
+ if (!this.config_entity[baseEntity]?.connection?.auth?.options?.tls?.key) {
262
+ throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' (no tenantIdGUID/serviceAccountKeyFile using raw) - missing options.tls.key configuration`)
268
263
  }
269
264
  tokenUrl = this.config_entity[baseEntity].connection.auth.options.tokenUrl
270
- let privateKey = this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?._key || ''
265
+ let privateKey = this.config_entity[baseEntity]?.connection?.auth?.options?.tls?._key || ''
271
266
  if (!privateKey) {
272
- privateKey = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.certificate.key, 'utf-8') || ''
273
- if (privateKey) this.config_entity[baseEntity].connection.auth.options.certificate._key = privateKey
267
+ privateKey = fs.readFileSync(this.config_entity[baseEntity].connection.auth.options.tls.key, 'utf-8') || ''
268
+ if (privateKey) this.config_entity[baseEntity].connection.auth.options.tls._key = privateKey
274
269
  }
275
270
 
276
- let jwtPayload = this.config_entity[baseEntity].connection.auth.options.rawJwtPayload
271
+ let jwtPayload = this.config_entity[baseEntity].connection.auth.options.jwtPayload
277
272
  if (!jwtPayload.iat) jwtPayload.iat = Math.floor(Date.now() / 1000) - 60
278
273
  if (!jwtPayload.exp) jwtPayload.exp = Math.floor(Date.now() / 1000) + 3600
279
274
 
@@ -419,6 +414,18 @@ export class HelperRest {
419
414
  org = utils.extendObj(org, opt.connection)
420
415
  }
421
416
 
417
+ // may use configuration type='oauth' and auto corrected to 'oauthJwtBearer'
418
+ if (this.config_entity[baseEntity]?.connection?.auth?.type == 'oauth') {
419
+ if (this.config_entity[baseEntity].connection.auth?.options?.tenantIdGUID) {
420
+ if (this.config_entity[baseEntity].connection.auth.options?.tls?.cert
421
+ && this.config_entity[baseEntity].connection.auth.options?.tls?.key
422
+ && this.config_entity[baseEntity].connection.auth.options.clientId
423
+ ) this.config_entity[baseEntity].connection.auth.type = 'oauthJwtBearer'
424
+ } else if (this.config_entity[baseEntity]?.connection?.auth?.options?.serviceAccountKeyFile) {
425
+ this.config_entity[baseEntity].connection.auth.type = 'oauthJwtBearer'
426
+ }
427
+ }
428
+
422
429
  switch (this.config_entity[baseEntity]?.connection?.auth?.type) {
423
430
  case 'basic':
424
431
  if (!this.config_entity[baseEntity]?.connection?.auth?.options?.username || !this.config_entity[baseEntity]?.connection?.auth?.options?.password) {
@@ -451,9 +458,9 @@ export class HelperRest {
451
458
  param.options.headers['Authorization'] = 'Bearer ' + Buffer.from(this.config_entity[baseEntity].connection.auth.options.token).toString('base64')
452
459
  break
453
460
  case 'oauthSamlBearer':
454
- if (!this.config_entity[baseEntity]?.connection?.auth?.options?.clientId || !this.config_entity[baseEntity]?.connection?.auth?.options?.companyId
455
- || !this.config_entity[baseEntity]?.connection?.auth?.options?.certificate?.key) {
456
- const err = new Error(`auth.type 'oauthSamlBearer' - missing configuration entity.${baseEntity}.connection.auth.options...`)
461
+ if (!this.config_entity[baseEntity]?.connection?.auth?.options?.samlPayload?.clientId || !this.config_entity[baseEntity]?.connection?.auth?.options?.samlPayload?.companyId
462
+ || !this.config_entity[baseEntity]?.connection?.auth?.options?.tls?.key) {
463
+ const err = new Error(`auth.type 'oauthSamlBearer' - missing configuration entity.${baseEntity}.connection.auth.options.tls and/or options.samlPayload.clientId/companyId`)
457
464
  throw err
458
465
  }
459
466
  param.accessToken = await this.getAccessToken(baseEntity, ctx)
@@ -462,7 +469,7 @@ export class HelperRest {
462
469
  case 'oauthJwtBearer':
463
470
  // auth.options.tenantIdGUID => Microsoft Entra ID
464
471
  // auth.options.serviceAccountKeyFile => Google Service Account
465
- // also support custom using tokenUrl/rawJwtPayload
472
+ // also support custom using tokenUrl/jwtPayload
466
473
  param.accessToken = await this.getAccessToken(baseEntity, ctx)
467
474
  param.options.headers['Authorization'] = `Bearer ${param.accessToken.access_token}`
468
475
  break
@@ -764,7 +771,7 @@ export class HelperRest {
764
771
  * ```
765
772
  * type defines authentication being used
766
773
  * if type not defined, no authentication used
767
- * valid type is: `basic`, `oauth`, `token`, `bearer` or `oauthSamlBearer`
774
+ * valid type is: `basic`, `oauth`, `token`, `bearer`, `oauthSamlBearer` or `oauthJwtBearer`
768
775
  *
769
776
  * for each valid type there are different auth.options
770
777
  *
@@ -815,10 +822,16 @@ export class HelperRest {
815
822
  * {
816
823
  * "options": {
817
824
  * "tokenUrl": "<tokenUrl>",
818
- * "clientId": "<clientId>",
819
- * "companyId": "<companyId>",
820
- * "userId": "<userId>",
821
- * "certificate": {
825
+ * "samlPayload": {
826
+ * "clientId": "<clientId>",
827
+ * "companyId": "<companyId>",
828
+ * "nameId": "<nameId>",
829
+ * "lifetime": "<optional>"
830
+ * "issuer": "<optional>",
831
+ * "userIdentifierFormat": "<optional>",
832
+ * "audience": "<optional>"
833
+ * },
834
+ * "tls": {
822
835
  * "key": "<key-file-name>", // location: config/certs
823
836
  * "cert": "<cert-file-name>", // location: config/certs
824
837
  * }
@@ -833,7 +846,7 @@ export class HelperRest {
833
846
  * "options": {
834
847
  * "tenantIdGUID": "<Entra ID tenantIdGUID", // Entra ID authentication, if baseUrls not defined, baseUrls automatically set to [https://graph.microsoft.com/beta]
835
848
  * "clientId": "<clientId>",
836
- * "certificate": { // files located in ./config/certs
849
+ * "tls": { // files located in ./config/certs
837
850
  * "key": "key.pem",
838
851
  * "cert": "cert.pem"
839
852
  * }
@@ -853,10 +866,10 @@ export class HelperRest {
853
866
  * {
854
867
  * "options": {
855
868
  * "tokenUrl": "<tokenUrl",
856
- * "certificate": {
869
+ * "tls": {
857
870
  * "key": "<signing-key-file-name>" // key.pem file located in ./config/certs
858
871
  * },
859
- * "rawJwtPayload": {
872
+ * "jwtPayload": {
860
873
  * "sub": "<subject>",
861
874
  * "iss": "<issuer>",
862
875
  * "aud": "<audience>",
@@ -2290,14 +2290,20 @@ export class ScimGateway {
2290
2290
  hostname = 'localhost'
2291
2291
  }
2292
2292
  try {
2293
- // using fs.readFileSync() instead of Bun.file() for nodejs compability
2293
+ // using fs.readFileSync() instead of Bun.file() for nodejs compatibility
2294
2294
  if (this.config.scimgateway?.certificate?.key && this.config.scimgateway?.certificate?.cert) {
2295
2295
  // TLS
2296
2296
  tls.key = this.config.scimgateway.certificate.key ? fs.readFileSync(this.config.scimgateway.certificate.key) : undefined
2297
2297
  tls.cert = this.config.scimgateway.certificate.cert ? fs.readFileSync(this.config.scimgateway.certificate.cert) : undefined
2298
- // loading tls.ca would require client certificates to be used
2298
+ if (this.config.scimgateway?.certificate?.ca) {
2299
+ if (Array.isArray(this.config.scimgateway.certificate.ca)) {
2300
+ for (let i = 0; i < this.config.scimgateway.certificate.ca.length; i++) {
2301
+ this.config.scimgateway.certificate.ca[i] = fs.readFileSync(this.config.scimgateway.certificate.ca[i])
2302
+ }
2303
+ } else tls.ca = fs.readFileSync(this.config.scimgateway.certificate.ca)
2304
+ }
2299
2305
  } else if (this.config.scimgateway?.certificate?.pfx && this.config.scimgateway?.certificate?.pfx?.bundle) {
2300
- // TLS PFX / PKCS#12
2306
+ // TODO: PFX/PKC#12 currently not supported by Bun
2301
2307
  tls.pfx = this.config.scimgateway.certificate.pfx.bundle ? fs.readFileSync(this.config.scimgateway.certificate.pfx.bundle) : undefined
2302
2308
  tls.passphrase = this.config.scimgateway.certificate.pfx.password ? utils.getSecret('scimgateway.certificate.pfx.password', this.configFile) : undefined
2303
2309
  }
@@ -2538,12 +2544,12 @@ export class ScimGateway {
2538
2544
  isMailLock = false
2539
2545
  }, (this.config.scimgateway.email.emailOnError.sendInterval || 15) * 1000 * 60)
2540
2546
 
2541
- const msgHtml = `<html><body><pre style="font-family: monospace; white-space: pre-wrap;">${msg}</pre><br/><p><strong>This is an automatically generated email - please do NOT reply to this email or forward to others</strong></p></body></html>`
2547
+ const msgHtml = `<html><body><pre style="font-family: monospace; white-space: pre-wrap;">${msg}</pre><br/><p>This is an automatically generated email - please do NOT reply to this email</p></body></html>`
2542
2548
  const msgObj = {
2543
2549
  from: this.config.scimgateway.email.emailOnError.from,
2544
2550
  to: this.config.scimgateway.email.emailOnError.to,
2545
2551
  cc: this.config.scimgateway.email.emailOnError.cc,
2546
- subject: this.config.scimgateway.email.emailOnError.subject ? this.config.scimgateway.email.emailOnError.subject : 'SCIM Gateway error message',
2552
+ subject: this.config.scimgateway.email.emailOnError.subject || 'SCIM Gateway error message',
2547
2553
  content: msgHtml,
2548
2554
  }
2549
2555
  this.sendMail(msgObj, true)
@@ -2870,9 +2876,7 @@ export class ScimGateway {
2870
2876
 
2871
2877
  const path = `/users/${msgObj.from}/sendMail`
2872
2878
  try {
2873
- if (this.config.scimgateway.email.auth?.options?.certificate?.key && this.config.scimgateway.email.auth?.options?.certificate?.cert) {
2874
- await this.helperRest.doRequest('undefined', 'POST', path, emailMessage, null, { connection: { auth: { type: 'oauthJwtBearer' } } })
2875
- } else await this.helperRest.doRequest('undefined', 'POST', path, emailMessage)
2879
+ await this.helperRest.doRequest('undefined', 'POST', path, emailMessage)
2876
2880
  logger.debug(`${gwName}[${pluginName}] sendMail subject '${msgObj.subject}' sent to: ${msgObj.to}${(msgObj.cc) ? ',' + msgObj.cc : ''}`)
2877
2881
  } catch (err: any) {
2878
2882
  logger.error(`${gwName}[${pluginName}] sendMail subject '${msgObj.subject}' sending failed: ${err.message}`)
@@ -2894,7 +2898,7 @@ Content-Transfer-Encoding: quoted-printable
2894
2898
  const emailMessage = { raw: encodedMessage }
2895
2899
  const path = `/gmail/v1/users/${msgObj.from}/messages/send`
2896
2900
  try { // using opt connection argument type=oauthJwtBearer and options scope/subject because we want to keep simplified email.auth.type=oauth and options serviceAccountKeyFile
2897
- await this.helperRest.doRequest('undefined', 'POST', path, emailMessage, null, { connection: { auth: { type: 'oauthJwtBearer', options: { scope: 'https://www.googleapis.com/auth/gmail.send', subject: msgObj.from } } } })
2901
+ await this.helperRest.doRequest('undefined', 'POST', path, emailMessage, null, { connection: { auth: { type: 'oauthJwtBearer', options: { jwtPayload: { scope: 'https://www.googleapis.com/auth/gmail.send', subject: msgObj.from } } } } })
2898
2902
  logger.debug(`${gwName}[${pluginName}] sendMail subject '${msgObj.subject}' sent to: ${msgObj.to}${(msgObj.cc) ? ',' + msgObj.cc : ''}`)
2899
2903
  } catch (err: any) {
2900
2904
  logger.error(`${gwName}[${pluginName}] sendMail subject '${msgObj.subject}' sending failed: ${err.message}`)
@@ -2981,7 +2985,7 @@ Content-Transfer-Encoding: quoted-printable
2981
2985
 
2982
2986
  // certificate full path
2983
2987
  if (key.includes('.certificate.') || key.includes('.tls.')) {
2984
- if (key.endsWith('.key') || key.endsWith('.cert') || key.endsWith('.ca') || key.endsWith('.pfx.bundle')) {
2988
+ if (key.endsWith('.key') || key.endsWith('.cert') || key.endsWith('.ca') || key.includes('.ca[') || key.endsWith('.pfx.bundle')) {
2985
2989
  let keyFile = path.join(this.configDir, '/certs/', dotConfig[key])
2986
2990
  if (dotConfig[key].startsWith('/') || dotConfig[key].includes('\\')) {
2987
2991
  keyFile = dotConfig[key]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scimgateway",
3
- "version": "5.1.5",
3
+ "version": "5.1.7",
4
4
  "type": "module",
5
5
  "description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
6
6
  "author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",
@@ -36,9 +36,9 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@ldapjs/asn1": "^2.0.0",
39
- "@nats-io/jetstream": "^3.0.0-35",
40
- "@nats-io/nats-core": "^3.0.0-48",
41
- "@nats-io/transport-node": "^3.0.0-33",
39
+ "@nats-io/jetstream": "^3.0.0-37",
40
+ "@nats-io/nats-core": "^3.0.0-50",
41
+ "@nats-io/transport-node": "^3.0.0-35",
42
42
  "@types/ldapjs": "^3.0.6",
43
43
  "@types/lokijs": "^1.5.14",
44
44
  "@types/tedious": "^4.0.14",
@@ -49,9 +49,9 @@
49
49
  "jsonwebtoken": "^9.0.2",
50
50
  "ldapjs": "^3.0.7",
51
51
  "lokijs": "^1.5.12",
52
- "mongodb": "^6.12.0",
52
+ "mongodb": "^6.13.0",
53
53
  "node-machine-id": "1.1.12",
54
- "nodemailer": "^6.9.16",
54
+ "nodemailer": "^6.10.0",
55
55
  "passport": "^0.7.0",
56
56
  "passport-azure-ad": "^4.3.5",
57
57
  "saml": "^3.0.1",
@@ -61,7 +61,7 @@
61
61
  "typescript": "^5.0.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@stylistic/eslint-plugin": "^2.10.1",
64
+ "@stylistic/eslint-plugin": "^3.1.0",
65
65
  "@types/bun": "latest",
66
66
  "@types/dot-object": "^2.1.6",
67
67
  "@types/jsonwebtoken": "^9.0.6",
@@ -69,8 +69,8 @@
69
69
  "@types/nodemailer": "^6.4.14",
70
70
  "@types/passport": "^1.0.16",
71
71
  "@types/passport-azure-ad": "^4.3.6",
72
- "@typescript-eslint/eslint-plugin": "^8.12.2",
73
- "@typescript-eslint/parser": "^8.12.2",
74
- "eslint": "^9.13.0"
72
+ "@typescript-eslint/eslint-plugin": "^8.23.0",
73
+ "@typescript-eslint/parser": "^8.23.0",
74
+ "eslint": "^9.20.0"
75
75
  }
76
76
  }