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

  • 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

ScaleJS and OpenUnison Architecture

Each ScaleJS application consists of two components:

  1. Static HTML & JavaScripts

  2. 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":

  1. Create a URL in OpenUnison called /scale with the proxyTo tag empty

  2. Create a URL in OpenUnison called /scale/main with the below filter configuration and the proxyTo tag empty

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

FilterHelper

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

  1. Create a URL in OpenUnison called /token with the proxyTo tag empty

  2. Create a URL in OpenUnison called /token/token with the below filter configuration and the proxyTo tag empty

  3. 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>
Password Token

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

TOTP Token

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

KubectlTokenLoader

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 &amp;&amp; kubectl config set-context kubernetes --cluster=kubernetes --user=$user_id$ &amp;&amp; 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$ &amp;&amp; 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":

  1. Create a URL in OpenUnison called /request with the proxyTo tag empty

  2. 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:

  1. An administrator needs to create a user

  2. 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:

  1. Create a URL in OpenUnison called /register with the proxyTo tag empty

  2. 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="&lt;ul&gt;&#xD;&#xA;&lt;li&gt;Don't be a jerk&lt;/li&gt;&#xD;&#xA;&lt;li&gt;Be awesome&lt;/li&gt;&#xD;&#xA;&lt;li&gt;Be cool&lt;/li&gt;&#xD;&#xA;&lt;/ul&gt;"/>

    <!-- 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:

  1. Create a URL in OpenUnison called /reset with the proxyTo tag empty

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