Maintain user identities and profiles.

# User Service

## Overview

The User Service is designed to assist in creating, locating, maintaining, and aggregating information about users of the Foundation Services. A user can be an individual or a group. A user's information is contained in a user context. The context contains the user's active repository connections, identities and profile. Identities are used to authenticate the user with various other services. The profile is actually a collection of global and application profile information that applies to the user.

The User Service is comprised of three major functional areas: User Context sharing, profile management and authentication services.

## User Context Sharing

The User Service itself can store and retrieve UserContext objects for sharing between applications. To store a UserContext, call the `setUser` method. To retrieve a UserContext, call the `getUser` method. The UserContext objects stored in the User Service are protected by a UserContextPermission. A UserContextPermission must be granted for an application to retrieve User Contexts. To grant this permission, add the following to the policy or auth.policy file:

    grant {
        com.sas.services.user.UserContextPermission "username" "read";
    };

Where "username" is the name used to store the user in the User Service. A "*" can be used to represent all users.

## Locale Usage

The UserContext has mechanisms for choosing the user's preferred locale. Since the UserContext itself can't interact with a client, the UI is responsible for providing any "hints" like an HTTP request locale using the UserContext.setLocale() method. The locale can then be retrieved via the Usercontext.getLocale() method. It will try to return first a locale from the user profile. This gives the user their preferred locale even if they're using a browser or other client in a different locale (because they're using a different computer, or whatever). Then it will check for the explicitly set locale, and finally fall back to the JVM default locale.

## Profile Management

A profile is a collection of name/value pairs that specify preferences, configuration or initialization data for a user for an application. The key is a string. The value can be a string, or a complex object. It's also possible for there to be multiple values for a key.

The simplest profile representation is a collection of name/value pairs where the values are all single-valued strings. If an application uses a profile that can be represented in this way, the generic Profile class can be used to access it.

Profile information is grouped according to application. All the data that's been configured for a user for a given application name is contained in one Profile object. The application profiles can then be nested in a tree structure. Information can be accessed in various ways. The "global" profile is at the root of the profile tree. Information in this profile transcends individual applications and usually contains information about the user (nickname, title, address, etc). Below this are the application profiles, which may have subprofiles. For instance, there may be an application profile named "Solutions", which has subprofiles named "Marketing", "Sales", and "Purchasing". Once the user context is created, the "Solutions" profile can be loaded using the loadProfile method of the UserService. This process should also load the subprofiles of the "Solutions" application. If this is successful, there are two options for accessing the information in the profile. To start, get the global profile from the UserContext with the `getProfile` method. The global profile can then be queried for the desired profile attribute using the `getAttribute` method, passing in the application string "Solutions/Sales". This will look first in the "Sales" profile, then in the "Solutions" profile, then in the global profile for the requested key. When a value is found, it's returned. This mechanism allows profile values to be set at a general level, and overridden at more specific levels.

Alternatively, the profile for the application can be retrieved by calling the `getProfile` method from the global Profile with the application path. For instance get the global profile using the `getProfile` method of the UserContext, then call the `getProfile` method of the global profile with the application string "Solutions/Sales", then call the Sales Profile method `getAttribute` with an application string of null and the desired key. This will search only the Sales profile, and return a null if no value for the key is found.

If a profile attribute may have multiple values for a key, an application can use the `getAttributes` method to retrieve a List that contains all the values. This method takes an application path string that may contain "*" as a wildcard. The profile tree is searched downward for matches on the application path. If the application matches, the profile is checked for values for the requested key. Any that are found are added to the result List.

The global profile has one special attribute, autoconnect that controls autoconnect repositories for an individual. Values for this attribute should refer to ReposDef configurations in the Information Service.

The global profile should ordinarily be configured to be the GlobalProfile class. Besides the normal Profile behavior, it also supports access to the Personal Repository and Group profiles.

Group profiles work pretty much the same way as personal profiles. If you make a `getAttribute` call to the GlobalProfile, it will look through the profile hierarchy as it normally would. If a value is found, it's returned as normal. If no value is found, it will start looking in the group profile data. It will return the first value it finds that matches the key and application strings.

The group profiles are loaded by the GlobalProfile load() method. It will get a list of the IdentityGroups that the Person object belongs to. It will then try to load a profile for each of those groups. For each group that has a profile, it will add it to the group profile collection. Group profiles may have the same hierarchical structure that the personal profiles have.

The GlobalProfile supports accessing the group profiles by getting the whole set of them using `getGroupProfiles()` which returns all group profiles, or `getGroupProfile(String name)` which returns a profile for a specific group.

#### Backend Persistence

The default Profile currently has a mechanism for representing simple profile data in a SAS Metadata Repository, DAV or LDAP, loading the data, and saving updates to the backend store. These mechanisms are specific to the repository type that's being used, but it's possible to create a new class that will use a new type of backend for the profile storage. The UserService is configured to use a specific service for profile storage in the initialization XML in the discovery configuration:

      <Profile Application="global"
             url="ldap://d5296.us.sas.com:389"
             class="com.sas.services.user.Profile"/>

This gives the UserService and the Profile class sufficient information to load the profile from the server. It is the responsibility of the controlling application to be sure it's connected to the correct directory server before calling the `loadProfile` method. When loadProfile is called, the configuration information is looked up and an instance of the configured profile class is created. It then tries to find a handle to the repository specified by the url, and calls the Profile object's `load` method. The generic Profile class creates an instance of ProfileStoreInterface based on the protocol of the repository (currently, there are OMIProfileStore and LDAPProfileStore classes), that handles reading and writing data to the backend.

The simplest model for applications is to create their specific profiles as subprofiles of the global profile. Once this is done, the application profile will be automatically loaded when the UserContext is created with the `UserService.newUser()` method. This way, the profile won't need to be explicitly loaded after it's created.

Example

        ProfileInterface po = userContext.getProfile(); // get global Profile
        try
        {
            ProfileInterface cpo = po.getProfile( "Myapp" );
        }
        catch( ServiceException servExcept )
        {
            // It looks like the application profile doesn't exist, so create it
            ProfileInterface newProfile = new Profile("Myapp"); // create the new profile object
            try
            {
                po.createSubprofile( newProfile ); // create profile in backend repository
                po.addProfile( "Myapp", newProfile, true );  // Add to global profile now.
            }
            catch( Throwable t )
            {
                t.printStackTrace( System.out );
            {
            // You can set default values here, then persist the changes.
        }

#### Custom Profiles

If your application's profile has complex objects, or you want to provide specific methods for accessing attributes rather than having to use a key (that is, getBackground() rather than getAttribute("background"), you'll have to write a subclass of com.sas.services.user.Profile that provides those functions. You'll also have to override the `load` and `persist` methods that handle reading and writing the data to the persistent store.

## Authentication Services

### Terms

Authentication |  | A process by which the identity of a user is verified.
Authentication Domain |  | A collection of services within which a credential set is valid. This collection is assigned a name, which should be used consistently through the enterprise.
Credential Set |  | An identity (usually a user ID), and a credential (password or certificate), along with an authentication domain name which can be used to authenticate a user.

## User Context

In order to create a new UserContext, the simplest procedure is to acquire a userID and password, and call the UserService `newUser` method. You will also need to provide an authentication domain name within which the ID and password are valid. The user service will use the Security Service to attempt to authenticate the user. If this succeeds, any credential sets in the repository(s) the user may have authenticated with are retrieved and stored in the UserContext. Also, the handles to the repository(s) are stored in the UserContext. If the authentication fails, a null is returned.

It's possible that the authentication could succeed, but other UserContext initializations may fail. In that case, a UserInitializationException is thrown. The UserInitializationException contains the partially initialized UserContext and a collection of Exceptions that occurred during the initialization process. Likely exceptions are auto connect failures and profile load failures. Applications can catch the UserInitializationException and get the UserContext via the getUser() method and proceed. It's strongly recommended, though, that the Exceptions be logged so that any underlying problems can be fixed.

The user identity is a credential set used as input to the authentication service. A user may have several identities for accessing a variety of external services (data repositories, servers, etc.). An identity is comprised of a principal name, a credential, and the name of the security domain that the identity should be valid in. This is used to reference credentials against services and resources to find an appropriate credential set to perform an operation. While the domain name is an arbitrary string, care should be taken to be consistent across an enterprise.

Authentication is performed using JAAS compatible classes. Login modules currently exist for the SAS Metadata Server and LDAP. These login modules authenticate the user, and initialize the UserContext by collecting available credential sets from the repository, and storing the repository handle. These are then available for use by the applications.

The configuration file for the login modules (login.config) contains the configured domain name for each module. Be sure that the domain you configure for at least one login module matches the domain that use on the newUser method. If not, the login will fail.

The user credential sets are protected from unauthorized access by use of the Java security classes. Credential sets in an identity can be protected using the security policy manager, and granting the CredentialPermission "readCredential" permission to the appropriate classes. The UserContextInterface methods that get identities will verify that either the current user (as set by a Subject.doAs() call), is the same as the Subject in the context that identities are being read from, or the current user has the CredentialPermission with the "readCredential" action. To grant this permission, add the following to the policy or auth.policy file:

    grant {
        com.sas.services.user.CredentialPermission "readCredential";
    };

This permission can be granted to a specific codebase or user. Check the JAAS documentation for more information.

## Samples

Below is some sample code which uses the definitions that are created in the bootstrap XML to retrieve a configured user context, and verify that it authenticates successfully.

                /*
                 * The Portal User should have been defined in the initialization
                 * for the user service.  Try to get the user context.
                 */
                UserContextInterface user = _userService.getUser( "Portal User" );
                if ( user != null )
                {
                    System.out.println (" got portal user " );
                }
                /*
                 * We should now have a fully initialized UserContext for this
                 * user.  We can do searches, use its profile, etc. as we
                 * would for any other user.
                 */

It is also possible to create users in the application. Below is a sample of creating a user at run time, simulating a user connecting to the application.

                /*
                 * Have the User Service create new user context.  The userID and
                 * password, would, of course, usually come from the user, and the
                 * domain string would normally come from the application
                 * configuration.
                 */
                UserContextInterface userMe = null;
                try
                {
                    userMe = _userService.newUser( "testUser",
                                                   "testPW", "Alpine" );
                }
                catch( UserInitializationException uiex )
                {
                    Exception[] exceptions = uiex.getExceptions();
                    for( int i = 0; i < exceptions.length; i++ )
                    {
                        Exception exception = exceptions[i];
                        exception.printStackTrace( System.out );
                    }
                    userMe = uiex.getUser();
                }

                //  If the authentication worked, do some work to get user
                //  information.
                if ( userMe != null )
                {
                    /*
                     * If the LDAP authentication service was used, some information
                     * was added to the profile to the global section.
                     */
                    System.out.println( "I authenticated" );
                    System.out.println( "My name is : " + userMe.getName() );
                    System.out.println( "My title is : " +
                    userMe.getProfile().getAttribute( null, "title" ) );
                    /*
                     * Get the portal profile as defined by the initialization file.
                     */
                    ProfileInterface po =
                        _userService.loadProfile( userMe, "Portal" );
                    /*
                     * if we got the profile, print out the sasportalpersonalwindows
                     * just to prove it.
                     */
                    if ( po != null )
                    {
                        Enumeration e = po.getValues( "sasportalpersonalwindows" );
                        while( e.hasMoreElements() )
                        {
                            System.out.println( "Personal window: " +
                                (String)e.nextElement() );
                        }
                    }

                }
                else
                {
                    /*
                     * Authentication didn't work.  Draw your own conclusions.
                     */
                    System.out.println( "I've apparently been fired" );
                }