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 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
- | **SCIM** | REST Webservice | Using plugin Loki as a SCIM provisioning endpoint. May become a SCIM version-gateway (e.g., 1.1 => 2.0) |
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
@@ -12,7 +12,7 @@
12
12
  //
13
13
 
14
14
  // start one or more plugins:
15
- // import './lib/plugin-scim.ts'
15
+ // import './lib/plugin-generic.ts'
16
16
  // import './lib/plugin-entra-id.ts'
17
17
  // import './lib/plugin-ldap.ts'
18
18
  // import './lib/plugin-mongodb.ts'
@@ -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')
@@ -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
  }