scimgateway 4.2.11 → 4.2.13

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
@@ -124,7 +124,7 @@ If internet connection is blocked, we could install on another machine and copy
124
124
 
125
125
  node c:\my-scimgateway
126
126
 
127
- Start a browser
127
+ Start a browser (note, Edge do not pop-up logon dialog box when using http)
128
128
 
129
129
  http://localhost:8880/ping
130
130
  => Health check with a "hello" response
@@ -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.13
1174
+
1175
+ [Fixed]
1176
+
1177
+ - `/ping` now excluded from info logs. If we want ping logging, use something else than lowercase e.g., `/Ping` or `/PING`
1178
+
1179
+ ### v4.2.12
1180
+
1181
+ [Added]
1182
+
1183
+ - 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.
1184
+
1172
1185
  ### v4.2.11
1173
1186
 
1174
1187
  [Added]
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
@@ -305,7 +317,8 @@ const ScimGateway = function () {
305
317
 
306
318
  const logResult = async (ctx, next) => {
307
319
  const started = Date.now()
308
- await next() // once all middleware below completes, this continues
320
+ await next() // once all middleware completes, below continues
321
+ if (ctx.request.url === '/ping' || ctx.request.url === '/favicon.ico') return
309
322
  const ellapsed = (Date.now() - started) + 'ms' // ctx.set('X-ResponseTime', ellapsed)
310
323
  const res = {
311
324
  statusCode: ctx.response.status,
@@ -316,41 +329,39 @@ const ScimGateway = function () {
316
329
  const [authType, authToken] = (ctx.request.header.authorization || '').split(' ') // [0] = 'Basic' or 'Bearer'
317
330
  if (authType === 'Basic') [userName] = (Buffer.from(authToken, 'base64').toString() || '').split(':')
318
331
  if (!userName && authType === 'Bearer') userName = 'token'
319
- if (ctx.request.url !== '/favicon.ico') {
320
- if (ctx.response.status < 200 || ctx.response.status > 299) {
321
- // statusCode check in logResult method...
322
- // "statusCode":xxx in error messages let plugin set error statusCode returned by scimgateway
323
- let pluginStatusCode = 0
324
- const reJson = '^.*"(statusCode)" *: *([0-9][0-9][0-9]).*'
325
- const rePattern = new RegExp(reJson, 'i')
326
- if (res.body.detail) {
327
- const arrMatches = res.body.detail.match(rePattern)
332
+ if (ctx.response.status < 200 || ctx.response.status > 299) {
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')
338
+ if (res.body.detail) {
339
+ const arrMatches = res.body.detail.match(rePattern)
340
+ if (Array.isArray(arrMatches) && arrMatches.length === 3) {
341
+ pluginStatusCode = parseInt(arrMatches[2])
342
+ }
343
+ } else if (res.body.Errors) {
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)
328
346
  if (Array.isArray(arrMatches) && arrMatches.length === 3) {
329
347
  pluginStatusCode = parseInt(arrMatches[2])
330
348
  }
331
- } else if (res.body.Errors) {
332
- if (Array.isArray(res.body.Errors) && res.body.Errors[0].description && res.body.Errors[0].description) {
333
- const arrMatches = res.body.Errors[0].description.match(rePattern)
334
- if (Array.isArray(arrMatches) && arrMatches.length === 3) {
335
- pluginStatusCode = parseInt(arrMatches[2])
336
- }
337
- }
338
349
  }
339
- if (pluginStatusCode > 0) {
340
- ctx.response.status = pluginStatusCode // auto change ctx.response.message
341
- res.statusCode = ctx.response.status
342
- res.statusMessage = ctx.response.message
343
- if (pluginStatusCode === 401 || pluginStatusCode === 403) { // don't reveal original SCIM error message details related to access denied (e.g. using Auth PassThrough)
344
- ctx.response.set('Content-Type', 'application/json; charset=utf-8')
345
- ctx.response.body = { error: 'Access denied' }
346
- res.body = ctx.response.body
347
- }
350
+ }
351
+ if (pluginStatusCode > 0) {
352
+ ctx.response.status = pluginStatusCode // auto change ctx.response.message
353
+ res.statusCode = ctx.response.status
354
+ res.statusMessage = ctx.response.message
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
348
359
  }
349
- // back to logResult...
350
- 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' : ''}`)
351
- } 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' : ''}`)
352
- requestCounter += 1 // logged on exit (not win process termination)
353
- }
360
+ }
361
+ // back to logResult...
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' : ''}`)
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' : ''}`)
364
+ requestCounter += 1 // logged on exit (not win process termination)
354
365
  if (ctx.response.body && typeof ctx.response.body === 'object' && ctx.response.status !== 401) ctx.set('Content-Type', 'application/scim+json; charset=utf-8')
355
366
  }
356
367
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scimgateway",
3
- "version": "4.2.11",
3
+ "version": "4.2.13",
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",