scimgateway 3.2.11 → 4.0.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 +247 -282
- package/config/plugin-api.json +9 -4
- package/config/plugin-azure-ad.json +9 -4
- package/config/plugin-forwardinc.json +19 -11
- package/config/plugin-ldap.json +9 -4
- package/config/plugin-loki.json +9 -4
- package/config/plugin-mongodb.json +101 -0
- package/config/plugin-mssql.json +9 -4
- package/config/plugin-restful.json +9 -4
- package/config/plugin-saphana.json +9 -4
- package/index.js +1 -0
- package/lib/plugin-api.js +1 -1
- package/lib/plugin-azure-ad.js +608 -572
- package/lib/plugin-forwardinc.js +303 -371
- package/lib/plugin-ldap.js +267 -382
- package/lib/plugin-loki.js +232 -177
- package/lib/plugin-mongodb.js +776 -0
- package/lib/plugin-mssql.js +105 -196
- package/lib/plugin-restful.js +171 -314
- package/lib/plugin-saphana.js +96 -131
- package/lib/postinstall.js +10 -8
- package/lib/scimdef-v1.js +18 -6
- package/lib/scimdef-v2.js +19 -5
- package/lib/scimgateway.js +487 -399
- package/lib/utils.js +30 -0
- package/package.json +9 -8
- package/test/index.js +1 -3
- package/test/lib/plugin-loki.js +79 -41
- package/test/lib/plugin-mongodb.js +631 -0
- package/test/lib/plugin-restful.js +66 -33
package/README.md
CHANGED
|
@@ -16,10 +16,11 @@ Validated through IdP's:
|
|
|
16
16
|
|
|
17
17
|
Latest news:
|
|
18
18
|
|
|
19
|
+
- New major version v4.0.0. getUsers() and getGroups() replacing some deprecated methods. No limitations on filtering/sorting. Admin user access can be limited to specific baseEntities. New MongoDB plugin
|
|
19
20
|
- ipAllowList for restricting access to allowlisted IP addresses or subnets e.g. Azure AD IP-range
|
|
20
|
-
- General LDAP plugin configured for Active Directory
|
|
21
|
+
- General LDAP plugin configured for Active Directory
|
|
21
22
|
- [PlugSSO](https://elshaug.xyz/docs/plugsso) using SCIM Gateway
|
|
22
|
-
-
|
|
23
|
+
- Authentication configuration allowing more than one admin user including option for readOnly
|
|
23
24
|
- Codebase moved from callback of h... to the the promise(d) land of async/await
|
|
24
25
|
- Supports configuration by environments and external files
|
|
25
26
|
- Health monitoring through "/ping" URL, and option for error notifications by email
|
|
@@ -44,13 +45,17 @@ SCIM Gateway is based on the popular asynchronous event driven framework [Node.j
|
|
|
44
45
|
**Following example plugins are included:**
|
|
45
46
|
|
|
46
47
|
* **Loki** (NoSQL Document-Oriented Database)
|
|
47
|
-
|
|
48
|
+
SCIM Gateway becomes a standalone SCIM endpoint
|
|
48
49
|
Demonstrates user provisioning towards document-oriented database
|
|
49
|
-
Using [LokiJS](
|
|
50
|
+
Using [LokiJS](https://github.com/techfort/LokiJS) for a fast, in-memory document-oriented database (much like MongoDB/PouchDB)
|
|
50
51
|
Default gives two predefined test users loaded using in-memory only (no persistence)
|
|
51
52
|
Setting `{"persistence": true}` gives persistence file store (no test users)
|
|
52
53
|
Example of a fully functional SCIM Gateway plugin
|
|
53
54
|
|
|
55
|
+
* **MongoDB** (NoSQL Document-Oriented Database)
|
|
56
|
+
Same as plugin "Loki" but using MongoDB
|
|
57
|
+
Shows how to implement a highly configurable multi tenant or multi endpoint solution through `baseEntity` in URL
|
|
58
|
+
|
|
54
59
|
* **RESTful** (REST Webservice)
|
|
55
60
|
Demonstrates user provisioning towards REST-Based endpoint
|
|
56
61
|
Using plugin "Loki" as a REST endpoint
|
|
@@ -58,7 +63,7 @@ Using plugin "Loki" as a REST endpoint
|
|
|
58
63
|
* **Forwardinc** (SOAP Webservice)
|
|
59
64
|
Demonstrates user provisioning towards SOAP-Based endpoint
|
|
60
65
|
Using endpoint Forwardinc that comes with Broadcom/CA IM SDK (SDKWS) - [wiki.ca.com](https://docops.ca.com/ca-identity-manager/12-6-8/EN/programming/connector-programming-reference/sdk-sample-connectors/sdkws-sdk-web-services-connector/sdkws-sample-connector-build-requirements "wiki.ca.com")
|
|
61
|
-
Shows how to implement a highly configurable multi tenant or multi endpoint solution
|
|
66
|
+
Shows how to implement a highly configurable multi tenant or multi endpoint solution through `baseEntity` in URL
|
|
62
67
|
|
|
63
68
|
* **MSSQL** (MSSQL Database)
|
|
64
69
|
Demonstrates user provisioning towards MSSQL database
|
|
@@ -70,7 +75,7 @@ Demonstrates SAP HANA specific user provisioning
|
|
|
70
75
|
Azure AD user provisioning including Azure license management (App Service plans) e.g. Office 365
|
|
71
76
|
Using Microsoft Graph API
|
|
72
77
|
Using customized SCIM attributes according to Microsoft Graph API
|
|
73
|
-
Includes CA ConnectorXpress metafile for creating
|
|
78
|
+
Includes Symantec/Broadcom/CA ConnectorXpress metafile for creating provisioning "Azure - ScimGateway" endpoint type
|
|
74
79
|
|
|
75
80
|
* **LDAP** (Directory)
|
|
76
81
|
Fully functional LDAP plugin
|
|
@@ -101,7 +106,7 @@ Create your own package directory e.g. C:\my-scimgateway and install SCIM Gatewa
|
|
|
101
106
|
mkdir c:\my-scimgateway
|
|
102
107
|
cd c:\my-scimgateway
|
|
103
108
|
npm init -y
|
|
104
|
-
npm install scimgateway
|
|
109
|
+
npm install scimgateway
|
|
105
110
|
|
|
106
111
|
**c:\\my-scimgateway** will now be `<package-root>`
|
|
107
112
|
|
|
@@ -130,9 +135,12 @@ If internet connection is blocked, we could install on another machine and copy
|
|
|
130
135
|
http://localhost:8880/Groups/Admins
|
|
131
136
|
=> Lists all attributes for specified user/group
|
|
132
137
|
|
|
138
|
+
http://localhost:8880/Groups?filter=displayName eq "Admins"&excludedAttributes=members
|
|
133
139
|
http://localhost:8880/Users?filter=userName eq "bjensen"&attributes=userName,id,name.givenName
|
|
134
|
-
http://localhost:8880/Users?filter=
|
|
135
|
-
|
|
140
|
+
http://localhost:8880/Users?filter=meta.created gte "2010-01-01T00:00:00Z"&attributes=userName,name.familyName,meta.created
|
|
141
|
+
http://localhost:8880/Users?filter=emails.value co "@example.com"&attributes=userName,name.familyName,emails&sortBy=name.familyName&sortOrder=descending
|
|
142
|
+
=> Filtering examples
|
|
143
|
+
|
|
136
144
|
|
|
137
145
|
"Ctrl + c" to stop the SCIM Gateway
|
|
138
146
|
|
|
@@ -170,10 +178,11 @@ To force a major upgrade (version x.\*.\* => y.\*.\*) that will brake compabilit
|
|
|
170
178
|
**index.js** defines one or more plugins to be started. We could comment out those we do not need. Default configuration only starts the loki plugin.
|
|
171
179
|
|
|
172
180
|
const loki = require('./lib/plugin-loki')
|
|
181
|
+
// const mongodb = require('./lib/plugin-mongodb')
|
|
173
182
|
// const restful = require('./lib/plugin-restful')
|
|
174
183
|
// const forwardinc = require('./lib/plugin-forwardinc')
|
|
175
184
|
// const mssql = require('./lib/plugin-mssql')
|
|
176
|
-
// const saphana = require('./lib/plugin-saphana') // prereq: npm install hdb
|
|
185
|
+
// const saphana = require('./lib/plugin-saphana') // prereq: npm install hdb
|
|
177
186
|
// const azureAD = require('./lib/plugin-azure-ad')
|
|
178
187
|
// const ldap = require('./lib/plugin-ldap')
|
|
179
188
|
// const api = require('./lib/plugin-api')
|
|
@@ -207,18 +216,22 @@ Below shows an example of config\plugin-saphana.json
|
|
|
207
216
|
{
|
|
208
217
|
"username": "gwadmin",
|
|
209
218
|
"password": "password",
|
|
210
|
-
"readOnly": false
|
|
219
|
+
"readOnly": false,
|
|
220
|
+
"baseEntities": []
|
|
211
221
|
}
|
|
212
222
|
],
|
|
213
223
|
"bearerToken": [
|
|
214
224
|
{
|
|
215
225
|
"token": null,
|
|
216
|
-
"readOnly": false
|
|
226
|
+
"readOnly": false,
|
|
227
|
+
"baseEntities": []
|
|
217
228
|
}
|
|
218
229
|
],
|
|
219
230
|
"bearerJwtAzure": [
|
|
220
231
|
{
|
|
221
|
-
"tenantIdGUID": null
|
|
232
|
+
"tenantIdGUID": null,
|
|
233
|
+
"readOnly": false,
|
|
234
|
+
"baseEntities": []
|
|
222
235
|
}
|
|
223
236
|
],
|
|
224
237
|
"bearerJwt": [
|
|
@@ -228,7 +241,8 @@ Below shows an example of config\plugin-saphana.json
|
|
|
228
241
|
"options": {
|
|
229
242
|
"issuer": null
|
|
230
243
|
},
|
|
231
|
-
"readOnly": false
|
|
244
|
+
"readOnly": false,
|
|
245
|
+
"baseEntities": []
|
|
232
246
|
}
|
|
233
247
|
]
|
|
234
248
|
},
|
|
@@ -304,7 +318,10 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
|
|
|
304
318
|
|
|
305
319
|
- **log.customMasking** - array of attributes to be masked e.g. `"customMasking": ["SSN", "weight"]`. By default SCIM Gateway includes masking of some standard attributes like password.
|
|
306
320
|
|
|
307
|
-
- **auth** - Contains one or more authentication/authorization methods used by clients for accessing gateway
|
|
321
|
+
- **auth** - Contains one or more authentication/authorization methods used by clients for accessing gateway - may also include:
|
|
322
|
+
- **auth.xx.readOnly** - true/false, true gives read only access - only allowing `GET` requests for corresponding admin user
|
|
323
|
+
- **auth.xx.baseEntities** - array containing one or more `baseEntity` allowed for this user e.g. ["client-a"] - empty array allowing all.
|
|
324
|
+
**Methods are disabled by setting corresponding admin user to null or remove methods not used**
|
|
308
325
|
|
|
309
326
|
- **auth.basic** - Array of one ore more basic authentication objects - Basic Authentication with **username**/**password**. Note, we set a clear text password that will become encrypted when gateway is started.
|
|
310
327
|
|
|
@@ -524,7 +541,7 @@ docker-compose**
|
|
|
524
541
|
mkdir /opt/my-scimgateway
|
|
525
542
|
cd /opt/my-scimgateway
|
|
526
543
|
npm init -y
|
|
527
|
-
npm install scimgateway
|
|
544
|
+
npm install scimgateway
|
|
528
545
|
cp ./config/docker/* .
|
|
529
546
|
|
|
530
547
|
**docker-compose.yml** <== Here is where you would set the exposed port and environment
|
|
@@ -587,6 +604,71 @@ To upgrade scimgateway docker image (remove the old stuff before running docker-
|
|
|
587
604
|
docker rm scimgateway
|
|
588
605
|
docker rm $(docker ps -a -q); docker rmi $(docker images -q -f "dangling=true")
|
|
589
606
|
|
|
607
|
+
## Azure Active Directory as IdP using SCIM Gateway
|
|
608
|
+
|
|
609
|
+
Azure AD could do automatic user provisioning by synchronizing users towards SCIM Gateway, and gateway plugins will update endpoints.
|
|
610
|
+
|
|
611
|
+
Plugin configuration file must include **SCIM Version "2.0"** (scimgateway.scim.version) and either **Bearer Token** (scimgateway.auth.bearerToken[x].token) or **Azure Tenant ID GUID** (scimgateway.auth.bearerJwtAzure[x].tenantIdGUID) or both:
|
|
612
|
+
|
|
613
|
+
scimgateway: {
|
|
614
|
+
"scim": {
|
|
615
|
+
"version": "2.0",
|
|
616
|
+
...
|
|
617
|
+
},
|
|
618
|
+
...
|
|
619
|
+
"auth": {
|
|
620
|
+
"bearerToken": [
|
|
621
|
+
{
|
|
622
|
+
"token": "shared-secret"
|
|
623
|
+
}
|
|
624
|
+
],
|
|
625
|
+
"bearerJwtAzure": [
|
|
626
|
+
{
|
|
627
|
+
"tenantIdGUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
628
|
+
}
|
|
629
|
+
]
|
|
630
|
+
}
|
|
631
|
+
...
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
`token` configuration must correspond with "Secret Token" defined in Azure AD
|
|
635
|
+
`tenantIdGUID` configuration must correspond with Azure Active Directory Tenant ID
|
|
636
|
+
|
|
637
|
+
In Azure Portal:
|
|
638
|
+
`Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Secret Token`
|
|
639
|
+
Note, when "Secret Token" is left blank, Azure will use JWT (tenantIdGUID)
|
|
640
|
+
|
|
641
|
+
`Azure-Azure Active Directory-Overview-Tenant ID`
|
|
642
|
+
|
|
643
|
+
User mappings attributes between AD and SCIM also needs to be configured
|
|
644
|
+
|
|
645
|
+
`Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Edit attribute mappings-Mappings`
|
|
646
|
+
|
|
647
|
+
Azure AD default SCIM attribute mapping for **USER** must have:
|
|
648
|
+
|
|
649
|
+
userPrincipalName mapped to userName (matching precedence #1)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
Azure AD default SCIM attribute mapping for **GROUP** must have:
|
|
653
|
+
|
|
654
|
+
displayName mapped to displayName (matching precedence #1)
|
|
655
|
+
members mapped to members
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
Some notes related to Azure AD:
|
|
660
|
+
|
|
661
|
+
- Azure Active Directory SCIM [documentation](https://docs.microsoft.com/en-us/azure/active-directory/active-directory-scim-provisioning)
|
|
662
|
+
|
|
663
|
+
- For using OAuth/JWT credentials, Azure configuration "Secret Token" (bearer token) should be blank. Plugin configuration must then include bearerJwtAzure.tenantIdGUID. Click "Test Connection" in Azure to verify
|
|
664
|
+
|
|
665
|
+
- Azure AD do a regular check for a "none" existing user/group. This check seems to be a "keep alive" to verify connection.
|
|
666
|
+
|
|
667
|
+
- Azure AD first checks if user/group exists, if not exist they will be created (no explore of all users like CA Identity Manager)
|
|
668
|
+
|
|
669
|
+
- Deleting a user in Azure AD sends a modify user `{"active":"False"}` which means user should be disabled. This logic is default set in attribute mappings expression rule `Switch([IsSoftDeleted], , "False", "True", "True", "False")`. Standard SCIM "DELETE" method seems not to be used.
|
|
670
|
+
|
|
671
|
+
|
|
590
672
|
## CA Identity Manager as IdP using SCIM Gateway
|
|
591
673
|
|
|
592
674
|
Using Symantec/Broadcom/CA Identity Manger, plugin configuration file must include **SCIM Version "1.1"** (scimgateway.scim.version).
|
|
@@ -616,82 +698,15 @@ Username, password and port must correspond with plugin configuration file. For
|
|
|
616
698
|
|
|
617
699
|
"baseEntity" is optional. This is a parameter used for multi tenant or multi endpoint solutions. We could create several endpoints having same base url with unique baseEntity. e.g:
|
|
618
700
|
|
|
619
|
-
http://localhost:8880/
|
|
620
|
-
http://localhost:8880/
|
|
701
|
+
http://localhost:8880/client-a
|
|
702
|
+
http://localhost:8880/client-b
|
|
621
703
|
|
|
622
704
|
Each baseEntity should then be defined in the plugin configuration file with custom attributes needed. Please see examples in plugin-forwardinc.json
|
|
623
705
|
|
|
624
706
|
IM 12.6 SP7 (and above) also supports pagination for SCIM endpoint (data transferred in bulks - endpoint explore of users). Loki plugin supports pagination. Other plugin may ignore this setting.
|
|
625
707
|
|
|
626
|
-
## SCIM Gateway REST API
|
|
627
|
-
|
|
628
|
-
Create = POST http://localhost:8880/Users
|
|
629
|
-
(body contains the user information)
|
|
630
|
-
|
|
631
|
-
Update = PATCH http://localhost:8880/Users/<id>
|
|
632
|
-
(body contains the attributes to be updated)
|
|
633
|
-
|
|
634
|
-
Search/Read = GET http://localhost:8880/Users?userName eq
|
|
635
|
-
"userID"&attributes=<comma separated list of scim-schema defined attributes>
|
|
636
|
-
|
|
637
|
-
Search/explore all users:
|
|
638
|
-
GET http://localhost:8880/Users?attributes=userName
|
|
639
|
-
|
|
640
|
-
Delete = DELETE http://localhost:8880/Users/<id>
|
|
641
|
-
|
|
642
|
-
Discovery:
|
|
643
|
-
|
|
644
|
-
GET http://localhost:8880/ServiceProviderConfigs
|
|
645
|
-
Specification compliance, authentication schemes, data models.
|
|
646
|
-
|
|
647
|
-
GET http://localhost:8880/Schemas
|
|
648
|
-
Introspect resources and attribute extensions.
|
|
649
|
-
|
|
650
|
-
Note:
|
|
651
|
-
|
|
652
|
-
- userName (mandatory) = UserID
|
|
653
|
-
- id (mandatory) = Unique id. Could be set to the same as UserID but don't have to.
|
|
654
|
-
|
|
655
|
-
## SAP Hana endpoint
|
|
656
|
-
|
|
657
|
-
Get all users (explore):
|
|
658
|
-
select USER_NAME from SYS.USERS where IS_SAML_ENABLED like 'TRUE';
|
|
659
|
-
|
|
660
|
-
Get a specific user:
|
|
661
|
-
select USER_NAME, USER_DEACTIVATED from SYS.USERS where USER_NAME like '<UserID>';
|
|
662
|
-
|
|
663
|
-
Create User:
|
|
664
|
-
CREATE USER <UserID> WITH IDENTITY '<UserID>' FOR SAML PROVIDER <SamlProvider>;
|
|
665
|
-
|
|
666
|
-
Delete user:
|
|
667
|
-
DROP USER <UserID>;
|
|
668
|
-
|
|
669
|
-
Modify user (enable user):
|
|
670
|
-
ALTER USER <UserID> ACTIVATE;
|
|
671
|
-
|
|
672
|
-
Modify user (disable user):
|
|
673
|
-
ALTER USER <UserID> DEACTIVATE;
|
|
674
|
-
|
|
675
|
-
Postinstallation:
|
|
676
|
-
|
|
677
|
-
cd c:\my-scimgateway
|
|
678
|
-
npm install hdb --save
|
|
679
708
|
|
|
680
|
-
|
|
681
|
-
Only SAML users will be explored and managed
|
|
682
|
-
|
|
683
|
-
Supported template attributes:
|
|
684
|
-
|
|
685
|
-
- User Name (UserID)
|
|
686
|
-
- Suspended (Enabled/Disabled)
|
|
687
|
-
|
|
688
|
-
Currently no other attributes needed. Trying to update other attributes will then give an error message. **The SCIM Provisioning template should therefore not include any other global user attribute references.**
|
|
689
|
-
|
|
690
|
-
SAP Hana converts UserID to uppercase. Provisioning use default lowercase. Provisioning template should therefore also convert to uppercase.
|
|
691
|
-
|
|
692
|
-
User Name = %$$TOUPPER(%AC%)%
|
|
693
|
-
|
|
694
|
-
## Azure Active Directory endpoint
|
|
709
|
+
## Azure Active Directory provisioning
|
|
695
710
|
Using plugin-azure-ad we could do user provisioning towards Azure AD including license management e.g. O365
|
|
696
711
|
|
|
697
712
|
For testing purposes we could get an Azure free account and in addition the free Office 365 for testing license management through Azure.
|
|
@@ -766,7 +781,8 @@ Note, for Symantec/Broadcom/CA Provisioning we have to use SCIM version 1.1
|
|
|
766
781
|
{
|
|
767
782
|
"username": "gwadmin",
|
|
768
783
|
"password": "password",
|
|
769
|
-
"readOnly": false
|
|
784
|
+
"readOnly": false,
|
|
785
|
+
"baseEntities": []
|
|
770
786
|
}
|
|
771
787
|
],
|
|
772
788
|
|
|
@@ -798,10 +814,10 @@ For multi-tenant or multi-endpoint support, we may add several entities:
|
|
|
798
814
|
"undefined": {
|
|
799
815
|
...
|
|
800
816
|
},
|
|
801
|
-
"
|
|
817
|
+
"client-a": {
|
|
802
818
|
...
|
|
803
819
|
},
|
|
804
|
-
"
|
|
820
|
+
"client-b": {
|
|
805
821
|
...
|
|
806
822
|
}
|
|
807
823
|
}
|
|
@@ -849,74 +865,39 @@ Endpoint configuration example:
|
|
|
849
865
|
|
|
850
866
|
For details, please see section "CA Identity Manager as IdP using SCIM Gateway"
|
|
851
867
|
|
|
868
|
+
## SCIM Gateway REST API
|
|
869
|
+
|
|
870
|
+
Create = POST http://localhost:8880/Users
|
|
871
|
+
(body contains the user information)
|
|
872
|
+
|
|
873
|
+
Update = PATCH http://localhost:8880/Users/<id>
|
|
874
|
+
(body contains the attributes to be updated)
|
|
875
|
+
|
|
876
|
+
Search/Read = GET http://localhost:8880/Users?userName eq
|
|
877
|
+
"userID"&attributes=<comma separated list of scim-schema defined attributes>
|
|
878
|
+
|
|
879
|
+
Search/explore all users:
|
|
880
|
+
GET http://localhost:8880/Users?attributes=userName
|
|
881
|
+
|
|
882
|
+
Delete = DELETE http://localhost:8880/Users/<id>
|
|
852
883
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
Azure AD could do automatic user provisioning by synchronizing users towards SCIM Gateway, and gateway plugins will update endpoints.
|
|
856
|
-
|
|
857
|
-
Plugin configuration file must include **SCIM Version "2.0"** (scimgateway.scim.version) and either **Bearer Token** (scimgateway.auth.bearerToken[x].token) or **Azure Tenant ID GUID** (scimgateway.auth.bearerJwtAzure[x].tenantIdGUID) or both:
|
|
858
|
-
|
|
859
|
-
scimgateway: {
|
|
860
|
-
"scim": {
|
|
861
|
-
"version": "2.0",
|
|
862
|
-
...
|
|
863
|
-
},
|
|
864
|
-
...
|
|
865
|
-
"auth": {
|
|
866
|
-
"bearerToken": [
|
|
867
|
-
{
|
|
868
|
-
"token": "shared-secret"
|
|
869
|
-
}
|
|
870
|
-
],
|
|
871
|
-
"bearerJwtAzure": [
|
|
872
|
-
{
|
|
873
|
-
"tenantIdGUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
874
|
-
}
|
|
875
|
-
]
|
|
876
|
-
}
|
|
877
|
-
...
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
`token` configuration must correspond with "Secret Token" defined in Azure AD
|
|
881
|
-
`tenantIdGUID` configuration must correspond with Azure Active Directory Tenant ID
|
|
882
|
-
|
|
883
|
-
In Azure Portal:
|
|
884
|
-
`Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Secret Token`
|
|
885
|
-
Note, when "Secret Token" is left blank, Azure will use JWT (tenantIdGUID)
|
|
886
|
-
|
|
887
|
-
`Azure-Azure Active Directory-Overview-Tenant ID`
|
|
888
|
-
|
|
889
|
-
User mappings attributes between AD and SCIM also needs to be configured
|
|
890
|
-
|
|
891
|
-
`Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Edit attribute mappings-Mappings`
|
|
892
|
-
|
|
893
|
-
Azure AD default SCIM attribute mapping for **USER** must have:
|
|
894
|
-
|
|
895
|
-
userPrincipalName mapped to userName (matching precedence #1)
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
Azure AD default SCIM attribute mapping for **GROUP** must have:
|
|
899
|
-
|
|
900
|
-
displayName mapped to displayName (matching precedence #1)
|
|
901
|
-
members mapped to members
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
Some notes related to Azure AD:
|
|
906
|
-
|
|
907
|
-
- Azure Active Directory SCIM [documentation](https://docs.microsoft.com/en-us/azure/active-directory/active-directory-scim-provisioning)
|
|
884
|
+
Discovery:
|
|
908
885
|
|
|
909
|
-
|
|
886
|
+
GET http://localhost:8880/ServiceProviderConfigs
|
|
887
|
+
Specification compliance, authentication schemes, data models.
|
|
888
|
+
|
|
889
|
+
GET http://localhost:8880/Schemas
|
|
890
|
+
Introspect resources and attribute extensions.
|
|
910
891
|
|
|
911
|
-
|
|
892
|
+
Note:
|
|
912
893
|
|
|
913
|
-
-
|
|
894
|
+
- userName (mandatory) = UserID
|
|
895
|
+
- id (mandatory) = Unique id. Could be set to the same as UserID but don't have to.
|
|
914
896
|
|
|
915
|
-
- Deleting a user in Azure AD sends a modify user `{"active":"False"}` which means user should be disabled. This logic is default set in attribute mappings expression rule `Switch([IsSoftDeleted], , "False", "True", "True", "False")`. Standard SCIM "DELETE" method seems not to be used.
|
|
916
897
|
|
|
917
898
|
## API Gateway
|
|
918
899
|
|
|
919
|
-
Gateway also works as an API Gateway when using url `/api` or `/<baseEntity>/api`
|
|
900
|
+
SCIM Gateway also works as an API Gateway when using url `/api` or `/<baseEntity>/api`
|
|
920
901
|
|
|
921
902
|
Following methods for the none SCIM based api-plugin are supported:
|
|
922
903
|
|
|
@@ -928,7 +909,7 @@ Following methods for the none SCIM based api-plugin are supported:
|
|
|
928
909
|
PATCH /api/{id} + body
|
|
929
910
|
DELETE /api/{id}
|
|
930
911
|
|
|
931
|
-
|
|
912
|
+
These methods can also be used in standard SCIM plugins
|
|
932
913
|
Please see example plugin: **plugin-api.js**
|
|
933
914
|
|
|
934
915
|
|
|
@@ -937,7 +918,7 @@ For JavaScript coding editor you may use [Visual Studio Code](https://code.visua
|
|
|
937
918
|
|
|
938
919
|
Preparation:
|
|
939
920
|
|
|
940
|
-
* Copy "best matching" example plugin e.g. `lib\plugin-
|
|
921
|
+
* Copy "best matching" example plugin e.g. `lib\plugin-mssql.js` and `config\plugin-mssql.json` and rename both copies to your plugin name prefix e.g. plugin-mine.js and plugin-mine.json (for SOAP Webservice endpoint we might use plugin-forwardinc as a template)
|
|
941
922
|
* Edit plugin-mine.json and define a unique port number for the gateway setting
|
|
942
923
|
* Edit index.js and add a new line for starting your plugin e.g. `let mine = require('./lib/plugin-mine');`
|
|
943
924
|
* Start SCIM Gateway and verify. If using CA Provisioning you could setup a SCIM endpoint using the port number you defined
|
|
@@ -945,20 +926,18 @@ Preparation:
|
|
|
945
926
|
Now we are ready for custom coding by editing plugin-mine.js
|
|
946
927
|
Coding should be done step by step and each step should be verified and tested before starting the next (they are all highlighted by comments in existing code).
|
|
947
928
|
|
|
948
|
-
1. **Turn off group functionality**
|
|
949
|
-
Please see
|
|
950
|
-
2. **
|
|
951
|
-
3. **getUser** (test provisioning retrieve account)
|
|
929
|
+
1. **Turn off group functionality** - getGroups to return empty response
|
|
930
|
+
Please see plugin-saphana that do not use groups.
|
|
931
|
+
2. **getUsers** (test provisioning retrieve accounts)
|
|
952
932
|
4. **createUser** (test provisioning new account)
|
|
953
933
|
5. **deleteUser** (test provisioning delete account)
|
|
954
934
|
6. **modifyUser** (test provisioning modify account)
|
|
955
|
-
7. **
|
|
956
|
-
|
|
957
|
-
8. **
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
12. **createGroup** (test provisioning new group)
|
|
935
|
+
7. **Turn on group functionality** - getGroups having logic for returning groups if groups are supported
|
|
936
|
+
7. **getGroups** (test provisioning retrieve groups)
|
|
937
|
+
8. **modifyGroup** (test provisioning modify group members)
|
|
938
|
+
12. **createGroup** (test provisioning new group)
|
|
939
|
+
13. **deleteGroup** (test provisioning delete account)
|
|
940
|
+
|
|
962
941
|
|
|
963
942
|
Template used by CA Provisioning role should only include endpoint supported attributes defined in our plugin. Template should therefore have no links to global user for none supported attributes (e.g. remove %UT% from "Job Title" if our endpoint/code do not support title)
|
|
964
943
|
|
|
@@ -1028,9 +1007,9 @@ Plugins should have following initialization:
|
|
|
1028
1007
|
// mandatory plugin initialization - end
|
|
1029
1008
|
|
|
1030
1009
|
|
|
1031
|
-
###
|
|
1010
|
+
### getUsers
|
|
1032
1011
|
|
|
1033
|
-
scimgateway.
|
|
1012
|
+
scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
|
|
1034
1013
|
let ret = {
|
|
1035
1014
|
"Resources": [],
|
|
1036
1015
|
"totalResults": null
|
|
@@ -1040,54 +1019,15 @@ Plugins should have following initialization:
|
|
|
1040
1019
|
}
|
|
1041
1020
|
|
|
1042
1021
|
* baseEntity = Optional for multi-tenant or multi-endpoint support (defined in base url e.g. `<baseurl>/client1` gives baseEntity=client1)
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1022
|
+
* getObj = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
|
|
1023
|
+
* attribute, operator and value are set when using "simpel filtering", e.g. getObj.attribute='userName', getObj.operator='eq' and getObj.value='bjensen', but not for advanced filtering having and/or/not
|
|
1024
|
+
* rawFilter is always set when filtering is used
|
|
1025
|
+
* startIndex = Pagination - The 1-based index of the first result in the current set of search results
|
|
1026
|
+
* count = Pagination - Number of elements to be returned in the current set of search results
|
|
1027
|
+
* attributes = array of attributes to be returned - if empty, all supported attributes should be returned
|
|
1045
1028
|
* ret:
|
|
1046
|
-
ret.Resources = array filled with user objects
|
|
1047
|
-
ret.totalResults = if supporting pagination, then
|
|
1048
|
-
|
|
1049
|
-
### exploreGroups
|
|
1050
|
-
|
|
1051
|
-
scimgateway.exploreGroups = async (baseEntity, attributes, startIndex, count) => {
|
|
1052
|
-
let ret = {
|
|
1053
|
-
"Resources": [],
|
|
1054
|
-
"totalResults": null
|
|
1055
|
-
}
|
|
1056
|
-
...
|
|
1057
|
-
return ret
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
* ret:
|
|
1061
|
-
ret.Resources = array filled with group objects containing group attributes where displayName is mandatory e.g [{"displayName":"Admins"}, {"displayName":"Employees"}]
|
|
1062
|
-
ret.totalResults = if supporting pagination attribute should be set to the total numbers of elements (groups) at the endpoint else set to null
|
|
1063
|
-
|
|
1064
|
-
### getUser
|
|
1065
|
-
|
|
1066
|
-
scimgateway.getUser = async (baseEntity, getObj, attributes) => {
|
|
1067
|
-
...
|
|
1068
|
-
return userObj
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
* getObj = `{ filter: <filterAttribute>, identifier: <identifier> }`
|
|
1072
|
-
e.g: getObj = { "filter": "userName", "identifier": "bjensen"}
|
|
1073
|
-
filter: **userName** and **id** must be supported
|
|
1074
|
-
* attributes = scim attributes to be returned. If no attributes defined, all should be returned.
|
|
1075
|
-
* return userObj: userObj containing scim userattributes/values
|
|
1076
|
-
eg:
|
|
1077
|
-
{"id":"bjensen","name":{"formatted":"Ms. Barbara J Jensen III","familyName":"Jensen","givenName":"Barbara"}}
|
|
1078
|
-
|
|
1079
|
-
Note, the value of the **id** attribute returned will be used by modifyUser and deleteUser
|
|
1080
|
-
|
|
1081
|
-
### createUser
|
|
1082
|
-
|
|
1083
|
-
scimgateway.createUser = async (baseEntity, userObj) => {
|
|
1084
|
-
...
|
|
1085
|
-
return null
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
* userObj = user object containing userattributes according to scim standard
|
|
1089
|
-
Note, multi-value attributes excluding user attribute 'groups' are customized from array to object based on type
|
|
1090
|
-
* return null: null if OK, else throw error
|
|
1029
|
+
ret.Resources = array filled with user objects according to getObj/attributes, we could normally include all attributes having id and userName as mandatory e.g [{"id": "bjensen", "userName": "bjensen"}, {"id":"jsmith", "userName":"jsmith"}]
|
|
1030
|
+
ret.totalResults = if supporting pagination, then it should be set to the total numbers of elements (users), else set to null
|
|
1091
1031
|
|
|
1092
1032
|
### deleteUser
|
|
1093
1033
|
|
|
@@ -1112,73 +1052,28 @@ Note, multi-value attributes excluding user attribute 'groups' are customized fr
|
|
|
1112
1052
|
Note, multi-value attributes excluding user attribute 'groups' are customized from array to object based on type
|
|
1113
1053
|
* return null: null if OK, else throw error
|
|
1114
1054
|
|
|
1115
|
-
###
|
|
1116
|
-
|
|
1117
|
-
scimgateway.getGroup = async (baseEntity, getObj, attributes) => {
|
|
1118
|
-
...
|
|
1119
|
-
return retObj
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
* getObj = `{ filter: <filterAttribute>, identifier: <identifier> }`
|
|
1124
|
-
e.g: getObj = { "filter": "displayName", "identifier": "GroupA" }
|
|
1125
|
-
filter: **displayName** and **id** must be supported
|
|
1126
|
-
* attributes = scim attributes to be returned. If no attributes defined, all should be returned.
|
|
1127
|
-
* return retObj: retObj containing group displayName and id (+ members if using default "users are member of group")
|
|
1128
|
-
|
|
1129
|
-
eg. using default "users are member of group":
|
|
1130
|
-
{"displayName":"Admins","id":"Admins","members":[{"value":"bjensen","display":"bjensen"]}
|
|
1131
|
-
|
|
1132
|
-
eg. using "groups are member of user":
|
|
1133
|
-
{"displayName":"Admins","id":"Admins"}
|
|
1055
|
+
### getGroups
|
|
1134
1056
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
let arrRet = []
|
|
1141
|
-
...
|
|
1142
|
-
return arrRet
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
Retrieve all groups for user id WHEN **"user member of groups"**. This setting is default SCIM behaviour. This means Group having multivalue attribute members containing id of users.
|
|
1147
|
-
|
|
1148
|
-
* id = user id (eg. bjensen)
|
|
1149
|
-
* attributes = scim attributes to be returned as object in array
|
|
1150
|
-
* arrRet = array containing the objects of id, displayName and members where members value only include current user id on the format:
|
|
1151
|
-
{ id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }
|
|
1152
|
-
|
|
1153
|
-
[
|
|
1154
|
-
{"id": "Admins", "displayName: "Admins", "members": [{"value": "bjensen"}]},
|
|
1155
|
-
{"id": "Employees", "displayName: "Employees", "members": [{"value": "bjensen"}]}
|
|
1156
|
-
]
|
|
1157
|
-
|
|
1158
|
-
If "user member of groups" not supported, then return []
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
### getGroupUsers
|
|
1163
|
-
|
|
1164
|
-
scimgateway.getGroupUsers = async (baseEntity, id, attributes) => {
|
|
1165
|
-
let arrRet = []
|
|
1057
|
+
scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
|
|
1058
|
+
let ret = {
|
|
1059
|
+
"Resources": [],
|
|
1060
|
+
"totalResults": null
|
|
1061
|
+
}
|
|
1166
1062
|
...
|
|
1167
|
-
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
Retrieve all users for a spesific group id WHEN **"group member of users"**. This means user having multivalue attribute groups having value set to group id
|
|
1171
|
-
|
|
1172
|
-
* id = group id (eg. UserGroup-1)
|
|
1173
|
-
* attributes = scim attributes to be returned as object in array
|
|
1174
|
-
* arrRet = array containing the objects of userName and groups.value e.g:
|
|
1063
|
+
return ret
|
|
1064
|
+
}
|
|
1175
1065
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1066
|
+
* baseEntity = Optional for multi-tenant or multi-endpoint support (defined in base url e.g. `<baseurl>/client1` gives baseEntity=client1)
|
|
1067
|
+
* getObj = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
|
|
1068
|
+
* attribute, operator and value are set when using "simpel filtering", e.g. getObj.attribute='displayName', getObj.operator='eq' and getObj.value='Admins', but not for advanced filtering having and/or/not
|
|
1069
|
+
* rawFilter is always set when filtering is used
|
|
1070
|
+
* startIndex = Pagination - The 1-based index of the first result in the current set of search results
|
|
1071
|
+
* count = Pagination - Number of elements to be returned in the current set of search results
|
|
1072
|
+
* attributes = array of attributes to be returned - if empty, all supported attributes should be returned
|
|
1073
|
+
* ret:
|
|
1074
|
+
ret.Resources = array filled with group objects according to getObj/attributes, we could normally include all attributes having id, displayName and members as mandatory e.g [{"id":"Admins", "displayName":"Admins", members":[{"value":"bjensen"}]}, {"id":"Employees", "displayName":"Employees"}, "members":[{"value":"jsmith","display":"John Smith"}]]
|
|
1075
|
+
ret.totalResults = if supporting pagination, then it should be set to the total numbers of elements (users), else set to null
|
|
1180
1076
|
|
|
1181
|
-
If "group member of users" not supported, then return []
|
|
1182
1077
|
|
|
1183
1078
|
### createGroup
|
|
1184
1079
|
scimgateway.createGroup = async (baseEntity, groupObj) => {
|
|
@@ -1215,13 +1110,6 @@ eg: {"value":"bjensen"},{"operation":"delete","value":"jsmith"}
|
|
|
1215
1110
|
If we do not support groups, then return null
|
|
1216
1111
|
|
|
1217
1112
|
|
|
1218
|
-
## Known limitations
|
|
1219
|
-
|
|
1220
|
-
* SCIM filtering only supports operator 'eq' returning unique object only, example:
|
|
1221
|
-
/Users?**filter**=userName **eq** "bjensen"&attributes=userName,id,name.givenName
|
|
1222
|
-
/Users?**filter**=emails.value **eq** "bjensen@example.com"&attributes=userName,phoneNumbers
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
1113
|
## License
|
|
1226
1114
|
|
|
1227
1115
|
MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
@@ -1229,12 +1117,89 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1229
1117
|
|
|
1230
1118
|
## Change log
|
|
1231
1119
|
|
|
1120
|
+
### v4.0.0
|
|
1121
|
+
**[MAJOR]**
|
|
1122
|
+
|
|
1123
|
+
- New `getUsers()` replacing deprecated exploreUsers(), getUser() and getGroupUsers()
|
|
1124
|
+
- New `getGroups()` replacing deprecated exploreGroups(), getGroup() and getGroupMembers()
|
|
1125
|
+
- Fully filter and sort support
|
|
1126
|
+
- Authentication configuration may now include a baseEntities array containing one or more `baseEntity` allowed for corresponding admin user
|
|
1127
|
+
- New plugin-mongodb, **thanks to Filipe Ribeiro and Miguel Ferreira (KEEP SOLUTIONS)**
|
|
1128
|
+
|
|
1129
|
+
Note, using this major version **require existing custom plugins to be upgraded**. If you do not want to upgrade your custom plugins, the old version have to be installed using: `npm install scimgateway@3.2.11`
|
|
1130
|
+
|
|
1131
|
+
How to upgrade your custom plugins:
|
|
1132
|
+
|
|
1133
|
+
Replace: scimgateway.exploreUsers = async (baseEntity, attributes, startIndex, count) => {
|
|
1134
|
+
With: scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
|
|
1135
|
+
|
|
1136
|
+
See comments in provided plugins regarding the new `getObj`. Also note that `attributes` is now an array and not a comma separated string like previous versions
|
|
1137
|
+
|
|
1138
|
+
In the very beginning, add:
|
|
1139
|
+
|
|
1140
|
+
// mandatory if-else logic - start
|
|
1141
|
+
if (getObj.operator) {
|
|
1142
|
+
if (getObj.operator === 'eq' && ['id', 'userName', 'externalId'].includes(getObj.attribute)) {
|
|
1143
|
+
// mandatory - unique filtering - single unique user to be returned - correspond to getUser() in versions < 4.x.x
|
|
1144
|
+
} else if (getObj.operator === 'eq' && getObj.attribute === 'group.value') {
|
|
1145
|
+
// optional - only used when groups are member of users, not default behavior - correspond to getGroupUsers() in versions < 4.x.x
|
|
1146
|
+
throw new Error(`${action} error: not supporting groups member of user filtering: ${getObj.rawFilter}`)
|
|
1147
|
+
} else {
|
|
1148
|
+
// optional - simpel filtering
|
|
1149
|
+
throw new Error(`${action} error: not supporting simpel filtering: ${getObj.rawFilter}`)
|
|
1150
|
+
}
|
|
1151
|
+
} else if (getObj.rawFilter) {
|
|
1152
|
+
// optional - advanced filtering having and/or/not - use getObj.rawFilter
|
|
1153
|
+
throw new Error(`${action} error: not supporting advanced filtering: ${getObj.rawFilter}`)
|
|
1154
|
+
} else {
|
|
1155
|
+
// mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
|
|
1156
|
+
}
|
|
1157
|
+
// mandatory if-else logic - end
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
In the new getUsers() replacing exploreUsers() "as-is", we then need some logic in the last "else" statement listed above.
|
|
1161
|
+
We also need to add logic from existing getGroup() and getGroupMembers()
|
|
1162
|
+
**Please have a look at provieded plugins to see different ways of doing this logic.**
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
Replace: scimgateway.exploreGroups = async (baseEntity, attributes, startIndex, count) => {
|
|
1166
|
+
With: scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
|
|
1167
|
+
|
|
1168
|
+
In the very beginning, add:
|
|
1169
|
+
|
|
1170
|
+
// mandatory if-else logic - start
|
|
1171
|
+
if (getObj.operator) {
|
|
1172
|
+
if (getObj.operator === 'eq' && ['id', 'displayName', 'externalId'].includes(getObj.attribute)) {
|
|
1173
|
+
// mandatory - unique filtering - single unique user to be returned - correspond to getUser() in versions < 4.x.x
|
|
1174
|
+
} else if (getObj.operator === 'eq' && getObj.attribute === 'members.value') {
|
|
1175
|
+
// mandatory - return all groups the user 'id' (getObj.value) is member of - correspond to getGroupMembers() in versions < 4.x.x
|
|
1176
|
+
// Resources = [{ id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }]
|
|
1177
|
+
} else {
|
|
1178
|
+
// optional - simpel filtering
|
|
1179
|
+
throw new Error(`${action} error: not supporting simpel filtering: ${getObj.rawFilter}`)
|
|
1180
|
+
}
|
|
1181
|
+
} else if (getObj.rawFilter) {
|
|
1182
|
+
// optional - advanced filtering having and/or/not - use getObj.rawFilter
|
|
1183
|
+
throw new Error(`${action} error: not supporting advanced filtering: ${getObj.rawFilter}`)
|
|
1184
|
+
} else {
|
|
1185
|
+
// mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreGroups() in versions < 4.x.x
|
|
1186
|
+
}
|
|
1187
|
+
// mandatory if-else logic - end
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
In the new getGroups() replacing exploreGroups() "as-is", we then need some logic in the last "else" statement listed above.
|
|
1191
|
+
We also need to add logic from existing getGroup() and getGroupMembers()
|
|
1192
|
+
**Please have a look at provieded plugins to see different ways of doing this logic.**
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
Delete deprecated exploreUsers(), getUser(), getGroupUsers(), exploreGroups(), getGroup() and getGroupMembers()
|
|
1196
|
+
|
|
1197
|
+
|
|
1232
1198
|
### v3.2.11
|
|
1233
1199
|
[Fixed]
|
|
1234
1200
|
|
|
1235
1201
|
- errorhandling related to running scimgateway as unikernel
|
|
1236
1202
|
|
|
1237
|
-
|
|
1238
1203
|
### v3.2.10
|
|
1239
1204
|
[Fixed]
|
|
1240
1205
|
|