Customizing Program Logic

The topics in this section describe how to:

  • Use JavaScript files

  • Write Java extensions

  • Customize the creation workflows for persona and functional users

  • Customize the consistency rules for automatic persona, functional user and user creation

  • Customize the JavaScript that exchanges personas and users

Using JavaScript Files

DirX Identity provides another powerful technique for customer extensions: JavaScript files.JavaScript files contain JavaScript code, a programming language that is slightly different from Java and a little bit easier to use.JavaScript files are used in DirX Identity to run small customized programs in conjunction with particular DirX Identity operations.The JavaScript programs are stored in separate script entries that can be referenced in object attributes and properties.

You can currently use JavaScript files to:

  • Create default values for object attributes

  • Make post-editing consistency checks of object attributes

  • Extend object operations like save, remove, and so on

Before you can use JavaScript files to extend DirX Identity, you need to know how to:

  • Use the JavaScript programming language and its pre-defined objects

  • Create a JavaScript file in the DirX Identity system

  • Integrate your JavaScript files into the DirX Identity system.

The next sections describe these tasks.

JavaScript Programming Overview

This section provides an overview of the JavaScript programming language, including:

  • Variables, values, and literals

  • Comments

  • Keywords

  • Operators

  • Functions

  • Objects, methods, and properties

  • Predefined objects

This section is not intended for use as a JavaScript reference. For detailed information about JavaScript, refer to the various guides and references available in textbook form and on the Internet.

JavaScript Variables, Values, and Literals

You can declare variables in JavaScript directly by writing the variable name and assigning it a value. A variable can also remain unassigned. In this case, use the keyword var to avoid a runtime error if the variable is never assigned a value referenced in some statement.

JavaScript recognizes the following types of values:

  • Numbers, such as 42 or 3.14159.

  • Logical (Boolean) values, either true or false.

  • Strings, such as "Howdy!".

  • null, which is a special keyword that indicates a null value; null is also a primitive value. Because JavaScript is case-sensitive, null is not the same as Null, NULL, or any other variant.

  • undefined, which is a top-level property whose value is undefined; undefined is also a primitive value.

Since JavaScript is a dynamically typed language, the type of assignment may vary during runtime. A variable can initially take a number value, and then take a string value later in the program. When a variable identifier is set by assignment outside a function, it is called a global variable, because it is available everywhere in the current document. When a variable is declared within a function, it is called a local variable, because it is available only within the function. Using the keyword var to declare a global variable is optional. However, var is must be used to declare a variable inside a function.

Literals are used to represent values. The following types of literals are valid:

  • Array literals […​,…​,…​] such as [1, 4, 9, 16, 25] or ["green", "yellow", "red"].

  • Boolean literals, either true or false.

  • Floating-point literals (signed or unsigned), for example 3.1415 or 1.523e-15.

  • Integers (signed or unsigned), such as -25 or 0x2BFF.

  • Object literals, which are sets of comma-separated properties and assigned values enclosed in curly braces; for example, flower = \{name: "Rose", height: 65, price: 2.25}

  • String literals enclosed in single quotes ( ' ) or double quotes ( "); for example, 'blah', "blah", or "one line \n another line".

The following table lists the special characters that can be used in string literals:

Table 1. Special Characters in String Literals
Character Meaning

\b

Backspace

\f

Form feed

\n

New line

\r

Carriage return

\t

Tab

\'

Apostrophe or single quote

\"

Double quote

\\

Backslash character (\)

\nnn

The character with the Latin-1 encoding specified by up to three octal digits nnn between 0 and 377. For example, \251 is the octal sequence for the copyright symbol.

\xnn

The character with the Latin-1 encoding specified by the two hexadecimal digits nn between 00 and FF. For example, \xA9 is the hexadecimal sequence for the copyright symbol.

\unnnn

The Unicode character specified by four hexadecimal digits nnnn. For example, \u00A9 is the Unicode sequence for the copyright symbol.

JavaScript Comments

Like the C or C++ programming languages, a single-line comment begins with “//” at any position in a line and terminates with the subsequent line-feed character.

A multi-line comment starts with “/*” at any position in a line and ends with “*/” at any position in a subsequent line or behind the start sequence on the same line.

JavaScript Keywords

The following table lists the most frequently used keywords and their meanings.

Table 2. JavaScript Keywords
Keyword Description

break

Terminates the current while or for loop and transfers program control to the statement following the terminated loop.

continue

Terminates execution of the block of statements in a while or for loop, and continues execution of the loop with the next iteration.

delete

Deletes an object, an object’s property, or an element at a specified index in an array.

do…while

Executes the specified statements until the test condition evaluates to false. Statements execute at least once.

export

Allows a signed script to provide properties, functions, and objects to other signed or unsigned scripts.

false

Boolean value literal.

for

Creates a loop that consists of three optional expressions, enclosed in parentheses and separated by semicolons, followed by a block of statements executed in the loop.

for…in

Iterates a specified variable over all the properties of an object. For each distinct property, JavaScript executes the specified statements.

function

Declares a function with the specified parameters. Acceptable parameters include strings, numbers, and objects.

if…else

Executes a set of statements if a specified condition is true. If the condition is false, another set of statements can be executed.

import

Allows a script to import properties, functions, and objects from a signed script that has exported the information.

label

Provides an identifier that can be used with break or continue to indicate where the program should continue execution.

new

Creates an instance of a user-defined object type or of one of the built-in object types.

null

The null-value for objects.

return

Specifies the value to be returned by a function.

switch

Allows a program to evaluate an expression and attempt to match the expression’s value to a case label.

this

Keyword that you can use to refer to the current object.

true

Boolean value literal.

typeof

Returns a string indicating the type of the unevaluated operand.

undefined

The null-value for variables (especially unassigned variables declared by "var".

var

Declares a variable, optionally initializing it to a value.

void

Specifies an expression to be evaluated without returning a value.

while

Creates a loop that evaluates an expression, and if it is true, executes a block of statements. The loop then repeats, as long as the specified condition is true.

with

Establishes the default object for a set of statements.

JavaScript Operators

The following table lists the operators used in JavaScript expressions and statements.

Table 3. JavaScript Operators
Operator Description

+ (Addition)

Adds 2 numbers.

++ (Increment)

Adds 1 to a variable representing a number (returning either the new or old value of the variable)

- (Unary negation, subtraction)

As a unary operator, negates the value of its argument. As a binary operator, subtracts 2 numbers.

 — (Decrement)

Subtracts 1 from a variable representing a number (returning either the new or old value of the variable)

* (Multiplication)

Multiplies 2 numbers.

/ (Division)

Divides 2 numbers.

% (Modulus)

Computes the integer remainder of dividing 2 numbers.

+ (String addition)

Concatenates 2 strings.

+=

Concatenates 2 strings and assigns the result to the first operand.

&& (Logical AND)

Returns the first operand if it can be converted to false; otherwise, returns the second operand. Thus, when used with Boolean values, && returns true if both operands are true; otherwise, returns false.

|| (Logical OR)

Returns the first operand if it can be converted to true; otherwise, returns the second operand. Thus, when used with Boolean values, || returns true if either operand is true; if both are false, returns false.

! (Logical NOT)

Returns false if its single operand can be converted to true; otherwise, returns true.

& (Bitwise AND)

Returns a one in each bit position if bits of both operands are ones.

^ (Bitwise XOR)

Returns a one in a bit position if bits of one but not both operands are one.

| (Bitwise OR)

Returns a one in a bit if bits of either operand is one.

~ (Bitwise NOT)

Flips the bits of its operand.

<< (Left shift)

Shifts its first operand in binary representation the number of bits to the left specified in the second operand, shifting in zeros from the right.

>> (Sign-propagating right shift)

Shifts the first operand in binary representation the number of bits to the right specified in the second operand, discarding bits shifted off.

>>> (Zero-fill right shift)

Shifts the first operand in binary representation the number of bits to the right specified in the second operand, discarding bits shifted off, and shifting in zeros from the left.

=

Assigns the value of the second operand to the first operand.

+=

Adds 2 numbers and assigns the result to the first.

-=

Subtracts 2 numbers and assigns the result to the first.

*=

Multiplies 2 numbers and assigns the result to the first.

/=

Divides 2 numbers and assigns the result to the first.

%=

Computes the modulus of 2 numbers and assigns the result to the first.

&=

Performs a bitwise AND and assigns the result to the first operand.

^=

Performs a bitwise XOR and assigns the result to the first operand.

|=

Performs a bitwise OR and assigns the result to the first operand.

<⇐

Performs a left shift and assigns the result to the first operand.

>>=

Performs a sign-propagating right shift and assigns the result to the first operand.

>>>=

Performs a zero-fill right shift and assigns the result to the first operand.

==

Returns true if the operands are equal.

!=

Returns true if the operands are not equal.

===

Returns true if the operands are equal and of the same type.

!==

Returns true if the operands are not equal and/or not of the same type.

>

Returns true if the left operand is greater than the right operand.

>=

Returns true if the left operand is greater than or equal to the right operand.

<

Returns true if the left operand is less than the right operand.

Returns true if the left operand is less than or equal to the right operand.

…​?…​:…​.

Performs a simple "if…​else"

,

Evaluates two expressions and returns the result of the second expression.

delete

Deletes an object, an object’s property, or an element at a specified index in an array.

new

Creates an instance of a user-defined object type or of one of the built-in object types.

this

Keyword that you can use to refer to the current object.

typeof

Returns a string indicating the type of the unevaluated operand.

void

Specifies an expression to be evaluated without returning a value.

JavaScript Functions

A function definition consists of the function keyword, followed by

  • The name of the function.

  • A list of arguments to the function, enclosed in parentheses and separated by commas.

  • The JavaScript statements that define the function, enclosed in curly braces, \{ }. The statements in a function can include calls to other functions defined in the current application.

Defining a function does not execute it. Defining the function simply names the function and specifies what to do when the function is called. Calling the function actually performs the specified actions with the indicated parameters.

JavaScript has several top-level predefined functions:

  • eval - Evaluates a string of JavaScript code without reference to a particular object, for example eval(expr).

  • isFinite - Evaluates an argument to determine whether it is a finite number, for example isFinite(number).

  • isNaN - Evaluates an argument to determine if it is "NaN" (not a number), for example isNaN(testValue).

  • parseInt and parseFloat - Return a numeric value when given a string as an argument, like parseInt(numberString) or parseFloat(numberString).

  • Number and String - Convert an object to a number or a string, like Number(object) or String(object).

  • escape and unescape - Encode and decode strings. The escape function returns the hexadecimal encoding of an argument in the ISO Latin character set. The unescape function returns the ASCII string for the specified hexadecimal encoding value.

JavaScript Objects, Methods and Properties

JavaScript is based on a simple object-based paradigm. An object is a construct with properties that are JavaScript variables or other objects. An object also has functions associated with it that are known as the object’s methods. JavaScript contains a number of predefined objects and new objects can also be defined.

A JavaScript object has properties associated with it. The properties of an object are accessed with a simple notation:

objectName.propertyName

Both the object name and property name are case-sensitive. A property is defined by assigning it a value.

A method is a function associated with an object. A method is defined the same way a standard function is defined. Then the following syntax is used to associate the function with an existing object:

objectName.methodName = functionName

where objectName is the name of an existing object, methodName is the name to be assigned to the method, and functionName is the name of the function.

In JavaScript, you create an object by using an object initializer or, alternatively, instantiating it with a constructor function and the new operator. The constructor function is assigned the name of the object to be created. An existing object may be deleted by the delete operator.

Pre-Defined JavaScript Objects

This topic briefly describes the pre-defined JavaScript objects, including:

  • Array - An object that represents and manipulates an array of data.

  • Boolean - An object that represents a boolean value.

  • Date - An object that represents and manipulates a date value.

  • Function - An object that represents a function. The function keyword can be used instead.

  • Math - An object with predefined functions and constants for mathematical calculations.

  • Number - An object that represents a numerical value.

  • Packages (java) - An object for accessing Java classes

  • RegExp - An object that handles expressions.

  • String - An object that represents a string and manipulates it.

For more information about these objects, consult a JavaScript language reference.

Array

JavaScript does not have an explicit array data type. However, the predefined Array object and its methods can be used to work with arrays in applications. The Array object has methods for manipulating arrays in various ways, such as joining, reversing, and sorting them. It has a property for determining the array length and other properties for use with regular expressions.

Constructors

arrayObjectName = new Array(element1, …​, elementN)

arrayObjectName = new Array(elementCount)

arrayObjectName is the name of the array object to be created. element1, …​, elementN are the data items added to the array. This is an explicit construction of the array. In the second statement, elementCount is just the number of the data items in the array, there is indeed no item assigned yet.

Methods

Array contains the following methods:

  • concat - Joins two arrays and returns a new array.

  • join - Joins all elements of an array into a string.

  • pop - Removes the last element from an array and returns that element.

  • push - Adds one or more elements to the end of an array and returns that last element added.

  • reverse - Transposes the elements of an array: the first array element becomes the last and the last becomes the first.

  • shift - Removes the first element from an array and returns that element

  • slice - Extracts a section of an array and returns a new array.

  • splice - Adds and/or removes elements from an array.

  • sort - Sorts the elements of an array.

  • unshift - Adds one or more elements to the front of an array and returns the new length of the array.

Boolean

The Boolean object is a wrapper around the primitive Boolean data type.

Constructor

booleanObjectName = new Boolean(value)

Do not confuse the primitive Boolean values true and false with the true and false values of the Boolean object. Any object whose value is not undefined or null, including a Boolean object whose value is false, evaluates to true when passed to a conditional statement.

Date

JavaScript does not have a date data type. However, the Date object and its methods can be used to work with dates and times in applications. The Date object has a large number of methods for setting, getting, and manipulating dates. It does not have any properties.

Constructor

dateObjectName = new Date([parameters])

where dateObjectName is the name of the Date object being created; it can be a new object or a property of an existing object. The parameters in the preceding syntax can be any of the following:

  • Nothing: creates today’s date and time. For example, today = new Date().

  • A string representing a date in the following form: "Month day, year hours:minutes:seconds." For example, Xmas95 = new Date("December 25, 1995 13:30:00"). If you omit hours, minutes, or seconds, the value will be set to zero.

  • A set of integer values for year, month, and day. For example, Xmas95 = new Date(1995,11,25). A set of values for year, month, day, hour, minute, and seconds. For example, Xmas95 = new Date(1995,11,25,9,30,0).

Methods

The Date object methods for handling dates and times fall into these broad categories:

  • "set" methods, for setting date and time values in Date objects.

  • "get" methods, for getting date and time values from Date objects.

  • "to" methods, for returning string values from Date objects.

  • parse and UTC methods, for parsing Date strings.

Function Object

The predefined Function object specifies a string of JavaScript code to be compiled as a function.

Constructor

functionObjectName = new Function ([arg1, arg2, …​ argN], functionBody)

functionObjectName is the name of a variable or a property of an existing object. It can also be an object followed by a lowercase event handler name, such as window.onerror. arg1, arg2, …​ argN are arguments to be used by the function as formal argument names. Each must be a string that corresponds to a valid JavaScript identifier; for example "x" or "theForm". functionBody is a string specifying the JavaScript code to be compiled as the function body.

Function objects are evaluated each time they are used. This is less efficient than declaring a function and calling it within your code, because declared functions are compiled.

Math

The predefined Math object has properties and methods for mathematical constants and functions. For example, the Math object’s PI property has the value of pi (3.141…​), which would be used in an application as

Math.PI

Similarly, standard mathematical functions are methods of Math. These include trigonometric, logarithmic, exponential, and other functions. Note that all trigonometric methods of Math take arguments in radians.

Constructor

None. Math is predefined and has static methods which can be directly called as

Math.methodName

where methodName is any of the methods listed below in the next section.

Methods

Math contains the following methods:

  • abs - Absolute value of the argument.

  • sin, cos, tan - Standard trigonometric functions; argument in radians.

  • acos, asin, atan - Inverse trigonometric functions; return values in radians.

  • exp, log - Exponential and natural logarithm, base e.

  • ceil - Returns least integer greater than or equal to argument.

  • floor - Returns greatest integer less than or equal to argument.

  • min, max - Returns greater or lesser (respectively) of two arguments.

  • pow - Exponential; first argument is base, second is exponent.

  • round - Rounds argument to nearest integer.

  • sqrt - Square root.

Number

The Number object has properties for numerical constants, such as maximum value, not-a-number, and infinity. The values of these properties cannot be changed.

Constructor

None. Number is predefined and has static properties which can be directly called as

Number.propertyName

where propertyName is the name of the respective property to be used. The valid properties are listed below.

Properties

Number contains the following properties:

  • MAX_VALUE - The largest representable number.

  • MIN_VALUE - The smallest representable number.

  • NaN - Special "not a number" value.

  • NEGATIVE_INFINITY - Special infinite value; returned on overflow.

  • POSITIVE_INFINITY - Special negative infinite value; returned on overflow.

Packages (java)

This object allows the access of Java classes by JavaScript code

Constructor

None. Packages and java are predefined and provide static access to Java packages.

Properties

The Packages object provides the following properties:

  • className - The fully qualified name of a Java class in a package other than netscape, java, or sun that is available to JavaScript.

  • java - Any class in the Java package java.*. Instead of writing Packages.java the synonym java can be used.

  • netscape - Any class in the Java package netscape.*.

  • sun - Any class in the Java package sun.*.

RegExp

The RegExp object lets you work with regular expressions.

Constructor

None. RegExp is predefined and has static methods which can be directly called as

RegExp.methodName

where methodName is the name of the method to be used for the manipulation of a regular expression.

Methods

RegExp contains the following methods:

  • exec - Executes a search for a match in a string. It returns an array of information.

  • test - Tests for a match in a string. It returns true or false.

String

The String object is a wrapper around the string primitive data type. Do not confuse a string literal with the String object. You can call any of the methods of the String object on a string literal value - JavaScript automatically converts the string literal to a temporary String object, calls the method, then discards the temporary String object. You can also use the String.length property with a string literal.

Constructor

stringObjectName = new String(stringLiteral)

where stringObjectName is the name of the new string object and stringLiteral is the value of the string object.

Methods

The methods of the String object are the following:

  • anchor - Creates HTML named anchor.

  • big, blink, bold,fixed, italics, small,strike, sub, sup - Creates HTML formatted string

  • charAt, charCodeAt - Returns the character or character code at the specified position in string.

  • indexOf, lastIndexOf - Returns the position of specified substring in the string or last position of specified substring, respectively.

  • link - Creates HTML hyperlink.

  • concat - Combines the text of two strings and returns a new string.

  • fromCharCode - Constructs a string from the specified sequence of ISO-Latin-1 codeset values.

  • split - Splits a String object into an array of strings by separating the string into substrings.

  • slice - Extracts a section of an string and returns a new string.

  • substring, substr - Returns the specified subset of the string, either by specifying the start and end indexes or the start index and a length.

  • match, replace, search - Used to work with regular expressions.

  • toLowerCase, toUpperCase - Returns the string in all lowercase or all uppercase, respectively.

Creating JavaScript Entries

JavaScript entries are contained in JavaScript folders located in a target system configuration or in the DirX Identity system configuration. To add a new JavaScript to DirX Identity, right-click any JavaScript folder and then select New JavaScript. You can then compose your JavaScript statements in the JavaScript code editor window.

Integrating JavaScript Entries

JavaScript entries can be easily integrated into a DirX Identity configuration. The different purposes require different techniques.

Constructing Object Property Values

JavaScript programs can be used to construct default values for an object’s properties. To integrate this type of a JavaScript entry, you extend the XML object description to reference the JavaScript entry from the object’s property definition, as follows:

<property name=... type =... defaultvalue="$(script:entryName)">
<script name="entryName" return="returnValue"
   reference="storage://DirXmetaRole/cn=entryName,cn=JavaScripts,$(./../..)?content=dxrObjDesc"/>
</property>

where entryName is the name of the JavaScript entry and returnValue is the constructed value to be used as the default value. The notation "$(script.entryName)" is a placeholder for the returned value of the JavaScript program. The variable notation $(./../..) in the reference property indicates a relative path starting from the XML object description that contains the property definition code. In the example shown here, the variable notation indicates a node two levels above the node of the object description (which can be a target system node or the domain configuration node).

Creating Consistency Checks for Property Values

JavaScript programs can also be used to check the consistency of a property value, usually during save operations. To add a consistency check, you add another reference within the property definition in the object description, as follows:

<property name=... type =... >
<script name="verifyContent"
    reference="storage://DirXmetaRole/cn=verifyContent,js,cn=JavaScripts,$(./../..)?content=dxrObjDesc"/>
</property>

The verifyContent.js JavaScript entry can, for example, check the length of the property value and display a log message if it is too short or too long.

You do not need to create a separate script to handle consistency checking. Instead, you can write the JavaScript code directly into the script tag, as follows:

<property name=... type =... >
<script name="verifyContent">
<![CDATA[
   importPackage(java.lang);
   obj=scriptContext.getObject();
   String value=obj.getProperty("someProperty");
   if (value.length() >= 10)
       System.out.println("someProperty is too long");
   else if (value.length < 5)
       System.out.println("someProperty is too short");
]]>
</script>
</property>

The location of the script tag determines the scope of the JavaScript program’s application. When inserted as shown in the previous example, it applies only to the property tag that encloses the script tag. When it is located within the properties tag, it applies to all properties of the object.

If the verifyContent script is property-specific (applied inside the property tag), you can get the value of the property via the scriptContext:

<property name=... type =... >
<script name="verifyContent">
<![CDATA[
  importPackage(java.lang);
  String value=scriptContext.getPropertyValue();
  if (value.length() >= 10)
      System.out.println("someProperty is too long");
  else if (value.length < 5)
      System.out.println("someProperty is too short");
]]>
</script>
</property>

In this case, ScriptContext also provides getPropertyDescriptor(), which returns the StoragePropertyDescriptor of this property.

Extending Object Operations

The following JavaScript programs can be used to extend operations on objects:

  • verifyContent - to be called before a property of an object is set.

  • save - to be called prior to saving of an object.

  • reset - to be called prior to resetting an object.

  • refresh - to be called prior to refreshing an object.

  • remove - to be called prior to removing an object.

  • commit - to be called prior to committing (performing) a transaction.

  • rollback - to be called prior to rolling back (discard) a transaction.

The scope of these scripts is again determined by the location of the script in the XML object description. If the script is located within the object tag, it runs on all corresponding operations for this object. If it is located outside any object tag located in a scripts.xml file entry, it runs on all respective operations for all objects. The file scripts.xml can be located as an additional object description in the corresponding folder.

Using the ScriptContext Object

A JavaScript program that is to be executed is passed the ScriptContext object as a global variable. This variable provides access to the application context and particularly to the object that is currently being manipulated. The ScriptContext object is referenced through the variable scriptContext. The following methods are relevant for script writers:

  • getObject() - Returns the StorageObject instance which is currently operated on. This object provides a getProperty method to return its properties and a setProperty(Object value) method to set a particular property.

  • getTransaction() - Returns the Transaction object which is currently used for the object manipulation. The Transaction object provides methods commit and rollback for performing or discarding a transaction.

    Normally, you should neither commit or rollback a transaction in the script. This should be left to the calling component, which created the transaction and might want to perform other changes. In case of a failure, which prevents storing the changes you should return an exception back to the caller rather than cause the transaction to rollback yourself.

Multi-value Attribute Handling

You can set default values of a multi-value attribute of an entry by setting these values in the assignment string, for example:

<property name="objectclass" defaultvalue="{dxrAccessPolicy,top}"/>

This instruction does not work if the values contain commas or if you want to perform more detailed operations. In this case, you must write a save script for an object. In this script, you can manage multi-value properties to any level of detail; for example:

obj.addMissing(propname, value) - add a missing value
obj.delExisting(propname, value) - delete a value

These methods work even if the value already exists. The system recognizes this automatically and simply does nothing.

You can call

obj.propertyModified(propname)

to check if the value of an attribute has changed to determine whether you need to calculate another value.

DirX Identity V8.6 allows you to create an array of your default values in the Javascript. In this case, you do not have the restrictions with containing commas or curly braces.

Here is an example returning an array of storage objects that should be used as default values for the secondary location link in combination with dependson:

Object description:

<property name="dxrSecLocationLink"
          type="siemens.dxm.storage.StorageObject"
          dependson="attname"
          label="More Locations"
          multivalue="true"
          defaultvalue="$(script:createDefLocs)"
          editorparams="choosefilter=dxrLocation;chooserootdn=cn=Countries,cn=BusinessObjects,$(rootDN)"
          editor="siemens.dxm.storage.beans.JnbReference"
          multivalueeditor="siemens.dxr.manager.controls.MetaRoleJnbMultiValue">
	<script name="createDefLocs"
	        return="locs"
	        reference="storage://DirXmetaRole/cn=defloc.js,cn=JavaScripts,cn=Configuration,$(rootDN)?content=dxrObjDesc"/>
</property>

Java script coding:

importPackage(java.lang);
importPackage(java.lang.reflect);
importPackage(java.util);
importPackage(Packages.siemens.dxm.storage);

var obj = scriptContext.getObject();
var sess = obj.getStorage();
// 2 fixed values
var l1 = sess.getObject("o=Mercato Aurum,cn=Companies,cn=BusinessObjects,cn=My-Company");
var l2 = sess.getObject("o=MultiMarket,cn=Companies,cn=BusinessObjects,cn=My-Company");
// add coding to handle dependencies for example add some more elements depending on attribute specified in dependson
…
// build array of StorageObjects
var objects = new Array(l1,l2);
var locs = toJavaArray(objects); // locs is the return variable

// convert JavaScript Array to Java Array
function toJavaArray(jsArray) {
    var javaArray = java.lang.reflect.Array.newInstance(StorageObject, jsArray.length);
    for (var i=0; i<jsArray.length; i++) {
        javaArray[i] = jsArray[i];
    }
    return javaArray;
}

LDAP Searches in JavaScript

Sometimes you need to perform LDAP searches in a JavaScript file, either in account creation or in consistency rules. DirX Identity lets you access the existing LDAP connection and offers a simple interface to receive the search result.

First you need a storage object from the script context, which holds the LDAP connection. Then you create a new object collection, which represents the search result. Pass the storage object and the usual LDAP search attributes to its constructor. See the following code snippet for a sample:

searchBase = "cn=Accounts,cn=myTargetSystem,cn=TargetSystems,cn=My Domain";
filter = ...;
stg = scriptContext.getObject.getStorage();
searchResult = new ObjectCollection(stg, searchBase, filter, 2, 0, 0, attrs, sort_attrs, ascending, null);

The other parameters are known from the LDAP interface of the Mozilla Netscape API: scpe, sizelimit, timelimit, requested attributes, sorting attributes.

Creating a Provisioning Entry

The following script shows how to create an entry - here a permission - in a provisioning domain:

importPackage(java.lang);
var obj=scriptContext.getObject();
// the parent folder for the permission to be created
var permissionFolder = "cn=Test,cn=Permissions,cn=My-Company";

// Create a permission with common name MyTestPermission in permissionFolder
var permission = createPermission(permissionFolder, "MyTestPermission");
if (permission != null) {
   scriptContext.logInfo("Permission dn="+permission.getDN()+" successfully created.");
   result = "SUCCESS";
}
else {
   result = "FAILURE";
}

// creates a permission with DN cn=<commonName>,<parentDN>
// using the object description with name dxrPermission.
function createPermission(parentDN, commonName) {
   var storage = scriptContext.getUserStorage();
   var od = storage.getObjectDescriptor("dxrPermission");
   var permission = storage.createObject(od, parentDN, commonName);
   try {
     permission.save();
   }
   catch (e) {
     scriptContext.logError("Create permission "+commonName+" failed: Error during save: "+e.toString());
     permission = null;
   }
   return permission;
}

Other Useful Methods

This section explains some other useful methods:

  • obj.save() - saves the object.

  • obj.checkAndSave() - runs a privilege resolution and saves the object.

Logging and Debugging

You can write your own debug messages:

obj.logger.log("DBG", "_text_");

for example

obj.logger.log("DBG", "dxrNameForAccount: tries="+tries);

To view the debug messages in the DirX Identity Manager log, set the following switches in the dxi.cfg file:

trace.level=9
trace.transcript=on

The second switch doubles some standard messages, that means you should use this switch only for debugging. For example the check for uniqueness is logged per default as well as the call of the Java Script and the result. You will find the messages in the system.nnn.log files in the folder install_path\GUI\log.

An example logging with the above described debug message looks like:

DBG(SVC102): Checking uniqueness of attribute=dxrname value=FFFFLLLLLLLL in subtree cn=ADS,cn=TargetSystems,cn=My-Company
DBG(SVC102): Check failed!
LOG(STG200): execute script 'dxrNameForAccounts' ...
LOG(STG200): execute script 'dxrNameForAccounts' ...
DBG(SVC102): dxrNameForAccount: tries=3
LOG(STG200): result is 'FFFFFLLLLLLLL' (0ms)
LOG(STG200): result is 'FFFFFLLLLLLLL' (0ms)
DBG(SVC613): Saving object DN=cn=test4,cn=Accounts,cn=TargetSystems,cn=My-Company.

Writing Java Extensions

This chapter contains hints for writing Java extensions, including:

  • How to write an extension to implement a consistency rule

  • How to write, configure and deploy a custom status module for users, personas and functional users

Writing an Extension to Implement a Consistency Rule

You can write your own Java extensions; for example, to implement a consistency rule.

The next sections describe a sample for a consistency rule using a Java action. The sample shows the interrelation of the Java program, the configuration and how to install it.

About the Java Action

The sample contains a Java action implemented in the method setSubjectAttribute of class com.mycompany.actions.ConsistencyActions.

Object setSubjectAttribute(StorageObject node, String attribute, String namingRule);

The method is used to create a new value for the subject’s attribute whose name is given in 'attribute' by evaluation of the supplied namingRule. The subject is passed in the parameter node. In simulation mode, the change is reported but node is not saved. In real mode, the node is saved making the change permanent.

Building the Project

We assume here that you are familiar with building Java projects and that ant and a Java compiler are installed and in your path.

To build the project, an ant build script (build.xml) is provided. The path to the DirX Identity installation must be adjusted: just correct the value of the property metarole.dir.

A successful run of ant will create the jar-file rules.jar in the local lib directory.

Installing rules.jar

To make rules.jar available to the DirX Identity Policy Agent, it must be added to the agent’s class path. The path for rules.jar must be configured:

REM add custom rules.jar here
SET MYRULES=C:\my-company\rules\lib\rules.jar

Next, the classpath of the original batch script MetaRolePolicyAgent.bat must be extended by %MYRULES%, for example:

"%DXI_JAVA_HOME%\bin\java" %debug% -Xmx512m -classpath "%DXICP%;%MYRULES%" -Djava.net.preferIPv4Stack=true siemens.dxr.policy.agent.PolicyAgent …​

Don’t just copy this line from the original: extend -classpath ("…​;%MYRULES%") to ensure that the currently referenced jars are not removed from the path.

Configuring the Action

The action is configured in the Operations tree of the Policy view of DirX Identity Manager. You may add some OperationContainers to structure this view. Then, a 'Java Class' object must be added whose name corresponds to the fully qualified classname of the Java class that implements the actions.

In our sample, this is com.mycompany.actions.ConsistencyActions.

Next, select the 'Java Class' configuration object and add a 'Java Method Action' for each method of this class that is intended to be used in consistency rules.

In our example, this is the method 'setSubjectAttribute'.

Next, configure the method’s parameters. The variable node contains the 'subject', the object being returned by the search configured in the consistency rule. Choose "subject" for the parameter’s context.

Attribute and namingRule are not determined at runtime but must be supplied by the rule using the action. Thus, choose "fixed" for their context. You can add more actions that are implemented by the same class.

Viewing the Configuration Sample

A configuration sample for the My-Company sample domain is provided in the file rules.zip on your DVD in the folder:

Documentation\DirXIdentity

Just import this sample. The action is configured under Operations/Java action samples/Java, a sample rule is provided under Rules/Java consistency rule samples.

The rule 'create account description' adds a description for all accounts that have an empty description attribute. It references the action 'setSubjectAttribute'.

When the rule is run, the policy agent performs the search defined in the rule’s filter. This search returns all accounts with empty description. For each account, the method 'setSubjectAttribute' is called with the current account and the fixed parameters 'attribute' and 'namingRule' defined by the rule.

You can define multiple rules that make use of the same action; for example, you could modify a user’s attribute that makes use of another naming rule.

Handling Multi-value Attributes

If you want to handle a multi-value attribute, you need to define this attribute to multi-value in the user.xml definition. Then you can work on this attribute (in this example, the attribute 'verified'):

importPackage(java.lang.reflect);

var verified = obj.getProperty("verifiedBy");

for (var j=0; j<java.lang.reflect.Array.getLength(verified); j++) {
    if ( verified[j].equals("somestring") ) {
        dosomething();
    }
}

Implementing Custom User Status Modules

The section "Managing States" in the DirX Identity Provisioning Administration Guide describes the default state machines for users, personas and functional users provided by DirX Identity.

To write your own customized state machines for users, personas or functional users, you:

  • Write your own Java class that implements the StateMachine interface

  • Configure and deploy your customized state machine for the object

Implementing the StateMachine Interface

The StateMachine interface contains String constants for the states and a definition of the method calculateState:

package siemens.dxr.service.states.api;
import siemens.dxr.service.ServiceException;
import siemens.dxr.service.nodes.SvcNode;
import siemens.dxr.service.store.SvcSession;
public interface StateMachine {
    public static final String ENABLED = "ENABLED";
    public static final String TBDEL = "TBDEL";
    public static final String TEMPLATE = "TEMPLATE";
    public static final String NEW = "NEW";
    public static final String DISABLED = "DISABLED";
    /**
    * Calculate the state for a user, persona or functional user
    * @param session The session holding the ldap bind
    * @param obj The object whose state is to be calculated
    * @return The new state
    */
     public String calculateState(SvcSession session, SvcNode obj) throws ServiceException;
}

Here is the implementation of the persona’s standard state machine. It is adapted from the user’s state machine with the following extensions:

  • The first statement in the method reads the state of the associated user (the persona’s owner). If this state equals TBDEL, no further calculation is done but the state is also used for the persona.

  • At the end of the method, it is checked if the user’s state equals DISABLED and the persona’s state is NOT TBDEL. In this case, DISABLED is also used as the persona’s state.

package siemens.dxr.service.states.impl;

import com.siemens.dirxcommon.logging.LogSupport;
import siemens.dxm.storage.StorageException;
import siemens.dxm.storage.StorageObject;
import siemens.dxm.util.GeneralizedTime;
import siemens.dxr.service.ISvcCodes;
import siemens.dxr.service.PropName;
import siemens.dxr.service.ServiceException;
import siemens.dxr.service.msgout.MsgID;
import siemens.dxr.service.nodes.SvcNode;
import siemens.dxr.service.states.api.StateMachine;
import siemens.dxr.service.store.SvcSession;

public class PersonaStateMachine implements StateMachine {

    LogSupport logger = LogSupport.forName(PersonaStateMachine.class);

    @Override
    public String calculateState(SvcSession session, SvcNode obj) throws ServiceException {
        String userState = getAssociatedUserState(session, obj);
        // take TBDEL state from user
        if (StateMachine.TBDEL.equals(userState)) {
            return userState;
        }
        int rc = ISvcCodes.SVC_UNCHANGED;
        String state;
        String newState = obj.getValue(PropName.dxrState);
        int counter = 0;
        // create actDate and initialize with today's date
        GeneralizedTime actDate = new GeneralizedTime();
        actDate.initDate();
        // get dates
        GeneralizedTime startDate = (GeneralizedTime) obj.getProperty(PropName.dxrStartDate);
        GeneralizedTime disableStartDate = (GeneralizedTime) obj.getProperty(PropName.dxrDisableStartDate);
        GeneralizedTime disableEndDate = (GeneralizedTime) obj.getProperty(PropName.dxrDisableEndDate);
        GeneralizedTime endDate = (GeneralizedTime) obj.getProperty(PropName.dxrEndDate);
        do {
            state = newState;
            // TEMPLATE does not change
            if (state.equalsIgnoreCase(StateMachine.TEMPLATE)) {
            } else if (state.equalsIgnoreCase(StateMachine.NEW)) {
                // start date empty or reached ==> change to ENABLED
                if (startDate == null || (startDate.compareDateTo(actDate) <= 0)) {
                    newState = StateMachine.ENABLED;
                }
            } else if (state.equalsIgnoreCase(StateMachine.ENABLED)) {
                // start date in future ==> change to NEW
                if ((startDate != null) && (startDate.compareDateTo(actDate) > 0)) {
                    newState = StateMachine.NEW;
                }
                // disableStartDate reached ==> disable user
                if ((disableStartDate != null) && (disableStartDate.compareDateTo(actDate) <= 0)) {
                    if (disableEndDate == null || (disableEndDate.compareDateTo(actDate) >= 0)) {
                        newState = StateMachine.DISABLED;
                    }
                }
                // end date passed ==> change to TBDEL
                if (endDate != null && endDate.compareDateTo(actDate) < 0) {
                    newState = StateMachine.TBDEL;
                }
            } else if (state.equalsIgnoreCase(StateMachine.DISABLED)) {
                // disableStartDate empty or in future ==> enable user
                if (disableStartDate == null || disableStartDate.compareDateTo(actDate) > 0) {
                    newState = StateMachine.ENABLED;
                }
                // disableEndDate passed ==> enable user
                if (disableEndDate != null && disableEndDate.compareDateTo(actDate) < 0) {
                    newState = StateMachine.ENABLED;
                }
                // end date passed ==> change to TBDEL
                if (endDate != null && endDate.compareDateTo(actDate) < 0) {
                    newState = StateMachine.TBDEL;
                }
            } else if (state.equalsIgnoreCase(StateMachine.TBDEL)) {
                // Enable user in case end date does not exist or is in future
                if (endDate == null || endDate.compareDateTo(actDate) > 0) {
                    newState = StateMachine.ENABLED;
                }
            } else {
                // User DN=%s has an illegal state: '%s'.
                logger.log(MsgID.ERR_USER_STATE_WRONG, obj.getDN(), state);
                rc = ISvcCodes.SVC_RESOLUTION_ERROR;
                throw new ServiceException("Illegal state detected", rc);
            }
            logger.log(MsgID.DBG, "persona state is " + state + ", newState is " + newState);
        }
        while (!state.equalsIgnoreCase(newState) && counter++ < 20);
        // state cycle detected
        if (!state.equalsIgnoreCase(newState)) {
            // State cycle detected for user DN=%s: last state=%s, new state=%s.
            logger.log(MsgID.ERR_USER_STATE_CYCLE, obj.getDN(), state, newState);
            rc = ISvcCodes.SVC_RESOLUTION_ERROR;
            throw new ServiceException("State cycle detected", rc);
        }
        // if the user is DISABLED and the persona is not TBDEL ==> DISABLE persona
        if (!StateMachine.TBDEL.equals(state) && StateMachine.DISABLED.equals(userState)) {
            state = userState;
        }
        return state;
    }

    /**
     * Checks whether the associated user exists and is in TBDEL state.
     *
     * @param session The session holding the ldap bind
     * @param obj     The object whose state is to be calculated
     * @return The associated user's state
     */
    private String getAssociatedUserState(SvcSession session, SvcNode obj) {
        String associatedEntryDN = obj.getValue(PropName.owner);
        if (associatedEntryDN != null && associatedEntryDN.length() > 0) {
            try {
                StorageObject associatedEntry = session.getObject(associatedEntryDN);
                return associatedEntry.getValue(PropName.dxrState);
            } catch (StorageException e) {
                // Cannot retrieve state of associated user DN={0} of persona DN={1}: {2}.
                logger.log(MsgID.ERR_ASSOC_USER_STATE, associatedEntryDN, obj.getID());
            }
        }
        return null;
    }
}

Configuring the Custom Status Module

The status module called for state calculations is configured in the object description; for example, PersonaCommon.xml for a persona. It uses the defaultstatusmodule element. This element must contain the fully-qualified Java class name of your implementation:

<defaultstatusmodule>siemens.dxr.service.states.impl.PersonaStateMachine</defaultstatusmodule>.

You can configure your own status modules for all user types for which you have created your own object descriptions.As long as the defaultstatusmodule tag is not defined for a user type, the standard status handling of the user is used.

Deploying the Custom Status Module

Compile your custom Java class into a jar file and then copy this jar file to all of the locations at which dxrServices.jar is installed.This is the only task you need to perform to have the Java-based Server, Web Center and Web Services find your code.

For applications that are started by a batch script (for example, DirX Identity Manager, the Policy Agent, the Service Agent, and so on) you must also add your custom jar file to the classpath or a ClassNotFoundException will occur.

Customizing User Creation Workflows

This section describes how to customize the creation workflows for personas, user facets and functional users.

Customizing the Persona Create Workflow

Whenever you use Web Center to create a new persona for a selected user, it starts the persona create workflow.This section describes the workflow’s PersonaFromUser activity and gives hints on how to customize it to your requirements.

When it starts the PersonaFromUser workflow, Web Center adds two attributes to the workflow context:

  • associatedEntry - the DN of the persona’s owner

  • destinationType - the object description name of the object to be created; that is, dxrPersona for a persona

These two context attributes are configured in the workflow’s PersonaFromUser activity with the parameters Context attribute for associated entry/destinationType. As long as you do not change Web Center’s configuration of the attribute names, do not change these settings.

The PersonaFromUser activity first uses these context attributes to calculate the parent folder for the new persona:

  • The activity reads the associated entry using the DN contained in the associatedEntry context attribute

  • The activity uses associated entry’s parent folder as the default folder for the new persona. In the PersonaFromUser activity, you can override this default folder by configuring the activity’s Parent Folder for Subject parameter. In Web Center, whether you can change the default folder depends on the configuration of the Enter Attributes’ User Base parameter: if the parameter is configured, the default value can be changed by the administrator. If not, the folder is not visible in Web Center and the default value is used.

The PersonaFromUser activity next uses the context attributes to calculate default values for some attributes using the associate entry as a template. To calculate these values, the activity uses a special object description that exists only for this purpose. To determine the object description’s name, the activity:

  • Reads the object description name from the destinationType context attribute. For a persona, this is dxrPersona, that is, the object description for a persona object if it is read from the Identity Store or is created in Identity Manager.

  • Searches the associated entry’s object description for the createFrom element to get the name of the object description for persona creation. For a user, two createFrom elements are present in UserCommon.xml:

    <createFrom destinationType="dxrPersona" objectDescriptionName="PersonaFromUser"/>
    <createFrom destinationType="dxrFunctionalUser" objectDescriptionName="FunctionalUserFromUser"/>

With the destinationType dxrPersona, the first createFrom tag matches and the activity creates the default values by applying the PersonaFromUser object description to the associatedEntry.

This mechanism has the advantage of allowing you to use the persona create workflows to create personas from personas (that is, a persona is the persona’s owner). For this purpose, the following createFrom tags are defined in PersonaCommon.xml:

<createFrom destinationType="dxrFunctionalUser" objectDescriptionName="FunctionalUserFromPersona"/>
<createFrom destinationType="dxrPersona" objectDescriptionName="PersonaFromPersona"/>

You can override this object description name by configuring the PersonaFromUser activity’s Name of Object Description parameter.

The PersonaFromUser activity creates a new persona object by applying the object description for persona creation (for example, UserToPersona) to the associatedEntry (which is a user in this case). You can modify this object description according to your needs.

The new persona’s common name is created using the JavaScript CommonNameForPersona.js:
<property name="cn" defaultvalue="$(script:CommonNameForPersona)">
    <script name="CommonNameForPersona"
    return="cn"
    reference="storage://DirXmetaRole/cn=CommonNameForPersona.js,cn=JavaScripts,cn=Customer Extensions,cn=Configuration,$(rootDN)?content=dxrObjDesc"/>
</property>

The script uses the user’s sn and givenName together with a UID to create a unique common name:

// calculates a unique common name for a persona
  importPackage(java.lang);
  var obj=scriptContext.getObject();
  if (obj.isNewObject()) {
     var givenName = obj.getValue("givenName");
     var sn = obj.getValue("sn");
     var uid = obj.getStorage().getUID("persona");
     obj.setProperty("dxrUID", uid);
     var cn = sn + " " + givenName + " " + uid;
  }
  var result = "SUCCESS";

You can change this script to use a more convenient common name that complies with your processes.

Customizing the User Facet Create Workflow

Whenever you use Web Center to create a new user facet for a selected user, it starts the user facet create workflow.

Customizing the Functional User Create Workflow

Whenever you use Web Center to create a new functional user for a selected user, it starts a functional user create workflow. This section describes the workflow’s FunctionalUserFromUser activity and gives hints on how to customize it to your requirements.

When it starts the FunctionalUserFromUser workflow, Web Center adds two attributes to the workflow context:

  • associatedEntry - the DN of the functional user’s sponsor

  • destinationType - the object description name of the object to be created; that is, dxrFunctionalUser for a functional user

These two context attributes are configured in the workflow’s FunctionalUserFromUser activity with the parameters Context attribute for associated entry/destinationType. As long as you do not change Web Center’s configuration of the attribute names, do not change these settings.

The FunctionalUserFromUser activity first uses these context attributes to calculate the parent folder for the new functional user:

The activity reads the associated entry using the DN contained in the associatedEntry context attribute. The activity uses the associated entry’s parent folder the default folder for the new functional user. In the activity, you can override this default folder by configuring the FunctionalUserFromUser activity’s Parent Folder for Subject parameter. The sample domain does this for the functional user create workflows, since it is a common practice to place the resources under a common folder. In Web Center, whether or not you can change this default folder depends on how the Enter Attributes’ User Base parameter is configured: if the parameter is configured, the default value can be changed by the administrator. If it is not configured, the folder is not visible in Web Center and the default value is used. Thus, for the create functional user workflows, the Enter Attributes’ User Base parameter is left empty, and all functional users are created under the folder configured in the Parent Folder for Subject parameter. See the section "Customizing the Persona Create Workflow" for instructions on how to configure a parent folder that can be changed by the administrator.

The FunctionalUserFromUser activity next uses these context attributes to calculate default values for some attributes, using the associate entry as a template. To calculate these values, the activity uses a special object description that exists only for this purpose. To determine the object description’s name, the activity:

  • Reads the object description name from the destinationType context attribute. For a functional user, this is dxrFunctionalUser; that is, the object description for a functional user object if it is read from the Identity Store or is created in DirX Identity Manager.

  • Searches for the createFrom element in the associated entry’s object description to get the name of the object description for functional user creation. For a user, two createFrom elements are present in UserCommon.xml:

    <createFrom destinationType="dxrPersona" objectDescriptionName="PersonaFromUser"/>
    <createFrom destinationType="dxrFunctionalUser" objectDescriptionName="FunctionalUserFromUser"/>

With the destinationType dxrFunctionalUser, the second createFrom tag matches and the activity creates the default values by applying the FunctionalUserFromUser object description to the associatedEntry.

This mechanism has the advantage of allowing the functional user create workflows to be used to create functional users from personas (that is, a persona is the functional user’s sponsor). For this purpose, the following createFrom tags are defined in PersonaCommon.xml:

<createFrom destinationType="dxrFunctionalUser" objectDescriptionName="FunctionalUserFromPersona"/>
<createFrom destinationType="dxrPersona" objectDescriptionName="PersonaFromPersona"/>

You can override this object description name by configuring FunctionalUserFromUser activity’s Name of Object Description parameter.

The FunctionalUserFromUser activity creates a new functional user object by applying the object description for functional user creation (for example, FunctionalUserFromUser) to the associatedEntry (which is a user in this case). You can customize this object description according to your needs.

The new functional user’s common name is created using the JavaScript CommonNameForFunctionalUsers.js. The script uses a UID to create a unique common name:
importPackage(java.lang);
var obj=scriptContext.getObject();
if (obj.isNewObject()) {
    var cn = obj.getStorage().getUID("persona");
    obj.setProperty("dxrUID", cn);
}
var result = "SUCCESS";
In Web Center, the common name is editable, so you can use this UID or enter a new common name for your resource.

You can also change this script to use a more convenient common name that complies with your processes.

Customizing the Consistency Rules for Automatic Creation

This section describes how to customize the consistency rules for automatic persona, functional user and user creation.

Customizing the Consistency Rule for Automatic Persona Creation

If multiple accounts in a target system are assigned to a single user, DirX Identity only manages one of these accounts: the primary account.DirX Identity’s role resolution process only manages the primary account’s group assignments and state.

In the chapter "Managing Target Systems" in the DirX Identity Provisioning Administration Guide, the subsections "Full Provisioning" and "Partial Provisioning" (see the section "Managing Personal Accounts") describe the different options for handling multiple accounts in a target system.If you decide to use the "Full Provisioning" approach, you must create personas for each assigned non-primary account.You can create the personas interactively in Web Center, and then manually link the account to the new persona, or you can use the consistency rule CreatePersonasForNonPrimaryAccounts to automate the process.

The CreatePersonasForNonPrimaryAccounts rule offers you the following features:

  • It checks to see if a primary account already exists for the user in the account’s target system.

  • Its persona creation process is configured in a JavaScript file that can be specific to the account’s target system. The JavaScript controls which attributes are read from the account, which attributes are read from the user, and the new persona’s parent folder and naming.

  • The user referenced from the account by its dxrUserLink is the persona’s owner.

  • It re-links the accounts' dxrUserLink to the new persona.

  • It can either create the new persona directly in the Identity Store, or it can start an approval workflow for persona creation that permits an approver to make changes to some attributes.

The next sections describe the components of this rule and their default operations and indicate how they can be customized.

How the Consistency Rule Works

The CreatePersonasForNonPrimaryAccounts consistency rule is located under the path PoliciesRulesDefaultConsistencyPersonas. By default, the rule performs the account search under all target systems with the following filter:

( objectclass="dxrTargetSystemAccount" and dxruserlink=* and ( not ( dxrisprimary=* ) or dxrisprimary="false" ) )

As you can see from the filter definition, the rule processes all accounts that are already assigned to a user and which are not flagged as primary accounts.

The rule passes to its AccountToPersona operation the name of the JavaScript that controls the persona creation process (in the nameOfJavaScript parameter). By default, the JavaScript AccountToPersona.js is configured as the name.

How the Operation Works

The AccountToPersona operation is located under the path PoliciesOperationsDefaultJavasiemens.dxr.policy.actions.PersonaManagementAccountToPersona.

First, the operation checks whether the user already has a primary account in the target system. If this is not the case, an error message is issued and the operation is aborted for the selected account.

Next, the operation next searches for JavaScripts with the RDN provided in the nameOfJavaScript parameter (by default, AccountToPersona.js) in the following order:

  • In the JavaScripts container of the account’s target system folder (target system-specific configuration).

  • In the JavaScripts container of the Customer Extensions folder. This folder is a customer-specific default configuration that is independent of the target system.

  • In the system configuration’s JavaScripts container. A template JavaScript is installed in this location. We recommend copying this template to the appropriate location - target system-specific or general Customer Extensions - and then tailoring it to your requirements.

If the operation finds a JavaScript with the configured RDN, it runs it. This JavaScript controls the creation of the persona from the user’s and the account’s data.

How the JavaScript Works

The AccountToPersona.js JavaScript calls a handler, implemented in Java that contains the program logic. In its configuration section, the JavaScript provides the following variables for configuring this handler:

  • createGroupAssignments - a Boolean flag. When set to true, the handler creates direct group assignments for the groups assigned to the account.

  • userAttributesToCopy - an array of strings that contain the names of attributes to be copied from the related user to the persona.

  • userAttributesToCopyWithNameMapping - similar to userAttributesToCopy, but source and target attribute names are different and are separated by a period (.). For example, dn.owner specifies that the user’s dn is copied to the persona’s owner attribute.

  • accountAttributesToExclude - an array of strings that contain the names of attributes that are not to be copied. By default, the handler copies all attributes that exist in the persona’s and in the account’s object description with the same name. Use this variable to define attributes that the handler should not copy.

  • accountAttributesToCopyWithNameMapping - the attributes to copy from the account to the persona with different source and target attribute names, separated by a period (.).

  • approvalWorkflowDN - the DN of an approval workflow to be started for approval of persona creation.

The configuration section is followed by:

  • Providing the handler, by calling scriptContext.getHandler().

  • Reading account and user from the handler.

  • Supplying the persona’s parentID with the parentID of the user.

  • Creating the persona’s cn from the user’s and the account’s cn.

  • Initializing the handler with the configured persona’s name and parent folder parameters: handler.initialize(cnOfPersona, parentID).

  • Calling the handler’s preparePersona method. This method performs all copying actions defined in the configuration section. This call creates a complete persona in memory with all attributes populated according to the variable definitions given the configuration section.

  • Reading the persona from the handler, handler.getPersona()

  • Calling the JavaScript "addAttributes" method. This method provides the option of creating persona attribute values using the full flexibility of JavaScript. In the sample, the description is populated.

  • Selecting whether to create the persona with an approval workflow or directly in the Identity Store:

    • To start an approval workflow, use handler.startApprovalWorkflow(approvalWorkflowDN).

    • To create the persona directly in the Identity Store, use handler.createPersonaInLdap().

How the Approval Workflow Works

The JavaScript variable approvalWorkflowDN contains the DN of the sample workflow Approve Create Persona from Account.

This workflow is a simplified variant of the Create Persona workflows, with only one approval activity - a Calculate GUID activity - and an Apply Change activity. The workflow does not contain any Enter Attributes or Request Privileges activities, since this data is already provided by the consistency rule in the form of subject and resource orders that are passed to the workflow when it is started. The absence of activities for entering data and assignments makes this workflow unsuitable for creating a new persona in Web Center.

You can tailor the Approve Create Persona from Account workflow to your needs; for example, you can change the set of attributes in the approval step, remove the calculate GUID activity or replace it with your own GUID generating activity. But note that the Apply Change activity uses a user hook called siemens.dxr.service.order.impl.ModifyAccountUserHook. This user hook is mandatory for re-linking the dxrUserLink to the persona prior to saving (and resolving) the new persona; otherwise the role resolution performed in the Apply Change step would create a new account for the new persona if direct group assignments are created for the persona.

Customizing the Consistency Rule for Automatic Functional User Creation

If you have imported accounts that cannot be assigned to a user, and you want to use the “Full Provisioning” approach (see the subsection "Full Provisioning" in the section "Managing Personal Accounts" in the chapter "Managing Target Systems" in the DirX Identity Provisioning Administration Guide), you need to decide whether you want to create a user or a functional user for the account. If the account represents a resource, like a global mailbox, a meeting room, or a trainee account, you may want to create a functional user for this account, rather than a user.

You can create the functional user interactively in Web Center and then manually link it to this functional user, or use the CreateFunctionalUsersForUnassignedAccounts consistency rule for automatic functional user creation.

You must be extremely careful when running this rule, since it will create functional users for all unassigned accounts of all target systems, and this is not easy to undo! You are responsible for ensuring that this action is only executed on accounts for which it makes sense to create a functional user. To accomplish this task, you can:

  • Run the rule in simulation mode first and examine its results, if you want to create the functional users directly in the Identity Store.

  • Create an approval workflow for each functional user, where the approval step verifies that functional user creation makes sense.

The CreateFunctionalUsersForUnassignedAccounts rule offers you the following features:

  • Its functional user creation process is configured in a JavaScript file that can be specific to the account’s target system. The JavaScript controls which attributes are read from the account, and the new functional user’s parent folder and naming.

  • It links the account’s dxrUserLink to the new functional user.

  • It can create the new functional user directly in the Identity Store or start an approval workflow for functional user creation that permits an approver to make changes to some attributes or reject the functional user creation entirely.

The next sections describe the components of this rule and their default operations and indicate how they can be customized.

How the Consistency Rule Works

The CreateFunctionalUsersForUnassignedAccounts consistency rule is located under the path PoliciesRulesDefaultConsistencyFunctionalUsers. By default, the rule performs the account search under all target systems using the filter definition:

( objectclass="dxrTargetSystemAccount" and not ( dxruserlink=* ) )

As you can see from the filter definition, the rule processes all accounts that are not already assigned to a user.

The rule passes name of the JavaScript that controls the functional user creation (in the parameter nameOfJavaScript) to its AccountToFunctionalUser operation. By default, the AccountToFunctionalUser.js JavaScript is configured as the name.

How the Operation Works

The AccountToFunctionalUser rule operation is located under the path PoliciesOperationsDefaultJavasiemens.dxr.policy.actions.FunctionalUserManagementAccountToFunctionalUser.

The operation searches for JavaScripts with the RDN provided in the nameOfJavaScript parameter (by default, the name AccountToFunctionalUser.js) in the following order:

  • In the JavaScripts container of the account’s target system (target system-specific configuration).

  • In the JavaScripts container of the Customer Extensions folder. This folder is a customer-specific default configuration that is independent of the target system.

  • In the system configuration’s JavaScripts container. Here a template JavaScript is installed in this location. We recommend copying this template to the appropriate location (target system-specific or general Customer Extension) and tailoring it to your requirements.

If the operation finds a JavaScript with the configured RDN, it runs it. This JavaScript controls the creation of the functional user from the account’s data.

How the JavaScript Works

The AccountToFunctionalUser.js JavaScript calls a handler, implemented in Java that contains the program logic. In its configuration section, the JavaScript provides the following variables for configuring this handler:

  • createGroupAssignments - a Boolean flag. When set to true, the handler creates direct group assignments for the groups assigned to the account.

  • accountAttributesToExclude - an array of strings that contain the names of attributes that are not to be copied. By default, the handler copies all attributes that exist in the functional user’s and in the account’s object descriptions with the same name. Use this variable to define attributes that the handler should not copy.

  • accountAttributesToCopyWithNameMapping - the attributes to copy from the account to the functional user with different source and target attribute names, separated by a period (.).

  • approvalWorkflowDN - the DN of an approval workflow to be started for approval of functional user creation.

The configuration section is followed by:

  • Providing the handler, by calling scriptContext.getHandler().

  • Reading the account from the handler.

  • Supplying the functional user’s parentID with a constant DN.

  • Creating the functional user’s cn from the account’s cn.

  • Initializing the handler with the configured functional user name and parent folder: handler.initialize(cnOfFunctionalUser, parentID).

  • Calling the handler’s prepareFunctionalUser method. This method performs all copying actions defined in the configuration section. This call creates a complete functional user in memory with all attributes populated according to the variable definitions given the configuration section.

  • Reading the functional user from the handler, handler.getFunctionalUser().

  • Calling the JavaScript "addAttributes" method. This method provides the option of creating functional user attribute values using the full flexibility of JavaScript. In the sample, the description is populated.

  • Selecting whether to create the functional user with an approval workflow or directly in the Identity Store:

    • To start an approval workflow, use handler.startApprovalWorkflow(approvalWorkflowDN).

    • To create the functional user directly in the Identity Store, use handler.createFunctionalUserInLdap().

How the Approval Workflow Works

The JavaScript variable approvalWorkflowDN contains the DN of the sample workflow Approve Create Functional User from Account.

This workflow is a simplified variant of the Create Functional User workflows, with only one approval activity - a Calculate GUID activity - and an Apply Change activity. The workflow does not contain any Enter Attributes or Request Privileges activities, since this data is already provided by the consistency rule in the form of subject and resource orders that are passed to the workflow when it is started. The absence of activities for entering data and assignments makes this workflow unsuitable for creating a new functional user in Web Center.

You can tailor the Approve Create Functional User from Account workflow to your needs; for example, you can change the set of attributes in the approval step, remove the calculate GUID activity or replace it with your own GUID generating activity. But note that the Apply Change activity uses a user hook called siemens.dxr.service.order.impl.ModifyAccountUserHook. This user hook is mandatory for re-linking the dxrUserLink to the functional user prior to saving (and resolving) the new functional user; otherwise the role resolution performed in the Apply Change step would create a new account for the new functional user if direct group assignments are created for the functional user.

Customizing the Consistency Rule for Automatic User Creation

If you have imported accounts that cannot be assigned to a user, and you want to use the “Full Provisioning” approach (see the subsection "Full Provisioning" in the section "Managing Personal Accounts" in the chapter "Managing Target Systems" in the DirX Identity Provisioning Administration Guide), you must decide whether you want to create a user or a functional user for the account.

If the criteria for creating a functional user do not apply, or you have a target system that is connected to your human resources system that is the master for user management, you may want to create a user from the unassigned account.

You can create the functional user interactively in Web Center and then manually link it to this functional user, or use the CreateUsersForUnassignedAccounts consistency rule for automatic user creation.

You must be extremely careful when running this rule, since it will create users for all unassigned accounts of all target systems, and this is not easy to undo! You are responsible for ensuring that this action is only executed on accounts for which it makes sense to create a user. To accomplish this task, you can:

  • Run the rule in simulation mode first and examine its results, if you want to create the users directly in the Identity Store.

  • Create an approval workflow for each functional user, where the approval step verifies that functional user creation makes sense.

The CreateUsersForUnassignedAccounts rule offers you the following features:

  • Its user creation process is configured in a JavaScript file that can be specific to the account’s target system. The JavaScript controls which attributes are read from the account, and the new user’s parent folder and naming.

  • It links the account’s dxrUserLink to the new user.

  • It can create the new user directly in the Identity Store or start an approval workflow for user creation that permits an approver to make changes to some attributes or reject the user creation entirely.

The next sections describe the components of this rule and their default operations and indicate how they can be customized.

How the Consistency Rule Works

The CreateUsersForUnassignedAccounts consistency rule is located under the path PoliciesRulesDefaultConsistencyUsers. By default, the rule performs the account search under all target systems using the filter definition:

( objectclass="dxrTargetSystemAccount" and not ( dxruserlink=* ) )

As you can see from the filter definition, the rule processes all accounts that are not already assigned to a user.

The rule passes name of the JavaScript that controls the functional user creation (in the parameter nameOfJavaScript) to its AccountToUser operation. By default, the AccountToUser.js JavaScript is configured as the name.

How the Operation Works

The AccountToUser rule operation is located under the path PoliciesOperationsDefaultJavasiemens.dxr.policy.actions.UserManagementAccountToFunctionalUser.

The operation searches for JavaScripts with the RDN provided in the nameOfJavaScript parameter (by default, the name AccountToUser.js) in the following order:

  • In the JavaScripts container of the account’s target system (target system-specific configuration).

  • In the JavaScripts container of the Customer Extensions folder. This folder is a customer-specific default configuration that is independent of the target system.

  • In the system configuration’s JavaScripts container. A template JavaScript is installed in this location. We recommend copying this template to the appropriate location (target system-specific or general Customer Extension) and tailoring it to your requirements.

If the operation finds a JavaScript with the configured RDN, it runs it. This JavaScript controls the creation of the user from the account’s data.

How the JavaScript Works

The AccountToUser.js JavaScript calls a handler, implemented in Java that contains the program logic. In its configuration section, the JavaScript provides the following variables for configuring this handler:

  • createGroupAssignments - a Boolean flag. When set to true, the handler creates direct group assignments for the groups assigned to the account.

  • accountAttributesToExclude - an array of strings that contain the names of attributes that are not to be copied. By default, the handler copies all attributes that exist in the user’s and in the account’s object descriptions with the same name. Use this variable to define attributes that the handler should not copy.

  • accountAttributesToCopyWithNameMapping - the attributes to copy from the account to the user with different source and target attribute names, separated by a period (.).

  • approvalWorkflowDN - the DN of an approval workflow to be started for approval of user creation.

The configuration section is followed by:

  • Providing the handler, by calling scriptContext.getHandler().

  • Reading the account from the handler.

  • Supplying the user’s parentID with a constant DN.

  • Creating the user’s cn from the account’s cn.

  • Initializing the handler with the configured user name and parent folder: handler.initialize(cnOfFUser, parentID).

  • Calling the handler’s prepareUser method. This method performs all copying actions defined in the configuration section. This call creates a complete functional user in memory with all attributes populated according to the variable definitions given the configuration section.

  • Reading the functional user from the handler, handler.getFunctionalUser().

  • Calling the JavaScript "addAttributes" method. This method provides the option of creating user attribute values using the full flexibility of JavaScript. In the sample, the description is populated.

  • Selecting whether to create the user with an approval workflow or directly in the Identity Store:

    • To start an approval workflow, use handler.startApprovalWorkflow(approvalWorkflowDN).

    • To create the user directly in the Identity Store, use handler.createUserInLdap().

How the Approval Workflow Works

The JavaScript variable approvalWorkflowDN contains the DN of the sample workflow Approve Create User from Account.

This workflow is a simplified variant of the Create User workflows, with only one approval activity - a Calculate GUID activity - and an Apply Change activity.The workflow does not contain any Enter Attributes or Request Privileges activities, since this data is already provided by the consistency rule in the form of subject and resource orders that are passed to the workflow when it is started.The absence of activities for entering data and assignments makes this workflow unsuitable for creating a new user in Web Center.

You can tailor the Approve Create User from Account workflow to your needs; for example, you can change the set of attributes in the approval step, remove the calculate GUID activity or replace it with your own GUID generating activity.But note that the Apply Change activity uses a user hook called siemens.dxr.service.order.impl.ModifyAccountUserHook.This user hook is mandatory for re-linking the dxrUserLink to the user prior to saving (and resolving) the new user; otherwise the role resolution performed in the Apply Change step would create a new account for the new user if direct group assignments are created for the user.

Customizing the JavaScript for Persona and User Exchange

Web Center permits you to exchange a persona and its related user.This process is controlled by the JavaScript ExchangeUserPersona.js, which is located in the JavaScripts folder in the Customer Extensions configuration tree.

The JavaScript calls a handler implemented in Java that contains the program logic.You can tailor it to your needs by changing the following JavaScript configuration variables:

  • attributesToPreserve - the list of attributes whose values do not change at the persona.The values are copied from the persona to the user before the object types are exchanged.

  • attributesToMove - the list of attributes whose values do not change at the user.The values are copied from the user to the persona before the object types are exchanged.

The configuration section that defines these attributes is followed by script code that:

  • Passes the attributesToPreserve to the handler

  • Passes the attributesToMove to the handler

  • Calls the handler.exchangeUserPersona()

The handler’s exchangeUserPersona method exchanges user and persona in the following steps:

  • Clears the owner and dxrUserUid at the persona.

  • Adds the auxiliary object class dxrPersona to the user.

  • Removes the auxiliary object class dxrPersona from the persona.

  • Exchanges the object descriptors of user and persona.

  • Copies the user attributes defined in attributesToMove from user to persona.

  • Copies the preservedPersonaAttributes from persona to user.

  • Set owner and dxrUserUid and owner at the user.

  • Saves the user. In the Identity Store, it is a persona from now on.

  • Saves the persona. In the Identity Store, it is a user from now on.