ScimGateway

Build Status npm Versionnpm Downloads chat disqus GitHub forks


Author: Jarle Elshaug

Validated on:

Latest news:

Overview

With ScimGateway we could do user management by using REST based SCIM protocol, and the gateway will translate and communicate towards destinations using endpoint specific protocols.

ScimGateway is a standalone product, however this document shows how the gateway could be used by products like CA Identity Manager.

Using CA 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 ScimGateway 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 ScimGateway plugins. ScimGateway works with IM as long as IM supports SCIM.

ScimGateway is based on the popular asynchronous event driven framework Node.js using javascripts. It is firewall friendly using REST webservices. Runs on almost all operating systems, and may loadbalance between hosts (horizontal) and cpu's (vertical). Could even be uploaded and run as a cloud application.

Following example plugins are included:

Installation

Install Node.js

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.

Install ScimGateway

Open a command window (run as administrator)
Create your own package directory e.g. C:\my-scimgateway and install ScimGateway 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 be 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.

Startup and verify default Loki plugin

node c:\my-scimgateway

Start a browser
http://localhost:8880/Users?attributes=userName  

Logon using gwadmin/password and two users should be listed  

http://localhost:8880/Users/bjensen
Lists all user attributes for specified user

"Ctrl + c" to stop the scimgateway

For more functionality using browser (post/patch/delete) a REST extension/add-on is needed.

Upgrade ScimGateway

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.

Upgrade to latest version:

cd c:\my-scimgateway
npm install [email protected]

Note, always backup/copy C:\my-scimgateway before upgrading. Custom plugins and corresponding configuration files will not be affected.

Configuration

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 api = require('./lib/plugin-api')
// const azureAD = require('./lib/plugin-azure-ad')

Each endpoint plugin needs a javascript file (.js) and a configuration file (.json). They both must have the same naming suffix. 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": {
    "scimversion": "1.1",
    "loglevel": "debug",
    "localhostonly": false,
    "port": 8884,
    "auth": {
      "basic": {
        "username": "gwadmin",
        "password": "password"
      },
      "bearer": {
        "token": null,
        "jwt": {
          "azure": {
            "tenantIdGUID": null
          },
          "standard": {
            "secret": null,
            "publicKey": null,
            "options": {
              "issuer": null
            }
          }
        }
      }
    },
    "certificate": {
      "key": null,
      "cert": null,
      "ca": null,
      "pfx": {
        "bundle": null,
        "password": null
      }
    }
  },
  "endpoint": {
    "host": "hostname",
    "port": 30015,
    "username": "username",
    "password": "password",
    "saml_provider": "saml_provider_name"
  }
}

Attribute explanation:

Definitions under "scimgateway" have fixed attributes but we can change the values. This section is used by the core functionality of the ScimGateway.

Definitions under "endpoint" are used by endpoint plugin for communicating with endpoint and needs to be defined according to our code.

(**) Both port number and password encryption seed may be overridden by setting environment variables before starting the gateway. Setting environment variable SEED will override default password seed. Setting the ScimGateway port in the configuration file to "process.env.XXX" where XXX is the environment variable let gateway use environment variable for port configuration. This could be useful in cloud systems e.g:

    "scimgateway": {
        ...
        "port": "process.env.PORT",
        ...
    }

Manual startup

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 to stop

Automatic startup - Windows Task Scheduler

Start Windows Task Scheduler (taskschd.msc), right click on "Task Scheduler Library" and choose "Create Task"

General tab:  
-----------
Name = ScimGateway
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:

Running as a isolated virtual Docker container

On Linux systems we may also run ScimGateway as a Docker image (using docker-compose)

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")  

CA Provisioningserver - SCIM Endpoint

Using the CA Provisioning Manager we have to configure

Endpoint type = SCIM (DYN Endpoint)

SCIM endpoint configuration example for Loki plugin (plugin-loki)

Endpoint Name = Loki  
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 ScimGateway installed. Portnumber must be included. Use HTTPS instead of HTTP if ScimGateway-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.

ScimGateway REST API

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:

SAP Hana endpoint

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%)%

Azure Active Directory endpoint

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.

Azure AD prerequisites

Application needs to be member of "User Account Administrator" when running behalf of application rather than user

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

Username and password used to connect the ScimGateway must be defined.

Update tenantIdGUID, clientID and clientSecret according to Azure AD prerequisites configuration.

If using proxy, set proxy to "http://<FQDN-ProxyHost>:<port>" e.g "http://proxy.mycompany.com:3128"

"endpoint": {
  "entity": {
    "undefined": {
      "proxy": null,
      "tenantIdGUID": "DomainName or DirectoryID (GUID)",
      "clientId": "Application ID",
      "clientSecret": "Generated application key value"
    }
  }
}

Note, clientSecret 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 ScimGateway 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}

For CA Provisioning, create endpoint type "Azure - ScimGateway"

Note, 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.

Using the CA Provisioning Manager we have to configure

Endpoint type = Azure - ScimGateway (DYN Endpoint)

Endpoint configuration example:

Endpoint Name = Azure-AD-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 Provisioningserver - SCIM Endpoint"

Azure Active Directory using ScimGateway

Azure AD could do automatic user provisioning by synchronizing users towards ScimGateway, and ScimGateway plugins will update endpoints.

Plugin configuration file must include scimversion "2.0" and either bearer.token or azure.tenantIdGUID (or both):

{
  "scimversion": "2.0",
  ...
  "auth": {
    ...
    "bearer": {
      "token": "shared-secret",
      "jwt": {
        "azure": {
          "tenantIdGUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        },
    ...
  },
  ...
}

bearer.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-Properties-Directory ID

User mappings attributes between AD and SCIM also needs to be configured

Azure-Azure Active Directory-Enterprise Application-<My Application>-Provisioning-Mappings

Azure AD default SCIM attribute mapping for USER have:

externalId mapped to mailNickname (matching precedence #1)  
userName mapped to userPrincipalName  

ScimGateway accepts externalId (as matching precedence) instead of userName, but userName and externalId must be mapped to the same AD attribute e.g:

externalId mapped to mailNickname (matching precedence #1)  
userName mapped to mailNickname  

or:  

externalId mapped to userPrincipalName (matching precedence #1)  
userName mapped to userPrincipalName  

Azure AD default SCIM attribute mapping for GROUP have:

externalId mapped to displayName (matching precedence #1)  
displayName mapped to mailNickname  

ScimGateway accepts externalId (as matching precedence) instead of displayName, but displayName and externalId must then be mapped to the same AD attribute e.g:

externalId mapped to displayName (matching precedence #1)
displayName mapped to displayName

Some notes related to Azure AD:

api-plugin

ScimGateway supports following methods for the none SCIM based api-plugin:

    GET /api  
    GET /api?queries  
    GET /api/{id}  
    POST /api + body  
    PUT /api/{id} + body  
    PATCH /api/{id} + body  
    DELETE /api/{id}  

How to build your own plugins

For javascript coding editor you may use Visual Studio Code

Preparation:

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).

  1. Turn off group functionality in getGroup, getGroupMembers, getGroupUsers and modifyGroupMembers
    Please see callback definitions in plugin-saphana that do not use groups.
  2. exploreUsers (test provisioning explore users)
  3. getUser (test provisioning retrieve account)
  4. createUser (test provisioning new account)
  5. deleteUser (test provisioning delete account)
  6. modifyUser (test provisioning modify account)
  7. exploreGroups (test provisioning explore groups)
  8. Turn on group functionality (if supporting groups)
  9. getGroup (test provisioning group list groups)
  10. getGroupMembers (test provisioning retrieve account - group list groups)
  11. modifyGroupMembers (test provisioning retrieve account - group add/remove groups)
  12. getGroupUsers (if using "groups member of user")
  13. createGroup (test provisioning new group)

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:

How to change "user member of groups" to "groups member of user"

Using Connector Xpress based on the original SCIM endpoint.

Delete defaults:
Group - Associations - with User Account
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 = Name

Objects Must Exist
Use DNs in Attribute = deactivated (toggled off)

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)

Methods

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
// mandatory plugin initialization - end

exploreUsers

scimgateway.on('exploreUsers', function (baseEntity, startIndex, count, callback) {
    var ret = {
        "Resources": [],
        "totalResults": null
    }
    ...
    callback(error, ret)
})  

exploreGroups

scimgateway.on('exploreGroups', function (baseEntity, startIndex, count, callback) {
    var ret = {
        "Resources": [],
        "totalResults": null
    }
    ...
    callback(error, ret);
})  

getUser

scimgateway.on('getUser', function (baseEntity, userName, attributes, callback) {
    ...
    callback(error, userObj)
})  

Note, CA Provisioning use two types of "getUser"

  1. Check if user exist: attributes=userName and/or id
  2. Retrive user: attributes=list of all attributes

createUser

scimgateway.on('createUser', function (baseEntity, userObj, callback) {
    ...
    callback(error)
}) 

deleteUser

scimgateway.on('deleteUser', function (baseEntity, id, callback) {
    ...
    callback(error)
}) 

modifyUser

scimgateway.on('modifyUser', function (baseEntity, id, attrObj, callback) {
    ...
    callback(error)
}) 

getGroup

scimgateway.on('getGroup', function (baseEntity, displayName, attributes, callback) {
    ...
    callback(error, retObj)
}) 

getGroupMembers

scimgateway.on('getGroupMembers', function (baseEntity, id, attributes, callback) {
    let arrRet = []
    ...
    callback(error, arrRet)
})

Retrieve all users for a spesific group WHEN "user member of group". This setting is CA IM default scim endpoint configuration. This means Group having multivalue attribute members containing userName.

getGroupUsers

scimgateway.on('getGroupUsers', function (baseEntity, groupName, attributes, callback) {
    let arrRet = []
    ...
    callback(error, arrRet)
})

Retrieve all users for a spesific group WHEN "group member of user". This means user having multivalue attribute groups containing value GroupName

createGroup

scimgateway.on('createGroup', function (baseEntity, groupObj, callback) {
    ...
    return callback(error)
})

modifyGroupMembers

scimgateway.on('modifyGroupMembers', function (baseEntity, id, members, callback) {
    ...
    return callback(error)
})

Known limitations

License

MIT

Change log

v1.0.1

[FIX]

v1.0.0

[ENHANCEMENT]

[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)

v0.5.3

[ENHANCEMENT]

v0.5.2

[ENHANCEMENT]

[UPGRADE]

v0.4.6

[ENHANCEMENT]

v0.4.5

[ENHANCEMENT]

[UPGRADE]

v0.4.4

[ENHANCEMENT]

[UPGRADE]

v0.4.2

[Fix]

v0.4.1

[ENHANCEMENT]

[Fix]

v0.4.0

[ENHANCEMENT]

[UPGRADE]

v0.3.8

[Fix]

v0.3.7

[ENHANCEMENT]

v0.3.6

[ENHANCEMENT]

[UPGRADE]

v0.3.5

[Fix]

v0.3.4

[ENHANCEMENT]

[Fix]

v0.3.3

[Fix]

[UPGRADE]

v0.3.2

[Fix]

v0.3.1

[ENHANCEMENT]

v0.3.0

[ENHANCEMENT]

[UPGRADE]

v0.2.2 - v0.2.8

[Doc]

v0.2.1

[Fix]

v0.2.0

Initial version