scimgateway 4.1.14 → 4.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
@@ -16,9 +16,10 @@ Validated through IdP's:
16
16
 
17
17
  Latest news:
18
18
 
19
+ - Authentication PassThrough letting plugin pass authentication directly to endpoint for avoid maintaining secrets at the gateway. Kubernetes health checks and shutdown handler support
19
20
  - **BREAKING**: [SCIM Stream](https://elshaug.xyz/docs/scim-stream) is the modern way of user provisioning letting clients subscribe to messages instead of traditional IGA top-down provisioning. SCIM Stream includes **SCIM Stream Gateway**, the next generation SCIM Gateway that supports message subscription and automated provisioning
20
21
  - Supports OAuth Client Credentials authentication
21
- - Major version v4.0.0. getUsers() and getGroups() replacing some deprecated methods. No limitations on filtering/sorting. Admin user access can be linked to specific baseEntities. New MongoDB plugin
22
+ - Major version v4.0.0. getUsers() and getGroups() replacing some deprecated methods. No limitations on filtering/sorting. Admin user access can be linked to specific baseEntities. New MongoDB plugin
22
23
  - ipAllowList for restricting access to allowlisted IP addresses or subnets e.g. Azure AD IP-range
23
24
  - General LDAP plugin configured for Active Directory
24
25
  - [PlugSSO](https://elshaug.xyz/docs/plugsso) using SCIM Gateway
@@ -261,7 +262,12 @@ Below shows an example of config\plugin-saphana.json
261
262
  "readOnly": false,
262
263
  "baseEntities": []
263
264
  }
264
- ]
265
+ ],
266
+ "passThrough": {
267
+ "enabled": false,
268
+ "readOnly": false,
269
+ "baseEntities": []
270
+ }
265
271
  },
266
272
  "certificate": {
267
273
  "key": null,
@@ -286,7 +292,12 @@ Below shows an example of config\plugin-saphana.json
286
292
  "to": null,
287
293
  "cc": null
288
294
  }
289
- }
295
+ },
296
+ "kubernetes": {
297
+ "enabled": false,
298
+ "shutdownTimeout": 15000,
299
+ "forceExitTimeout": 1000
300
+ }
290
301
  },
291
302
  "endpoint": {
292
303
  "host": "hostname",
@@ -300,7 +311,7 @@ Below shows an example of config\plugin-saphana.json
300
311
 
301
312
  Configuration file have two main JSON objects: `scimgateway` and `endpoint`
302
313
 
303
- Definitions in `scimgateway` object have fixed attributes, but values can be modified. This object is used by the core functionality of the SCIM Gateway.
314
+ Definitions in `scimgateway` object have fixed attributes, but values can be modified. Sections not used/configured can be removed. This object is used by the core functionality of the SCIM Gateway.
304
315
 
305
316
  Definitions in `endpoint` object are customized according to our plugin code. Plugin typically need this information for communicating with endpoint
306
317
 
@@ -354,6 +365,8 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
354
365
 
355
366
  - **auth.bearerOAuth** - Array of one or more Client Credentials OAuth configuration objects. **`client_id`** and **`client_secret`** are mandatory. client_secret value will become encrypted when gateway is started. OAuth token request url is **/oauth/token** e.g. http://localhost:8880/oauth/token
356
367
 
368
+ - **auth.passThrough** - Setting **auth.passThrough.enabled=true** will bypass SCIM Gateway authentication. Gateway will instead pass ctx containing authentication header to the plugin. Plugin could then use this information for endpoint authentication and we don't have any password/token stored at the gateway. Note, this also requires plugin binary having `scimgateway.authPassThroughAllowed = true` and endpoint logic for handling/passing ctx.request.header.authorization
369
+
357
370
  - **certificate** - If not using TLS certificate, set "key", "cert" and "ca" to **null**. When using TLS, "key" and "cert" have to be defined with the filename corresponding to the primary-key and public-certificate. Both files must be located in the `<package-root>\config\certs` directory e.g:
358
371
 
359
372
  "certificate": {
@@ -401,6 +414,11 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
401
414
  - **emailOnError.smtp.to** - Comma separated list of recipients email addresses e.g: "someone@example.com"
402
415
  - **emailOnError.smtp.cc** - Comma separated list of cc email addresses
403
416
 
417
+ - **kubernetes** - Enable Kubernetes support for healthchecks and graceful shutdown.
418
+ - **kubernetes.enabled** - true or false, true will enable Kubernets health checks and shutdown handler
419
+ - **kubernetes.shutdownTimeout** - Number of milliseconds to wait before shutting down (default 15000).
420
+ - **kubernetes.forceExitTimeout** - Number of milliseconds before forceful exiting (default 1000).
421
+
404
422
  - **endpoint** - Contains endpoint specific configuration according to our **plugin code**.
405
423
 
406
424
  #### Configuration notes
@@ -1033,12 +1051,13 @@ Plugins should have following initialization:
1033
1051
  let validScimAttr = [] // empty array - all attrbutes are supported by endpoint
1034
1052
  // add any external config process.env and process.file
1035
1053
  config = scimgateway.processExtConfig(pluginName, config)
1054
+ scimgateway.authPassThroughAllowed = false
1036
1055
  // mandatory plugin initialization - end
1037
1056
 
1038
1057
 
1039
1058
  ### getUsers
1040
1059
 
1041
- scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
1060
+ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
1042
1061
  let ret = {
1043
1062
  "Resources": [],
1044
1063
  "totalResults": null
@@ -1060,7 +1079,7 @@ ret.totalResults = if supporting pagination, then it should be set to the total
1060
1079
 
1061
1080
  ### deleteUser
1062
1081
 
1063
- scimgateway.deleteUser = async (baseEntity, id) => {
1082
+ scimgateway.deleteUser = async (baseEntity, id, ctx) => {
1064
1083
  ...
1065
1084
  return null
1066
1085
  }
@@ -1070,7 +1089,7 @@ ret.totalResults = if supporting pagination, then it should be set to the total
1070
1089
 
1071
1090
  ### modifyUser
1072
1091
 
1073
- scimgateway.modifyUser = async (baseEntity, id, attrObj) => {
1092
+ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
1074
1093
  ...
1075
1094
  return null
1076
1095
  }
@@ -1083,7 +1102,7 @@ Note, multi-value attributes excluding user attribute 'groups' are customized fr
1083
1102
 
1084
1103
  ### getGroups
1085
1104
 
1086
- scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
1105
+ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
1087
1106
  let ret = {
1088
1107
  "Resources": [],
1089
1108
  "totalResults": null
@@ -1105,7 +1124,7 @@ ret.totalResults = if supporting pagination, then it should be set to the total
1105
1124
 
1106
1125
 
1107
1126
  ### createGroup
1108
- scimgateway.createGroup = async (baseEntity, groupObj) => {
1127
+ scimgateway.createGroup = async (baseEntity, groupObj, ctx) => {
1109
1128
  ...
1110
1129
  return null
1111
1130
  })
@@ -1115,7 +1134,7 @@ groupObj.displayName contains the group name to be created
1115
1134
  * return null: null if OK, else throw error
1116
1135
 
1117
1136
  ### deleteGroup
1118
- scimgateway.deleteGroup = async (baseEntity, id) => {
1137
+ scimgateway.deleteGroup = async (baseEntity, id, ctx) => {
1119
1138
  ...
1120
1139
  return null
1121
1140
  }
@@ -1125,7 +1144,7 @@ groupObj.displayName contains the group name to be created
1125
1144
 
1126
1145
  ### modifyGroup
1127
1146
 
1128
- scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
1147
+ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
1129
1148
  ...
1130
1149
  return null
1131
1150
  }
@@ -1146,6 +1165,53 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1146
1165
 
1147
1166
  ## Change log
1148
1167
 
1168
+ ### v4.2.0
1169
+
1170
+ [Added]
1171
+
1172
+ - Kubernetes health checks and shutdown handler support
1173
+
1174
+ Plugin configuration prerequisite: **kubernetes.enabled=true**
1175
+
1176
+ "kubernetes": {
1177
+ "enabled": true,
1178
+ "shutdownTimeout": 15000,
1179
+ "forceExitTimeout": 1000
1180
+ }
1181
+
1182
+ **Thanks to Kevin Osborn**
1183
+
1184
+ ### v4.1.15
1185
+
1186
+ [Added]
1187
+
1188
+ - Authentication PassThrough for passing the authentication directly to plugin without being processed by scimgateway. Plugin can then pass this authentication to endpoint for avoid maintaining secrets at the gateway.
1189
+
1190
+ Plugin configuration prerequisites: **auth.passThrough.enabled=true**
1191
+
1192
+ "auth": {
1193
+ ...
1194
+ "passThrough": {
1195
+ "enabled": true,
1196
+ "readOnly": false,
1197
+ "baseEntities": []
1198
+ }
1199
+ ...
1200
+ }
1201
+
1202
+ Plugin binary prerequisites:
1203
+
1204
+ scimgateway.authPassThroughAllowed = true
1205
+ // also need endpoint logic for handling/passing ctx.request.header.authorization
1206
+
1207
+
1208
+ For upgrading existing custom plugins, above mention prerequisites needs to be included and in addition all plugin methods must include the `ctx` parameter e.g.:
1209
+
1210
+ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx)
1211
+ // tip, see provided example plugins
1212
+
1213
+ **Thanks to Kevin Osborn**
1214
+
1149
1215
  ### v4.1.14
1150
1216
 
1151
1217
  [Fixed]
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,12 +87,19 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
88
98
  "entity": {
89
99
  "undefined": {
90
- "baseUrls": ["http://fakerestapi.azurewebsites.net"],
100
+ "baseUrls": [
101
+ "http://fakerestapi.azurewebsites.net"
102
+ ],
91
103
  "username": "endpointuser",
92
104
  "password": "password",
93
105
  "proxy": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,6 +87,11 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,6 +87,11 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,6 +87,11 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,6 +87,11 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
@@ -63,7 +63,12 @@
63
63
  "readOnly": false,
64
64
  "baseEntities": []
65
65
  }
66
- ]
66
+ ],
67
+ "passThrough": {
68
+ "enabled": false,
69
+ "readOnly": false,
70
+ "baseEntities": []
71
+ }
67
72
  },
68
73
  "certificate": {
69
74
  "key": null,
@@ -88,6 +93,11 @@
88
93
  "to": null,
89
94
  "cc": null
90
95
  }
96
+ },
97
+ "kubernetes": {
98
+ "enabled": false,
99
+ "shutdownTimeout": 15000,
100
+ "forceExitTimeout": 1000
91
101
  }
92
102
  },
93
103
  "endpoint": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,6 +87,11 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,6 +87,11 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
@@ -57,7 +57,12 @@
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
60
- ]
60
+ ],
61
+ "passThrough": {
62
+ "enabled": false,
63
+ "readOnly": false,
64
+ "baseEntities": []
65
+ }
61
66
  },
62
67
  "certificate": {
63
68
  "key": null,
@@ -82,12 +87,19 @@
82
87
  "to": null,
83
88
  "cc": null
84
89
  }
90
+ },
91
+ "kubernetes": {
92
+ "enabled": false,
93
+ "shutdownTimeout": 15000,
94
+ "forceExitTimeout": 1000
85
95
  }
86
96
  },
87
97
  "endpoint": {
88
98
  "entity": {
89
99
  "undefined": {
90
- "baseUrls": ["http://localhost:8880"],
100
+ "baseUrls": [
101
+ "http://localhost:8880"
102
+ ],
91
103
  "scimVersion": "2.0",
92
104
  "username": "gwadmin",
93
105
  "password": "password",
@@ -98,7 +110,9 @@
98
110
  }
99
111
  },
100
112
  "clientA": {
101
- "baseUrls": ["http://localhost:8880"],
113
+ "baseUrls": [
114
+ "http://localhost:8880"
115
+ ],
102
116
  "scimVersion": "2.0",
103
117
  "username": "gwadmin",
104
118
  "password": "password",
package/lib/plugin-api.js CHANGED
@@ -46,6 +46,7 @@ const configDir = path.join(__dirname, '..', 'config')
46
46
  const configFile = path.join(`${configDir}`, `${pluginName}.json`)
47
47
  let config = require(configFile).endpoint
48
48
  config = scimgateway.processExtConfig(pluginName, config) // add any external config process.env and process.file
49
+ scimgateway.authPassThroughAllowed = false // true enables auth passThrough (no scimgateway authentication). scimgateway instead includes ctx (ctx.request.header) in plugin methods. Note, requires plugin-logic for handling/passing ctx.request.header.authorization to be used in endpoint communication
49
50
  // mandatory plugin initialization - end
50
51
 
51
52
  const _serviceClient = {}
@@ -58,7 +59,7 @@ const _serviceClient = {}
58
59
  // post http://localhost:8890/api
59
60
  // body = {"eventName":"AssignAccessRoleEvent","subjectName":"RACF_System-B","userID":"peter01"}
60
61
  //
61
- scimgateway.postApi = async (baseEntity, apiObj) => {
62
+ scimgateway.postApi = async (baseEntity, apiObj, ctx) => {
62
63
  const action = 'postApi'
63
64
  scimgateway.logger.debug(`${pluginName} handling "${action}" apiObj=${JSON.stringify(apiObj)}`)
64
65
 
@@ -92,7 +93,7 @@ scimgateway.postApi = async (baseEntity, apiObj) => {
92
93
  // put http://localhost:8890/api/1
93
94
  // body = {"eventName":"AssignAccessRoleEvent","subjectName":"RACF_System-B","userID":"peter01"}
94
95
  //
95
- scimgateway.putApi = async (baseEntity, id, apiObj) => {
96
+ scimgateway.putApi = async (baseEntity, id, apiObj, ctx) => {
96
97
  const action = 'putApi'
97
98
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} apiObj=${JSON.stringify(apiObj)}`)
98
99
 
@@ -126,7 +127,7 @@ scimgateway.putApi = async (baseEntity, id, apiObj) => {
126
127
  // patch http://localhost:8890/api/1
127
128
  // body = {"eventName":"AssignAccessRoleEvent","subjectName":"RACF_System-B","userID":"peter01"}
128
129
  //
129
- scimgateway.patchApi = async (baseEntity, id, apiObj) => {
130
+ scimgateway.patchApi = async (baseEntity, id, apiObj, ctx) => {
130
131
  const action = 'patchApi'
131
132
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} apiObj=${JSON.stringify(apiObj)}`)
132
133
 
@@ -160,7 +161,7 @@ scimgateway.patchApi = async (baseEntity, id, apiObj) => {
160
161
  // get http://localhost:8890/api/1
161
162
  // get http://localhost:8890/api?queries
162
163
  //
163
- scimgateway.getApi = async (baseEntity, id, apiQuery, apiObj) => {
164
+ scimgateway.getApi = async (baseEntity, id, apiQuery, apiObj, ctx) => {
164
165
  const action = 'getApi'
165
166
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} apiQuery=${JSON.stringify(apiQuery)} apiObj=${JSON.stringify(apiObj)}`)
166
167
 
@@ -191,7 +192,7 @@ scimgateway.getApi = async (baseEntity, id, apiQuery, apiObj) => {
191
192
  // example:
192
193
  // delete http://localhost:8890/api/1
193
194
  //
194
- scimgateway.deleteApi = async (baseEntity, id) => {
195
+ scimgateway.deleteApi = async (baseEntity, id, ctx) => {
195
196
  const action = 'deleteApi'
196
197
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
197
198
 
@@ -87,6 +87,7 @@ const configDir = path.join(__dirname, '..', 'config')
87
87
  const configFile = path.join(`${configDir}`, `${pluginName}.json`)
88
88
  let config = require(configFile).endpoint
89
89
  config = scimgateway.processExtConfig(pluginName, config) // add any external config process.env and process.file
90
+ scimgateway.authPassThroughAllowed = false // true enables auth passThrough (no scimgateway authentication). scimgateway instead includes ctx (ctx.request.header) in plugin methods. Note, requires plugin-logic for handling/passing ctx.request.header.authorization to be used in endpoint communication
90
91
  // mandatory plugin initialization - end
91
92
 
92
93
  if (config.map) { // having licensDetails map here instead of config file
@@ -135,7 +136,7 @@ const lock = new scimgateway.Lock()
135
136
  // =================================================
136
137
  // getUsers
137
138
  // =================================================
138
- scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
139
+ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
139
140
  //
140
141
  // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
141
142
  // rawFilter is always included when filtering
@@ -238,7 +239,7 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
238
239
  // =================================================
239
240
  // createUser
240
241
  // =================================================
241
- scimgateway.createUser = async (baseEntity, userObj) => {
242
+ scimgateway.createUser = async (baseEntity, userObj, ctx) => {
242
243
  const action = 'createUser'
243
244
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" userObj=${JSON.stringify(userObj)}`)
244
245
 
@@ -255,7 +256,7 @@ scimgateway.createUser = async (baseEntity, userObj) => {
255
256
  try {
256
257
  await doRequest(baseEntity, method, path, body)
257
258
  if (attrObj.servicePlan) {
258
- await scimgateway.modifyUser(baseEntity, userObj.userName, attrObj)
259
+ await scimgateway.modifyUser(baseEntity, userObj.userName, attrObj, ctx)
259
260
  return null
260
261
  } else return (null)
261
262
  } catch (err) {
@@ -268,7 +269,7 @@ scimgateway.createUser = async (baseEntity, userObj) => {
268
269
  // =================================================
269
270
  // deleteUser
270
271
  // =================================================
271
- scimgateway.deleteUser = async (baseEntity, id) => {
272
+ scimgateway.deleteUser = async (baseEntity, id, ctx) => {
272
273
  const action = 'deleteUser'
273
274
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
274
275
  const method = 'DELETE'
@@ -285,7 +286,7 @@ scimgateway.deleteUser = async (baseEntity, id) => {
285
286
  // =================================================
286
287
  // modifyUser
287
288
  // =================================================
288
- scimgateway.modifyUser = async (baseEntity, id, attrObj) => {
289
+ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
289
290
  const action = 'modifyUser'
290
291
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} attrObj=${JSON.stringify(attrObj)}`)
291
292
  const arrLicAdd = []
@@ -517,7 +518,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj) => {
517
518
  // =================================================
518
519
  // getGroups
519
520
  // =================================================
520
- scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
521
+ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
521
522
  //
522
523
  // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
523
524
  // rawFilter is always included when filtering
@@ -652,7 +653,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
652
653
  // =================================================
653
654
  // createGroup
654
655
  // =================================================
655
- scimgateway.createGroup = async (baseEntity, groupObj) => {
656
+ scimgateway.createGroup = async (baseEntity, groupObj, ctx) => {
656
657
  const action = 'createGroup'
657
658
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" groupObj=${JSON.stringify(groupObj)}`)
658
659
  const body = { displayName: groupObj.displayName }
@@ -663,7 +664,7 @@ scimgateway.createGroup = async (baseEntity, groupObj) => {
663
664
  const path = '/Groups'
664
665
 
665
666
  try {
666
- const res = await scimgateway.getGroups(baseEntity, { attribute: 'displayName', operator: 'eq', value: groupObj.displayName }, ['id', 'displayName'])
667
+ const res = await scimgateway.getGroups(baseEntity, { attribute: 'displayName', operator: 'eq', value: groupObj.displayName }, ['id', 'displayName'], ctx)
667
668
  if (res && res.Resources && res.Resources.length > 0) {
668
669
  throw new Error(`group ${groupObj.displayName} already exist`)
669
670
  }
@@ -679,7 +680,7 @@ scimgateway.createGroup = async (baseEntity, groupObj) => {
679
680
  // =================================================
680
681
  // deleteGroup
681
682
  // =================================================
682
- scimgateway.deleteGroup = async (baseEntity, id) => {
683
+ scimgateway.deleteGroup = async (baseEntity, id, ctx) => {
683
684
  const action = 'deleteGroup'
684
685
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
685
686
  throw new Error(`${action} error: ${action} is not supported`)
@@ -688,7 +689,7 @@ scimgateway.deleteGroup = async (baseEntity, id) => {
688
689
  // =================================================
689
690
  // modifyGroup
690
691
  // =================================================
691
- scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
692
+ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
692
693
  const action = 'modifyGroup'
693
694
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id} attrObj=${JSON.stringify(attrObj)}`)
694
695
 
@@ -755,7 +756,7 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
755
756
  // =================================================
756
757
  // getServicePlans
757
758
  // =================================================
758
- scimgateway.getServicePlans = async (baseEntity, getObj, attributes) => {
759
+ scimgateway.getServicePlans = async (baseEntity, getObj, attributes, ctx) => {
759
760
  //
760
761
  // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
761
762
  // rawFilter is always included when filtering - attribute, operator and value are included when requesting unique object or simpel filtering
@@ -875,7 +876,7 @@ scimgateway.getServicePlans = async (baseEntity, getObj, attributes) => {
875
876
  // =================================================
876
877
  // createServicePlan
877
878
  // =================================================
878
- scimgateway.createServicePlan = async (baseEntity, id) => {
879
+ scimgateway.createServicePlan = async (baseEntity, id, ctx) => {
879
880
  const action = 'createServicePlan'
880
881
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
881
882
  throw new Error(`${action} error: ${action} is not supported`)
@@ -884,7 +885,7 @@ scimgateway.createServicePlan = async (baseEntity, id) => {
884
885
  // =================================================
885
886
  // deleteServicePlan
886
887
  // =================================================
887
- scimgateway.deleteServicePlan = async (baseEntity, id) => {
888
+ scimgateway.deleteServicePlan = async (baseEntity, id, ctx) => {
888
889
  const action = 'deleteServicePlan'
889
890
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
890
891
  throw new Error(`${action} error: ${action} is not supported`)
@@ -893,7 +894,7 @@ scimgateway.deleteServicePlan = async (baseEntity, id) => {
893
894
  // =================================================
894
895
  // modifyServicePlan
895
896
  // =================================================
896
- scimgateway.modifyServicePlan = async (baseEntity, id) => {
897
+ scimgateway.modifyServicePlan = async (baseEntity, id, ctx) => {
897
898
  const action = 'modifyServicePlan'
898
899
  scimgateway.logger.debug(`${pluginName}[${baseEntity}] handling "${action}" id=${id}`)
899
900
  throw new Error(`${action} error: ${action} is not supported`)