Skip to main content
Previous section   Next section

Using Delegated Authentication

Caché supports the use of user-defined authentication mechanisms. This is known as delegated authentication. Delegated authentication allows administrators to implement custom mechanisms to replace the authentication and role-management activities that are part of Caché security.

This chapter covers the following topics:

Overview of Delegated Authentication

To use delegated authentication, the steps are:

  1. Create the user-defined authentication code in the ZAUTHENTICATE routine. This can include the use of two-factor authentication. This routine can also perform basic setup for a user account, such as specifying roles and other user properties.

    If you are using HealthShare Health Connect, create a custom ZAUTHENTICATE routine as described in this guide. Do not use the ZAUTHENTICATE routine that is part of the HSLIB namespace.

    If you are using HealthShare Unified Care Record, you cannot create a custom version of ZAUTHENTICATE to implement delegated authentication because Unified Care Record comes with its own version of the routine. Instead, you must customize methods in the class HS.Local.ZAUTHENTICATE. For more information, see “Unified Care Record's Authentication Mechanism” in the Unified Care Record Security Guide.

  2. Enable delegated authentication for the Caché instance on the Authentication Options page.

  3. Enable delegated authentication for the relevant services, applications, or both, as required.

  4. Optionally enable two-factor authentication for the Caché instance and, if required, for web applications and client/server applications.

For example, to use delegated authentication for an instance’s Management Portal, the steps are:

  1. Create the user-defined authentication code in ZAUTHENTICATE.

  2. Enable delegated authentication for the Caché instance as a whole.

  3. Enable delegated authentication for the set of /csp/sys* applications.

How Delegated Authentication Works

When a user attempts to log in and Caché invokes delegated authentication, the sequence of events is:

  1. When a service or application uses delegated authentication, a login attempt automatically results in a call to the ZAUTHENTICATE routine. The authentication code in this routine can be any user-defined ObjectScript, class methods, or $ZF callout code.

  2. The next step depends on whether or not authentication succeeds and whether or not this is the first login using ZAUTHENTICATE:

    • If ZAUTHENTICATE succeeds and this is the first time that the user has been authenticated through this mechanism, the user is added to the list of Caché users with a type of “Delegated user”. If ZAUTHENTICATE sets roles or other characteristics, these become part of the user’s properties.

    • If ZAUTHENTICATE succeeds and this is not the first login, ZAUTHENTICATE updates the user’s properties.

    • If ZAUTHENTICATE fails, then the user receives an access denied error and is unable to access the system. To determine why this has occurred:

      1. Check the Reason for Failing to Login field in the User Profile.

      2. For more detailed information, check the audit log for the relevant %System/%Login/LoginFailure event. If auditing or the LoginFailure event are not enabled, you may need to enable both of these and then re-create the circumstances of the login failure.

  3. If two-factor authentication is enabled for the instance and the relevant services, then there is a check that the user’s PhoneNumber and PhoneProvider properties have been set. If these properties are set, then two-factor authentication proceeds; if they are not set, two-factor authentication cannot proceed and the user is not authenticated.

  4. A delegated user is listed as such in the Type column of the list of users on the Users page (System Administration > Security > Users). The user’s properties are displayed read-only in the Management Portal and are not editable from within Caché (since all the information comes from outside Caché).

    Note:

    A Delegated user cannot also be a Password user.

Creating Delegated (User-Defined) Authentication Code

This section describes various aspects of creating your own ZAUTHENTICATE routine:

Authentication Code Fundamentals

A system-supplied template of ZAUTHENTICATE is available in the SAMPLES namespace in the routine ZAUTHENTICATE.mac for instances with Manager Utility Source Code installed. To install Manager Utility Source Code, select that option as part of a Custom install on the Setup Type page of the installation wizard.

To create your own ZAUTHENTICATE.mac:

  1. To use ZAUTHENTICATE.mac as a template, copy its contents and save them into a ZAUTHENTICATE.mac routine in the %SYS namespace.

  2. Review the comments in the system-supplied code for ZAUTHENTICATE. These provide important guidance about how to implement a custom version of the routine.

  3. Edit the routine by adding custom authentication code and any desired code to set user account characteristics.

Caution:

Because Caché places no constraints on the authentication code in ZAUTHENTICATE, the application programmer is responsible for ensuring that this code is sufficiently secure.

Signature

The signature of ZAUTHENTICATE is:

ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, 
        Properties) PUBLIC {

    // authentication code
    // optional code to specify user account properties and roles
}
Copy code to clipboard

where:

  • ServiceName — A string representing the name of the service through which the user is connecting to Caché, such as %Service_Console or %Service_CSP.

  • Namespace — A string representing the namespace on the Caché server to which a connection is being established. This is for use with the %Service_Bindings service, such as with Studio or ODBC.

  • Username — A string representing the name of the account entered by the user that is to be validated by the routine’s code.

  • Password — A string representing the password entered by the user that is to be validated.

  • CredentialsPassed by reference. Not implemented in this version of Caché.

  • PropertiesPassed by reference. An array of returned values that defines characteristics of the account named by Username.

When Caché calls ZAUTHENTICATE, it has values for these arguments and supplies them to the routine.

Authentication Code

The content of authentication code is application specific. If authentication succeeds, the routine should return the $$$OK macro; otherwise, it should return an error code. See the section “Return Value and Error Messages” for more information on return values.

Caution:

Because Caché does not and cannot place any constraints on the authentication code in ZAUTHENTICATE, the application programmer is responsible for ensuring that this code is sufficiently secure.

The GetCredentials Entry Point

ZAUTHENTICATE includes an GetCredentials entry point. This entry point is called whenever delegated authentication is enabled for a service, and is called before the user is prompted for a username and password. Instead of getting a username and password from the user, code in the function (created by the application developer) specifies the username and password. The username and password returned are then authenticated in the normal manner as if the user entered them. A possible use of this mechanism is to provide a username and password within the entry point and then, within authentication code, to $roles for the process.

The username and password returned from this entry point can be obtained by any mechanism that the application developer chooses. They can come from a global, an external DLL or LDAP call, or simply set within the routine. The application developer could even provide code to prompt for the username and password, such as in a terminal connection or with a custom CSP login page.

If the entry point returns an error status, then the error is logged in the audit log, and the user is denied access to the system; the one exception to this is if the error status $SYSTEM.Status.Error($$$GetCredentialsFailed) is returned, in which the normal username/password prompting proceeds.

In the following example of a GetCredentials entry point, the code performs different actions for different services:

  • For %Service_Console, it does not prompt the user for any information and sets the process’s username and password to _SYSTEM and SYS, respectively.

  • For %Service_Bindings, it forces the user to provide a username and password.

  • For web applications, it checks if the application in use is the /csp/samples application; if it is that application, it sets the username and password to AdminUser and Test. For all other web applications, it denies access.

  • For any other service, it denies access.

Finally, the Error entry point performs clean-up as necessary.

The code is:

GetCredentials(ServiceName,Namespace,Username,Password,Credentials) Public {

  // For console sessions, authenticate as _SYSTEM.
  If ServiceName="%Service_Console" {
    Set Username="_SYSTEM"
    Set Password="SYS"
    Quit $SYSTEM.Status.OK()
  }

  // For the CSP samples application, authenticate as AdminUser.
  If $isobject($get(%request)) { 
    If %request.Application="/csp/samples/" {
      Set Username="AdminUser"
      Set Password="Test"
      Quit $System.Status.OK()
    }
  }

  // For bindings connections, use regular prompting.
  If ServiceName="%Service_Bindings" {
    Quit $SYSTEM.Status.Error($$$GetCredentialsFailed)
  }

  // For all other connections, deny access.
  Quit $SYSTEM.Status.Error($$$AccessDenied)
}
Copy code to clipboard

For more details, see the comments for this entry point in ZAUTHENTICATE.mac.

The SendTwoFactorToken Entry Point

ZAUTHENTICATE includes an SendTwoFactorToken entry point. This entry point is for use with two-factor authentication. If it is defined and the Caché instance has two-factor authentication enabled, then you can override the default system setting for the format of the message and token that the instance sends to the user’s mobile phone. This allows for messages that can vary by application even on the same Caché instance.

For more details and an example of how to use this entry point, see this entry point in the sample ZAUTHENTICATE.mac.

Setting Values for Roles and Other User Characteristics

If initial authentication succeeds, ZAUTHENTICATE can establish the roles and other characteristics for the authenticated user. For subsequent logins, ZAUTHENTICATE can update these elements of the user record.

For this to happen, code in ZAUTHENTICATE sets the values of the Properties array. (Properties is passed by reference to ZAUTHENTICATE.) Typically, the source for the values being set is a repository of user information that is available to ZAUTHENTICATE.

User Properties

The elements in the Properties array are:

  • Properties("Comment") — Any text

  • Properties("FullName") — The first and last name of the user

  • Properties("NameSpace") — The default namespace for a Terminal login

  • Properties("Roles") — The comma-separated list of roles that the user holds in Caché

  • Properties("Routine") — The routine that is executed for a Terminal login

  • Properties("Password") — The user’s password

  • Properties("Username") — The user’s username

  • Properties("PhoneNumber") — The user’s mobile phone number, for use with two-factor authentication

  • Properties("PhoneProvider") — The user’s mobile phone’s service provider, for use with two-factor authentication

Each of these elements is described in more detail in one of the following sections.

Note:

The value of each element in the properties array determines the value of its associated property for the user being authenticated. It is not possible to use only a subset of the properties or to manipulate their values after authentication.

Comment

If ZAUTHENTICATE sets the value of Properties("Comment"), then that string becomes the value of the user account’s Comment property in Caché. (This property is described in the section “Properties of Users,” in the “Users” chapter.) If no value is passed back to the calling routine, then the value of Comment for the user account is a null string and the relevant field in the Management Portal then holds no content.

FullName

If ZAUTHENTICATE sets the value of Properties("FullName"), then that string becomes the value of the user account’s Full name property in Caché. (This property is described in the section “Properties of Users,” in the “Users” chapter.) If no value is passed back to the calling routine, then the value of Full name for the user account is a null string and the relevant field in the Management Portal then holds no content.

NameSpace

If ZAUTHENTICATE sets the value of Properties("Namespace"), then that string becomes the value of the user account’s Startup Namespace property in Caché. (This property is described in the section “Properties of Users,” in the “Users” chapter.) If no value is passed back to the calling routine, then the value of Startup Namespace for the user account is a null string and the relevant field in the Management Portal then holds no content.

Once connected to Caché, the value of Startup Namespace (hence, that of Properties("Namespace")) determines the initial namespace for any user authenticated for local access (such as for Console, Terminal, or Telnet). If Startup Namespace has no value (since Properties("Namespace") has no value), then the initial namespace for any user authenticated for local access is determined as follows:

  1. If the USER namespace exists, that is the initial namespace.

  2. If the USER namespace does not exist, the initial namespace is the %SYS namespace.

Note:

If the user does not have the appropriate privileges for the initial namespace, access is denied.

Password

If ZAUTHENTICATE sets the value of Properties("Password"), then that string becomes the value of the user account’s Password property in Caché. (This property is described in the section “Properties of Users,” in the “Users” chapter.) If no value is passed back to the calling routine, then the value of Password for the user account is a null string and the relevant field in the Management Portal then holds no content.

Roles

If ZAUTHENTICATE sets the value of Properties("Roles"), then that string specifies the Roles to which a user is assigned; this value is a string containing a comma-delimited list of roles. If no value is passed back to the calling routine, then the value of Roles for the user account is a null string and the relevant field in the Management Portal then holds no content. Information about a user’s roles is available on the Roles tab of a user’s Edit User page.

If any roles returned in Properties("Roles") are not defined, then the user is not assigned to the role.

Hence, the logged-in user is assigned to roles as follows:

  • If a role is listed in Properties("Roles") and is defined by the Caché instance, then the user is assigned to the role.

  • If a role is listed in Properties("Roles") and is not defined by the Caché instance, then the user is not assigned to the role.

  • A user is always assigned to those roles associated with the _PUBLIC user. A user also has access to all public resources. For information on the _PUBLIC user, see the section “The _PUBLIC Account” in the “Users” chapter; for information on public resources, see the section “Services and Their Resources” in the “Resources” chapter.

Routine

If ZAUTHENTICATE sets the value of Properties("Routine"), then that string becomes the value of the user account’s Startup Tag^Routine property in Caché. (This property is described in the section “Properties of Users,” in the “Users” chapter.) If no value is passed back to the calling routine, then the value of Startup Tag^Routine for the user account is a null string and the relevant field in the Management Portal then holds no content.

If Properties("Routine") has a value, then this value specifies the routine to execute automatically following login on a terminal-type service (such as for Console, Terminal, or Telnet). If Properties("Routine") has no value, then login starts the Terminal session in programmer mode.

Username

If ZAUTHENTICATE returns the Username property, then the value of Username is written to the security database after any processing in the function; this provides chance to modify the value that the user entered at the prompt. If ZAUTHENTICATE does not return the Username property, then the value of the property is written to the security database as entered.

If ZAUTHENTICATE sets the value of Properties("Username"), then that string becomes the value of the user account’s Name property in Caché. (This property is described in the section “Properties of Users”, in the “Users” chapter.) This provides the application programmer with an opportunity to normalize content provided by the end-user at the login prompt.

If there is no explicit call that passes the value of Properties("Username") back to the calling routine, then there is no normalization and the value entered by the end-user at the prompt serves as the value of the user account’s Name property without any modification.

PhoneNumber and PhoneProvider

These are properties associated with two-factor authentication.

If ZAUTHENTICATE sets the value of Properties("PhoneNumber") and Properties("PhoneProvider"), then these then these are written to the Caché database for the user as the user’s mobile phone number and mobile phone service provider. If these are not passed back to the calling routine, then the phone number and service provider written to the Caché security database are a null string. Hence, to use two-factor authentication with delegated authentication, you must supply both of these.

The User Information Repository

ZAUTHENTICATE can refer to any kind of repository of user information, such as a global or an external file. It is up to the code in the routine to set any external properties in the Properties array so that the authenticated user can be created or updated with this information. For example, while a repository can include information such as roles and namespaces, ZAUTHENTICATE code must make that information available to Caché.

If information in the repository changes, this information is only propagated back into the Caché user information if there is code in ZAUTHENTICATE to perform this action. Also, if there is such code, changes to users’ roles must occur in the repository; if you change a user’s roles during a session, the change does not become effective until the next login, at which point the user’s roles are re-set by ZAUTHENTICATE.

Return Value and Error Messages

The routine returns one of the following values:

  • Success — $$$OK. This indicates that username/password combination was successfully authenticated

  • Failure — $SYSTEM.Status.Error($$$ERRORMESSAGE). This indicates that authentication failed.

ZAUTHENTICATE can return system-defined or application-specific error messages. All these messages use the Error method of the %SYSTEM.Status class. This method is invoked as $SYSTEM.Status.Error and takes one or two arguments, depending on the error condition.

The available system-defined error messages are:

  • $SYSTEM.Status.Error($$$AccessDenied) — Error message of “Access Denied”

  • $SYSTEM.Status.Error($$$InvalidUsernameOrPassword) — Error message of “Invalid Username or Password”

  • $SYSTEM.Status.Error($$$UserNotAuthorizedOnSystem,Username) — Error message of “User Username is not authorized”

  • $SYSTEM.Status.Error($$$UserAccountIsDisabled,Username) — Error message of “User Username account is disabled”

  • $SYSTEM.Status.Error($$$UserInvalidUsernameOrPassword,Username) — Error message of “User Username invalid name or password”

  • $SYSTEM.Status.Error($$$UserLoginTimeout) — Error message of “Login timeout”

  • $SYSTEM.Status.Error($$$UserCTRLC) — Error message of “Login aborted”

  • $SYSTEM.Status.Error($$$UserDoesNotExist,Username) — Error message of “User Username does not exist”

  • $SYSTEM.Status.Error($$$UserInvalid,Username) — Error message of “Username Username is invalid”

  • $SYSTEM.Status.Error($$$PasswordChangeRequired) — Error message of “Password change required”

  • $SYSTEM.Status.Error($$$UserAccountIsExpired,Username) — Error message of “User Username account has expired”

  • $SYSTEM.Status.Error($$$UserAccountIsInactive,Username) — Error message of “User Username account is inactive”

  • $SYSTEM.Status.Error($$$UserInvalidPassword) — Error message of “Invalid password”

  • $SYSTEM.Status.Error($$$ServiceDisabled,ServiceName) — Error message of “Logins for Service Servicename are disabled”

  • $SYSTEM.Status.Error($$$ServiceLoginsDisabled) — Error message of “Logins are disabled”

  • $SYSTEM.Status.Error($$$ServiceNotAuthorized,ServiceName) — Error message of “User not authorized for service”

To generate a custom message, use the $SYSTEM.Status.Error() method, passing it the $$$GeneralError macro and specifying any custom text as the second argument. For example:

$SYSTEM.Status.Error($$$GeneralError,"Any text here")
Copy code to clipboard

Note that when an error message is returned to the caller, it is logged in the audit database (if LoginFailure event auditing is turned on). However, the only error message the user sees is $SYSTEM.Status.Error($$$AccessDenied). However, the user also sees the message for the $$$PasswordChangeRequired error. Return this error if you want the user to change from the current to a new password.

Setting Up Delegated Authentication

Once you have created a ZAUTHENTICATE routine to perform authentication (and, optionally, authorization tasks), the next step is to enable it for the instance’s relevant services or applications. This procedure is:

  1. Enable delegated authentication for entire instance. On the Authentication/CSP Session Options page (System Administration > Security > System Security > Authentication/CSP Session Options), select Allow Delegated authentication and click Save.

    With delegated authentication enabled for the instance, a Delegated check box appears on the Edit Service page for relevant services and the Edit Web Application page for those applications.

  2. Enable delegated authenticated for services and applications, as appropriate.

The following services support delegated authentication:

  • %Service_Bindings

  • %Service_CSP

  • %Service_CacheDirect

  • %Service_CallIn

  • %Service_ComPort

  • %Service_Console

  • %Service_Login

  • %Service_Terminal

  • %Service_Telnet

These fall into several categories of access modes:

  • Local access

    %Service_CallIn, %Service_ComPort, %Service_Console, %Service_Login, %Service_Terminal, %Service_Telnet

    To use delegated authentication with local connections, enable it for the service.

  • Client/Server access

    %Service_Bindings, %Service_CacheDirect,

    To use delegated authentication with client/server connections, enable it for the service.

  • CSP access

    %Service_CSP

    To use delegated authentication with web-based connections (through CSP or Zen), enable it for the web application. You may also enable it for the CSP gateway by enabling the service %Service_CSP

After Delegated Authentication Succeeds

Once the user has authenticated, two important topics are:

The State of the System

Any user who is initially authenticated using delegated authentication is listed in the table of users on the Users page (System Administration > Security > Users) as having a type of “Delegated user”. If a system administrator has explicitly created a user through the Management Portal (or using any other native Caché facility), that user has a type of Password user. If a user attempts to log in using delegated authentication and is successfully authenticated, Caché determines that this user already exists as a Password user — not a Delegated user — and so login fails.

Changing Passwords

The ZAUTHENTICATE routine also includes an entry point, ChangePassword, to include code to change a user’s password. The signature of this entry point is:

ChangePassword(Username,NewPassword,OldPassword,Status) Public {}
Copy code to clipboard

where

  • Username is a string specifying the user whose password is being changed.

  • NewPassword is a string specifying the new value of the user’s password.

  • OldPassword is a string specifying the old value of the user’s password.

  • Status (passed by reference) receives a Caché status value indicating either that the password change has been successful or specifying the error that caused the routine to fail.