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 +25 -9
- package/config/plugin-api.json +1 -1
- package/config/plugin-entra-id.json +3 -2
- package/config/plugin-ldap.json +1 -1
- package/config/plugin-loki.json +1 -1
- package/config/plugin-mongodb.json +1 -1
- package/config/plugin-mssql.json +1 -1
- package/config/plugin-saphana.json +1 -1
- package/config/plugin-scim.json +1 -1
- package/config/plugin-soap.json +1 -1
- package/lib/scimgateway.js +64 -49
- package/package.json +1 -1
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
|
-
-
|
|
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
|
|
456
|
-
-
|
|
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
|
-
|
|
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]
|
package/config/plugin-api.json
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
"payloadSize": null,
|
|
6
6
|
"scim": {
|
|
7
7
|
"version": "2.0",
|
|
8
|
-
"customSchema": null,
|
|
9
8
|
"skipTypeConvert": false,
|
|
10
|
-
"
|
|
9
|
+
"skipMetaLocation": false,
|
|
10
|
+
"usePutSoftSync": false,
|
|
11
|
+
"usePutGroupMemberOfUser": false
|
|
11
12
|
},
|
|
12
13
|
"log": {
|
|
13
14
|
"loglevel": {
|
package/config/plugin-ldap.json
CHANGED
package/config/plugin-loki.json
CHANGED
package/config/plugin-mssql.json
CHANGED
package/config/plugin-scim.json
CHANGED
package/config/plugin-soap.json
CHANGED
package/lib/scimgateway.js
CHANGED
|
@@ -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.
|
|
358
|
-
} else logger.info(`${gwName}[${pluginName}] ${ellapsed} ${ctx.request.
|
|
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
|
-
}
|
|
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.
|
|
630
|
-
logger.debug(`${gwName}[${pluginName}] client ip ${ctx.request.
|
|
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 (
|
|
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
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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 (
|
|
891
|
-
|
|
892
|
-
scimdata.meta =
|
|
893
|
-
|
|
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
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
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
|
-
|
|
1330
|
-
|
|
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
|
-
|
|
1558
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
"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",
|