scimgateway 4.4.3 → 4.4.4

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
@@ -207,8 +207,8 @@ Below shows an example of config\plugin-saphana.json
207
207
  "payloadSize": null,
208
208
  "scim": {
209
209
  "version": "2.0",
210
- "customSchema": null,
211
210
  "skipTypeConvert" : false,
211
+ "skipMetaLocation" false,
212
212
  "usePutSoftSync" : false,
213
213
  "usePutGroupMemberOfUser": false
214
214
  },
@@ -345,9 +345,6 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
345
345
 
346
346
  - **scim.version** - "1.1" or "2.0". Default is "2.0".
347
347
 
348
- - **scim.customSchema** - filename of JSON file located in `<package-root>\config\schemas` containing custom schema attributes, see configuration notes
349
- **Note, scim.customSchema is obsolete, instead use:**: 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.
350
-
351
348
  - **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-entra-id), e.g.:
352
349
 
353
350
  "emails": {
@@ -364,6 +361,7 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
364
361
  {"value": "jsmith@hotmail.com"}
365
362
  ]
366
363
 
364
+ - **scim.skipMetaLocation** - true or false, default false. If set to true, `meta.location` which contains protocol and hostname from request-url, will be excluded from response e.g. `"{...,meta":{"location":"https://my-company.com/<...>"}}`. If using reverse proxy and not including headers `X-Forwarded-Proto` and `X-Forwarded-Host`, originator will be the proxy and we might not want to expose internal protocol and hostname being used by the proxy request.
367
365
 
368
366
  - **scim.usePutSoftSync** - true or false, default false. `PUT /Users/bjensen` will replace the user bjensen with body content. If set to `true`, only PUT body content will be replaced. Any additional existing user attributes and groups supported by plugin will remain as-is.
369
367
 
@@ -450,10 +448,12 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
450
448
 
451
449
  #### Configuration notes
452
450
 
453
- - Setting environment variable `SEED` will override default password seeding logic.
451
+ - Custom Schemas, ServiceProviderConfig and ResourceType can be used 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.
452
+ - Using reverse proxy and we want ipAllowList and correct meta.location response, following headers must be set by proxy: `X-Forwarded-For`, `X-Forwarded-Proto` and `X-Forwarded-Host`
453
+ - Setting environment variable `SEED` with some random characters will override default password seeding logic. This also allow copying configuration file with encrypted secrets from one machine to another.
454
454
  - All configuration can be set based on environment variables. Syntax will then be `"process.env.<ENVIRONMENT>"` where `<ENVIRONMENT>` is the environment variable used. E.g. scimgateway.port could have value "process.env.PORT", then using environment variable PORT.
455
- - All configuration can be set based on corresponding JSON-content (dot notation) in external file using plugin name as parent JSON object. Syntax will then be `"process.file.<path>"` where `<path>` is the file used. E.g. endpoint.password could have value "process.file./var/run/vault/secrets.json"
456
- - Also, individual secret file may be used for a plain text secret per file. Syntax will then be `"process.text.<path>"` where `<path>` is the file which contains raw (`UTF-8`) character value. E.g. endpoint.password could have value "process.text./var/run/vault/endpoint.password". This enables that the config file itself be loaded from a ConfigMap while specific values are mounted either from `secrets.json` style files as mentioned above OR from traditional secrets files mounted in the file system, one value per file.
455
+ - All configuration values can be moved to a single external file having JSON dot notation content with plugin name as parent JSON object. Syntax in original configuration file used by the gateway will then be `"process.file.<path>"` where `<path>` is the file used. E.g. key endpoint.password could have value "process.file./var/run/vault/secrets.json"
456
+ - All configuration values can be moved to multiple external files, each file containing one single value. Syntax in original configuration file used by the gateway will then be `"process.text.<path>"` where `<path>` is the file which contains raw (`UTF-8`) character value. E.g. key endpoint.password could have value "process.text./var/run/vault/endpoint.password".
457
457
 
458
458
  Example:
459
459
 
@@ -489,7 +489,11 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
489
489
  }
490
490
 
491
491
 
492
- secrets.json for plugin-soap - example (dot notation):
492
+ jwt.secret file content example:
493
+
494
+ thisIsSecret
495
+
496
+ secrets.json file content example for plugin-soap:
493
497
 
494
498
  {
495
499
  "plugin-soap.scimgateway.auth.basic[0].username": "gwadmin",
@@ -498,7 +502,6 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
498
502
  "plugin-soap.endpoint.password": "secret"
499
503
  }
500
504
 
501
- - Custom Schemas, ServiceProviderConfig and ResourceType will be used 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.
502
505
 
503
506
 
504
507
  ## Manual startup
@@ -1144,6 +1147,19 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1144
1147
 
1145
1148
  ## Change log
1146
1149
 
1150
+ ### v4.4.4
1151
+
1152
+ [Added]
1153
+
1154
+ - New configuration: **scim.skipMetaLocation**
1155
+ true or false, default false. If set to true, `meta.location` which contains protocol and hostname from request-url, will be excluded from response e.g. `"{...,meta":{"location":"https://my-company.com/<...>"}}`. If using reverse proxy and not including headers `X-Forwarded-Proto` and `X-Forwarded-Host`, originator will be the proxy and we might not want to expose internal protocol and hostname being used by the proxy request.
1156
+
1157
+ Below is an example of nginx reverse proxy configuration supporting SCIM Gateway ipAllowList and correct meta.location response:
1158
+
1159
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1160
+ proxy_set_header X-Forwarded-Proto $scheme;
1161
+ proxy_set_header X-Forwarded-Host $http_host;
1162
+
1147
1163
  ### v4.4.3
1148
1164
 
1149
1165
  [Added]
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,9 +5,10 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
10
- "usePutSoftSync": false
9
+ "skipMetaLocation": false,
10
+ "usePutSoftSync": false,
11
+ "usePutGroupMemberOfUser": false
11
12
  },
12
13
  "log": {
13
14
  "loglevel": {
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -5,8 +5,8 @@
5
5
  "payloadSize": null,
6
6
  "scim": {
7
7
  "version": "2.0",
8
- "customSchema": null,
9
8
  "skipTypeConvert": false,
9
+ "skipMetaLocation": false,
10
10
  "usePutSoftSync": false,
11
11
  "usePutGroupMemberOfUser": false
12
12
  },
@@ -273,7 +273,7 @@ const ScimGateway = function () {
273
273
  else scimDef = require('../lib/scimdef-v1')
274
274
  }
275
275
 
276
- if (config.scim.customSchema) { // merge plugin custom schema extension into core schemas
276
+ if (config.scim.customSchema) { // legacy - merge plugin custom schema extension into core schemas
277
277
  let custom
278
278
  try {
279
279
  custom = JSON.parse(fs.readFileSync(`${configDir}/schemas/${config.scim.customSchema}`, 'utf8'))
@@ -354,8 +354,8 @@ const ScimGateway = function () {
354
354
  ctx.response.body = { error: 'Access denied' }
355
355
  res.body = ctx.response.body
356
356
  }
357
- 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' : ''}`)
358
- } 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' : ''}`)
357
+ logger.error(`${gwName}[${pluginName}] ${ellapsed} ${ctx.request.ip} ${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' : ''}`)
358
+ } else logger.info(`${gwName}[${pluginName}] ${ellapsed} ${ctx.request.ip} ${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' : ''}`)
359
359
  requestCounter += 1 // logged on exit (not win process termination)
360
360
  if (ctx.response.body && typeof ctx.response.body === 'object' && ctx.response.status !== 401) ctx.set('Content-Type', 'application/scim+json; charset=utf-8')
361
361
  }
@@ -605,7 +605,7 @@ const ScimGateway = function () {
605
605
  })
606
606
  }
607
607
  }
608
- } // authentication
608
+ }
609
609
 
610
610
  const verifyContentType = (ctx, next) => {
611
611
  return new Promise((resolve) => {
@@ -623,18 +623,16 @@ const ScimGateway = function () {
623
623
 
624
624
  const ipAllowList = (ctx, next) => {
625
625
  return new Promise((resolve) => {
626
- ctx.request.ipcli = ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress
627
- ctx.request.ipcli = ctx.request.ipcli.split(',')[0] // used by logResult
628
626
  if (!ipAllowListChecker) return resolve(next())
629
- if (ipAllowListChecker(ctx.request.ipcli)) return resolve(next())
630
- logger.debug(`${gwName}[${pluginName}] client ip ${ctx.request.ipcli} not in ipAllowList`)
627
+ if (ipAllowListChecker(ctx.request.ip)) return resolve(next()) // if proxy, prereq: request includes header X-Forwarded-For and koa app.proxy=true
628
+ logger.debug(`${gwName}[${pluginName}] client ip ${ctx.request.ip} not in ipAllowList`)
631
629
  ctx.status = 401
632
630
  ctx.body = { error: 'Access denied' }
633
631
  resolve(ctx)
634
632
  })
635
633
  }
636
634
 
637
- const app = new Koa()
635
+ const app = new Koa({ proxy: true })
638
636
  const router = new Router()
639
637
 
640
638
  // Middleware run in the order they are defined and communicates through ctx
@@ -653,7 +651,7 @@ const ScimGateway = function () {
653
651
  app.use(router.allowedMethods())
654
652
 
655
653
  app.on('error', (err, ctx) => { // catching none try/catch in app middleware, also bodyparser and body not json
656
- logger.error(`${gwName}[${pluginName}] Koa method: ${ctx.method} url: ${ctx.origin + ctx.path} body: ${JSON.stringify(ctx.request.body)} error: ${err.message}`)
654
+ logger.error(`${gwName}[${pluginName}] Koa method: ${ctx.method} url: ${ctx.request.origin + ctx.path} body: ${JSON.stringify(ctx.request.body)} error: ${err.message}`)
657
655
  })
658
656
 
659
657
  router.get('/ping', async (ctx) => { // auth not required
@@ -664,9 +662,8 @@ const ScimGateway = function () {
664
662
 
665
663
  // Google App Engine B-class instance start/stop request
666
664
  router.get(['/_ah/start', '/_ah/stop'], async (ctx) => {
667
- const cli = ctx.request.ipcli
668
665
  const ver = process.env.GAE_VERSION
669
- if (!cli || cli !== '0.1.0.3' || !ver || !ctx.origin.includes(`.${ver}.`)) { // ctx.origin = http://<instance>.<version>.<project-id>.<region>.r.appspot.com
666
+ if (ctx.request.ip !== '0.1.0.3' || !ver || !ctx.request.origin.includes(`.${ver}.`)) { // ctx.request.origin = http://<instance>.<version>.<project-id>.<region>.r.appspot.com
670
667
  ctx.status = 403 // request not coming from GCP App Engine
671
668
  return
672
669
  }
@@ -680,14 +677,16 @@ const ScimGateway = function () {
680
677
  router.get(['/(|scim/)(ServiceProviderConfigs|ServiceProviderConfig)',
681
678
  '/:baseEntity/(|scim/)(ServiceProviderConfigs|ServiceProviderConfig)'], async (ctx) => {
682
679
  const tx = scimDef.ServiceProviderConfigs
683
- const location = ctx.origin + ctx.path
684
- if (tx.meta) tx.meta.location = location
685
- else {
686
- tx.meta = {}
687
- tx.meta.location = location
680
+ if (!config.scim.skipMetaLocation) {
681
+ const location = ctx.request.origin + ctx.path
682
+ if (tx.meta) tx.meta.location = location
683
+ else {
684
+ tx.meta = {}
685
+ tx.meta.location = location
686
+ }
688
687
  }
689
688
  ctx.body = tx
690
- logger.debug(`${gwName}[${pluginName}] GET ${ctx.originalUrl} Response = ${JSON.stringify(tx)}`)
689
+ logger.debug(`${gwName}[${pluginName}] GET ${ctx.request.originalUrl} Response = ${JSON.stringify(tx)}`)
691
690
  })
692
691
 
693
692
  // Initial connection, step #2: GET /Schemas
@@ -820,7 +819,7 @@ const ScimGateway = function () {
820
819
  `/:baseEntity/(|scim/)(!${undefined}|Users|Groups|servicePlans)/:id`], async (ctx) => {
821
820
  if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map(item => item.trim()).join()
822
821
  if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map(item => item.trim()).join()
823
- let u = ctx.originalUrl.substr(0, ctx.originalUrl.lastIndexOf('/'))
822
+ let u = ctx.request.originalUrl.substr(0, ctx.request.originalUrl.lastIndexOf('/'))
824
823
  u = u.substr(u.lastIndexOf('/') + 1) // u = Users, Groups
825
824
  const handle = handler[u]
826
825
  const id = decodeURIComponent(require('path').basename(ctx.params.id, '.json')) // supports <id>.json
@@ -883,14 +882,16 @@ const ScimGateway = function () {
883
882
  }
884
883
  }
885
884
  }
886
- const location = ctx.origin + ctx.path
887
885
  userObj = addPrimaryAttrs(userObj)
888
886
  scimdata = utils.stripObj(userObj, ctx.query.attributes, ctx.query.excludedAttributes)
889
887
  scimdata = addSchemas(scimdata, handle.description, isScimv2)
890
- if (scimdata.meta) scimdata.meta.location = location
891
- else {
892
- scimdata.meta = {}
893
- scimdata.meta.location = location
888
+ if (!config.scim.skipMetaLocation) {
889
+ const location = ctx.request.origin + ctx.path
890
+ if (scimdata.meta) scimdata.meta.location = location
891
+ else {
892
+ scimdata.meta = {}
893
+ scimdata.meta.location = location
894
+ }
894
895
  }
895
896
  ctx.body = scimdata
896
897
  } catch (err) {
@@ -909,7 +910,7 @@ const ScimGateway = function () {
909
910
  '/:baseEntity/(|scim/)(Users|Groups|servicePlans|AppRoles)'], async (ctx) => {
910
911
  if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map(item => item.trim()).join()
911
912
  if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map(item => item.trim()).join()
912
- let u = ctx.originalUrl.substr(ctx.originalUrl.lastIndexOf('/') + 1) // u = Users, Groups, servicePlans, ...
913
+ let u = ctx.request.originalUrl.substr(ctx.request.originalUrl.lastIndexOf('/') + 1) // u = Users, Groups, servicePlans, ...
913
914
  const ui = u.indexOf('?')
914
915
  if (ui > 0) u = u.substr(0, ui)
915
916
  const handle = handler[u]
@@ -1089,8 +1090,9 @@ const ScimGateway = function () {
1089
1090
  }
1090
1091
  }
1091
1092
 
1092
- let location = ctx.origin + ctx.path
1093
+ let location = ctx.request.origin + ctx.path
1093
1094
  if (ctx.query.attributes || (ctx.query.excludedAttributes && ctx.query.excludedAttributes.includes('meta'))) location = null
1095
+ if (config.scim.skipMetaLocation) location = null
1094
1096
  for (let i = 0; i < scimdata.Resources.length; i++) {
1095
1097
  scimdata.Resources[i] = addPrimaryAttrs(scimdata.Resources[i])
1096
1098
  scimdata.Resources[i] = utils.stripObj(scimdata.Resources[i], ctx.query.attributes, ctx.query.excludedAttributes)
@@ -1125,7 +1127,7 @@ const ScimGateway = function () {
1125
1127
  //
1126
1128
  router.post([`/(|scim/)(!${undefined}|Users|Groups)(|.json)(|.xml)`,
1127
1129
  `/:baseEntity/(|scim/)(!${undefined}|Users|Groups)(|.json)(|.xml)`], async (ctx) => {
1128
- let u = ctx.originalUrl.substr(ctx.originalUrl.lastIndexOf('/') + 1) // u = Users<.json|.xml>, Groups<.json|.xml>
1130
+ let u = ctx.request.originalUrl.substr(ctx.request.originalUrl.lastIndexOf('/') + 1) // u = Users<.json|.xml>, Groups<.json|.xml>
1129
1131
  u = u.split('?')[0] // Users?AzureAdScimPatch062020
1130
1132
  const handle = handler[u.split('.')[0]]
1131
1133
  logger.debug(`${gwName}[${pluginName}] [Create ${handle.description}]`)
@@ -1154,7 +1156,7 @@ const ScimGateway = function () {
1154
1156
  return
1155
1157
  }
1156
1158
 
1157
- logger.debug(`${gwName}[${pluginName}] POST ${ctx.originalUrl} body=${strBody}`)
1159
+ logger.debug(`${gwName}[${pluginName}] POST ${ctx.request.originalUrl} body=${strBody}`)
1158
1160
  jsonBody = JSON.parse(strBody) // using a copy
1159
1161
  const [scimdata, err] = ScimGateway.prototype.convertedScim(jsonBody)
1160
1162
  logger.debug(`${gwName}[${pluginName}] convertedBody=${JSON.stringify(scimdata)}`)
@@ -1197,12 +1199,14 @@ const ScimGateway = function () {
1197
1199
  if (obj && obj.id) jsonBody = obj // id found, using returned object
1198
1200
  }
1199
1201
 
1200
- const location = `${ctx.origin}${ctx.path}/${jsonBody.id}`
1201
- if (!jsonBody.meta) jsonBody.meta = {}
1202
- jsonBody.meta.location = location
1202
+ if (!config.scim.skipMetaLocation) {
1203
+ const location = `${ctx.request.origin}${ctx.path}/${jsonBody.id}`
1204
+ if (!jsonBody.meta) jsonBody.meta = {}
1205
+ jsonBody.meta.location = location
1206
+ ctx.set('Location', location)
1207
+ }
1203
1208
  delete jsonBody.password
1204
1209
  jsonBody = addPrimaryAttrs(jsonBody)
1205
- ctx.set('Location', location)
1206
1210
  ctx.status = 201
1207
1211
  ctx.body = jsonBody
1208
1212
  } catch (err) {
@@ -1228,7 +1232,7 @@ const ScimGateway = function () {
1228
1232
  //
1229
1233
  router.delete([`/(|scim/)(!${undefined}|Users|Groups)/:id`,
1230
1234
  `/:baseEntity/(|scim/)(!${undefined}|Users|Groups)/:id`], async (ctx) => {
1231
- let u = ctx.originalUrl.substr(0, ctx.originalUrl.lastIndexOf('/'))
1235
+ let u = ctx.request.originalUrl.substr(0, ctx.request.originalUrl.lastIndexOf('/'))
1232
1236
  u = u.substr(u.lastIndexOf('/') + 1) // u = Users, Groups
1233
1237
  const handle = handler[u]
1234
1238
  const id = ctx.params.id
@@ -1274,7 +1278,7 @@ const ScimGateway = function () {
1274
1278
  `/:baseEntity/(|scim/)(!${undefined}|Users|Groups|servicePlans)/:id`], async (ctx) => {
1275
1279
  if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map(item => item.trim()).join()
1276
1280
  if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map(item => item.trim()).join()
1277
- let u = ctx.originalUrl.substr(0, ctx.originalUrl.lastIndexOf('/'))
1281
+ let u = ctx.request.originalUrl.substr(0, ctx.request.originalUrl.lastIndexOf('/'))
1278
1282
  u = u.substr(u.lastIndexOf('/') + 1) // u = Users, Groups
1279
1283
  const handle = handler[u]
1280
1284
  const id = decodeURIComponent(ctx.params.id)
@@ -1326,8 +1330,10 @@ const ScimGateway = function () {
1326
1330
  return
1327
1331
  }
1328
1332
 
1329
- const location = ctx.origin + ctx.path
1330
- ctx.set('Location', location)
1333
+ if (!config.scim.skipMetaLocation) {
1334
+ const location = ctx.request.origin + ctx.path
1335
+ ctx.set('Location', location)
1336
+ }
1331
1337
  const userObj = addPrimaryAttrs(scimdata.Resources[0])
1332
1338
  scimdata = utils.stripObj(userObj, ctx.query.attributes, ctx.query.excludedAttributes)
1333
1339
  scimdata = addSchemas(scimdata, handle.description, isScimv2)
@@ -1352,7 +1358,7 @@ const ScimGateway = function () {
1352
1358
  })
1353
1359
 
1354
1360
  const replaceUsrGrp = async (ctx) => {
1355
- let u = ctx.originalUrl.substr(0, ctx.originalUrl.lastIndexOf('/'))
1361
+ let u = ctx.request.originalUrl.substr(0, ctx.request.originalUrl.lastIndexOf('/'))
1356
1362
  u = u.substr(u.lastIndexOf('/') + 1) // u = Users, Groups
1357
1363
  const handle = handler[u]
1358
1364
  const id = ctx.params.id
@@ -1365,7 +1371,7 @@ const ScimGateway = function () {
1365
1371
  if (customErrorCode) ctx.status = customErrorCode
1366
1372
  ctx.body = e
1367
1373
  } else {
1368
- logger.debug(`${gwName}[${pluginName}] PUT ${ctx.originalUrl} body=${strBody}`)
1374
+ logger.debug(`${gwName}[${pluginName}] PUT ${ctx.request.originalUrl} body=${strBody}`)
1369
1375
  try {
1370
1376
  // get current object
1371
1377
  logger.debug(`${gwName}[${pluginName}] calling "${handle.getMethod}" and awaiting result`)
@@ -1554,8 +1560,10 @@ const ScimGateway = function () {
1554
1560
  }
1555
1561
  }
1556
1562
 
1557
- const location = ctx.origin + ctx.path
1558
- ctx.set('Location', location)
1563
+ if (!config.scim.skipMetaLocation) {
1564
+ const location = ctx.request.origin + ctx.path
1565
+ ctx.set('Location', location)
1566
+ }
1559
1567
  scimdata = addSchemas(scimdata, handle.description, isScimv2)
1560
1568
  ctx.status = 200
1561
1569
  ctx.body = scimdata
@@ -1588,10 +1596,8 @@ const ScimGateway = function () {
1588
1596
  ctx.body = apiErr(pluginName, err)
1589
1597
  } else {
1590
1598
  logger.debug(`${gwName}[${pluginName}] calling "postApi" and awaiting result`)
1591
-
1592
1599
  try {
1593
1600
  let result = await this.postApi(ctx.params.baseEntity, apiObj, ctx.ctxCopy)
1594
- const location = ctx.origin + ctx.path
1595
1601
  if (result) {
1596
1602
  if (typeof result === 'object') result = { result: result }
1597
1603
  else {
@@ -1604,7 +1610,10 @@ const ScimGateway = function () {
1604
1610
  } else result = {}
1605
1611
  if (!result.meta) result.meta = {}
1606
1612
  result.meta.result = 'success'
1607
- result.meta.location = location
1613
+ if (!config.scim.skipMetaLocation) {
1614
+ const location = ctx.request.origin + ctx.path
1615
+ result.meta.location = location
1616
+ }
1608
1617
  ctx.status = 201
1609
1618
  ctx.body = result
1610
1619
  } catch (err) {
@@ -1637,7 +1646,6 @@ const ScimGateway = function () {
1637
1646
 
1638
1647
  try {
1639
1648
  let result = await this.putApi(ctx.params.baseEntity, id, apiObj, ctx.ctxCopy)
1640
- const location = ctx.origin + ctx.path
1641
1649
  if (result) {
1642
1650
  if (typeof result === 'object') result = { result: result }
1643
1651
  else {
@@ -1650,7 +1658,10 @@ const ScimGateway = function () {
1650
1658
  } else result = {}
1651
1659
  if (!result.meta) result.meta = {}
1652
1660
  result.meta.result = 'success'
1653
- result.meta.location = location
1661
+ if (!config.scim.skipMetaLocation) {
1662
+ const location = ctx.request.origin + ctx.path
1663
+ result.meta.location = location
1664
+ }
1654
1665
  ctx.status = 200
1655
1666
  ctx.body = result
1656
1667
  } catch (err) {
@@ -1683,7 +1694,6 @@ const ScimGateway = function () {
1683
1694
 
1684
1695
  try {
1685
1696
  let result = await this.patchApi(ctx.params.baseEntity, id, apiObj, ctx.ctxCopy)
1686
- const location = ctx.origin + ctx.path
1687
1697
  if (result) {
1688
1698
  if (typeof result === 'object') result = { result: result }
1689
1699
  else {
@@ -1696,7 +1706,10 @@ const ScimGateway = function () {
1696
1706
  } else result = {}
1697
1707
  if (!result.meta) result.meta = {}
1698
1708
  result.meta.result = 'success'
1699
- result.meta.location = location
1709
+ if (!config.scim.skipMetaLocation) {
1710
+ const location = ctx.request.origin + ctx.path
1711
+ result.meta.location = location
1712
+ }
1700
1713
  ctx.status = 200
1701
1714
  ctx.body = result
1702
1715
  } catch (err) {
@@ -1727,7 +1740,6 @@ const ScimGateway = function () {
1727
1740
 
1728
1741
  try {
1729
1742
  let result = await this.getApi(ctx.params.baseEntity, ctx.params.id, ctx.query, apiObj, ctx.ctxCopy)
1730
- const location = ctx.origin + ctx.path
1731
1743
  if (result) {
1732
1744
  if (typeof result === 'object') result = { result: result }
1733
1745
  else {
@@ -1740,7 +1752,10 @@ const ScimGateway = function () {
1740
1752
  } else result = {}
1741
1753
  if (!result.meta) result.meta = {}
1742
1754
  result.meta.result = 'success'
1743
- result.meta.location = location
1755
+ if (!config.scim.skipMetaLocation) {
1756
+ const location = ctx.request.origin + ctx.path
1757
+ result.meta.location = location
1758
+ }
1744
1759
  ctx.status = 200
1745
1760
  ctx.body = result
1746
1761
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scimgateway",
3
- "version": "4.4.3",
3
+ "version": "4.4.4",
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",