scimgateway 4.1.15 → 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 +40 -11
- package/config/plugin-api.json +5 -0
- package/config/plugin-azure-ad.json +5 -0
- package/config/plugin-forwardinc.json +5 -0
- package/config/plugin-ldap.json +5 -0
- package/config/plugin-loki.json +5 -0
- package/config/plugin-mongodb.json +5 -0
- package/config/plugin-mssql.json +5 -0
- package/config/plugin-saphana.json +5 -0
- package/config/plugin-scim.json +5 -0
- package/lib/scimgateway.js +42 -1
- package/package.json +2 -1
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
|
|
@@ -291,7 +292,12 @@ Below shows an example of config\plugin-saphana.json
|
|
|
291
292
|
"to": null,
|
|
292
293
|
"cc": null
|
|
293
294
|
}
|
|
294
|
-
}
|
|
295
|
+
},
|
|
296
|
+
"kubernetes": {
|
|
297
|
+
"enabled": false,
|
|
298
|
+
"shutdownTimeout": 15000,
|
|
299
|
+
"forceExitTimeout": 1000
|
|
300
|
+
}
|
|
295
301
|
},
|
|
296
302
|
"endpoint": {
|
|
297
303
|
"host": "hostname",
|
|
@@ -305,7 +311,7 @@ Below shows an example of config\plugin-saphana.json
|
|
|
305
311
|
|
|
306
312
|
Configuration file have two main JSON objects: `scimgateway` and `endpoint`
|
|
307
313
|
|
|
308
|
-
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.
|
|
309
315
|
|
|
310
316
|
Definitions in `endpoint` object are customized according to our plugin code. Plugin typically need this information for communicating with endpoint
|
|
311
317
|
|
|
@@ -408,6 +414,11 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
|
|
|
408
414
|
- **emailOnError.smtp.to** - Comma separated list of recipients email addresses e.g: "someone@example.com"
|
|
409
415
|
- **emailOnError.smtp.cc** - Comma separated list of cc email addresses
|
|
410
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
|
+
|
|
411
422
|
- **endpoint** - Contains endpoint specific configuration according to our **plugin code**.
|
|
412
423
|
|
|
413
424
|
#### Configuration notes
|
|
@@ -1040,12 +1051,13 @@ Plugins should have following initialization:
|
|
|
1040
1051
|
let validScimAttr = [] // empty array - all attrbutes are supported by endpoint
|
|
1041
1052
|
// add any external config process.env and process.file
|
|
1042
1053
|
config = scimgateway.processExtConfig(pluginName, config)
|
|
1054
|
+
scimgateway.authPassThroughAllowed = false
|
|
1043
1055
|
// mandatory plugin initialization - end
|
|
1044
1056
|
|
|
1045
1057
|
|
|
1046
1058
|
### getUsers
|
|
1047
1059
|
|
|
1048
|
-
scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
|
|
1060
|
+
scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
|
|
1049
1061
|
let ret = {
|
|
1050
1062
|
"Resources": [],
|
|
1051
1063
|
"totalResults": null
|
|
@@ -1067,7 +1079,7 @@ ret.totalResults = if supporting pagination, then it should be set to the total
|
|
|
1067
1079
|
|
|
1068
1080
|
### deleteUser
|
|
1069
1081
|
|
|
1070
|
-
scimgateway.deleteUser = async (baseEntity, id) => {
|
|
1082
|
+
scimgateway.deleteUser = async (baseEntity, id, ctx) => {
|
|
1071
1083
|
...
|
|
1072
1084
|
return null
|
|
1073
1085
|
}
|
|
@@ -1077,7 +1089,7 @@ ret.totalResults = if supporting pagination, then it should be set to the total
|
|
|
1077
1089
|
|
|
1078
1090
|
### modifyUser
|
|
1079
1091
|
|
|
1080
|
-
scimgateway.modifyUser = async (baseEntity, id, attrObj) => {
|
|
1092
|
+
scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
|
|
1081
1093
|
...
|
|
1082
1094
|
return null
|
|
1083
1095
|
}
|
|
@@ -1090,7 +1102,7 @@ Note, multi-value attributes excluding user attribute 'groups' are customized fr
|
|
|
1090
1102
|
|
|
1091
1103
|
### getGroups
|
|
1092
1104
|
|
|
1093
|
-
scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
|
|
1105
|
+
scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
1094
1106
|
let ret = {
|
|
1095
1107
|
"Resources": [],
|
|
1096
1108
|
"totalResults": null
|
|
@@ -1112,7 +1124,7 @@ ret.totalResults = if supporting pagination, then it should be set to the total
|
|
|
1112
1124
|
|
|
1113
1125
|
|
|
1114
1126
|
### createGroup
|
|
1115
|
-
scimgateway.createGroup = async (baseEntity, groupObj) => {
|
|
1127
|
+
scimgateway.createGroup = async (baseEntity, groupObj, ctx) => {
|
|
1116
1128
|
...
|
|
1117
1129
|
return null
|
|
1118
1130
|
})
|
|
@@ -1122,7 +1134,7 @@ groupObj.displayName contains the group name to be created
|
|
|
1122
1134
|
* return null: null if OK, else throw error
|
|
1123
1135
|
|
|
1124
1136
|
### deleteGroup
|
|
1125
|
-
scimgateway.deleteGroup = async (baseEntity, id) => {
|
|
1137
|
+
scimgateway.deleteGroup = async (baseEntity, id, ctx) => {
|
|
1126
1138
|
...
|
|
1127
1139
|
return null
|
|
1128
1140
|
}
|
|
@@ -1132,7 +1144,7 @@ groupObj.displayName contains the group name to be created
|
|
|
1132
1144
|
|
|
1133
1145
|
### modifyGroup
|
|
1134
1146
|
|
|
1135
|
-
scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
|
|
1147
|
+
scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
|
|
1136
1148
|
...
|
|
1137
1149
|
return null
|
|
1138
1150
|
}
|
|
@@ -1153,11 +1165,27 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1153
1165
|
|
|
1154
1166
|
## Change log
|
|
1155
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
|
+
|
|
1156
1184
|
### v4.1.15
|
|
1157
1185
|
|
|
1158
1186
|
[Added]
|
|
1159
1187
|
|
|
1160
|
-
-
|
|
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.
|
|
1161
1189
|
|
|
1162
1190
|
Plugin configuration prerequisites: **auth.passThrough.enabled=true**
|
|
1163
1191
|
|
|
@@ -1182,6 +1210,7 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1182
1210
|
scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx)
|
|
1183
1211
|
// tip, see provided example plugins
|
|
1184
1212
|
|
|
1213
|
+
**Thanks to Kevin Osborn**
|
|
1185
1214
|
|
|
1186
1215
|
### v4.1.14
|
|
1187
1216
|
|
package/config/plugin-api.json
CHANGED
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/lib/scimgateway.js
CHANGED
|
@@ -26,6 +26,7 @@ const callsite = require('callsite')
|
|
|
26
26
|
const utils = require('../lib/utils')
|
|
27
27
|
const countries = require('../lib/countries')
|
|
28
28
|
const { createChecker } = require('is-in-subnet')
|
|
29
|
+
const { createTerminus } = require('@godaddy/terminus')
|
|
29
30
|
require('events').EventEmitter.prototype._maxListeners = Infinity
|
|
30
31
|
|
|
31
32
|
/**
|
|
@@ -82,6 +83,7 @@ const ScimGateway = function () {
|
|
|
82
83
|
if (!config.certificate.pfx) config.certificate.pfx = {}
|
|
83
84
|
if (!config.emailOnError) config.emailOnError = {}
|
|
84
85
|
if (!config.emailOnError.smtp) config.emailOnError.smtp = {}
|
|
86
|
+
if (!config.kubernetes) config.kubernetes = {}
|
|
85
87
|
|
|
86
88
|
if (config.ipAllowList && Array.isArray(config.ipAllowList) && config.ipAllowList.length > 0) {
|
|
87
89
|
ipAllowListChecker = createChecker(config.ipAllowList)
|
|
@@ -491,7 +493,7 @@ const ScimGateway = function () {
|
|
|
491
493
|
const obj = config.auth.passThrough
|
|
492
494
|
if (obj.baseEntities) {
|
|
493
495
|
if (Array.isArray(obj.baseEntities) && obj.baseEntities.length > 0) {
|
|
494
|
-
if (!baseEntity || !obj.baseEntities.includes(baseEntity)) throw new Error(`baseEntity=${baseEntity} not allowed for
|
|
496
|
+
if (!baseEntity || !obj.baseEntities.includes(baseEntity)) throw new Error(`baseEntity=${baseEntity} not allowed for passThrough according to passThrough configuration baseEntitites=${obj.baseEntities}`)
|
|
495
497
|
}
|
|
496
498
|
}
|
|
497
499
|
if (obj.readOnly === true && method !== 'GET') throw new Error('only allowing readOnly for passThrough according to passThrough configuration readOnly=true')
|
|
@@ -1768,6 +1770,45 @@ const ScimGateway = function () {
|
|
|
1768
1770
|
}
|
|
1769
1771
|
}
|
|
1770
1772
|
|
|
1773
|
+
function onSignal () {
|
|
1774
|
+
logger.info('server is starting cleanup')
|
|
1775
|
+
return Promise.all([
|
|
1776
|
+
// your clean logic, like closing database connections
|
|
1777
|
+
])
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
function onShutdown () {
|
|
1781
|
+
logger.info('cleanup finished, server is shutting down')
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
function beforeShutdown () {
|
|
1785
|
+
return new Promise(resolve => {
|
|
1786
|
+
setTimeout(resolve, config.kubernetes.shutdownTimeout || 15000)
|
|
1787
|
+
})
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
function healthCheck () {
|
|
1791
|
+
return Promise.resolve(
|
|
1792
|
+
// optionally include a resolve value to be included as
|
|
1793
|
+
// info in the health check response
|
|
1794
|
+
)
|
|
1795
|
+
}
|
|
1796
|
+
const options = {
|
|
1797
|
+
// health check options
|
|
1798
|
+
healthChecks: {
|
|
1799
|
+
'/healthcheck': healthCheck, // a function returning a promise indicating service health,
|
|
1800
|
+
verbatim: true // [optional = false] use object returned from /healthcheck verbatim in response
|
|
1801
|
+
},
|
|
1802
|
+
|
|
1803
|
+
// cleanup options
|
|
1804
|
+
timeout: config.kubernetes?.forceExitTimeout || 1000, // [optional = 1000] number of milliseconds before forceful exiting
|
|
1805
|
+
beforeShutdown, // [optional] called before the HTTP server starts its shutdown
|
|
1806
|
+
onSignal, // [optional] cleanup function, returning a promise (used to be onSigterm)
|
|
1807
|
+
onShutdown // [optional] called right before exiting
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
if (config.kubernetes.enabled) createTerminus(server, options)
|
|
1811
|
+
|
|
1771
1812
|
// set loglevel according to config
|
|
1772
1813
|
const arrValidLevel = ['silly', 'debug', 'verbose', 'info', 'warn', 'error']
|
|
1773
1814
|
for (let i = 0; i < logger.transports.length; i++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scimgateway",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
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",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"node": ">=7.6.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@godaddy/terminus": "^4.11.2",
|
|
35
36
|
"callsite": "^1.0.0",
|
|
36
37
|
"dot-object": "^2.1.4",
|
|
37
38
|
"https-proxy-agent": "^5.0.1",
|