scimgateway 4.2.10 → 4.2.12

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
@@ -326,6 +326,7 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
326
326
  - **scim.version** - "1.1" or "2.0". Default is "2.0". For Symantec/Broadcom/CA Identity Manager "1.1" should be used.
327
327
 
328
328
  - **scim.customSchema** - filename of JSON file located in `<package-root>\config\schemas` containing custom schema attributes, see configuration notes
329
+ **additional information**: Schemas, ServiceProviderConfig and ResourceType can be customized if `lib/scimdef-v2.js (or scimdef-v1.js)` exists. Original scimdef-v2.js/scimdef-v1.js can be copied from node_modules/scimgateway/lib to your plugin/lib and customized.
329
330
 
330
331
  - **scim.skipTypeConvert** - true or false, default false. Multivalue attributes supporting types e.g. emails, phoneNumbers, ims, photos, addresses, entitlements and x509Certificates (but not roles, groups and members) will be become "type converted objects" when sent to modifyUser and createUser. This for simplicity of checking attributes included and also for the endpointMapper method (used by plugin-ldap and plugin-azure-ad), e.g.:
331
332
 
@@ -1169,6 +1170,18 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1169
1170
 
1170
1171
  ## Change log
1171
1172
 
1173
+ ### v4.2.12
1174
+
1175
+ [Added]
1176
+
1177
+ - Schemas, ServiceProviderConfig and ResourceType can be customized if `lib/scimdef-v2.js (or scimdef-v1.js)` exists. Original scimdef-v2.js/scimdef-v1.js can be copied from node_modules/scimgateway/lib to your plugin/lib and customized.
1178
+
1179
+ ### v4.2.11
1180
+
1181
+ [Added]
1182
+
1183
+ - Plugin can set error statusCode returned by scimgateway through error message. Error message must then contain string `"statusCode":xxx` where xxx is HTTP status code e.g., 401. Plugin using REST will have statusCode automatically included in error message thrown by plugin. This could be useful for auth.PassThrough.
1184
+
1172
1185
  ### v4.2.10
1173
1186
 
1174
1187
  [Fixed]
package/lib/scimdef-v1.js CHANGED
@@ -11,23 +11,39 @@ module.exports.ServiceProviderConfigs = {
11
11
  "bulk": {
12
12
  "supported": false,
13
13
  "maxOperations": 10000,
14
- "maxPayloadSize": 10000000
14
+ "maxPayloadSize": 1048576
15
15
  },
16
16
  "filter": {
17
17
  "supported": true,
18
- "maxResults": 100
18
+ "maxResults": 200
19
19
  },
20
20
  "changePassword": { "supported": true },
21
21
  "sort": { "supported": false },
22
- "etag": { "supported": true },
23
- "authenticationSchemes": [{
24
- "type": "httpbasic",
25
- "name": "Http Basic",
26
- "description": "The HTTP Basic Access Authentication scheme. This scheme is not considered to be a secure method of user authentication (unless used in conjunction with some external secure system such as SSL), as the user name and password are passed over the network as cleartext.",
27
- "specUrl": "http://www.ietf.org/rfc/rfc2617.txt",
28
- "documentationUrl": "http://en.wikipedia.org/wiki/Basic_access_authentication",
29
- "primary": true
30
- }],
22
+ "etag": { "supported": false },
23
+ "authenticationSchemes": [
24
+ {
25
+ "type": "httpbasic",
26
+ "name": "HTTP Basic",
27
+ "description": "Authentication scheme using the HTTP Basic Standard",
28
+ "specURI": "http://www.rfc-editor.org/info/rfc2617",
29
+ "documentationUri": "https://elshaug.xyz",
30
+ "primary": true
31
+ },
32
+ {
33
+ "type": "oauthbearertoken",
34
+ "name": "OAuth Bearer Token",
35
+ "description": "Authentication scheme using the OAuth Bearer Token Standard",
36
+ "specUri": "http://www.rfc-editor.org/info/rfc6750",
37
+ "documentationUri": "https://elshaug.xyz"
38
+ },
39
+ {
40
+ "type": "oauth2",
41
+ "name": "OAuth v2.0",
42
+ "description": "Authentication Scheme using the OAuth Standard",
43
+ "specUri": "http://tools.ietf.org/html/rfc6749",
44
+ "documentationUri": "https://elshaug.xyz"
45
+ }
46
+ ],
31
47
  "xmlDataFormat": { "supported": false }
32
48
  }
33
49
 
package/lib/scimdef-v2.js CHANGED
@@ -27,15 +27,29 @@ module.exports.ServiceProviderConfigs = {
27
27
  "etag": {
28
28
  "supported": false
29
29
  },
30
- "documentationUri": "http://example.com/help/scim.html",
30
+ "documentationUri": "https://elshaug.xyz",
31
31
  "authenticationSchemes": [
32
32
  {
33
+ "type": "httpbasic",
33
34
  "name": "HTTP Basic",
34
35
  "description": "Authentication scheme using the HTTP Basic Standard",
35
36
  "specURI": "http://www.rfc-editor.org/info/rfc2617",
36
- "documentationUri": "http://en.wikipedia.org/wiki/Basic_access_authentication",
37
- "type": "httpbasic",
37
+ "documentationUri": "https://elshaug.xyz",
38
38
  "primary": true
39
+ },
40
+ {
41
+ "type": "oauthbearertoken",
42
+ "name": "OAuth Bearer Token",
43
+ "description": "Authentication scheme using the OAuth Bearer Token Standard",
44
+ "specUri": "http://www.rfc-editor.org/info/rfc6750",
45
+ "documentationUri": "https://elshaug.xyz"
46
+ },
47
+ {
48
+ "type": "oauth2",
49
+ "name": "OAuth v2.0",
50
+ "description": "Authentication Scheme using the OAuth Standard",
51
+ "specUri": "http://tools.ietf.org/html/rfc6749",
52
+ "documentationUri": "https://elshaug.xyz"
39
53
  }
40
54
  ],
41
55
  "xmlDataFormat": { "supported": false }
@@ -37,7 +37,8 @@ const ScimGateway = function () {
37
37
  const stack = callsite()
38
38
  const requester = stack[1].getFileName()
39
39
  const pluginName = path.basename(requester, '.js')
40
- const configDir = path.join(path.dirname(requester), '..', 'config')
40
+ const pluginDir = path.dirname(requester)
41
+ const configDir = path.join(pluginDir, '..', 'config')
41
42
  const configFile = path.join(`${configDir}`, `${pluginName}.json`) // config name prefix same as pluging name prefix
42
43
  let config = require(configFile).scimgateway
43
44
  let extConfigErr
@@ -46,7 +47,7 @@ const ScimGateway = function () {
46
47
  } catch (err) { extConfigErr = err }
47
48
 
48
49
  const gwName = path.basename(__filename, '.js') // prefix of current file
49
- const logDir = path.join(path.dirname(requester), '..', 'logs')
50
+ const logDir = path.join(pluginDir, '..', 'logs')
50
51
  const Log = require('../lib/logger').Log
51
52
  const log = new Log(utils.extendObj(utils.copyObj(config.log), { category: pluginName, colorize: process.stdout.isTTY || false, loglevel: { file: (!config.log.loglevel.file || config.log.loglevel.file === 'off') ? null : 'debug', console: 'debug' } }), path.join(`${logDir}`, `${pluginName}.log`))
52
53
  const logger = log.logger()
@@ -246,10 +247,14 @@ const ScimGateway = function () {
246
247
  let isScimv2 = false
247
248
  if (config.scim.version === '2.0' || config.scim.version === 2) {
248
249
  isScimv2 = true
249
- scimDef = require('../lib/scimdef-v2')
250
- } else scimDef = require('../lib/scimdef-v1')
250
+ if (fs.existsSync(pluginDir + '/scimdef-v2.js')) scimDef = require(pluginDir + '/scimdef-v2') // using custom
251
+ else scimDef = require('../lib/scimdef-v2')
252
+ } else {
253
+ if (fs.existsSync(pluginDir + '/scimdef-v1.js')) scimDef = require(pluginDir + '/scimdef-v1') // using custom
254
+ else scimDef = require('../lib/scimdef-v1')
255
+ }
251
256
 
252
- if (config.scim.customSchema) { // merge plugin custom schema extension into core schemas
257
+ if (config.scim.customSchema) { // merge plugin custom schema extension into core schemas (better using above custom scimdef-v2.js in plugin lib folder)
253
258
  let custom
254
259
  try {
255
260
  custom = JSON.parse(fs.readFileSync(`${configDir}/schemas/${config.scim.customSchema}`, 'utf8'))
@@ -285,8 +290,15 @@ const ScimGateway = function () {
285
290
  }
286
291
  }
287
292
 
288
- this.testmodeusers = scimDef.TestmodeUsers.Resources // exposed and used by plugin-loki
289
- this.testmodegroups = scimDef.TestmodeGroups.Resources // exposed and used by plugin-loki
293
+ // exposed and used by plugin-loki
294
+ this.testmodeusers = []
295
+ this.testmodegroups = []
296
+ if (scimDef.TestmodeUsers && scimDef.TestmodeUsers.Resources) {
297
+ this.testmodeusers = scimDef.TestmodeUsers.Resources
298
+ }
299
+ if (scimDef.TestmodeGroups && scimDef.TestmodeGroups.Resources) {
300
+ this.testmodegroups = scimDef.TestmodeGroups.Resources
301
+ }
290
302
 
291
303
  // multiValueTypes array contains attributes that will be used by "type converted objects" logic
292
304
  // groups, roles, and members are excluded
@@ -318,22 +330,35 @@ const ScimGateway = function () {
318
330
  if (!userName && authType === 'Bearer') userName = 'token'
319
331
  if (ctx.request.url !== '/favicon.ico') {
320
332
  if (ctx.response.status < 200 || ctx.response.status > 299) {
321
- let isEndpointAccessDenied = false
333
+ // statusCode check in logResult method...
334
+ // "statusCode":xxx in error messages let plugin set error statusCode returned by scimgateway
335
+ let pluginStatusCode = 0
336
+ const reJson = '^.*"(statusCode)" *: *([0-9][0-9][0-9]).*'
337
+ const rePattern = new RegExp(reJson, 'i')
322
338
  if (res.body.detail) {
323
- if (res.body.detail.includes('\"statusCode\":401')) isEndpointAccessDenied= true // eslint-disable-line
339
+ const arrMatches = res.body.detail.match(rePattern)
340
+ if (Array.isArray(arrMatches) && arrMatches.length === 3) {
341
+ pluginStatusCode = parseInt(arrMatches[2])
342
+ }
324
343
  } else if (res.body.Errors) {
325
- if (Array.isArray(res.body.Errors) && res.body.Errors[0].description && res.body.Errors[0].description.includes('\"statusCode\":401')) { // eslint-disable-line
326
- isEndpointAccessDenied = true
344
+ if (Array.isArray(res.body.Errors) && res.body.Errors[0].description && res.body.Errors[0].description) {
345
+ const arrMatches = res.body.Errors[0].description.match(rePattern)
346
+ if (Array.isArray(arrMatches) && arrMatches.length === 3) {
347
+ pluginStatusCode = parseInt(arrMatches[2])
348
+ }
327
349
  }
328
350
  }
329
- if (isEndpointAccessDenied) { // don't reveal original SCIM error message details related to access denied (e.g. using Auth PassThrough)
330
- ctx.response.set('Content-Type', 'application/json; charset=utf-8')
331
- ctx.response.status = 401 // ctx.response.message becomes default 'Unauthorized'
332
- ctx.response.body = { error: 'Access denied' }
351
+ if (pluginStatusCode > 0) {
352
+ ctx.response.status = pluginStatusCode // auto change ctx.response.message
333
353
  res.statusCode = ctx.response.status
334
354
  res.statusMessage = ctx.response.message
335
- res.body = ctx.response.body
355
+ if (pluginStatusCode === 401 || pluginStatusCode === 403) { // don't reveal original SCIM error message details related to access denied (e.g. using Auth PassThrough)
356
+ ctx.response.set('Content-Type', 'application/json; charset=utf-8')
357
+ ctx.response.body = { error: 'Access denied' }
358
+ res.body = ctx.response.body
359
+ }
336
360
  }
361
+ // back to logResult...
337
362
  logger.error(`${gwName}[${pluginName}] ${ellapsed} ${ctx.request.ipcli} ${userName} ${ctx.request.method} ${ctx.request.href} Inbound = ${JSON.stringify(ctx.request.body)} Outbound = ${JSON.stringify(res)}${(config.log.loglevel.file === 'debug' && ctx.request.url !== '/ping') ? '\n' : ''}`)
338
363
  } else logger.info(`${gwName}[${pluginName}] ${ellapsed} ${ctx.request.ipcli} ${userName} ${ctx.request.method} ${ctx.request.href} Inbound = ${JSON.stringify(ctx.request.body)} Outbound = ${JSON.stringify(res)}${(config.log.loglevel.file === 'debug' && ctx.request.url !== '/ping') ? '\n' : ''}`)
339
364
  requestCounter += 1 // logged on exit (not win process termination)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scimgateway",
3
- "version": "4.2.10",
3
+ "version": "4.2.12",
4
4
  "description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
5
5
  "author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",
6
6
  "homepage": "https://elshaug.xyz",