scimgateway 4.2.6 → 4.2.8
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 +38 -15
- package/config/plugin-api.json +2 -1
- package/config/plugin-azure-ad.json +2 -1
- package/config/plugin-ldap.json +2 -1
- package/config/plugin-loki.json +2 -1
- package/config/plugin-mongodb.json +2 -1
- package/config/plugin-mssql.json +2 -1
- package/config/plugin-saphana.json +2 -1
- package/config/plugin-scim.json +2 -1
- package/config/{plugin-forwardinc.json → plugin-soap.json} +3 -2
- package/index.js +1 -1
- package/lib/plugin-azure-ad.js +9 -3
- package/lib/plugin-mongodb.js +46 -21
- package/lib/plugin-scim.js +1 -1
- package/lib/{plugin-forwardinc.js → plugin-soap.js} +1 -1
- package/lib/postinstall.js +2 -2
- package/lib/scimgateway.js +137 -108
- package/package.json +60 -60
package/README.md
CHANGED
|
@@ -64,9 +64,10 @@ Can be used as SCIM version-gateway e.g. 1.1=>2.0 or 2.0=>1.1
|
|
|
64
64
|
Can be used to chain several SCIM Gateway's
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
* **
|
|
68
|
-
Demonstrates user provisioning towards SOAP-Based endpoint
|
|
69
|
-
|
|
67
|
+
* **Soap** (SOAP Webservice)
|
|
68
|
+
Demonstrates user provisioning towards SOAP-Based endpoint
|
|
69
|
+
Excample WSDLs are included
|
|
70
|
+
Using endpoint "Forwardinc" as an example (comes with Symantec/Broadcom/CA IM SDK - SDKWS)
|
|
70
71
|
Shows how to implement a highly configurable multi tenant or multi endpoint solution through `baseEntity` in URL
|
|
71
72
|
|
|
72
73
|
* **MSSQL** (MSSQL Database)
|
|
@@ -188,7 +189,7 @@ When maintaining a set of modifications it useful to disable the postinstall ope
|
|
|
188
189
|
const loki = require('./lib/plugin-loki')
|
|
189
190
|
// const mongodb = require('./lib/plugin-mongodb')
|
|
190
191
|
// const scim = require('./lib/plugin-scim')
|
|
191
|
-
// const
|
|
192
|
+
// const soap = require('./lib/plugin-soap')
|
|
192
193
|
// const mssql = require('./lib/plugin-mssql')
|
|
193
194
|
// const saphana = require('./lib/plugin-saphana') // prereq: npm install hdb
|
|
194
195
|
// const azureAD = require('./lib/plugin-azure-ad')
|
|
@@ -212,7 +213,8 @@ Below shows an example of config\plugin-saphana.json
|
|
|
212
213
|
"version": "2.0",
|
|
213
214
|
"customSchema": null,
|
|
214
215
|
"skipTypeConvert" : false,
|
|
215
|
-
"usePutSoftSync" : false
|
|
216
|
+
"usePutSoftSync" : false,
|
|
217
|
+
"usePutGroupMemberOfUser": false
|
|
216
218
|
},
|
|
217
219
|
"log": {
|
|
218
220
|
"loglevel": {
|
|
@@ -342,7 +344,9 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
|
|
|
342
344
|
]
|
|
343
345
|
|
|
344
346
|
|
|
345
|
-
- **scim.usePutSoftSync** - true or false, default false. `PUT /Users/bjensen` will replace the user bjensen with body content. If body contains groups, usePutSoftsync=true will prevent removing any existing groups that are not included in body.groups
|
|
347
|
+
- **scim.usePutSoftSync** - true or false, default false. `PUT /Users/bjensen` will replace the user bjensen with body content. If body contains groups, usePutSoftsync=true will prevent removing any existing groups that are not included in body.groups
|
|
348
|
+
|
|
349
|
+
- **scim."usePutGroupMemberOfUser** - true or false, default false. `PUT /Users/<user>` will replace the user with body content. If body contains groups and usePutGroupMemberOfUser=true, groups will be set on user object (groups are member of user) instead of default user member of groups
|
|
346
350
|
|
|
347
351
|
- **log.loglevel.file** - off, error, info, or debug. Output to plugin-logfile e.g. `logs\plugin-saphana.log`
|
|
348
352
|
|
|
@@ -462,20 +466,20 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
|
|
|
462
466
|
}
|
|
463
467
|
|
|
464
468
|
|
|
465
|
-
secrets.json for plugin-
|
|
469
|
+
secrets.json for plugin-soap - example (dot notation):
|
|
466
470
|
|
|
467
471
|
{
|
|
468
|
-
"plugin-
|
|
469
|
-
"plugin-
|
|
470
|
-
"plugin-
|
|
471
|
-
"plugin-
|
|
472
|
+
"plugin-soap.scimgateway.auth.basic[0].username": "gwadmin",
|
|
473
|
+
"plugin-soap.scimgateway.auth.basic[0].password": "password",
|
|
474
|
+
"plugin-soap.endpoint.username": "superuser",
|
|
475
|
+
"plugin-soap.endpoint.password": "secret"
|
|
472
476
|
}
|
|
473
477
|
|
|
474
478
|
- Custom schema attributes can be added by plugin configuration `scim.customSchema` having value set to filename of a JSON schema-file located in `<package-root>/config/schemas` e.g:
|
|
475
479
|
|
|
476
480
|
"scim": {
|
|
477
481
|
"version": "2.0",
|
|
478
|
-
"customSchema": "plugin-
|
|
482
|
+
"customSchema": "plugin-soap-schema.json"
|
|
479
483
|
},
|
|
480
484
|
|
|
481
485
|
JSON file have following syntax:
|
|
@@ -748,7 +752,7 @@ Username, password and port must correspond with plugin configuration file. For
|
|
|
748
752
|
http://localhost:8880/client-a
|
|
749
753
|
http://localhost:8880/client-b
|
|
750
754
|
|
|
751
|
-
Each baseEntity should then be defined in the plugin configuration file with custom attributes needed. Please see examples in plugin-
|
|
755
|
+
Each baseEntity should then be defined in the plugin configuration file with custom attributes needed. Please see examples in plugin-soap.json
|
|
752
756
|
|
|
753
757
|
IM 12.6 SP7 (and above) also supports pagination for SCIM endpoint (data transferred in bulks - endpoint explore of users). Loki plugin supports pagination. Other plugin may ignore this setting.
|
|
754
758
|
|
|
@@ -965,7 +969,7 @@ For JavaScript coding editor you may use [Visual Studio Code](https://code.visua
|
|
|
965
969
|
|
|
966
970
|
Preparation:
|
|
967
971
|
|
|
968
|
-
* Copy "best matching" example plugin e.g. `lib\plugin-mssql.js` and `config\plugin-mssql.json` and rename both copies to your plugin name prefix e.g. plugin-mine.js and plugin-mine.json (for SOAP Webservice endpoint we might use plugin-
|
|
972
|
+
* Copy "best matching" example plugin e.g. `lib\plugin-mssql.js` and `config\plugin-mssql.json` and rename both copies to your plugin name prefix e.g. plugin-mine.js and plugin-mine.json (for SOAP Webservice endpoint we might use plugin-soap as a template)
|
|
969
973
|
* Edit plugin-mine.json and define a unique port number for the gateway setting
|
|
970
974
|
* Edit index.js and add a new line for starting your plugin e.g. `let mine = require('./lib/plugin-mine');`
|
|
971
975
|
* Start SCIM Gateway and verify. If using CA Provisioning you could setup a SCIM endpoint using the port number you defined
|
|
@@ -988,7 +992,7 @@ Please see plugin-saphana that do not use groups.
|
|
|
988
992
|
|
|
989
993
|
Template used by CA Provisioning role should only include endpoint supported attributes defined in our plugin. Template should therefore have no links to global user for none supported attributes (e.g. remove %UT% from "Job Title" if our endpoint/code do not support title)
|
|
990
994
|
|
|
991
|
-
CA Provisioning using default SCIM endpoint do not support SCIM Enterprise User Schema Extension (having attributes like employeeNumber, costCenter, organization, division, department and manager). If we need these or other attributes not found in CA Provisioning, we could define our own by using the free-text "type" definition in the multivalue entitlements or roles attribute. In the template entitlements definition, we could for example define type=Company and set value to %UCOMP%. Please see plugin-
|
|
995
|
+
CA Provisioning using default SCIM endpoint do not support SCIM Enterprise User Schema Extension (having attributes like employeeNumber, costCenter, organization, division, department and manager). If we need these or other attributes not found in CA Provisioning, we could define our own by using the free-text "type" definition in the multivalue entitlements or roles attribute. In the template entitlements definition, we could for example define type=Company and set value to %UCOMP%. Please see plugin-soap.js using Company as a multivalue "type" definition.
|
|
992
996
|
|
|
993
997
|
Using CA Connector Xpress we could create a new SCIM endpoint type based on the original SCIM. We could then add/remove attributes and change from default assign "user to groups" to assign "groups to user". There are also other predefined endpoints based on the original SCIM. You may take a look at "ServiceNow - WSL7" and "Zendesk - WSL7".
|
|
994
998
|
|
|
@@ -1165,6 +1169,25 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1165
1169
|
|
|
1166
1170
|
## Change log
|
|
1167
1171
|
|
|
1172
|
+
### v4.2.8
|
|
1173
|
+
|
|
1174
|
+
[Fixed]
|
|
1175
|
+
|
|
1176
|
+
- PUT did not allow group name to be modified
|
|
1177
|
+
|
|
1178
|
+
### v4.2.7
|
|
1179
|
+
|
|
1180
|
+
[Added]
|
|
1181
|
+
|
|
1182
|
+
- new plugin configuration **scim.usePutGroupMemberOfUser** can be set to true or false, default false. `PUT /Users/<user>` will replace user with body content. If body contains groups and usePutGroupMemberOfUser=true, groups will be set on user object (groups are member of user) instead of default user member of groups
|
|
1183
|
+
- plugin-forwardinc renamed to plugin-soap
|
|
1184
|
+
- Dependencies bump
|
|
1185
|
+
|
|
1186
|
+
[Fixed]
|
|
1187
|
+
|
|
1188
|
+
- plugin-azure-ad fixed some issues introduced in v4.2.4
|
|
1189
|
+
- plugin-mongodb fixed some issues introduced in v4.2.4
|
|
1190
|
+
|
|
1168
1191
|
### v4.2.6
|
|
1169
1192
|
|
|
1170
1193
|
[Fixed]
|
package/config/plugin-api.json
CHANGED
package/config/plugin-ldap.json
CHANGED
package/config/plugin-loki.json
CHANGED
package/config/plugin-mssql.json
CHANGED
package/config/plugin-scim.json
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"version": "2.0",
|
|
8
8
|
"customSchema": null,
|
|
9
9
|
"skipTypeConvert": false,
|
|
10
|
-
"usePutSoftSync": false
|
|
10
|
+
"usePutSoftSync": false,
|
|
11
|
+
"usePutGroupMemberOfUser": false
|
|
11
12
|
},
|
|
12
13
|
"log": {
|
|
13
14
|
"loglevel": {
|
|
@@ -156,4 +157,4 @@
|
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
|
-
}
|
|
160
|
+
}
|
package/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const loki = require('./lib/plugin-loki')
|
|
13
13
|
// const mongodb = require('./lib/plugin-mongodb')
|
|
14
14
|
// const scim = require('./lib/plugin-scim')
|
|
15
|
-
// const
|
|
15
|
+
// const soap = require('./lib/plugin-soap')
|
|
16
16
|
// const mssql = require('./lib/plugin-mssql')
|
|
17
17
|
// const saphana = require('./lib/plugin-saphana') // prereq: npm install hdb --save
|
|
18
18
|
// const azureAD = require('./lib/plugin-azure-ad')
|
package/lib/plugin-azure-ad.js
CHANGED
|
@@ -578,7 +578,9 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
578
578
|
} else if (getObj.operator === 'eq' && getObj.attribute === 'members.value') {
|
|
579
579
|
// mandatory - return all groups the user 'id' (getObj.value) is member of - correspond to getGroupMembers() in versions < 4.x.x
|
|
580
580
|
// Resources = [{ id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }]
|
|
581
|
-
|
|
581
|
+
// not using below expand because Azure returns only a maximum of 20 items for the expanded relationship
|
|
582
|
+
// path = `/users/${getObj.value}/memberOf/microsoft.graph.group?$select=id,displayName&$expand=members($select=id,displayName)`
|
|
583
|
+
path = `/users/${getObj.value}/memberOf/microsoft.graph.group?$select=id,displayName`
|
|
582
584
|
} else {
|
|
583
585
|
// optional - simpel filtering
|
|
584
586
|
throw new Error(`${action} error: not supporting simpel filtering: ${getObj.rawFilter}`)
|
|
@@ -633,6 +635,10 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
633
635
|
}
|
|
634
636
|
})
|
|
635
637
|
delete response.body.value[i].members
|
|
638
|
+
} else if (getObj.operator === 'eq' && getObj.attribute === 'members.value') { // Not using expand-members. Only includes current user as member, but should have requested all...
|
|
639
|
+
members = [{
|
|
640
|
+
value: getObj.value
|
|
641
|
+
}]
|
|
636
642
|
}
|
|
637
643
|
|
|
638
644
|
const [scimObj] = scimgateway.endpointMapper('inbound', response.body.value[i], config.map.group) // endpoint => SCIM/CustomSCIM attribute standard
|
|
@@ -1174,9 +1180,9 @@ const getAccessToken = async (baseEntity, ctx) => {
|
|
|
1174
1180
|
const clientIdentifier = getClientIdentifier(ctx)
|
|
1175
1181
|
const d = new Date() / 1000 // seconds (unix time)
|
|
1176
1182
|
if (_serviceClient[baseEntity] && _serviceClient[baseEntity][clientIdentifier] && _serviceClient[baseEntity][clientIdentifier].accessToken &&
|
|
1177
|
-
(_serviceClient[baseEntity].accessToken.validTo >= d + 30)) { // avoid simultaneously token requests
|
|
1183
|
+
(_serviceClient[baseEntity][clientIdentifier].accessToken.validTo >= d + 30)) { // avoid simultaneously token requests
|
|
1178
1184
|
lock.release()
|
|
1179
|
-
return _serviceClient[baseEntity].accessToken
|
|
1185
|
+
return _serviceClient[baseEntity][clientIdentifier].accessToken
|
|
1180
1186
|
}
|
|
1181
1187
|
|
|
1182
1188
|
const action = 'getAccessToken'
|
package/lib/plugin-mongodb.js
CHANGED
|
@@ -48,15 +48,16 @@ scimgateway.authPassThroughAllowed = false // true enables auth passThrough (no
|
|
|
48
48
|
|
|
49
49
|
const validFilterOperators = ['eq', 'ne', 'aeq', 'dteq', 'gt', 'gte', 'lt', 'lte', 'between', 'jgt', 'jgte', 'jlt', 'jlte', 'jbetween', 'regex', 'in', 'nin', 'keyin', 'nkeyin', 'definedin', 'undefinedin', 'contains', 'containsAny', 'type', 'finite', 'size', 'len', 'exists']
|
|
50
50
|
|
|
51
|
-
if (!config.entity) throw new Error('error: configuration entity is missing')
|
|
52
|
-
if (!scimgateway.authPassThroughAllowed) { // not using Auth PassThrough, loading db handler at startup using username/password from config
|
|
53
|
-
for (const baseEntity in config.entity) {
|
|
54
|
-
loadHandler(baseEntity)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
51
|
async function loadHandler (baseEntity, ctx) {
|
|
59
52
|
const action = 'loadHander'
|
|
53
|
+
|
|
54
|
+
const clientIdentifier = getClientIdentifier(ctx)
|
|
55
|
+
if (config.entity[baseEntity].isLoaded) { // loadHandler only once
|
|
56
|
+
if (!clientIdentifier) return clientIdentifier // not using Auth PassThrough
|
|
57
|
+
if (config.entity[baseEntity][clientIdentifier]) return clientIdentifier // authenticated
|
|
58
|
+
throw new Error('{"error":"Access denied","statusCode":401}') // string: "statusCode":401 ensure gateway returns 401
|
|
59
|
+
}
|
|
60
|
+
|
|
60
61
|
if (!config.entity[baseEntity].baseUrl) { // mongodb://host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]] - e.g: mongodb://localhost:27017/db?tls=true&tlsInsecure=true
|
|
61
62
|
throw new Error(`${action} error: configuration entity.${baseEntity}.baseUrl is missing`)
|
|
62
63
|
}
|
|
@@ -102,6 +103,9 @@ async function loadHandler (baseEntity, ctx) {
|
|
|
102
103
|
groups.createIndex({ id: 1 }, { unique: true })
|
|
103
104
|
}
|
|
104
105
|
} catch (error) {
|
|
106
|
+
if (clientIdentifier && error.message.includes('Authentication')) {
|
|
107
|
+
throw new Error('{"error":"Access denied","statusCode":401}') // string: "statusCode":401 ensure gateway returns 401
|
|
108
|
+
}
|
|
105
109
|
throw new Error(`${action} error: failed to connect to database '${client.s.options.dbName}' - ${error.message}`)
|
|
106
110
|
}
|
|
107
111
|
|
|
@@ -147,11 +151,12 @@ async function loadHandler (baseEntity, ctx) {
|
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
}
|
|
150
|
-
const clientIdentifier = getClientIdentifier(ctx)
|
|
151
154
|
if (!config.entity[baseEntity][clientIdentifier]) config.entity[baseEntity][clientIdentifier] = {}
|
|
152
155
|
config.entity[baseEntity][clientIdentifier].collection = {}
|
|
153
156
|
config.entity[baseEntity][clientIdentifier].collection.users = users
|
|
154
157
|
config.entity[baseEntity][clientIdentifier].collection.groups = groups
|
|
158
|
+
config.entity[baseEntity].isLoaded = true
|
|
159
|
+
return clientIdentifier
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
// =================================================
|
|
@@ -173,6 +178,8 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
173
178
|
const action = 'getUsers'
|
|
174
179
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" getObj=${getObj ? JSON.stringify(getObj) : ''} attributes=${attributes}`)
|
|
175
180
|
|
|
181
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
182
|
+
|
|
176
183
|
if (getObj.operator) { // convert to plugin supported syntax
|
|
177
184
|
switch (getObj.operator) {
|
|
178
185
|
case 'co':
|
|
@@ -203,10 +210,6 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
203
210
|
}
|
|
204
211
|
}
|
|
205
212
|
|
|
206
|
-
const clientIdentifier = getClientIdentifier(ctx)
|
|
207
|
-
if (ctx && !config.entity[baseEntity][clientIdentifier]) { // first (or previous failed) PassThrough attempt - have to load connection
|
|
208
|
-
await loadHandler(baseEntity, ctx)
|
|
209
|
-
}
|
|
210
213
|
const users = config.entity[baseEntity][clientIdentifier].collection.users
|
|
211
214
|
let findObj
|
|
212
215
|
|
|
@@ -275,6 +278,8 @@ scimgateway.createUser = async (baseEntity, userObj, ctx) => {
|
|
|
275
278
|
const action = 'createUser'
|
|
276
279
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" userObj=${JSON.stringify(userObj)}`)
|
|
277
280
|
|
|
281
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
282
|
+
|
|
278
283
|
const notValid = scimgateway.notValidAttributes(userObj, validScimAttr) // We should check for unsupported endpoint attributes
|
|
279
284
|
if (notValid) {
|
|
280
285
|
throw new Error(`${action} error: unsupported scim attributes: ${notValid} (supporting only these attributes: ${validScimAttr.toString()})`)
|
|
@@ -308,7 +313,7 @@ scimgateway.createUser = async (baseEntity, userObj, ctx) => {
|
|
|
308
313
|
userObj = encodeDotDate(userObj)
|
|
309
314
|
|
|
310
315
|
try {
|
|
311
|
-
const users = config.entity[baseEntity].collection.users
|
|
316
|
+
const users = config.entity[baseEntity][clientIdentifier].collection.users
|
|
312
317
|
await users.insertOne(userObj)
|
|
313
318
|
return null
|
|
314
319
|
} catch (err) {
|
|
@@ -327,7 +332,9 @@ scimgateway.deleteUser = async (baseEntity, id, ctx) => {
|
|
|
327
332
|
const action = 'deleteUser'
|
|
328
333
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
|
|
329
334
|
|
|
330
|
-
const
|
|
335
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
336
|
+
|
|
337
|
+
const users = config.entity[baseEntity][clientIdentifier].collection.users
|
|
331
338
|
try {
|
|
332
339
|
/*
|
|
333
340
|
const now = Date.now()
|
|
@@ -354,6 +361,8 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
|
|
|
354
361
|
const action = 'modifyUser'
|
|
355
362
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} attrObj=${JSON.stringify(attrObj)}`)
|
|
356
363
|
|
|
364
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
365
|
+
|
|
357
366
|
const notValid = scimgateway.notValidAttributes(attrObj, validScimAttr) // We should check for unsupported endpoint attributes
|
|
358
367
|
if (notValid) {
|
|
359
368
|
throw new Error(`${action} error: unsupported scim attributes: ${notValid} (supporting only these attributes: ${validScimAttr.toString()})`)
|
|
@@ -363,7 +372,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
|
|
|
363
372
|
let res
|
|
364
373
|
|
|
365
374
|
try {
|
|
366
|
-
const users = config.entity[baseEntity].collection.users
|
|
375
|
+
const users = config.entity[baseEntity][clientIdentifier].collection.users
|
|
367
376
|
res = await users.find({ id }, { projection: { _id: 0 } }).toArray()
|
|
368
377
|
if (res.length === 0) throw new Error('user does not exist')
|
|
369
378
|
if (res.length > 1) throw new Error('user is not unique, more than one have been found')
|
|
@@ -474,7 +483,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
|
|
|
474
483
|
userObj = encodeDotDate(userObj)
|
|
475
484
|
|
|
476
485
|
try {
|
|
477
|
-
const users = config.entity[baseEntity].collection.users
|
|
486
|
+
const users = config.entity[baseEntity][clientIdentifier].collection.users
|
|
478
487
|
await users.replaceOne({ id: id }, userObj)
|
|
479
488
|
return null
|
|
480
489
|
} catch (err) {
|
|
@@ -501,6 +510,8 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
501
510
|
const action = 'getGroups'
|
|
502
511
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" getObj=${getObj ? JSON.stringify(getObj) : ''} attributes=${attributes}`)
|
|
503
512
|
|
|
513
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
514
|
+
|
|
504
515
|
if (getObj.operator) { // convert to plugin supported syntax
|
|
505
516
|
switch (getObj.operator) {
|
|
506
517
|
case 'co':
|
|
@@ -574,7 +585,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
574
585
|
|
|
575
586
|
try {
|
|
576
587
|
const projection = attributes.length > 0 ? getProjectionFromAttributes(attributes) : { _id: 0 }
|
|
577
|
-
const groups = config.entity[baseEntity].collection.groups
|
|
588
|
+
const groups = config.entity[baseEntity][clientIdentifier].collection.groups
|
|
578
589
|
const groupsArr = await groups.find(findObj, { projection: projection }).sort({ _id: 1 }).skip(getObj.startIndex - 1).limit(getObj.count).toArray()
|
|
579
590
|
const totalResults = await groups.countDocuments(findObj, { projection: projection })
|
|
580
591
|
const arr = groupsArr.map((obj) => {
|
|
@@ -595,6 +606,8 @@ scimgateway.createGroup = async (baseEntity, groupObj, ctx) => {
|
|
|
595
606
|
const action = 'createGroup'
|
|
596
607
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" groupObj=${JSON.stringify(groupObj)}`)
|
|
597
608
|
|
|
609
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
610
|
+
|
|
598
611
|
if (!groupObj.meta) {
|
|
599
612
|
const now = Date.now()
|
|
600
613
|
groupObj.meta = {
|
|
@@ -608,7 +621,7 @@ scimgateway.createGroup = async (baseEntity, groupObj, ctx) => {
|
|
|
608
621
|
groupObj = encodeDotDate(groupObj)
|
|
609
622
|
|
|
610
623
|
try {
|
|
611
|
-
const groups = config.entity[baseEntity].collection.groups
|
|
624
|
+
const groups = config.entity[baseEntity][clientIdentifier].collection.groups
|
|
612
625
|
await groups.insertOne(groupObj)
|
|
613
626
|
return null
|
|
614
627
|
} catch (err) {
|
|
@@ -627,7 +640,9 @@ scimgateway.deleteGroup = async (baseEntity, id, ctx) => {
|
|
|
627
640
|
const action = 'deleteGroup'
|
|
628
641
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
|
|
629
642
|
|
|
630
|
-
const
|
|
643
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
644
|
+
|
|
645
|
+
const groups = config.entity[baseEntity][clientIdentifier].collection.groups
|
|
631
646
|
try {
|
|
632
647
|
/*
|
|
633
648
|
const now = Date.now()
|
|
@@ -654,8 +669,10 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
|
|
|
654
669
|
const action = 'modifyGroup'
|
|
655
670
|
scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} attrObj=${JSON.stringify(attrObj)}`)
|
|
656
671
|
|
|
657
|
-
const
|
|
658
|
-
|
|
672
|
+
const clientIdentifier = await loadHandler(baseEntity, ctx) // includes Auth PassThrough logic and loaded only once
|
|
673
|
+
|
|
674
|
+
const users = config.entity[baseEntity][clientIdentifier].collection.users
|
|
675
|
+
const groups = config.entity[baseEntity][clientIdentifier].collection.groups
|
|
659
676
|
let res
|
|
660
677
|
let isModified = false
|
|
661
678
|
|
|
@@ -851,3 +868,11 @@ process.on('SIGINT', () => {
|
|
|
851
868
|
}
|
|
852
869
|
}
|
|
853
870
|
})
|
|
871
|
+
|
|
872
|
+
// connect MongoDb and load users/groups
|
|
873
|
+
if (!config.entity) throw new Error('error: configuration entity is missing')
|
|
874
|
+
if (!scimgateway.authPassThroughAllowed) { // not using Auth PassThrough, loading db handler at startup using username/password from config
|
|
875
|
+
for (const baseEntity in config.entity) {
|
|
876
|
+
loadHandler(baseEntity)
|
|
877
|
+
}
|
|
878
|
+
}
|
package/lib/plugin-scim.js
CHANGED
package/lib/postinstall.js
CHANGED
|
@@ -29,7 +29,7 @@ if (!fsExistsSync('../../lib')) fs.mkdirSync('../../lib')
|
|
|
29
29
|
|
|
30
30
|
if (!fsExistsSync('../../config/plugin-loki.json')) fs.writeFileSync('../../config/plugin-loki.json', fs.readFileSync('./config/plugin-loki.json'))
|
|
31
31
|
if (!fsExistsSync('../../config/plugin-scim.json')) fs.writeFileSync('../../config/plugin-scim.json', fs.readFileSync('./config/plugin-scim.json'))
|
|
32
|
-
if (!fsExistsSync('../../config/plugin-
|
|
32
|
+
if (!fsExistsSync('../../config/plugin-soap.json')) fs.writeFileSync('../../config/plugin-soap.json', fs.readFileSync('./config/plugin-soap.json'))
|
|
33
33
|
if (!fsExistsSync('../../config/plugin-mssql.json')) fs.writeFileSync('../../config/plugin-mssql.json', fs.readFileSync('./config/plugin-mssql.json'))
|
|
34
34
|
if (!fsExistsSync('../../config/plugin-saphana.json')) fs.writeFileSync('../../config/plugin-saphana.json', fs.readFileSync('./config/plugin-saphana.json'))
|
|
35
35
|
if (!fsExistsSync('../../config/plugin-api.json')) fs.writeFileSync('../../config/plugin-api.json', fs.readFileSync('./config/plugin-api.json'))
|
|
@@ -39,7 +39,7 @@ if (!fsExistsSync('../../config/plugin-mongodb.json')) fs.writeFileSync('../../c
|
|
|
39
39
|
|
|
40
40
|
fs.writeFileSync('../../lib/plugin-loki.js', fs.readFileSync('./lib/plugin-loki.js'))
|
|
41
41
|
fs.writeFileSync('../../lib/plugin-scim.js', fs.readFileSync('./lib/plugin-scim.js'))
|
|
42
|
-
fs.writeFileSync('../../lib/plugin-
|
|
42
|
+
fs.writeFileSync('../../lib/plugin-soap.js', fs.readFileSync('./lib/plugin-soap.js'))
|
|
43
43
|
fs.writeFileSync('../../lib/plugin-mssql.js', fs.readFileSync('./lib/plugin-mssql.js'))
|
|
44
44
|
fs.writeFileSync('../../lib/plugin-saphana.js', fs.readFileSync('./lib/plugin-saphana.js'))
|
|
45
45
|
fs.writeFileSync('../../lib/plugin-api.js', fs.readFileSync('./lib/plugin-api.js'))
|
package/lib/scimgateway.js
CHANGED
|
@@ -1280,6 +1280,7 @@ const ScimGateway = function () {
|
|
|
1280
1280
|
ctx.body = e
|
|
1281
1281
|
return
|
|
1282
1282
|
}
|
|
1283
|
+
delete scimdata.id
|
|
1283
1284
|
logger.debug(`${gwName}[${pluginName}] calling "${handle.modifyMethod}" and awaiting result`)
|
|
1284
1285
|
try {
|
|
1285
1286
|
await this[handle.modifyMethod](ctx.params.baseEntity, id, scimdata, ctx.ctxCopy)
|
|
@@ -1290,22 +1291,26 @@ const ScimGateway = function () {
|
|
|
1290
1291
|
}
|
|
1291
1292
|
logger.debug(`${gwName}[${pluginName}] calling "${handle.getMethod}" and awaiting result`)
|
|
1292
1293
|
const res = await this[handle.getMethod](ctx.params.baseEntity, { attribute: 'id', operator: 'eq', value: id }, ctx.query.attributes ? ctx.query.attributes.split(',').map(item => item.trim()) : [], ctx.ctxCopy)
|
|
1294
|
+
|
|
1293
1295
|
scimdata = {
|
|
1294
|
-
Resources: []
|
|
1295
|
-
totalResults: null
|
|
1296
|
+
Resources: []
|
|
1296
1297
|
}
|
|
1297
1298
|
if (res) {
|
|
1298
1299
|
if (res.Resources && Array.isArray(res.Resources)) {
|
|
1299
1300
|
scimdata.Resources = res.Resources
|
|
1300
|
-
scimdata.totalResults = res.totalResults
|
|
1301
1301
|
} else if (Array.isArray(res)) scimdata.Resources = res
|
|
1302
|
-
else if (typeof (res) === 'object'
|
|
1302
|
+
else if (typeof (res) === 'object') scimdata.Resources[0] = res
|
|
1303
|
+
else scimdata.Resources = []
|
|
1304
|
+
} else scimdata.Resources = []
|
|
1305
|
+
if (scimdata.Resources.length === 0 || scimdata.Resources.length > 1) {
|
|
1306
|
+
ctx.status = 204
|
|
1307
|
+
return
|
|
1303
1308
|
}
|
|
1304
|
-
|
|
1309
|
+
|
|
1305
1310
|
const location = ctx.origin + ctx.path
|
|
1306
1311
|
ctx.set('Location', location)
|
|
1307
|
-
|
|
1308
|
-
scimdata = utils.stripObj(
|
|
1312
|
+
const userObj = addPrimaryAttrs(scimdata.Resources[0])
|
|
1313
|
+
scimdata = utils.stripObj(userObj, ctx.query.attributes, ctx.query.excludedAttributes)
|
|
1309
1314
|
scimdata = addSchemas(scimdata, handle.description, isScimv2)
|
|
1310
1315
|
ctx.status = 200
|
|
1311
1316
|
ctx.body = scimdata
|
|
@@ -1362,17 +1367,27 @@ const ScimGateway = function () {
|
|
|
1362
1367
|
}
|
|
1363
1368
|
}
|
|
1364
1369
|
}
|
|
1370
|
+
if (config.scim.usePutGroupMemberOfUser) { // group member of user instead of default user member of group
|
|
1371
|
+
if (clearedObj.groups && Array.isArray(clearedObj.groups)) {
|
|
1372
|
+
for (let i = 0; i < clearedObj.groups.length; i++) {
|
|
1373
|
+
if (clearedObj.groups[i].operation && clearedObj.groups[i].operation === 'delete') {
|
|
1374
|
+
clearedObj.groups.splice(i, 1) // delete
|
|
1375
|
+
i -= 1
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1365
1380
|
}
|
|
1366
1381
|
|
|
1367
1382
|
// merge cleared object with the new
|
|
1368
1383
|
const newObj = utils.extendObj(clearedObj, jsonBody)
|
|
1369
1384
|
delete newObj.id
|
|
1370
|
-
delete newObj.userName
|
|
1371
|
-
delete newObj.externalId
|
|
1372
|
-
delete newObj.groups // do not support "group member of users"
|
|
1373
1385
|
delete newObj.schemas
|
|
1374
1386
|
delete newObj.meta
|
|
1375
|
-
if (
|
|
1387
|
+
if (!config.scim.usePutGroupMemberOfUser) delete newObj.groups
|
|
1388
|
+
// not allowing userName/displayName set to blank
|
|
1389
|
+
if (!newObj.userName) delete newObj.userName
|
|
1390
|
+
if (!newObj.displayName) delete newObj.displayName
|
|
1376
1391
|
|
|
1377
1392
|
let [scimdata, err] = ScimGateway.prototype.convertedScim(newObj)
|
|
1378
1393
|
if (err) throw err
|
|
@@ -1382,125 +1397,139 @@ const ScimGateway = function () {
|
|
|
1382
1397
|
await this[handle.modifyMethod](ctx.params.baseEntity, id, scimdata, ctx.ctxCopy)
|
|
1383
1398
|
|
|
1384
1399
|
// add/remove groups
|
|
1385
|
-
if (
|
|
1386
|
-
if (
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
let currentGroups
|
|
1390
|
-
if (currentObj.groups && Array.isArray(currentObj.groups)) currentGroups = currentObj.groups
|
|
1391
|
-
else { // try to get current groups the standard way
|
|
1392
|
-
let res
|
|
1393
|
-
try {
|
|
1394
|
-
res = await this[handler.groups.getMethod](ctx.params.baseEntity, { attribute: 'members.value', operator: 'eq', value: decodeURIComponent(id) }, ['id', 'displayName'], ctx.ctxCopy)
|
|
1395
|
-
} catch (err) {} // method may be implemented but throwing error like groups not supported/implemented
|
|
1396
|
-
currentGroups = []
|
|
1397
|
-
if (res && res.Resources && Array.isArray(res.Resources) && res.Resources.length > 0) {
|
|
1398
|
-
for (let i = 0; i < res.Resources.length; i++) {
|
|
1399
|
-
if (!res.Resources[i].id) continue
|
|
1400
|
-
const el = {}
|
|
1401
|
-
el.value = res.Resources[i].id
|
|
1402
|
-
if (res.Resources[i].displayName) el.display = res.Resources[i].displayName
|
|
1403
|
-
currentGroups.push(el) // { "value": "Admins", "display": "Admins"}
|
|
1404
|
-
}
|
|
1400
|
+
if (!config.scim.usePutGroupMemberOfUser) { // default user member of group
|
|
1401
|
+
if (jsonBody.groups && Array.isArray(jsonBody.groups)) { // only if groups included, { "groups": [] } will remove all existing
|
|
1402
|
+
if (typeof this[handler.groups.getMethod] !== 'function' || typeof this[handler.groups.modifyMethod] !== 'function') {
|
|
1403
|
+
throw new Error('replaceUser error: put operation can not be fully completed for the user`s groups, methods like getGroups() and modifyGroup() are not implemented')
|
|
1405
1404
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1405
|
+
let currentGroups
|
|
1406
|
+
if (currentObj.groups && Array.isArray(currentObj.groups)) currentGroups = currentObj.groups
|
|
1407
|
+
else { // try to get current groups the standard way
|
|
1408
|
+
let res
|
|
1409
|
+
try {
|
|
1410
|
+
res = await this[handler.groups.getMethod](ctx.params.baseEntity, { attribute: 'members.value', operator: 'eq', value: decodeURIComponent(id) }, ['id', 'displayName'], ctx.ctxCopy)
|
|
1411
|
+
} catch (err) {} // method may be implemented but throwing error like groups not supported/implemented
|
|
1412
|
+
currentGroups = []
|
|
1413
|
+
if (res && res.Resources && Array.isArray(res.Resources) && res.Resources.length > 0) {
|
|
1414
|
+
for (let i = 0; i < res.Resources.length; i++) {
|
|
1415
|
+
if (!res.Resources[i].id) continue
|
|
1416
|
+
const el = {}
|
|
1417
|
+
el.value = res.Resources[i].id
|
|
1418
|
+
if (res.Resources[i].displayName) el.display = res.Resources[i].displayName
|
|
1419
|
+
currentGroups.push(el) // { "value": "Admins", "display": "Admins"}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1410
1422
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1423
|
+
currentGroups = currentGroups.map((el) => {
|
|
1424
|
+
if (el.value) {
|
|
1425
|
+
el.value = decodeURIComponent(el.value)
|
|
1426
|
+
}
|
|
1427
|
+
return el
|
|
1428
|
+
})
|
|
1413
1429
|
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1430
|
+
const addGrps = []
|
|
1431
|
+
const removeGrps = []
|
|
1432
|
+
// add
|
|
1433
|
+
for (let i = 0; i < jsonBody.groups.length; i++) {
|
|
1434
|
+
if (!jsonBody.groups[i].value) continue
|
|
1435
|
+
jsonBody.groups[i].value = decodeURIComponent(jsonBody.groups[i].value)
|
|
1436
|
+
let found = false
|
|
1437
|
+
for (let j = 0; j < currentGroups.length; j++) {
|
|
1438
|
+
if (jsonBody.groups[i].value === currentGroups[j].value) {
|
|
1439
|
+
found = true
|
|
1440
|
+
break
|
|
1441
|
+
}
|
|
1425
1442
|
}
|
|
1443
|
+
if (!found && jsonBody.groups[i].value) addGrps.push(jsonBody.groups[i].value)
|
|
1426
1444
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
break
|
|
1445
|
+
// remove
|
|
1446
|
+
for (let i = 0; i < currentGroups.length; i++) {
|
|
1447
|
+
let found = false
|
|
1448
|
+
for (let j = 0; j < jsonBody.groups.length; j++) {
|
|
1449
|
+
if (!jsonBody.groups[j].value) continue
|
|
1450
|
+
jsonBody.groups[j].value = decodeURIComponent(jsonBody.groups[j].value)
|
|
1451
|
+
if (currentGroups[i].value === jsonBody.groups[j].value) {
|
|
1452
|
+
found = true
|
|
1453
|
+
break
|
|
1454
|
+
}
|
|
1438
1455
|
}
|
|
1456
|
+
if (!found && currentGroups[i].value) removeGrps.push(currentGroups[i].value)
|
|
1439
1457
|
}
|
|
1440
|
-
if (!found && currentGroups[i].value) removeGrps.push(currentGroups[i].value)
|
|
1441
|
-
}
|
|
1442
1458
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1459
|
+
const addGroups = async (grp) => {
|
|
1460
|
+
const obj = { members: [{ value: id }] }
|
|
1461
|
+
return await this[handler.groups.modifyMethod](ctx.params.baseEntity, grp, obj, ctx.ctxCopy)
|
|
1462
|
+
}
|
|
1447
1463
|
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1464
|
+
const removeGroups = async (grp) => {
|
|
1465
|
+
const obj = { members: [{ operation: 'delete', value: id }] }
|
|
1466
|
+
return await this[handler.groups.modifyMethod](ctx.params.baseEntity, grp, obj, ctx.ctxCopy)
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
let errRemove
|
|
1470
|
+
if (!config.scim.usePutSoftSync) { // default will remove any existing groups not included, usePutSoftSync=true prevents removing existing groups (only add groups)
|
|
1471
|
+
await Promise.all(removeGrps.map((grp) => removeGroups(grp)))
|
|
1472
|
+
.then()
|
|
1473
|
+
.catch((err) => {
|
|
1474
|
+
errRemove = err
|
|
1475
|
+
})
|
|
1476
|
+
}
|
|
1452
1477
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
await Promise.all(removeGrps.map((grp) => removeGroups(grp)))
|
|
1478
|
+
let errAdd
|
|
1479
|
+
await Promise.all(addGrps.map((grp) => addGroups(grp)))
|
|
1456
1480
|
.then()
|
|
1457
1481
|
.catch((err) => {
|
|
1458
|
-
|
|
1482
|
+
errAdd = err
|
|
1459
1483
|
})
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
let errAdd
|
|
1463
|
-
await Promise.all(addGrps.map((grp) => addGroups(grp)))
|
|
1464
|
-
.then()
|
|
1465
|
-
.catch((err) => {
|
|
1466
|
-
errAdd = err
|
|
1467
|
-
})
|
|
1468
1484
|
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1485
|
+
let errMsg = ''
|
|
1486
|
+
if (errRemove) errMsg = `removeGroups error: ${errRemove.message}`
|
|
1487
|
+
if (errAdd) errMsg += `${errMsg ? ' ' : ''}addGroups error: ${errAdd.message}`
|
|
1488
|
+
if (errMsg) throw new Error(errMsg)
|
|
1489
|
+
}
|
|
1473
1490
|
}
|
|
1474
1491
|
|
|
1475
1492
|
// get updated object
|
|
1476
1493
|
logger.debug(`${gwName}[${pluginName}] calling "${handle.getMethod}" and awaiting result`)
|
|
1477
1494
|
res = await this[handle.getMethod](ctx.params.baseEntity, { attribute: 'id', operator: 'eq', value: id }, [], ctx.ctxCopy)
|
|
1478
1495
|
|
|
1479
|
-
scimdata = {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1496
|
+
scimdata = {
|
|
1497
|
+
Resources: []
|
|
1498
|
+
}
|
|
1499
|
+
if (res) {
|
|
1500
|
+
if (res.Resources && Array.isArray(res.Resources)) {
|
|
1501
|
+
scimdata.Resources = res.Resources
|
|
1502
|
+
} else if (Array.isArray(res)) scimdata.Resources = res
|
|
1503
|
+
else if (typeof (res) === 'object') scimdata.Resources[0] = res
|
|
1504
|
+
else scimdata.Resources = []
|
|
1505
|
+
} else scimdata.Resources = []
|
|
1506
|
+
if (scimdata.Resources.length === 0 || scimdata.Resources.length > 1) {
|
|
1507
|
+
ctx.status = 204
|
|
1508
|
+
return
|
|
1509
|
+
}
|
|
1510
|
+
scimdata = scimdata.Resources[0]
|
|
1484
1511
|
|
|
1485
1512
|
// include groups
|
|
1486
|
-
if (
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1513
|
+
if (!config.scim.usePutGroupMemberOfUser) { // default user member of group
|
|
1514
|
+
if (handle.getMethod === handler.users.getMethod && typeof this[handler.groups.getMethod] === 'function') {
|
|
1515
|
+
logger.debug(`${gwName}[${pluginName}] calling "${handler.groups.getMethod}" and awaiting result`)
|
|
1516
|
+
const res = await this[handler.groups.getMethod](ctx.params.baseEntity, { attribute: 'members.value', operator: 'eq', value: id }, ['id', 'displayName'], ctx.ctxCopy)
|
|
1517
|
+
let grps = []
|
|
1518
|
+
if (res && res.Resources && Array.isArray(res.Resources)) grps = res.Resources
|
|
1519
|
+
else if (Array.isArray(res)) grps = res
|
|
1520
|
+
else if (res && typeof (res) === 'object' && Object.keys(res).length > 0) grps = [res]
|
|
1521
|
+
else throw Error(`put using method ${handler.groups.getMethod} - got unexpected response: ${JSON.stringify(res)}`)
|
|
1522
|
+
|
|
1523
|
+
if (grps.length > 0) {
|
|
1524
|
+
scimdata.groups = []
|
|
1525
|
+
for (let i = 0; i < grps.length; i++) {
|
|
1526
|
+
const el = {}
|
|
1527
|
+
el.value = grps[i].id
|
|
1528
|
+
if (grps[i].displayName) el.display = grps[i].displayName
|
|
1529
|
+
if (isScimv2) el.type = 'direct'
|
|
1530
|
+
else el.type = { value: 'direct' }
|
|
1531
|
+
if (el.value) scimdata.groups.push(el) // { "value": "Admins", "display": "Admins", "type": "direct"}
|
|
1532
|
+
}
|
|
1504
1533
|
}
|
|
1505
1534
|
}
|
|
1506
1535
|
}
|
package/package.json
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "scimgateway",
|
|
3
|
-
"version": "4.2.
|
|
4
|
-
"description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
|
|
5
|
-
"author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",
|
|
6
|
-
"homepage": "https://elshaug.xyz",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"main": "lib/scimgateway.js",
|
|
9
|
-
"scripts": {
|
|
10
|
-
"postinstall": "node lib/postinstall.js",
|
|
11
|
-
"start": "node index.js",
|
|
12
|
-
"test": "mocha -R spec ./test"
|
|
13
|
-
},
|
|
14
|
-
"bin": {
|
|
15
|
-
"scimgateway": "./index.js"
|
|
16
|
-
},
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "https://github.com/jelhub/scimgateway.git"
|
|
20
|
-
},
|
|
21
|
-
"keywords": [
|
|
22
|
-
"scim",
|
|
23
|
-
"gateway",
|
|
24
|
-
"proxy",
|
|
25
|
-
"azure",
|
|
26
|
-
"identity",
|
|
27
|
-
"manager",
|
|
28
|
-
"provisioning",
|
|
29
|
-
"iga"
|
|
30
|
-
],
|
|
31
|
-
"engines": {
|
|
32
|
-
"node": ">=14.0.0"
|
|
33
|
-
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"@godaddy/terminus": "^4.
|
|
36
|
-
"callsite": "^1.0.0",
|
|
37
|
-
"dot-object": "^2.1.4",
|
|
38
|
-
"https-proxy-agent": "^5.0.1",
|
|
39
|
-
"is-in-subnet": "^4.0.1",
|
|
40
|
-
"jsonwebtoken": "^9.0.0",
|
|
41
|
-
"koa": "^2.14.
|
|
42
|
-
"koa-bodyparser": "^4.
|
|
43
|
-
"koa-router": "^12.0.0",
|
|
44
|
-
"ldapjs": "^
|
|
45
|
-
"lokijs": "^1.5.12",
|
|
46
|
-
"mongodb": "^
|
|
47
|
-
"node-machine-id": "1.1.9",
|
|
48
|
-
"nodemailer": "^6.
|
|
49
|
-
"passport": "^0.6.0",
|
|
50
|
-
"passport-azure-ad": "^4.3.
|
|
51
|
-
"soap": "^0.
|
|
52
|
-
"tedious": "^
|
|
53
|
-
"winston": "^3.
|
|
54
|
-
},
|
|
55
|
-
"devDependencies": {
|
|
56
|
-
"chai": "^4.2.0",
|
|
57
|
-
"mocha": "^9.2.0",
|
|
58
|
-
"supertest": "^
|
|
59
|
-
}
|
|
60
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "scimgateway",
|
|
3
|
+
"version": "4.2.8",
|
|
4
|
+
"description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
|
|
5
|
+
"author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",
|
|
6
|
+
"homepage": "https://elshaug.xyz",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "lib/scimgateway.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node lib/postinstall.js",
|
|
11
|
+
"start": "node index.js",
|
|
12
|
+
"test": "mocha -R spec ./test"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"scimgateway": "./index.js"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/jelhub/scimgateway.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"scim",
|
|
23
|
+
"gateway",
|
|
24
|
+
"proxy",
|
|
25
|
+
"azure",
|
|
26
|
+
"identity",
|
|
27
|
+
"manager",
|
|
28
|
+
"provisioning",
|
|
29
|
+
"iga"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=14.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@godaddy/terminus": "^4.12.0",
|
|
36
|
+
"callsite": "^1.0.0",
|
|
37
|
+
"dot-object": "^2.1.4",
|
|
38
|
+
"https-proxy-agent": "^5.0.1",
|
|
39
|
+
"is-in-subnet": "^4.0.1",
|
|
40
|
+
"jsonwebtoken": "^9.0.0",
|
|
41
|
+
"koa": "^2.14.2",
|
|
42
|
+
"koa-bodyparser": "^4.4.0",
|
|
43
|
+
"koa-router": "^12.0.0",
|
|
44
|
+
"ldapjs": "^3.0.2",
|
|
45
|
+
"lokijs": "^1.5.12",
|
|
46
|
+
"mongodb": "^5.6.0",
|
|
47
|
+
"node-machine-id": "1.1.9",
|
|
48
|
+
"nodemailer": "^6.9.3",
|
|
49
|
+
"passport": "^0.6.0",
|
|
50
|
+
"passport-azure-ad": "^4.3.5",
|
|
51
|
+
"soap": "^1.0.0",
|
|
52
|
+
"tedious": "^16.1.0",
|
|
53
|
+
"winston": "^3.9.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"chai": "^4.2.0",
|
|
57
|
+
"mocha": "^9.2.0",
|
|
58
|
+
"supertest": "^6.3.3"
|
|
59
|
+
}
|
|
60
|
+
}
|