Logon Service
See also: Runtime Authentication, Roles and Credentials
The Logon Service is a System Service (a special type of Integration Service) supplied with the system that can be customized to meet your requirements for authenticating users. The Logon Service is responsible for checking that a user is valid (e.g. by checking a userid/password) and then setting the userid as authenticated. Optionally, additional information - roles and credentials – can be associated with the user.
A Logon Service can be used for any or all of the following:
· Checking userid/password combinations against a database
· Looking up a userid in an Ldap Registry (such as Active Directory)
· Obtaining user roles
· Obtaining user credentials such as email, department, manager etc
Information about the user is supplied by the caller and is passed into the Logon Service in the Request document. This might contain a userid/password combination entered by the user, a userid extracted from the application server (e.g. for Windows domain single sign-on), some sort of user token extracted from the Http request header etc. Or any combination of these.
The Logon Service can be invoked automatically when a new session connects or it can be invoked programmatically at any time e.g. when a user attempts to access a restricted service:
The Logon Service is a supplied system service named LOGON_SERVICE that is located from the designer tree System --> Internal Web Services --> System Services.
When the service is invoked, the script configured for the Logon Service’s integration event is executed. This script processes the information passed in the Request document, performs whatever processing is required, then builds a Response document that contains an authenticated userid, and optionally role and credential information for the user.
The format of the input and output documents for the service are defined in the LOGON_REQUEST System Services Resource and cannot be changed. The input format is defined by the Request document and consists of up to three parameters where each parameter has a value and source type:
The format of the output is defined by the Response document, and must be populated by the script:
USERID: this must be set by the implementing script. This userid is then accepted by the system as authenticated and is used for the duration of the user’s session.
CUSTOMROLES: this is an optional table of custom roles that will be added to the user’s security definition.
EBASEROLES: this is an optional table of Ebase roles that will be added to the user’s security definition. Note that when using Ebase Workflow, at least one Ebase role should be configured to provide the user with security authorizations required by the workflow system.
CREDENTIALS: this is an optional table of credentials that will be added to the user’s security definition.
ERRORCODE: set this to a value other than 00000 to indicate that the logon has failed.
ERRORDESCRIPTION: set this in conjunction with ERRORCODE to indicate the reason why the logon has been rejected.
The LOGON_SERVICE service can be tested by clicking the test
icon on the toolbar of the System Service Editor.
The test dialog shown below is then displayed. Fill in test values in the
request document and click the Submit
button.
This Javascript script is configured to run on the System Service event of the Logon Service. It expects to receive a userid and password and will validate these against Active Directory and also extract role and credential information. This example uses methods on the API LdapServices class. Note that the XILdap.properties file must be configured to provide connection properties before these methods can be used.
importPackage(com.ebasetech.xi.api);
importPackage(com.ebasetech.xi.services);
var
user = fields.PARAM1_VALUE.value;
var
password = fields.PARAM2_VALUE.value;
// Authenticate the user against Active Directory
if (LdapServices.authenticate(user,
password))
{
// All OK, so set the userid
fields.USERID.value
= user;
// then read role and credential info from
Active Directory
extractUserData(user);
}
else
{
fail("ERROR",
"Authentication Failed");
}
function extractUserData(user)
{
// Get all attributes for the user
var
attrs = LdapServices.getUserAttributes(user);
// Extract roles
extractRoles(attrs);
// Extract credentials
extractCredentials(attrs);
}
/**
* Extract role names from Active Directory memberOf attributes for the user. For example, the
following attribute:
*
"CN=Ebase Finance,OU=Ebase
Groups,OU=Security Groups,OU=Ebase
Users and Groups,DC=ebasetech,DC=com"
* becomes role name "Ebase_Finance".
*/
function extractRoles(attrs)
{
var
members = attrs.memberOf;
if (members)
{
for each (var member in members)
{
var
names = member.split(",");
if
(names && names.length > 0)
{
var first = names[0];
var attrWords = first.split("=");
if
(attrWords && attrWords.length
== 2)
{
var key = attrWords[0];
var value = attrWords[1];
if
(key == "CN")
{
// replace
any spaces with underscores
var groupName
= value.replace(/\ /g, "_");
tables.CUSTOMROLES.insertRow();
tables.CUSTOMROLES.ROLEID.value = groupName;
}
}
}
}
tables.CUSTOMROLES.updateTable();
}
}
/**
* Extract credentials from Active Directory
e.g. the "mail" property is added as credential "email"
etc.
*/
function extractCredentials(attrs)
{
var
credentials = [ ["mail", "email"], ["displayName", "name"], ["telephoneNumber", "phone"] ];
for each (var credential in credentials)
{
var registryName =
credential[0];
var credentialName =
credential[1];
var attrArray = attrs[registryName];
if (attrArray && attrArray.length
> 0)
{
tables.CREDENTIALS.insertRow();
tables.CREDENTIALS.ID.value
= credentialName;
tables.CREDENTIALS.VALUE.value
= attrArray[0];
}
}
tables.CREDENTIALS.updateTable();
}
function fail(code, description)
{
fields.USERID.value
= null;
fields.ERRORCODE.value
= code;
fields.ERRORDESCRIPTION.value
= description;
}
In the shipped product, XI Security properties are configured to provide the userid supplied by the application server.
Authentication is performed by one of the following 2 scripts, depending on version of Ebase:
1. JS_LOGON_SERVICE_LOGIC javascript script – is used starting from
Ebase V4.5.2.
This allows for the userid being provided by the application server, and extracts user roles and credentials from Active Directory.
Calls functions defined in script JS_LOGON_SERVICE_FUNCTIONS.
importPackage(com.ebasetech.xi.api);
importPackage(com.ebasetech.xi.services);
var
useridSource = fields.PARAM1_SOURCE.value;
var
userid = fields.PARAM1_VALUE.value;
var
pwd = fields.PARAM2_VALUE.value;
fields.USERID.value = null;
// 1. Check for no userid provided, send user to JSP signon
panel
if ( !userid )
{
fail("JSP",
"Blank userid: PARAM1_VALUE is null");
}
// 2. Check for userid provided by application server, probably via
integration with Active Directory.
// Accept the user id and setup security roles - one role for
each AD group.
// Extract any user
attributes from AD and add these as credentials.
// Also include the internal workflow role to allow ALL
workflow operations by this user
else if ( isUseridFromApplicationServer(useridSource, userid) )
{
populateRolesFromADGroups(userid);
populateAttributesFromAD(userid);
populateRolesForWorkFlow();
}
// 3. Check for
input is from JSP signon panel
else if ( isJSPSignon(useridSource) )
{
// authenticate userid
against Ebase internal security system and populate internal security roles
if ( authenticateInternalUser(userid, pwd) )
{
getInternalRoles(userid);
}
else
{
// if authentication fails, set error code
to 'JSP' to indicate try again (up to configured maximum attempts)
fail("JSP",
"Invalid userid/password combination...");
}
}
// If we reach this
point, we have a valid user, so set this in the response document to indicate
successful signon
fields.USERID.value = userid;
// Uncomment this while testing - it Writes contents of
EBASEROLES, CUSTOMROLES and CREDENTIALS tables to the application server log
// printRolesAndCredentials(userid);
2. LOGON_SERVICE_LOGIC FPL script. Is used in Ebase V4.5.1 and earlier.
This authenticates a supplied user/password combination against the supplied Ebase security system and adds any defined roles.
if [PARAM1_SOURCE = 'JSP']
// authenticate userid
(PARAM1_VALUE) and password (PARAM2_VALUE) against Ebase internal security
system
if [ javamethod('com.ebasetech.ufs.security.authentication.EbaseUserManager.authenticateEbaseUser',
PARAM1_VALUE, PARAM2_VALUE) ]
// authentication successful, so..
// 1. set the
user id in the response
set USERID =
PARAM1_VALUE;
// 2. fetch the
Ebase security roles from the database and transfer them to the response
set
EBASE_INTERNAL_ROLES-USERID = USERID;
fetchtable EBASE_INTERNAL_ROLES;
copytable EBASE_INTERNAL_ROLES to
EBASEROLES;
updatetable EBASEROLES;
else
//
authentication failed: return with error
set ERRORCODE =
'1';
set ERRORDESCRIPTION = 'Invalid userid/password
combination';
endif
endif
The Logon Service can be invoked in two ways:
1. Automatically – when a user first connects, using XILogonExit
2. On demand – using SecurityManager.logon()
Users can be authenticated at any time by calling the SecurityManager.logon() method – this is only available to API scripting languages such as Javascript. Use of this method makes it possible to mix authenticated and unauthenticated users in the same system, authenticating users at the point where they need to access a protected service.
The following example shows a logon function where form fields USER and PASSWORD contain the userid and password, and the parameter source type for both is arbitrarily set to “Script” (which might be checked by the Logon Service). The function returns true if the logon is successful, and generates and error message and returns false if it fails. When the Logon Service returns an error code or fails to set a userid in the response document, a LogonException is thrown and this exception contains the error code and error description returned by the Logon Service.
function logon()
{
try
{
system.securityManager.logon([ ["Script", fields.USER.value],
["Script", fields.PASSWORD.value] ] );
return true;
}
catch (e)
{
event.owner.addErrorMessage(e.javaException.message, false);
event.owner.addErrorMessage("code: " + e.javaException.errorCode
+ ", description: " + e.javaException.errorDescription);
return false;
}
}