Copyright 2015-2018, Tremolo Security, Inc.
Introduction
ScaleJS is a collection of AngualrJS applications and RESTful web services that provides a mechanism for users and applications to interact with OpenUnison. Each application provides a specific function that is common in identity management:
-
ScaleJS Main - Primary ScaleJS Application
-
Update the user’s profile
-
Links to authorized applications
-
Request access to applications/roles/workflows
-
Approve or reject requests
-
View reports
-
-
ScaleJS Token - View tokens used for authentication
-
Temporary passwords
-
Time based One Time Passwords (TOTP)
-
-
ScaleJS Single Request - Request a single workflow
-
Useful when a user doesn’t have access to ScaleJS
-
-
ScaleJS Register - User registration
-
Either self registration or administrator registration
-
Google reCaptcha (https://www.google.com/recaptcha/intro/index.html)
-
Ability to pre-set password
-
Input validation
-
-
ScaleJS Password - Set/Reset a user’s password
-
Validation on typical and custom validation rules
-
-
ScaleJS Operators - Provides an "operators" view of ScaleJS Main
-
Lookup users by their attribute inside of data stores
-
Run workflows directly on found users without using the ScaleJS Main "Shopping Cart"
-
Edit users directly
-
Deployment Overview
Each ScaleJS application consists of two components:
-
Static HTML & JavaScripts
-
OpenUnison filters
The static pages contain no configuration or secret information, so may be hosted anywhere. The filter must be hosted on a specific sub directory to the static pages. For instance, for the ScaleJS main application if the static pages are accessed by going to https://app.host.com/scale/index.html then the web services must be accessible at https://app.host.com/scale/main. Each application names its web services differently which is noted in the individual application configurations below.
Authentication
ScaleJS doesn’t perform any authentication outside of OpenUnison’s built in capabilities. The basic guidelines to follow are:
-
Use the same authentication level for both the static pages and the web services
-
If separating the HTML and web services, use the OAuth2 authentication mechanism (see below)
-
Treat ScaleJS as any other PII (Personally Identifiable Information) focussed application
Authorization
OpenUnison’s built in authorization features are all honored by ScaleJS including:
-
URL level - Do I have access to this URL?
-
Organization Level - Do I have access to the organization this report or workflow is contained in?
-
Dynamic Workflows - Have I changed or tampered with the parameters for a dynamic workflow?
Deployment Models
Both of ScaleJS' components can be hosted either locally inside of OpenUnison or remotely. Each has its own advantages and implications.
HTML Pages
The static pages are stateless and contain only execution code. This makes its deployment very flexible.
Local
The static pages can be stored directly in the OpenUnison web application or war file. If deploying using Maven (recommended) then adding these pages means copying them into the src/main/webapp directory of your Maven project. This model offers some advantages:
-
Single source tree for both OpenUnison and ScaleJS
-
war files are portable across environments
When using this method, its important to configure the URL that stores ScaleJS' static content to NOT have the proxyTo option:
<url regex="false" authChain="Default Login Form" overrideHost="false" overrideReferer="false">
<host>demoidp.tremolo.lan</host>
<filterChain>
<!-- Tell the browser not to cache the pages -->
<filter class="com.tremolosecurity.proxy.filters.SetNoCacheHeaders"/>
</filterChain>
<uri>/scale</uri>
<results>
<auSuccess></auSuccess>
<auFail>Default Login Failure</auFail>
<azSuccess></azSuccess>
<azFail>Default Login Failure</azFail>
</results>
<azRules>
<rule scope="dn" constraint="dc=domain,dc=com"/>
</azRules>
</url>
Proxied
While the pages may be static, there’s no reason why they can’t be hosted on another server. This can be a web server or even object storage such as Amazon S3. The only difference between this model and the local model is the proxyTo must be specified to tell OpenUnison where to get the pages from.
RESTful Services
The RESTful services can be deployed either within the same OpenUnison as the static pages or in the a separate instance.
Local
When deploying in the same instance of OpenUnison there’s nothing special to do. Configure the filter associated with the application on the correct URL. This offers a simpler deployment model, but keeps authentication in the same system where you are storing the keys to identity information.
Proxied
In a production environment, its recommended that OpenUnison host the web services and workflows on one instance and the static pages and authentication on another instance. This way, should the proxy be compromised then the keys to the provisioning system would not be at risk.
The easiest way to do this is to setup a URL on the reverse proxy with a LastMile configuration that will generate an OAuth2 Bearer Token compatible header:
<url regex="false" authChain="Default Login Form" overrideHost="true" overrideReferer="true">
<host>demoidp.tremolo.lan</host>
<filterChain>
<filter class="com.tremolosecurity.proxy.filters.HideCookie"/>
<filter class="com.tremolosecurity.proxy.filters.LastMile">
<param name="encKeyAlias" value="lastmile-oauth2"/>
<param name="encKeyPass" value=""/>
<param name="timeScew" value="90000"/>
<param name="headerName" value="Authorization"/>
<param name="roleAttribute" value=""/>
<param name="createHeaders" value="false"/>
<param name="userAttribute" value="userPrincipalName"/>
<param name="keyStore" value=""/>
<param name="lastMileType" value="apache"/>
<param name="ignoreURI" value=""/>
<param name="verifyOnly" value="false"/>
<param name="postValidateClassName" value=""/>
<param name="headerPrefix" value="Bearer"/>
<param name="attribs" value="userPrincipalName=userPrincipalName"/>
</filter>
</filterChain>
<uri>/scale/main</uri>
<proxyTo>https://ws.host${fullURI}</proxyTo>
<results>
<auSuccess></auSuccess>
<auFail></auFail>
<azSuccess></azSuccess>
<azFail></azFail>
</results>
<azRules>
<rule scope="dn" constraint="dc=domain,dc=com"/>
</azRules>
</url>
Then when configuring the OpenUnison on ws.host make sure that the authentication chain for the host uses the OAuth2 mechanism to validate the request.
Application Configuration
ScaleMain
The main ScaleJS application is the heart of ScaleJS where most user interaction happens. Deployment requires that OpenUnison is configured with provisioning enabled. Assuming that you plan for ScaleJS to run in "/scale":
-
Create a URL in OpenUnison called /scale with the proxyTo tag empty
-
Create a URL in OpenUnison called /scale/main with the below filter configuration and the proxyTo tag empty
-
Create an application in OpenUnison separate from ScaleJS Main, and with its own session cookie, with the CheckSession filter enabled and pointing to the ScaleJS Main application on /scale/sessioncheck
The sessioncheck url is used to determine if the user’s session is still active and to warn the user when the session is about to expire.
<filter class="com.tremolosecurity.scalejs.ws.ScaleMain">
<!-- The name of the attribute that stores the value to be displayed when referencing the currently logged in user, ie cn or displayName -->
<param name="displayNameAttribute" value="displayName"/>
<!-- The title to show on the home page -->
<param name="frontPage.title" value="Azure Cloud Linux Management"/>
<!-- Sub text for the home page -->
<param name="frontPage.text" value="Use this portal as the gateway for accessing your linux servers and requesting access to systems."/>
<!-- Determines if a user can be edited -->
<param name="canEditUser" value="true"/>
<!-- Determine if users can delegate requests (yes,no or custom) -->
<param name="canDelegate" value="yes" />
<!-- Determine if users can attempt to pre-approve requests (yes,no or custom) -->
<param name="canPreApprove" value="yes" />
<!-- The name of the workflow to run when a user submits an update request -->
<param name="workflowName" value="ipa-update-sshkey"/>
<!-- When the below number of minutes are left in the user's session, warn the user -->
<parma name="warnMinutesLeft" value="5" />
<!-- For each attribute, define an attributeNames, displayName, readOnly -->
<param name="attributeNames" value="uid"/>
<param name="uid.displayName" value="Login ID"/>
<param name="uid.readOnly" value="true"/>
<param name="attributeNames" value="sn"/>
<param name="sn.displayName" value="Last Name"/>
<param name="sn.readOnly" value="true"/>
<param name="attributeNames" value="givenName"/>
<param name="givenName.displayName" value="First Name"/>
<param name="givenName.readOnly" value="true"/>
<param name="attributeNames" value="displayName"/>
<param name="displayName.displayName" value="Display Name"/>
<param name="displayName.readOnly" value="true"/>
<!-- if an attribute is editable, determine if its required -->
<param name="attributeNames" value="ipaSshPubKey"/>
<param name="ipaSshPubKey.displayName" value="SSH Public Key"/>
<param name="ipaSshPubKey.readOnly" value="false"/>
<param name="ipaSshPubKey.required" value="true"/>
<param name="attributeNames" value="loginShell"/>
<param name="loginShell.displayName" value="Login Shell"/>
<param name="loginShell.readOnly" value="false"/>
<param name="loginShell.required" value="true"/>
<!-- The name of the attribute that identifies the user uniquely -->
<param name="uidAttributeName" value="uid"/>
<!-- An attribute that specifies which roles a user is a member of. If left blank, then the user's DN in the virtual directory is compared against memberOf attributes -->
<param name="roleAttribute" value=""/>
<!-- List of attributes to include in the approval screen -->
<param name="approvalAttributeNames" value="uid"/>
<param name="approvalAttributeNames" value="givenName"/>
<param name="approvalAttributeNames" value="sn"/>
<param name="approvalAttributeNames" value="mail"/>
<param name="approvalAttributeNames" value="displayName"/>
<!-- Labels for each of the attributes -->
<param name="approvals.uid" value="Login ID"/>
<param name="approvals.givenName" value="First Name"/>
<param name="approvals.sn" value="Last Name"/>
<param name="approvals.mail" value="Email Address"/>
<param name="approvals.displayName" value="Display Name"/>
<!-- If set to true, the organization tree is shown on the main page, helpful when there are numerous links to organize them by organization -->
<param name="showPortalOrgs" value="false"/>
<!-- The URL to redirect the user to when they logout -->
<param name="logoutURL" value="/logout"/>
<!-- Optional class that can make dynamic decisions about editing the user's profile, must implement com.tremolosecurity.scalejs.sdk.UiDecisions -->
<param name="uiHelperClassName" value="com.tremolosecurity.scalejs.helpers.FilterHelper"/>
<!-- parameters for the ui helper -->
<param name="uihelper.params" value="allowedFilters=(uid=test)"/>
<param name="uihelper.params" value="filterMap=(uid=mmosley)=uid,givenNmae"/>
<param name="uihelper.params" value="filterMap=(uid=testx)=l"/>
</filter>
Each attribute can be configured with the following validation parameters:
Option |
Description |
Example |
regEx |
Optional regular expression to validate the input against |
.@. |
regExFailedMsg |
Message to be displayed to the user if validation fails |
Email addresses require an @ |
minChars |
Minimum number of characters in a field |
5 |
maxChars |
Maximum number of characters in a field, 0 for unlimited |
10 |
type |
One of text, textarea or list |
text |
values |
If type is list, list this multiple times for the values to use in the drop down |
label=value |
UI Helpers
If you need more fine grained control over what attributes are available in the user profile screen, you can implement the com.tremolosecurity.scalejs.sdk.UiDecisions interface. The below examples are pre-built helpers.
This helper uses LDAP filters to determine if a user may edit their profile and what attributes a user may edit.
<!-- Name of the helper -->
<param name="uiHelperClassName" value="com.tremolosecurity.scalejs.helpers.FilterHelper"/>
<!-- List of filters that allow a user to edit their profile, may be listed multiple times -->
<param name="uihelper.params" value="allowedFilters=(uid=test)"/>
<!-- Each filter determines which attributes a user may edit -->
<param name="uihelper.params" value="filterMap=(uid=mmosley)=uid,givenNmae"/>
<param name="uihelper.params" value="filterMap=(uid=testx)=l"/>
Token
The ScaleJS Token provides a mechanism to display tokens to a logged in user. Tokens can be temporary passwords, access keys for remote services, TOTP codes or any other token. In order to add a token, implement the com.tremolosecurity.scalejs.token.sdk.TokenLoader interface. Deployment requires that OpenUnison is configured with provisioning enabled. Assuming that you plan for ScaleJS Token to run in "/token":
-
Create a URL in OpenUnison called /token with the proxyTo tag empty
-
Create a URL in OpenUnison called /token/token with the below filter configuration and the proxyTo tag empty
-
Create an application in OpenUnison separate from ScaleJS Token, and with its own session cookie, with the CheckSession filter enabled and pointing to the ScaleJS Token application on /token/sessioncheck
The web services must always be configured on a url called "token" in the same directory as the AngularJS application.
<filter class="com.tremolosecurity.scalejs.token.ws.ScaleToken">
<!-- The name of the attribute that stores the value to be displayed when referencing the currently logged in user, ie cn or displayName -->
<param name="displayNameAttribute" value="displayName"/>
<!-- The title to show on the home page -->
<param name="frontPage.title" value="Azure Cloud Linux Management"/>
<!-- Sub text for the home page -->
<param name="frontPage.text" value="Use this portal as the gateway for accessing your linux servers and requesting access to systems."/>
<!-- The URL to redirect the user to when they logout -->
<param name="logoutURL" value="/logout"/>
<!-- The URL to access ScaleMain -->
<param name="homeURL" value="/scale/index.html"/>
<!-- When the below number of minutes are left in the user's session, warn the user -->
<param name="warnMinutesLeft" value="5" />
<!-- Implementation of the token loader -->
<param name="tokenClassName" value="com.tremolosecurity.scalejs.token.password.LoadToken"/>
<!-- Token specific parameters (see below) -->
<param name="encryptionKey" value="lastmile-oauth2"/>
<param name="attributeName" value="physicalDeliveryOfficeName"/>
</filter>
The password token is for displaying random or temporary credentials that have been encrypted. The below options are available.
Class Name |
com.tremolosecurity.scalejs.token.password.LoadToken |
encryptionKey |
The name of the key in the keystore to decrypt the temporary password |
attributeName |
The name of the attribute that stores the temporary password |
The TOTP token is compatible with Google Authenticator, FreeOTP and other multi-factor applications. It will display a QR Code with the key. The below options are available.
Class Name |
com.tremolosecurity.scalejs.token.totp.TOTPToken |
encryptionKey |
The name of the key in the keystore to decrypt the temporary password |
attributeName |
The name of the attribute that stores the temporary password |
The Kubectl Token Loader will generate a Kubernetes OpenID Connect client configuration based on a template that a user can copy and paste into their terminal window to enable kubectl access. Alternatively, a fully kubectl configuration can be generated and added to the user’s configuration. The template has three objects available to it:
Variable |
Class Name |
Description |
user |
com.tremolosecurity.proxy.auth.AuthInfo |
Contains attributes about the user and data about the user’s authentication |
token |
com.tremolosecurity.proxy.auth.util.OpenIDConnectToken |
Stores information about the current user’s session |
user_id |
java.lang.String |
The ID of the currently logged in user |
Templates include data from variables between "$" symbols. The below template is a good starting point for most deployments:
kubectl config set-cluster kubernetes --server=https://k8s-master.mydomain.com:6443 --certificate-authority=~/k8s_ca.pem && kubectl config set-context kubernetes --cluster=kubernetes --user=$user_id$ && kubectl config set-credentials $user_id$ --auth-provider=oidc --auth-provider-arg=idp-issuer-url=$token.claims.issuer$ --auth-provider-arg=client-id=$token.trustName$ --auth-provider-arg=refresh-token=$token.refreshToken$ --auth-provider-arg=idp-certificate-authority=~/unison-ca.pem --auth-provider-arg=id-token=$trust.encodedIdJSON$ && kubectl config use-context kubernetes
Class Name |
com.tremolosecurity.scalejs.KubectlTokenLoader |
uidAttributeName |
The name of the user attribute that is used to identify the user to Kubernetes |
kubectlUsage |
A message telling the user how to use the data on the screen |
kubectlTemplate |
A template used to generate a new kubectl config |
k8sCaCertName |
The name of the certificate in OpenUnison’s certificate store that represents Kubernetes' master CA |
unisonCaCertName |
The name of the certificate in OpenUnison’s certificate store that represents OpenUnison’s CA |
Single Request
The ScaleJS Single Request application provides a way for a logged in user to request a specific workflow without choosing it from ain. This is useful in situations where a user must first request access before gaining access to ScaleJS Main or for workflows that you don’t want random users to request. Deployment requires that OpenUnison is configured with provisioning enabled. Assuming that you plan for ScaleJS Single Request to run in "/request":
-
Create a URL in OpenUnison called /request with the proxyTo tag empty
-
Create a URL in OpenUnison called /request/singlerequest with the below filter configuration and the proxyTo tag empty
The web services must always be configured on a url called "singlerequest" in the same directory as the AngularJS application.
<filter class="com.tremolosecurity.scalejs.singlerequest.ws.ScaleSingleRequest">
<!-- The name of the attribute that stores the value to be displayed when referencing the currently logged in user, ie cn or displayName -->
<param name="displayNameAttribute" value="displayName"/>
<!-- The title to show on the home page -->
<param name="frontPage.title" value="Azure Cloud Linux Management"/>
<!-- Sub text for the home page -->
<param name="frontPage.text" value="Use this portal as the gateway for accessing your linux servers and requesting access to systems."/>
<!-- The URL to redirect the user to when they logout -->
<param name="logoutURL" value="/logout"/>
<!-- The URL to access ScaleMain -->
<param name="homeURL" value="/scale/index.html"/>
<!-- The name of the workflow -->
<param name="workflowName" value="oneWorkflowName"/>
<!-- Set to true if attributes from authentication should be added to the workflow, such as when creating a user based on attributes from a SAML2 assertion -->
<param name="useAttributesFromAuthentication" value="false" />
</filter>
Register
The registration application has two use cases:
-
An administrator needs to create a user
-
An external user needs to sign up for a service (usually from an untrusted network)
If ScaleJS Register is accessed by a non-anonymous user (ie an administrator) then the request will be submitted and attempt to auto-approve the request (assuming the requester is allowed to approve the request). Attributes are configurable. In addition to attributes to specify a user may also be required to verify they are not a robot using Google’s ReCaptcha process (https://www.google.com/recaptcha/intro/index.html) and accept terms and conditions.
Assuming that ScaleJS Register will be configured in /register:
-
Create a URL in OpenUnison called /register with the proxyTo tag empty
-
Create a URL in OpenUnison called /register/register with the below filter configuration and the proxyTo tag empty
<filter class="com.tremolosecurity.scalejs.register.ws.ScaleRegister">
<!-- The name of the attribute that stores the value to be displayed when referencing the currently logged in user, ie cn or displayName -->
<param name="displayNameAttribute" value="gecos"/>
<!-- The name of the attribute that identifies the user uniquely -->
<param name="uidAttributeName" value="uid"/>
<!-- If a custom submission class isn't used, the workflow to execute -->
<param name="workflowName" value=""/>
<!-- The URL to redirect the user to when they logout -->
<param name="logoutURL" value="/logout"/>
<!-- The title to show on the home page -->
<param name="frontPage.title" value="Create New User"/>
<!-- Sub text for the home page -->
<param name="frontPage.text" value="Create new user"/>
<!-- The URL to access ScaleMain -->
<param name="homeURL" value="/scale/index.html"/>
<!-- Set to true if the password should be pre-set, should NOT be used for anonymous access -->
<param name="preSetPassword" value="true"/>
<!-- If set to true, the user must suply a reason for the reuest -->
<param name="requireReason" value="true"/>
<!-- If true, the user object is submitted as the logged in user instead of as a new user -->
<param name="submitLoggedInUser" value="false"/>
<!-- If set to true, a recaptcha is required, registration at https://www.google.com/recaptcha/intro/index.html required -->
<param name="requireReCaptcha" value="false"/>
<!-- Site key for Google ReCaptcha -->
<param name="rcSiteKey" value="6Lcw-yMTAAAAAMzxDxcDkVocC4CUqjAjlrGmJeNU"/>
<!-- Secret key for Google ReCaptcha -->
<param name="rcSecret" value="6Lcw-yMTAAAAAJ1qGhDrBZ3n6bo7Vxfma6p-FwyH"/>
<!-- Set to true if the user must accept terms and conditions -->
<param name="requireTermsAndConditions" value="true"/>
<!-- Terms and conditions text, rendered as HTML -->
<param name="termsAndConditionsText" value="<ul>
<li>Don't be a jerk</li>
<li>Be awesome</li>
<li>Be cool</li>
</ul>"/>
<!-- Set to true if a custom class, implementing com.tremolosecurity.scalejs.register.sdk.CreateRegisterUser -->
<param name="useCallWorkflowClass" value="true"/>
<!-- Implementation of com.tremolosecurity.scalejs.register.sdk.CreateRegisterUser, required if useCallWorkflowClass is true -->
<param name="callWorkflowClassName" value="com.tremolosecurity.scalejs.register.sdk.test.TestRegisterUser"/>
<!-- Initialization parameters for the custom workflow submission implementation -->
<param name="callWorkflowInit" value="option1=d"/>
<param name="callWorkflowInit" value="option1=w"/>
<param name="callWorkflowInit" value="option1=z"/>
<!-- for each attribute, have an attributeNames as the name of the attribute -->
<param name="attributeNames" value="uid"/>
<!-- how to display the attribute -->
<param name="uid.displayName" value="Login ID"/>
<!-- The message to show if the regular expression fails -->
<param name="uid.regExFailedMsg" value="Bad message"/>
<!-- Minimum number of characters -->
<param name="uid.minChars" value="0"/>
<!-- Maximum number of characters (0 for unlimited) -->
<param name="uid.maxChars" value="0"/>
<!-- Set to true if this value must be unique in the virtual directory -->
<param name="uid.unique" value="true"/>
<!-- May be text or list -->
<param name="uid.type" value="text"/>
<!-- If set to true, the attribute is required -->
<param name="uid.required" value="true"/>
<!-- List based attribute -->
<param name="attributeNames" value="jurisdiction"/>
<param name="jurisdiction.displayName" value="Jurisdiction"/>
<param name="jurisdiction.regEx" value=".*"/>
<param name="jurisdiction.regExFailedMsg" value="Invalid jurisdiction"/>
<param name="jurisdiction.minChars" value="0"/>
<param name="jurisdiction.maxChars" value="0"/>
<param name="jurisdiction.unique" value="false"/>
<param name="jurisdiction.type" value="list"/>
<!-- List of values for the list box, left side of equals is the label, right side is the value -->
<param name="jurisdiction.values" value="e=f"/>
<param name="jurisdiction.values" value="a=b"/>
<!-- List based attribute with a dynamic source -->
<param name="attributeNames" value="code"/>
<param name="code.displayName" value="Code"/>
<param name="code.regEx" value=".*"/>
<param name="code.regExFailedMsg" value="Invalid Code"/>
<param name="code.minChars" value="0"/>
<param name="code.maxChars" value="0"/>
<param name="code.unique" value="false"/>
<param name="code.type" value="list"/>
<!-- List of values are generated from an implementation of com.tremolosecurity.scalejs.sdk.SourceList -->
<param name="code.dynamicValueSource.className" value="com.me.LoadFromSource"/>
<param name="jurisdiction.values" value="a=b"/>
<!- Searchable list lets a user type in a search value and dynamicly list available options, useful for very large sets -->
<param name="attributeNames" value="somecode"/>
<param name="somecode.displayName" value="Some Code"/>
<param name="somecode.readOnly" value="false"/>
<param name="somecode.required" value="false"/>
<param name="somecode.regEx" value=".*"/>
<param name="somecode.regExFailedMsg" value=""/>
<param name="somecode.minChars" value="0"/>
<param name="somecode.maxChars" value="0"/>
<param name="somecode.unique" value="false"/>
<param name="somecode.type" value="text-list"/>
<param name="somecode.values" value="a=b"/>
<param name="somecode.values" value="c=d"/>
<param name="somecode.values" value="x=y"/>
<param name="somecode.dynamicValueSource.className" value="com.tremolosecurity.scalejs.register.dynamicSource.LoadFromDatabaseTarget"/>
<param name="somecode.dynamicValueSource.config" value="targetName=metadata"/>
<param name="somecode.dynamicValueSource.config" value="noParamSQL=SELECT * FROM items"/>
<param name="somecode.dynamicValueSource.config" value="paramSQL=SELECT * FROM items WHERE infoid LIKE ?"/>
<param name="somecode.dynamicValueSource.config" value="nameField=infoid"/>
<param name="somecode.dynamicValueSource.config" value="valueField=infoid"/>
<param name="somecode.dynamicValueSource.config" value="maxEntrie=8"/>
<param name="somecode.dynamicValueSource.config" value="exactSQL=SELECT * FROM items WHERE infoid = ?"/>
<param name="somecode.dynamicValueSource.config" value="errorMessage=Invalid value, please set a correct one"/>
<!-- Text area attribute -->
<param name="attributeNames" value="description"/>
<!-- how to display the attribute -->
<param name="description.displayName" value="User Description"/>
<!-- The message to show if the regular expression fails -->
<param name="description.regExFailedMsg" value="Bad message"/>
<!-- Minimum number of characters -->
<param name="description.minChars" value="0"/>
<!-- Maximum number of characters (0 for unlimited) -->
<param name="description.maxChars" value="0"/>
<!-- Set to true if this value must be unique in the virtual directory -->
<param name="description.unique" value="true"/>
<!-- May be text or list -->
<param name="description.type" value="textarea"/>
<!-- If set to true, the attribute is required -->
<param name="description.required" value="false"/>
</filter>
Dynamic Sources
For list and searchable lists dynamic sources can be used to extract values instead of hard coding them in the config. This is useful when data may change across environments or be very large.
com.tremolosecurity.scalejs.register.dynamicSource.LoadFromDatabaseTarget
This dynamic source will load name/value pairs from a database target using specified SQL statements
targetName |
The name of the provisioning target |
metadata |
noParamSQL |
SQL to run when there are no search parameters |
SELECT * FROM items |
paramSQL |
SQL to run when a user has entered a search tearm |
SELECT * FROM items WHERE infoid LIKE ? |
exactSQL |
SQL to use when validating input |
SELECT * FROM items WHERE infoid = ? |
nameField |
The field to use as the label |
name |
valueField |
The field to use as the value |
value |
maxEntries |
For searchable lists, the maximum number of results to return. 0 will return all reasults |
10 |
errorMessage |
The message to display to the user when validation fails |
Invalid input |
com.tremolosecurity.scalejs.register.dynamicSource.LoadFromLDAP
This dynamic source will list options based on an LDAP lookup.
nameField |
The field to use as the label |
name |
valueField |
The field to use as the value |
value |
maxEntries |
For searchable lists, the maximum number of results to return. 0 will return all reasults |
10 |
searchBase |
The distinguished name of the starting point for all searches,cn=groups,dc=domain,dc=com |
objectClass |
The LDAP object class that entries of the response must be a member of |
groupOfUniqueNames |
errorMessage |
Password
The ScaleJS Password application provides an interface for users to set or reset their passwords depending on how Unison is configured. In addition to some basic validation, the ScaleJS Password application provides the ability to have custom password validation. Out of the box, ScaleJS Password comes with implementations that do basic validation or align with Active Directory’s rules for passwords. Custom validators may be created by implementing com.tremolosecurity.scalejs.password.sdk.PasswordValidator.
Assuming that ScaleJS Password will be configured in /reset:
-
Create a URL in OpenUnison called /reset with the proxyTo tag empty
-
Create a URL in OpenUnison called /reset/password with the below filter configuration and the proxyTo tag empty
<filter class="com.tremolosecurity.scalejs.password.ws.ScalePassword">
<!-- The name of the attribute that stores the value to be displayed when referencing the currently logged in user, ie cn or displayName -->
<param name="displayNameAttribute" value="uid"/>
<!-- The title to show on the home page -->
<param name="frontPage.title" value="reset password"/>
<!-- Sub text for the home page -->
<param name="frontPage.text" value="reset your password"/>
<!-- The URL to access ScaleMain -->
<param name="homeURL" value="/scale/index.html"/>
<!-- The URL to redirect the user to when they logout -->
<param name="logoutURL" value="/logout"/>
<!-- The name of the attribute that identifies the user uniquely -->
<param name="uidAttributeName" value="uid"/>
<!-- Reason for the workflow -->
<param name="reason" value="Password reset"/>
<!-- if true, the password reset workflow will be run synchronously -->
<param name="synchronous" value="false" />
<!-- Workflow for the password reset call -->
<param name="workflowName" value="printuser"/>
<!-- Implementation of com.tremolosecurity.scalejs.password.sdk.PasswordValidator -->
<param name="validatorClassName" value="com.tremolosecurity.scalejs.password.validators.ActiveDirectoryValidator"/>
<!-- List of parameters for the validator -->
<param name="validator.params" value="requireLower=false"/>
<param name="validator.params" value="requireUpper=false"/>
<param name="validator.params" value="requireNumber=false"/>
<param name="validator.params" value="requireSpecial=false"/>
<param name="validator.params" value="minRequirements=4"/>
<param name="validator.params" value="maxChars=10"/>
<param name="validator.params" value="minChars=8"/>
<param name="validator.params" value="attributesToCheck=Last Name=sn"/>
<param name="validator.params" value="attributesToCheck=User Principal Name=upn"/>
<param name="validator.params" value="attributesToCheck=Login ID=uid"/>
</filter>
Basic Validator
The basic validator provides some simple options for checking a password.
<filter class="com.tremolosecurity.scalejs.password.ws.ScalePassword">
<!- ... ->
<!-- Implementation of com.tremolosecurity.scalejs.password.sdk.PasswordValidator -->
<param name="validatorClassName" value="com.tremolosecurity.scalejs.password.validators.BasicValidator"/>
<!-- Require a lower case letter -->
<param name="validator.params" value="requireLower=false"/>
<!-- Require an upper case letter -->
<param name="validator.params" value="requireUpper=false"/>
<!-- Require a number -->
<param name="validator.params" value="requireNumber=false"/>
<!-- Require a special character -->
<param name="validator.params" value="requireSpecial=false"/>
<!-- The minimum number of the above options that must be true (0-4) -->
<param name="validator.params" value="minRequirements=4"/>
<!-- Maximum number of characters -->
<param name="validator.params" value="maxChars=10"/>
<!-- Minimum number of characters -->
<param name="validator.params" value="minChars=8"/>
</filter>
Active Directory Validator
The Active Directory Validator has all of the features of the basic validator, with the addition of checking to see if the password has 3 consecutive characters in a set of attributes you specify.
<filter class="com.tremolosecurity.scalejs.password.ws.ScalePassword">
<!- ... ->
<!-- Implementation of com.tremolosecurity.scalejs.password.sdk.PasswordValidator -->
<param name="validatorClassName" value="com.tremolosecurity.scalejs.password.validators.ActiveDirectoryValidator"/>
<!-- Require a lower case letter -->
<param name="validator.params" value="requireLower=false"/>
<!-- Require an upper case letter -->
<param name="validator.params" value="requireUpper=false"/>
<!-- Require a number -->
<param name="validator.params" value="requireNumber=false"/>
<!-- Require a special character -->
<param name="validator.params" value="requireSpecial=false"/>
<!-- The minimum number of the above options that must be true (0-4) -->
<param name="validator.params" value="minRequirements=4"/>
<!-- Maximum number of characters -->
<param name="validator.params" value="maxChars=10"/>
<!-- Minimum number of characters -->
<param name="validator.params" value="minChars=8"/>
<!-- List of attributes to check, with the label=attribute name -->
<param name="validator.params" value="attributesToCheck=Last Name=sn"/>
<param name="validator.params" value="attributesToCheck=User Principal Name=upn"/>
<param name="validator.params" value="attributesToCheck=Login ID=uid"/>
</filter>
Operators
The operators screen works with ScaleJS main to provide an alternative view and integration with OpenUnison from a technical operator’s standpoint. Instead of using the self service interface, an operator can lookup users and act directly on them rather then having to navigate to a workflow and submitting it on a user’s behalf. These updates can make supporting users much easier.
<filter class="com.tremolosecurity.scalejs.ws.ScaleJSOperator">
<!-- List of directory bases to search in the form of label=distinguished name -->
<param name="bases" value="Internal=ou=int,o=Tremolo"/>
<param name="bases" value="External=ou=ext,o=Tremolo"/>
<param name="bases" value="Both=o=Tremolo"/>
<!-- List of attributes and labels that can be searched on -->
<param name="searchableAttributes" value="givenname=First Name" />
<param name="searchableAttributes" value="sn=Last Name" />
<param name="searchableAttributes" value="mail=Email Address" />
<param name="searchableAttributes" value="uid=Login ID" />
<!-- List of attributes to include in the results -->
<param name="resultAttributes" value="givenname=First Name" />
<param name="resultAttributes" value="sn=Last Name" />
<param name="resultAttributes" value="mail=Email Address" />
<param name="resultAttributes" value="uid=Login ID" />
<!-- The name of the OpenUnison application that hosts ScaleJS -->
<param name="scaleMainAppName" value="ScaleJS" />
<!-- The relative URI to ScaleJS' web services -->
<param name="scaleMainURI" value="/scalejs/main" />
<!-- Link to use for the home link -->
<param name="homeUrl" value="/scalejs/index.html" />
</filter>
NOTE if you want to limit the scope of searches for the operator based on data other then the base, you can limit it using a search filter. Add an instance of org.apache.directory.ldap.client.api.search.FilterBuilder
to the request object in an HttpFilter
called "ops.search.filter".
Web Services
All of ScaleJS' web services are documented in the Unison integration guide. These services can be used in any application, not just ScaleJS.