scimgateway 6.1.19 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/config/{plugin-scim.json → plugin-generic.json} +63 -0
- package/index.ts +1 -1
- package/lib/helper-rest.ts +5 -0
- package/lib/plugin-entra-id.ts +4 -2
- package/lib/plugin-generic.ts +552 -0
- package/lib/postinstall.ts +2 -2
- package/lib/scimgateway.ts +10 -1
- package/lib/utils-scim.ts +16 -1
- package/package.json +1 -1
- package/test/index.ts +1 -1
- package/test/lib/{plugin-scim_test.ts → plugin-generic_test.ts} +7 -5
- package/lib/plugin-scim.ts +0 -493
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
Latest news:
|
|
19
19
|
|
|
20
|
+
- New `plugin-generic` replacing previous `plugin-scim`. This new plugin use the endpointMapper for flexible attribute mapping and also supports the new mapper option `valueMap` (e.g., group filtering and mapping).
|
|
20
21
|
- Now supports `GET /Roles` and `GET /Entitlements` endpoint requests, with corresponding user management via the standard SCIM `roles` and `entitlements` attributes. The Entra ID plugin uses `entitlements` for Entra ID licenses (read-only) and `roles` for Entra ID Permanent and Eligible roles (full management).
|
|
21
22
|
- Bun binary build is now supported, allowing SCIM Gateway to be compiled into a single executable binary for simplified deployment and execution. SCIM Gateway can now run as an ES module (TypeScript) in Node.js.
|
|
22
23
|
- Major release **v6.0.0** introduces changes to API method responses (not SCIM-related) and a new method `publicApi()` for handling public path `/pub/api` requests with no authentication required. In addition, the configuration option `bearerJwtAzure.tenantIdGUID` has been replaced by `bearerJwt.azureTenantId`. See the version history for details.
|
|
@@ -61,7 +62,7 @@ The following fully functional plugins are included for demonstration and produc
|
|
|
61
62
|
| **Loki** | NoSQL Database | Transforms the SCIM Gateway into a standalone SCIM endpoint utilizing the internal [LokiJS](https://github.com/techfort/LokiJS) database. Includes two test users and groups |
|
|
62
63
|
| **MongoDB** | NoSQL Database | Similar to the Loki plugin, but using an externally managed MongoDB database, showcasing multi-tenant and multi-endpoint capabilities via `baseEntity` |
|
|
63
64
|
| **Entra ID** | REST Webservices | Entra ID user provisioning via Microsoft Graph API |
|
|
64
|
-
| **
|
|
65
|
+
| **Generic** | REST Webservice | Generic template plugin configured to use plugin-loki as a SCIM provisioning endpoint. Supports the endpointMapper `valueMap` option for allowlisting and mapping (e.g., groups). Can also be used as a SCIM version gateway (e.g., 1.1 => 2.0) |
|
|
65
66
|
| **API** | REST Webservices | A non-SCIM plugin demonstrating API Gateway functionality for custom REST specifications |
|
|
66
67
|
| **Soap** | SOAP Webservice | Demonstrates user provisioning to a SOAP-based endpoint with example WSDLs |
|
|
67
68
|
| **MSSQL** | Database | Demonstrates user provisioning to an MSSQL database |
|
|
@@ -1304,6 +1305,52 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1304
1305
|
|
|
1305
1306
|
## Change log
|
|
1306
1307
|
|
|
1308
|
+
### v6.2.0
|
|
1309
|
+
|
|
1310
|
+
[Fixed]
|
|
1311
|
+
|
|
1312
|
+
- `helper-rest` failed on Bun v1.3.14 due to stricter compliance with Fetch standards.
|
|
1313
|
+
|
|
1314
|
+
[Improved]
|
|
1315
|
+
|
|
1316
|
+
- New `plugin-generic` replacing previous `plugin-scim`. This new plugin use the endpointMapper for flexible attribute mapping and also supports the new mapper option `valueMap` (e.g., group filtering and mapping). The default configuration uses one-to-one SCIM mapping, with plugin-loki as the target SCIM endpoint.
|
|
1317
|
+
- endpointMapper now supports the 'valueMap' option
|
|
1318
|
+
|
|
1319
|
+
Example configuration:
|
|
1320
|
+
|
|
1321
|
+
"map": {
|
|
1322
|
+
"group": {
|
|
1323
|
+
...
|
|
1324
|
+
"displayName": {
|
|
1325
|
+
"mapTo": "displayName",
|
|
1326
|
+
"type": "string",
|
|
1327
|
+
"valueMap": {
|
|
1328
|
+
"outboundEndpointGrp1": "inboundScimGrp1",
|
|
1329
|
+
"Employees": "Admins"
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
...
|
|
1333
|
+
}
|
|
1334
|
+
...
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
Using the above settings restricts the client using SCIM Gateway with regard to group management.
|
|
1338
|
+
The client will only see and be able to manage groups with SCIM names "inboundScimGrp1" and "Admins",
|
|
1339
|
+
if their mapped counterparts exist at the target endpoint as "outboundEndpointGrp1" and "Employees".
|
|
1340
|
+
|
|
1341
|
+
Use case:
|
|
1342
|
+
|
|
1343
|
+
- Allowlisting specific groups or user objects that includes attribute mapping having the valueMap option configured.
|
|
1344
|
+
- Supporting different inbound/outbound names (e.g., Entra ID group provisioning to SCIM Gateway).
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
### v6.1.20
|
|
1348
|
+
|
|
1349
|
+
[Fixed]
|
|
1350
|
+
|
|
1351
|
+
- plugin-entra-id: Roles introduced in v6.1.19 were missing when retrieving a single user.
|
|
1352
|
+
|
|
1353
|
+
|
|
1307
1354
|
### v6.1.19
|
|
1308
1355
|
|
|
1309
1356
|
[Fixed]
|
|
@@ -156,6 +156,69 @@
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
+
},
|
|
160
|
+
"map": {
|
|
161
|
+
"group": {
|
|
162
|
+
"id": {
|
|
163
|
+
"mapTo": "id",
|
|
164
|
+
"type": "string"
|
|
165
|
+
},
|
|
166
|
+
"displayName": {
|
|
167
|
+
"mapTo": "displayName",
|
|
168
|
+
"type": "string",
|
|
169
|
+
"valueMap": {}
|
|
170
|
+
},
|
|
171
|
+
"members": {
|
|
172
|
+
"mapTo": "members",
|
|
173
|
+
"type": "complexArray"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"user": {
|
|
177
|
+
"id": {
|
|
178
|
+
"mapTo": "id",
|
|
179
|
+
"type": "string"
|
|
180
|
+
},
|
|
181
|
+
"userName": {
|
|
182
|
+
"mapTo": "userName",
|
|
183
|
+
"type": "string",
|
|
184
|
+
"xvalueMap": {
|
|
185
|
+
"bjensen": "Xbjensen"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"active": {
|
|
189
|
+
"mapTo": "active",
|
|
190
|
+
"type": "boolean"
|
|
191
|
+
},
|
|
192
|
+
"password": {
|
|
193
|
+
"mapTo": "password",
|
|
194
|
+
"type": "string"
|
|
195
|
+
},
|
|
196
|
+
"title": {
|
|
197
|
+
"mapTo": "title",
|
|
198
|
+
"type": "string"
|
|
199
|
+
},
|
|
200
|
+
"name": {
|
|
201
|
+
"mapTo": "name",
|
|
202
|
+
"type": "complexObject",
|
|
203
|
+
"subAttributes": []
|
|
204
|
+
},
|
|
205
|
+
"emails": {
|
|
206
|
+
"mapTo": "emails",
|
|
207
|
+
"type": "complexArray"
|
|
208
|
+
},
|
|
209
|
+
"phoneNumbers": {
|
|
210
|
+
"mapTo": "phoneNumbers",
|
|
211
|
+
"type": "complexArray"
|
|
212
|
+
},
|
|
213
|
+
"roles": {
|
|
214
|
+
"mapTo": "roles",
|
|
215
|
+
"type": "complexArray"
|
|
216
|
+
},
|
|
217
|
+
"entitlements": {
|
|
218
|
+
"mapTo": "entitlements",
|
|
219
|
+
"type": "complexArray"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
159
222
|
}
|
|
160
223
|
}
|
|
161
224
|
}
|
package/index.ts
CHANGED
package/lib/helper-rest.ts
CHANGED
|
@@ -702,6 +702,11 @@ export class HelperRest {
|
|
|
702
702
|
}
|
|
703
703
|
}
|
|
704
704
|
|
|
705
|
+
// Bun v1.3.14 became stricter and more aligned with standards.
|
|
706
|
+
delete options.host
|
|
707
|
+
delete options.port
|
|
708
|
+
delete options.protocol
|
|
709
|
+
|
|
705
710
|
// execute request
|
|
706
711
|
const f = await fetch(url, options)
|
|
707
712
|
if (!f.status) throw new Error('Response missing status code')
|
package/lib/plugin-entra-id.ts
CHANGED
|
@@ -294,6 +294,7 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
294
294
|
if (!path) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
|
|
295
295
|
|
|
296
296
|
if (path.includes('$count=true')) { // $count=true requires ConsistencyLevel
|
|
297
|
+
// note: when using $expand, the $count=true might be ignored by target endpoint and the ctx.paging.totalResults updated by doReqest() will be incremental
|
|
297
298
|
if (!options.headers) options.headers = {}
|
|
298
299
|
options.headers.ConsistencyLevel = 'eventual'
|
|
299
300
|
}
|
|
@@ -355,6 +356,7 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
355
356
|
delete response.body.value[i].signInActivity.lastNonInteractiveSignInRequestId
|
|
356
357
|
delete response.body.value[i].signInActivity.lastSuccessfulSignInRequestId
|
|
357
358
|
}
|
|
359
|
+
|
|
358
360
|
if ((attributes.includes('roles') || attributes.length === 0) && mapAttributesTo.includes('roles')) {
|
|
359
361
|
response.body.value[i].roles = await getUserRoles(baseEntity, response.body.value[i].id, response.body.value[i].groups, ctx?.headers ? { headers: ctx?.headers } : undefined)
|
|
360
362
|
}
|
|
@@ -729,6 +731,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
729
731
|
if (!path) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
|
|
730
732
|
|
|
731
733
|
if (path.includes('$count=true')) { // $count=true requires ConsistencyLevel
|
|
734
|
+
// note: when using $expand, the $count=true might be ignored by target endpoint and the ctx.paging.totalResults updated by doReqest() will be incremental
|
|
732
735
|
if (!options.headers) options.headers = {}
|
|
733
736
|
options.headers.ConsistencyLevel = 'eventual'
|
|
734
737
|
}
|
|
@@ -1356,7 +1359,6 @@ const getUserRoles = async (baseEntity: string, userId: string, groups: Record<s
|
|
|
1356
1359
|
})
|
|
1357
1360
|
|
|
1358
1361
|
const permanentRoles = rolesAssignments.permanent.filter((role: any) => Ids.includes(role.principalId)).map((role: any) => {
|
|
1359
|
-
if (eligibleRoles.filter((r: any) => r.value === role.roleDefinitionId).length > 0) return null // eligible role activated becomes listed as permanent, skip those...
|
|
1360
1362
|
const roleDef = roleDefs[role.roleDefinitionId]
|
|
1361
1363
|
if (roleDef) {
|
|
1362
1364
|
if (includeAssignmentId === true) return { type: 'Permanent', value: roleDef.id, display: roleDef.displayName, assignmentId: role.id }
|
|
@@ -1396,7 +1398,7 @@ const fnCunckExecute = async (fnArr: { index?: number, fn: () => Promise<any> }[
|
|
|
1396
1398
|
if (statusCode !== 404) throw new Error(errMsg)
|
|
1397
1399
|
}
|
|
1398
1400
|
results.forEach((result, idx) => {
|
|
1399
|
-
if (result.status === 'fulfilled' && arrChunk[idx].index && responseValue && key) {
|
|
1401
|
+
if (result.status === 'fulfilled' && typeof arrChunk[idx].index === 'number' && responseValue && key) {
|
|
1400
1402
|
if (result.value) responseValue[arrChunk[idx].index][key] = result.value
|
|
1401
1403
|
else responseValue[arrChunk[idx].index][key] = result
|
|
1402
1404
|
}
|