
Research
Security News
Lazarus Strikes npm Again with New Wave of Malicious Packages
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
scimgateway
Advanced tools
Author: Jarle Elshaug
Validated through IdP's:
Latest news:
With SCIM Gateway we could do user management by using REST based SCIM 1.1 or 2.0 protocol. Gateway will translate incoming SCIM requests and expose CRUD functionality (create, read, update and delete user/group) towards destinations using endpoint specific protocols. Gateway do not require SCIM to be used, it's also an API Gateway that could be used for other things than user provisioning.
SCIM Gateway is a standalone product, however this document shows how the gateway could be used by products like Symatec/Broadcom/CA Identity Manager.
Using Identity Manager, we could setup one or more endpoints of type SCIM pointing to the gateway. Specific ports could then be used for each type of endpoint, and the SCIM Gateway would work like a "CA Connector Server" communicating with endpoints.
Instead of using IM-SDK for building our own integration for none supported endpoints, we can now build new integration based on SCIM Gateway plugins. SCIM Gateway works with IM as long as IM supports SCIM.
SCIM Gateway is based on the popular asynchronous event driven framework Node.js using JavaScript. It is firewall friendly using REST webservices. Runs on almost all operating systems, and may load balance between hosts (horizontal) and cpu's (vertical). Could even be uploaded and run as a cloud application.
Following example plugins are included:
Loki (NoSQL Document-Oriented Database)
Gives a SCIM endpoint located on SCIM Gateway
Demonstrates user provisioning towards document-oriented database
Using LokiJS for a fast, in-memory document-oriented database (much like MongoDB/PouchDB)
Default gives two predefined test users loaded using in-memory only (no persistence)
Setting {"persistence": true}
gives persistence file store (no test users)
Example of a fully functional SCIM Gateway plugin
RESTful (REST Webservice)
Demonstrates user provisioning towards REST-Based endpoint
Using plugin "Loki" as a REST endpoint
Forwardinc (SOAP Webservice)
Demonstrates user provisioning towards SOAP-Based endpoint
Using endpoint Forwardinc that comes with Broadcom/CA IM SDK (SDKWS) - wiki.ca.com
Shows how to implement a highly configurable multi tenant or multi endpoint solution using baseEntity
parameter
MSSQL (MSSQL Database)
Demonstrates user provisioning towards MSSQL database
SAP HANA (SAP HANA Database)
Demonstrates SAP HANA specific user provisioning
Azure AD (REST Webservices)
Azure AD user provisioning including Azure license management (App Service plans) e.g. Office 365
Using Microsoft Graph API
Using customized SCIM attributes according to Microsoft Graph API
Includes CA ConnectorXpress metafile for creating CA IM "Azure - ScimGateway" endpoint type
LDAP (Directory)
Fully functional LDAP plugin
Pre-configured for Microsoft Active Directory
Using endpointMapper (like plugin-azure-ad) for attribute flexibility
API (REST Webservices)
Demonstrates API Gateway/plugin functionality using post/put/patch/get/delete
None SCIM plugin, becomes what you want it to become.
Methods listed can also be used in standard SCIM plugins
Endpoint complexity could be put in this plugin, and client could instead communicate through Gateway using your own simplified REST specification.
One example of usage could be creation of tickets in ServiceDesk/HelpDesk and also the other way, closing a ticket could automatically approve/reject corresponding workflow in Identity Manager.
Node.js is a prerequisite and have to be installed on the server.
Download the windows installer (.msi 64-bit) and install using default options.
Open a command window (run as administrator)
Create your own package directory e.g. C:\my-scimgateway and install SCIM Gateway within this package.
mkdir c:\my-scimgateway
cd c:\my-scimgateway
npm init -y
npm install scimgateway --save
Please ignore any error messages unless soap WSSecurityCert functionality is needed in your custom plugin code. Module soap installation of optional dependency 'ursa' that also includes 'node-gyp' then needs misc. prerequisites to bee manually installed.
c:\my-scimgateway will now be <package-root>
index.js, lib and config directories containing example plugins have been copied to your package from the original scimgateway package located under node_modules.
If internet connection is blocked, we could install on another machine and copy the scimgateway folder.
node c:\my-scimgateway
Start a browser
http://localhost:8880/ping
=> Health check with a "hello" response
http://localhost:8880/Users
http://localhost:8880/Groups
or
http://localhost:8880/Users?attributes=userName
http://localhost:8880/Groups?attributes=displayName
=> Logon using gwadmin/password and two users and groups should be listed
http://localhost:8880/Users/bjensen
http://localhost:8880/Groups/Admins
=> Lists all attributes for specified user/group
http://localhost:8880/Users?filter=userName eq "bjensen"&attributes=userName,id,name.givenName
http://localhost:8880/Users?filter=emails.value eq "bjensen@example.com"&attributes=userName,phoneNumbers
=> Filtering supporting operator 'eq' returning unique object with attributes specified
"Ctrl + c" to stop the SCIM Gateway
For more functionality using browser (post/patch/delete) a REST extension/add-on is needed.
Tip, take a look at mocha test scripts located in
node_modules\scimgateway\test\lib
Not needed after a fresh install
Check if newer versions are available:
cd c:\my-scimgateway
npm outdated
Lists current, wanted and latest version. No output on screen means we are running the latest version.
The best and easiest way to upgrade is renaming existing scimgateway package folder, create a new one and do a fresh installation. After the installation you copy index.js, config and lib folder
(your customized plugins) from your previous installation to the new installation. You should also read the version history to see if your custom plugins needs to be updated.
Alternatives are:
Upgrade to latest minor version:
cd c:\my-scimgateway
npm install scimgateway
Note, always backup/copy C:\my-scimgateway before upgrading. Custom plugins and corresponding configuration files will not be affected.
To force a major upgrade (version x.*.* => y.*.*) that will brake compability with any existing custom plugins, we have to include the @latest
suffix in the install command: npm install scimgateway@latest
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.
const loki = require('./lib/plugin-loki')
// const restful = require('./lib/plugin-restful')
// const forwardinc = require('./lib/plugin-forwardinc')
// const mssql = require('./lib/plugin-mssql')
// const saphana = require('./lib/plugin-saphana') // prereq: npm install hdb --save
// const azureAD = require('./lib/plugin-azure-ad')
// const ldap = require('./lib/plugin-ldap')
// const api = require('./lib/plugin-api')
Each endpoint plugin needs a JavaScript file (.js) and a configuration file (.json). They both must have the same naming prefix. For SAP Hana endpoint we have:
lib\plugin-saphana.js
config\plugin-saphana.json
Edit specific plugin configuration file according to your needs.
Below shows an example of config\plugin-saphana.json
{
"scimgateway": {
"port": 8884,
"localhostonly": false,
"scim": {
"version": "2.0",
"customSchema": null
},
"log": {
"loglevel": {
"file": "debug",
"console": "error"
},
"customMasking": []
},
"auth": {
"basic": [
{
"username": "gwadmin",
"password": "password",
"readOnly": false
}
],
"bearerToken": [
{
"token": null,
"readOnly": false
}
],
"bearerJwtAzure": [
{
"tenantIdGUID": null
}
],
"bearerJwt": [
{
"secret": null,
"publicKey": null,
"options": {
"issuer": null
},
"readOnly": false
}
]
},
"certificate": {
"key": null,
"cert": null,
"ca": null,
"pfx": {
"bundle": null,
"password": null
}
},
"ipAllowList": [],
"emailOnError": {
"smtp": {
"enabled": false,
"host": null,
"port": 587,
"proxy": null,
"authenticate": true,
"username": null,
"password": null,
"sendInterval": 15,
"to": null,
"cc": null
}
}
},
"endpoint": {
"host": "hostname",
"port": 30015,
"username": "username",
"password": "password",
"saml_provider": "saml_provider_name"
}
}
Configuration file have two main JSON objects: scimgateway
and endpoint
Definitions in scimgateway
object have fixed attributes, but values can be modified. This object is used by the core functionality of the SCIM Gateway.
Definitions in endpoint
object are customized according to our plugin code. Plugin typically need this information for communicating with endpoint
port - Gateway will listen on this port number. Clients (e.g. Provisioning Server) will be using this port number for communicating with the gateway.
localhostonly - true or false. False means gateway accepts incoming requests from all clients. True means traffic from only localhost (127.0.0.1) is accepted (gateway must then be installed on the CA Connector Server).
scim.version - "1.1" or "2.0". Default is "2.0". For Symantec/Broadcom/CA Identity Manager "1.1" should be used.
scim.customSchema - filename of JSON file located in <package-root>\config\schemas
containing custom schema attributes, see configuration notes
log.loglevel.file - off, error, info, or debug. Output to plugin-logfile e.g. logs\plugin-saphana.log
log.loglevel.console - off, error, info, or debug. Output to stdout and errors to stderr.
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.
auth - Contains one or more authentication/authorization methods used by clients for accessing gateway. Methods are disabled by setting corresponding attributes to null or remove methods not used. Methods having user/object set to "readOnly": true
gives read only access (only allowing GET
requests for corresponding admin user).
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.
auth.bearerToken - Array of one or more bearer token objects - Shared token/secret (supported by Azure). Clear text value will become encrypted when gateway is started.
auth.bearerJwtAzure - Array of one or more JWT used by Azure SyncFabric. tenantIdGUID must be set to Azure Active Directory Tenant ID.
auth.bearerJwt - Array of one or more standard JWT objects. Using secret or publicKey for signature verification. publicKey should be set to the filename of public key or certificate pem-file located in <package-root>\config\certs
. Clear text secret will become encrypted when gateway is started. options.issuer is mandatory. Other options may also be included according to jsonwebtoken npm package definition.
certificate - If not using SSL/TLS certificate, set "key", "cert" and "ca" to null. When using SSL/TLS, "key" and "cert" have to be defined with the filename corresponding to the primary-key and public-certificate. Both files must be located in the <package-root>\config\certs
directory e.g:
"certificate": {
"key": "key.pem",
"cert": "cert.pem",
"ca": null
}
Example of how to make a self signed certificate:
openssl req -nodes -newkey rsa:2048 -x509 -sha256 -days 3650 -keyout key.pem -out cert.pem -subj "/O=Testing/OU=SCIM Gateway/CN=<FQDN>" -config "<path>\openssl.cnf"
<FQDN>
is Fully Qualified Domain Name of the host having SCIM Gateway installed
Note, when using Broadcom/CA Provisioning, the "certificate authority - CA" also have to be imported on the Connector Server. For self-signed certificate CA and the certificate (public key) is the same.
PFX / PKCS#12 bundle can be used instead of key/cert/ca e.g:
"pfx": {
"bundle": "certbundle.pfx",
"password": "password"
}
Note, we should normally use certificate (https) for communicating with SCIM Gateway unless we install ScimGatway locally on the manager (e.g. on the CA Connector Server). When installed on the manager, we could use http://localhost:port
or http://127.0.0.1:port
which will not be passed down to the data link layer for transmission. We could then also set {"localhostonly": true}
ipAllowList - Array of one or more IPv4/IPv6 subnets (CIDR) allowed for incoming traffic. E.g. using Azure AD as IdP, we would like to restrict access to IP addresses used by Azure AD. Azure IP-range can be downloaded from: https://azureipranges.azurewebsites.net, enter AzureActiveDirectory in the search list and select JSON download. Copy the "addressPrefixes" array content and paste into ipAllowList array. CIDR single IP-host syntax is a.b.c.d/32. Note, front-end HTTP proxy or a load balancer must include the X-Forwarded-For header. Configuration example:
"ipAllowList": [
"13.66.60.119/32",
"13.66.143.220/30",
...
"2603:1056:2000::/48",
"2603:1057:2::/48"
]
emailOnError - Contains configuration for sending error notifications by email. Note, only the first error will be sent until sendInterval have passed
emailOnError.smtp.enabled - true or false, value set to true will enable email notifications
emailOnError.smtp.host - Mailserver e.g. "smtp.office365.com"
emailOnError.smtp.port - Port used by mailserver e.g. 587, 25 or 465
emailOnError.smtp.proxy - If using mailproxy e.g. "http://proxy-host:1234"
emailOnError.smtp.authenticate - true or false, set to true will use username/password authentication
emailOnError.smtp.username - Mail account for authentication and also the sender of the email, e.g. "user@outlook.com"
emailOnError.smtp.password - Mail account password
emailOnError.smtp.sendInterval - Mail notifications on error are deferred until sendInterval minutes have passed since the last notification. Default 15 minutes
emailOnError.smtp.to - Comma separated list of recipients email addresses e.g: "someone@example.com"
emailOnError.smtp.cc - Comma separated list of cc email addresses
actions - Pre and post actions onAddGroups/onRemoveGroups. Needed logic to be defined in plugin method pre_post_Action
actions.preAction.onAddGroups - Array of groups e.g. ["Admins", "Employees"]
actions.preAction.onRemoveGroups - Array of groups e.g. ["Admins", "Employees"]
actions.postAction.onAddGroups - Array of groups e.g. ["Admins", "Employees"]
actions.postAction.onRemoveGroups - Array of groups e.g. ["Admins", "Employees"]
endpoint - Contains endpoint specific configuration according to our plugin code.
Setting environment variable SEED
will override default password seeding logic.
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.
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"
Example:
{
"scimgateway": {
...
"port": "process.env.PORT",
...
"loglevel": {
"file": "process.env.LOG_LEVEL_FILE",
...
"auth": {
"basic": [
{
"username": "process.file./var/run/vault/secrets.json",
"password": "process.file./var/run/vault/secrets.json"
},
...
],
...
},
"endpoint": {
...
"username": "process.file./var/run/vault/secrets.json",
"password": "process.file./var/run/vault/secrets.json",
...
}
}
secrets.json for plugin-forwardinc - example (dot notation):
{
"plugin-forwardinc.scimgateway.auth.basic[0].username": "gwadmin",
"plugin-forwardinc.scimgateway.auth.basic[0].password": "password",
"plugin-forwardinc.endpoint.username": "superuser",
"plugin-forwardinc.endpoint.password": "secret"
}
Custom schema attributes can be added by plugin configuration scim.customSchema
having value set to filename of a JSON schema-file located in <package-root>/config/schemas
e.g:
"scim": {
"version": "2.0",
"customSchema": "plugin-forwardinc-schema.json"
},
JSON file have following syntax:
[
{
"name": "User",
"attributes": [...]
},
{
"name": "Group",
"attributes": [...]
}
]
Where array attributes
contains custom attribute objects according to SCIM 1.1 or 2.0 spesification e.g:
"attributes": [
{
"name": "musicPreference",
"type": "string",
"multiValued": false,
"description": "Music Preferences",
"readOnly": false,
"required": false,
"caseExact": false
},
{
"name": "populations",
"type": "complex",
"multiValued": true,
"multiValuedAttributeChildName": "population",
"description": "Population array",
"readOnly": false,
"required": false,
"caseExact": false,
"subAttributes": [
{
"name": "value",
"type": "string",
"multiValued": false,
"description": "Population value",
"readOnly": false,
"required": true,
"caseExact": false
}
]
}
]
Note, custom schema attributes will be merged into core:1.0/2.0 schema, and names must not conflict with standard SCIM attribute names.
Gateway can now be started from a command window running in administrative mode
3 ways to start:
node c:\my-scimgateway
node c:\my-scimgateway\index.js
<package-root>node .
Ctrl+c to stop
Start Windows Task Scheduler (taskschd.msc), right click on "Task Scheduler Library" and choose "Create Task"
General tab:
-----------
Name = SCIM Gateway
User account = SYSTEM
Run with highest privileges
Triggers tab:
-------------
Begin the task = At startup
Actions tab:
------------
Action = Start a program
Program/script = c:\Program Files\nodejs\node.exe
Arguments = c:\my-scimgateway
Settings - tab:
---------------
Stop the task if runs longer than = Disabled (greyed out)
Verification:
<pakage-root>\logs
On Linux systems we may also run SCIM Gateway as a Docker image (using docker-compose)
Install SCIM Gateway within your own package and copy provided docker files:
mkdir /opt/my-scimgateway
cd /opt/my-scimgateway
npm init -y
npm install scimgateway --save
cp ./config/docker/* .
docker-compose.yml <== Here is where you would set the exposed port and environment
Dockerfile <== Main dockerfile
DataDockerfile <== Handles volume mapping
docker-compose-debug.yml <== Debugging
Create a scimgateway user on your Linux VM.
adduser scimgateway
Create a directory on your VM host for the scimgateway configs:
mkdir /home/scimgateway/config
Copy your updated configuration file e.g. /opt/my-scimgateway/config/plugin-loki.json to /home/scimgateway/config. Use scp to perform the copy.
NOTE: /home/scimgateway/config is where all important configuration and loki datastore will reside outside of the running docker container. If you upgrade scimgateway you won't lose your configurations and data.
Build docker images and start it up
docker-compose up --build -d
NOTE: Add the -d flag to run the command above detached.
Be sure to confirm that port 8880 is available with a simple http request
If using default plugin-loki and we have configured {"persistence": true}
, we could confirm scimgateway created loki.db:
su scimgateway
cd /home/scimgateway/config
ls loki.db
To list running containers information:
docker ps
To list available images:
docker images
To view the logs:
docker logs scimgateway
To execute command within your running container:
docker exec scimgateway <bash command>
To stop scimgateway:
docker-compose stop
To restart scimgateway:
docker-compose start
To debug running container (using Visual Studio Code):
docker-compose -f docker-compose.yml -f docker-compose-debug.yml up -d
Start Visual Studio Code and follow these debugging instructions
To upgrade scimgateway docker image (remove the old stuff before running docker-compose up --build):
docker rm scimgateway
docker rm $(docker ps -a -q); docker rmi $(docker images -q -f "dangling=true")
Using Symantec/Broadcom/CA Identity Manger, plugin configuration file must include SCIM Version "1.1" (scimgateway.scim.version).
In the Provisioning Manager we have to use
Endpoint type = SCIM (DYN Endpoint)
or create our own custom endpoint type based on this one
SCIM endpoint configuration example for Loki plugin (plugin-loki)
Endpoint Name = Loki-8880
User Name = gwadmin
Password = password
SCIM Authentication Method = HTTP Basic Authentication
SCIM Based URL = http://localhost:8880
or:
SCIM Based URL = http://localhost:8880/<baseEntity>
Username, password and port must correspond with plugin configuration file. For "Loki" plugin it will be config\plugin-loki.json
"SCIM Based URL" refer to the FQDN (or localhost) having SCIM Gateway installed. Portnumber must be included. Use HTTPS instead of HTTP if SCIM Gateway configuration includes certificates.
"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:
http://localhost:8880/clientA
http://localhost:8880/clientB
Each baseEntity should then be defined in the plugin configuration file with custom attributes needed. Please see examples in plugin-forwardinc.json
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.
Create = POST http://example.com:8880/Users
(body contains the user information)
Update = PATCH http://example.com:8880/Users/<id>
(body contains the attributes to be updated)
Search/Read = GET http://example.com:8880/Users?userName eq
"userID"&attributes=<comma separated list of scim-schema defined attributes>
Search/explore all users:
GET http://example.com:8880/Users?attributes=userName
Delete = DELETE http://example.com:8880/Users/<id>
Discovery:
GET http://example.com:8880/ServiceProviderConfigs
Specification compliance, authentication schemes, data models.
GET http://example.com:8880/Schemas
Introspect resources and attribute extensions.
Note:
Get all users (explore):
select USER_NAME from SYS.USERS where IS_SAML_ENABLED like 'TRUE';
Get a specific user:
select USER_NAME, USER_DEACTIVATED from SYS.USERS where USER_NAME like '<UserID>';
Create User:
CREATE USER <UserID> WITH IDENTITY '<UserID>' FOR SAML PROVIDER <SamlProvider>;
Delete user:
DROP USER <UserID>;
Modify user (enable user):
ALTER USER <UserID> ACTIVATE;
Modify user (disable user):
ALTER USER <UserID> DEACTIVATE;
Postinstallation:
cd c:\my-scimgateway
npm install hdb --save
Only SAML users will be explored and managed
Supported template attributes:
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.
SAP Hana converts UserID to uppercase. Provisioning use default lowercase. Provisioning template should therefore also convert to uppercase.
User Name = %$$TOUPPER(%AC%)%
Using plugin-azure-ad we could do user provisioning towards Azure AD including license management e.g. O365
For testing purposes we could get an Azure free account and in addition the free Office 365 for testing license management through Azure.
There are two alternative ways of configuring Azure AD. Alternative #1 is probably best and easiest
For some odd reasons Application needs to be member of "User Administrator" for having privileges to manage office/mobile phone on users that is member of any administrator roles
Also note, enable/disable user (accountEnabled - through Graph API) will fail if user have an "Administrator" role other than above mentioned "User Administrator" e.g. "Group Administrator"/"Application Administrator". To be sure we can enable/disable all users, Application needs to be member of "Company Administrator" - 62e90394-69f5-4237-9190-012177145e10. Configuration below using "User Administrator"
Edit index.js
Uncomment startup of plugin-azure-ad, other plugins could be comment out if not needed
const azureAD = require('./lib/plugin-azure-ad')
Edit plugin-azure-ad.json
Note, for Symantec/Broadcom/CA Provisioning we have to use SCIM version 1.1
scimgateway: {
"scim": {
"version": "1.1"
},
username
and password
used to connect the SCIM Gateway must be defined.
"auth": {
"basic": [
{
"username": "gwadmin",
"password": "password",
"readOnly": false
}
],
Update tenantIdGUID
, clientID
and clientSecret
according to what you copied from the previous Azure AD configuration.
If using proxy, set proxy.host to "http://<FQDN-ProxyHost>:<port>"
e.g "http://proxy.mycompany.com:3128"
"endpoint": {
"entity": {
"undefined": {
"tenantIdGUID": "DomainName or DirectoryID (GUID)",
"clientId": "Application ID",
"clientSecret": "Generated application key value",
"proxy": {
"host": null,
"username": null,
"password": null
}
}
}
}
Note, clientSecret and any proxy.password will become encrypted in this file on the first Azure connection.
For multi-tenant or multi-endpoint support, we may add several entities:
"endpoint": {
"entity": {
"undefined": {
...
},
"clientA": {
...
},
"clientB": {
...
}
}
}
For additional details, see baseEntity description.
Note, we should normally use certificate (https) for communicating with SCIM Gateway unless we install gateway locally on the manager (e.g. on the CA Connector Server). When installed on the manager, we could use http://localhost:port
or http://127.0.0.1:port
which will not be passed down to the data link layer for transmission. We could then also set {"localhostonly": true}
Create a new endpoint type "Azure - ScimGateway"
index.js
plugin-azure-ad.json
must also be knownNote, metafile "Azure - ScimGateway.xml" is based on CA "Azure - WSL7" with some minor adjustments like using Microsoft Graph API attributes instead of Azure AD Graph attributes.
Provisioning Manager configuration
Endpoint type = Azure - ScimGateway (DYN Endpoint)
Endpoint configuration example:
Endpoint Name = AzureAD-8881
User Name = gwadmin
Password = password
SCIM Authentication Method = HTTP Basic Authentication
SCIM Based URL = http://localhost:8881
or
SCIM Based URL = http://localhost:8881/<baseEntity>
For details, please see section "CA Identity Manager as IdP using SCIM Gateway"
Azure AD could do automatic user provisioning by synchronizing users towards SCIM Gateway, and gateway plugins will update endpoints.
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:
scimgateway: {
"scim": {
"version": "2.0",
...
},
...
"auth": {
"bearerToken": [
{
"token": "shared-secret"
}
],
"bearerJwtAzure": [
{
"tenantIdGUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
]
}
...
}
token
configuration must correspond with "Secret Token" defined in Azure AD
tenantIdGUID
configuration must correspond with Azure Active Directory Tenant ID
In Azure Portal:
Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Secret Token
Note, when "Secret Token" is left blank, Azure will use JWT (tenantIdGUID)
Azure-Azure Active Directory-Overview-Tenant ID
User mappings attributes between AD and SCIM also needs to be configured
Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Edit attribute mappings-Mappings
Azure AD default SCIM attribute mapping for USER must have:
userPrincipalName mapped to userName (matching precedence #1)
Azure AD default SCIM attribute mapping for GROUP must have:
displayName mapped to displayName (matching precedence #1)
members mapped to members
Some notes related to Azure AD:
Azure Active Directory SCIM documentation
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
Azure AD do a regular check for a "none" existing user/group. This check seems to be a "keep alive" to verify connection.
Azure AD first checks if user/group exists, if not exist they will be created (no explore of all users like CA Identity Manager)
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.
Gateway also works as an API Gateway when using url /api
or /<baseEntity>/api
Following methods for the none SCIM based api-plugin are supported:
GET /api
GET /api?queries
GET /api/{id}
POST /api + body
PUT /api/{id} + body
PATCH /api/{id} + body
DELETE /api/{id}
Please see example plugin: plugin-api.js
For JavaScript coding editor you may use Visual Studio Code
Preparation:
lib\plugin-loki.js
and config\plugin-loki.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)let mine = require('./lib/plugin-mine');
Now we are ready for custom coding by editing plugin-mine.js 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).
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)
CA Provisioning using default SCIM endpoint do not support SCIM Enterprise User Schema Extension (having attributes like employeeNumber, costCenter, organization, division, department and manager). If we need these or other attributes not found in CA Provisioning, we could define our own by using the free-text "type" definition in the multivalue entitlements or roles attribute. In the template entitlements definition, we could for example define type=Company and set value to %UCOMP%. Please see plugin-forwardinc.js using Company as a multivalue "type" definition.
Using CA Connector Xpress we could create a new SCIM endpoint type based on the original SCIM. We could then add/remove attributes and change from default assign "user to groups" to assign "groups to user". There are also other predefined endpoints based on the original SCIM. You may take a look at "ServiceNow - WSL7" and "Zendesk - WSL7".
For project setup:
Using Connector Xpress based on the original SCIM endpoint.
Delete defaults:
Group - Associations - with User Account
Group - Attributes - members
User Account - Attributes - Group Membership
Create new attribute:
User Account - Attributes: Groups - Flexi DN - Multivalue - groups
Create User - Group associations:
User Account - Accociations - Direct association with = Group
User Account - Accociations - with Group
Note, "Include a Reverse Association" - not needed if we don't need Group object functionality e.g list/add/remove group members
User Attribute = Physical Attribute = Groups
Match Group = By Attribute = ID
Objects Must Exist
Use DNs in Attribute = activated (toggled on)
Include a Reverse Association (if needed)
Group Attribute = Virtual Attribute = User Membership
Match User Account = By Attribute = User Name
Note, groups should be capability attribute (updated when account is synchronized with template):
advanced options - Synchronized = enabled (toggled on)
Plugins should have following initialization:
// mandatory plugin initialization - start
const path = require('path')
let ScimGateway = null
try {
ScimGateway = require('scimgateway')
} catch (err) {
ScimGateway = require('./scimgateway')
}
let scimgateway = new ScimGateway()
let pluginName = path.basename(__filename, '.js')
let configDir = path.join(__dirname, '..', 'config')
let configFile = path.join(`${configDir}`, `${pluginName}.json`)
let config = require(configFile).endpoint
let validScimAttr = [] // empty array - all attrbutes are supported by endpoint
// add any external config process.env and process.file
config = scimgateway.processExtConfig(pluginName, config)
// mandatory plugin initialization - end
scimgateway.exploreUsers = async (baseEntity, attributes, startIndex, count) => {
let ret = {
"Resources": [],
"totalResults": null
}
...
return ret
}
<baseurl>/client1
gives baseEntity=client1)scimgateway.exploreGroups = async (baseEntity, attributes, startIndex, count) => {
let ret = {
"Resources": [],
"totalResults": null
}
...
return ret
}
scimgateway.getUser = async (baseEntity, getObj, attributes) => {
...
return userObj
}
{ filter: <filterAttribute>, identifier: <identifier> }
Note, the value of the id attribute returned will be used by modifyUser and deleteUser
scimgateway.createUser = async (baseEntity, userObj) => {
...
return null
}
scimgateway.deleteUser = async (baseEntity, id) => {
...
return null
}
scimgateway.modifyUser = async (baseEntity, id, attrObj) => {
...
return null
}
scimgateway.getGroup = async (baseEntity, getObj, attributes) => {
...
return retObj
}
getObj = { filter: <filterAttribute>, identifier: <identifier> }
e.g: getObj = { "filter": "displayName", "identifier": "GroupA" }
filter: displayName and id must be supported
attributes = scim attributes to be returned. If no attributes defined, all should be returned.
return retObj: retObj containing group displayName and id (+ members if using default "users are member of group")
eg. using default "users are member of group":
{"displayName":"Admins","id":"Admins","members":[{"value":"bjensen","display":"bjensen"]}
eg. using "groups are member of user":
{"displayName":"Admins","id":"Admins"}
If we do not support groups, callback(null, null)
scimgateway.getGroupMembers = async (baseEntity, id, attributes) => {
let arrRet = []
...
return arrRet
}
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.
id = user id (eg. bjensen)
attributes = scim attributes to be returned as object in array
arrRet = array containing the objects of id, displayName and members where members value only include current user id on the format:
{ id: > , displayName: , members [{value: }] }
[
{"id": "Admins", "displayName: "Admins", "members": [{"value": "bjensen"}]},
{"id": "Employees", "displayName: "Employees", "members": [{"value": "bjensen"}]}
]
If "user member of groups" not supported, then return []
scimgateway.getGroupUsers = async (baseEntity, id, attributes) => {
let arrRet = []
...
return arrRet
}
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
id = group id (eg. UserGroup-1)
attributes = scim attributes to be returned as object in array
arrRet = array containing the objects of userName and groups.value e.g:
[
{"userName", "bjensen": [{"value": "UserGroup-1"}]},
{"userName", "jsmith"}: [{"value": "UserGroup-1"}]}
]
If "group member of users" not supported, then return []
scimgateway.createGroup = async (baseEntity, groupObj) => {
...
return null
})
scimgateway.deleteGroup = async (baseEntity, id) => {
...
return null
}
scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
...
return null
}
Installation gives error messages related to the module soap optional dependency to 'ursa' that also includes 'node-gyp'. These error messages can be ignored unless soap WSSecurityCert functionality is needed in custom plugin code.
SCIM filtering only supports operator 'eq' returning unique object only, example:
/Users?filter=userName eq "bjensen"&attributes=userName,id,name.givenName
/Users?filter=emails.value eq "bjensen@example.com"&attributes=userName,phoneNumbers
MIT © Jarle Elshaug
[Fix]
[Fix]
find
instead of findOne
to ensure returning unique user[Fix]
groups
(when "user member of groups"
) e.g. GET /Users/bjensen should return all user attributes including the virtual groups
attribute. Now this user attribute will be automatically handled by scimgateway if not included in the plugin response.[Added]
[Fix]
[Added]
[Added]
ipAllowList for restricting access to allowlisted IP addresses or subnets e.g. Azure AD IP-range
Configuration example:
"ipAllowList": [
"13.66.60.119/32",
"13.66.143.220/30",
...
"2603:1056:2000::/48",
"2603:1057:2::/48"
]
Example plugins now configured for SCIM v2.0 instead of v1.1
New configuration:
"scim": {
"version": "2.0"
}
Old configuration:
"scim": {
"version": "1.1"
}
[Added]
pre_post_Action
[Fixed]
[Fixed]
[Fixed]
[Added]
[Fixed]
[Fixed]
[Added]
getApi supports body (apiObj).
Old syntax:
scimgateway.getApi = async (baseEntity, id, apiQuery) => {
New syntax:
scimgateway.getApi = async (baseEntity, id, apiQuery, apiObj) => {
[MAJOR]
scimgateway.scim.customUniqueAttrMapping
- replaced by getObj logic[UPGRADE]
Note, this is a major upgrade (^2.x.x => ^3.x.x) that will brake compatibility with any existing custom plugins. To force a major upgrade, suffix @latest
must be include in the npm install command, but it's recommended to do a fresh install and copy any custom plugins instead of upgrading an existing package
Old syntax:
scimgateway.getUser = async (baseEntity, userName, attributes) => {
scimgateway.getGroup = async (baseEntity, displayName, attributes) => {
scimgateway.modifyGroupMembers = async (baseEntity, id, members) => {
New syntax:
scimgateway.getUser = async (baseEntity, getObj, attributes) => {
const userName = getObj.identifier // gives v2.x compatibility
scimgateway.getGroup = async (baseEntity, getObj, attributes) => {
const displayName = getObj.identifier // gives v2.x compatibility
scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
// attrObj.members corresponds to members in deprecated modifyGroupMembers
getUser comments:
getObj = { filter: <filterAttribute>, identifier: <identifier> }
e.g: getObj = { filter: 'userName', identifier: 'bjensen'}
filter: userName and id must be supported
getGroup comments:
getObj = { filter: <filterAttribute>, identifier: <identifier> }
e.g: getObj = { filter: 'displayName', identifier: 'GroupA' }
filter: displayName and id must be supported
Please see provided example plugins
Using the new getObj parameter gives more flexibility in the way of lookup a user e.g:
http://localhost:8880/Users?filter=emails.value eq "jsmith@example.com"&attributes=userName,name.givenName
getObj = { filter: 'emails.value', identifier: 'jsmith@example.com'}
attributes = 'userName,name.givenName'
Configuration file, auth settings have changed and now using arrays allowing more than one user/object to be set. "readOnly": true
can also be set for allowing read only access for a spesific user (does not apply to bearerJwtAzure).
New syntax is:
"auth": {
"basic": [
{
"username": "gwadmin",
"password": "password",
"readOnly": false
}
],
"bearerToken": [
{
"token": null,
"readOnly": false
}
],
"bearerJwtAzure": [
{
"tenantIdGUID": null
}
],
"bearerJwt": [
{
"secret": null,
"publicKey": null,
"options": {
"issuer": null
},
"readOnly": false
}
]
}
[Fixed]
[Fixed]
[Added]
Log masking of custom defined attributes.
customMasking may include an array of attributes to be masked
e.g. "customMasking": ["SSN", "weight"]
Note, configurationfiles must be changed (old syntax still supported)
old syntax:
"loglevel": {
"file": "debug",
"console": "error"
},
new syntax:
"log": {
"loglevel": {
"file": "debug",
"console": "error"
},
"customMasking": []
},
By default SCIM Gateway includes masking of standard attributes like password
[Fixed]
Thanks to Luca Moretto
[Fixed]
[Fixed]
url.parse
[Fixed]
err.name = 'DuplicateKeyError'
) when failing on creating a duplicate user[Added]
[Fixed]
err.name = 'DuplicateKeyError'
now gives correct status code 409 instead of defult 500 (see plugin-loki.js)[Fixed]
loglevel.colorize
is not needed)[Fixed]
[Added]
[Fixed]
[Added]
scim.customSchema
having value set to filename of a JSON schema-file located in <package-root>/config/schemas
[UPGRADE]
Configurationfiles for custom plugins should be changed
old syntax:
"scimversion": "1.1",
new syntax:
"scim": {
"version": "1.1",
"customSchema": null
},
Note, "1.1" is default, if using "2.0" the new syntax must be used.
[Fixed]
[MAJOR]
[UPGRADE]
Note, this is a major upgrade (^1.x.x => ^2.x.x) and will brake compatibility with any existing custom plugins. To force a major upgrade, suffix @latest
must be include in the npm install command, but it's recommended to do a fresh install and copy any custom plugins instead of upgrading an existing package
cd c:\my-scimgateway
npm install scimgateway@latest
Custom plugins needs some changes (please see included example plugins)
scimgateway.on(xxx, function (..., callback)
replaced with scimgateway.xxx = async (...)
returning a result or throwing an errordoRequest
method having endpoint failover logic through array baseUrls/baseServiceEndpoints
defined in corresponding plugin configuration file.attributes
included in exploreUsers and exploreGroups method[Fixed]
[UPGRADE]
getUser
and getGroup
methods needs to be updated. If user/group not found then return callback(null, null)
instead of callback(err)[Fixed]
[Added]
[UPGRADE]
Configurationfiles for custom plugins should be changed
old syntax:
loglevel: "debug"
new syntax:
"loglevel": {
"file": "debug",
"console": "error",
"colorize": true
}
[Fixed]
[Fixed]
[UPGRADE]
[Fixed]
[Fixed]
[Fixed]
[Added]
[Added]
[UPGRADE]
Configuration files for custom plugins must include the emailOnError object for enabling error notifications by email. Please see the syntax in provided example plugins and details described in the "Configuration" section of this document.
[Added]
[Fixed]
[Added]
[Fixed]
Thanks to ywchuang
[Added]
[Fixed]
[Fixed]
[Fixed]
[Fixed]
[Added]
[UPGRADE]
Method getGroupMembers
must be updated for all custom plugins
Replace:
scimgateway.on('getGroupMembers', function (baseEntity, id, attributes, startIndex, count, callback) {
...
let ret = {
'Resources' : [],
'totalResults' : null
}
...
ret.Resources.push(userGroup)
...
callback(null, ret)
With:
scimgateway.on('getGroupMembers', function (baseEntity ,id ,attributes, callback) {
...
let arrRet = []
...
arrRet.push(userGroup)
...
callback(null, arrRet)
[Added]
[Added]
[UPGRADE]
config/plugin-xxx.json
needs to be updated regarding the new scimgateway.auth
section:
[Added]
config\docker
includes docker configuration examples[Added]
SEED
overrides default password seeding"process.env.XXX"
lets environment variable XXX define the port[UPGRADE]
config/plugin-xxx.json
needs to be updated:
[Added]
plugin-loki
plugin-testmode
[UPGRADE]
lib/plugin-testmode.js
and config/plugin-testmode.json
[Fixed]
[Added]
Mocha test scripts for automated testing of plugin-testmode
Automated tests run on Travis-ci.org (click on build badge)
Thanks to Jeffrey Gilbert
[Fixed]
[Added]
[UPGRADE]
[Fixed]
[Added]
[Added]
[UPGRADE]
scimgateway.on('createGroup',...
Please see example plugins for details.[Fixed]
[Added]
plugin-mssql
scimgateway.getArrayObject
[Fixed]
[Fixed]
[UPGRADE]
scimgateway.on('getGroupMembers',...
New arguments have been added "startIndex" and "count". Also a new return variable "ret". Please see example plugins for details.[Fixed]
[Added]
plugin-restful
[Added]
<Base URL>/[baseEntity]
for multi tenant or multi endpoint flexibility[UPGRADE]
[Doc]
[Fixed]
Initial version
FAQs
Using SCIM protocol as a gateway for user provisioning to other endpoints
The npm package scimgateway receives a total of 1,806 weekly downloads. As such, scimgateway popularity was classified as popular.
We found that scimgateway demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
Security News
Socket CEO Feross Aboukhadijeh discusses the open web, open source security, and how Socket tackles software supply chain attacks on The Pair Program podcast.
Security News
Opengrep continues building momentum with the alpha release of its Playground tool, demonstrating the project's rapid evolution just two months after its initial launch.