[ Team LiB ] Previous Section Next Section

Creating a Custom Security Provider

There are times when application needs demand the use of a customer security provider. As each security provider was described in the previous chapter, a mention was made of situations that require a custom security provider. The first step is deciding which type of custom provider you need.

The weblogic.security.spi package contains classes for custom security providers to implement and extend. There's an interface here for every kind of provider in the WebLogic Server security framework, including AdjudicatorProvider, AuthenticationProvider, and AuthorizationProviders. All these interfaces extend the SecurityProvider interface. The methods of these interfaces are callback methods that WLS invokes in precise situations of the security framework's life cycle. In other words, WLS interacts with the security provider classes by calling the interface methods after specific events occur.

Your custom security provider must implement one of these interfaces. Additionally, some utility classes must be written to extend the classes or implement the interfaces IdentityAsserter, PrincipalValidator, and LoginModule.

BEA offers a collection of sample custom security providers available at the code direct section of its dev2dev site at http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp These examples are meant to be a starting point and should not be used in production environments. Look for WebLogic Server 8.1: Sample Security Providers and download the SampleSecurityProviders81.zip file. When unzipped, this file contains a readme.pdf file with instructions on how to understand, build, and run the customer security providers.

Unzip this file so that the sample_security_providers directory is under your bea_home directory. For example, if WebLogic Server is installed at c:\bea, the new directory should be c:\bea\sample_security_providers.

In this section, we look at the issues involved in creating a custom authentication provider. The sample custom authentication provider uses a Properties object to store user and group information and stores the object in a file. This is an example of a custom security provider database. There is also a customer login module that works with this provider.

To create a custom authentication provider, we must create a class that implements AuthenticationProvider. Because AuthenticationProvider extends SecurityProvider, that interface must be implemented as well.

Implementing SecurityProvider

The SecurityProvider interface has three methods that need to be implemented. Here are the methods and the events that might trigger them:


public void initialize(ProviderMBean providerMBean,
SecurityServices securityServices)

The preceding line allows the security provider to initialize itself—it's called when WLS starts. The initialize method takes two parameters:

  • providerMBean is a managed bean (MBean) and has configuration data that allows our customer provider to be managed in WebLogic Server. An MBean is a Java class that represents a Java Management eXtensions (JMX) manageable resource. The WebLogic Server admin console uses MBeans to configure WebLogic Server resources. Therefore, to control our security provider with the admin console, we must interface with WebLogic Server using this MBean. This MBean will be created later in this section with the WebLogic Server WebLogicMBeanMaker utility.

  • securityServices is an object that allows our custom provider to obtain and use an auditor provider.


public String getDescription()

The preceding line returns a brief description of the custom security provider. This shows up in the console when listing the security providers.


public void shutdown()

The preceding line shuts down the custom security provider. This is invoked when WLS shuts down.

Implementing AuthenticationProvider


public AppConfigurationEntry getAssertionModuleConfiguration()

This method is called to get an identity assertion provider's LoginModule. The returned AppConfigurationEntry contains information about the associated LoginModule including its class name. This method is called by the associated MBean.


public IdentityAsserter getIdentityAsserter()

This line indicates which IdentityAsserter is associated with this authentication provider (or null if we aren't supporting mapping tokens to usernames). The returned class has the callbacks that are invoked when a user's name is to be verified during authentication.


public AppConfigurationEntry getLoginModuleConfiguration()

This indicates which LoginModule is associated with this authentication provider. AppConfigurationEntry is a JAAS class that holds the LoginModule's class name, control flag, and configuration map. This interface is utilized by the MBean.


public PrincipalValidator getPrincipalValidator()

The preceding line indicates which PrincipalValidator is associated with this authentication provider. The PrincipalValidator class is used to verify that Principals placed in a Subject have not been tampered with since the Subject was filled with Principals.

SampleAuthenticationProviderImpl

The SampleAuthenticationProviderImpl class implements the AuthenticationProvider interface. This class enables the authentication provider to initialize, start, and stop. This class is a sample of what you would code when implementing the AuthenticationProvider interface. The following are some excerpts from SampleAuthenticationProviderImpl.java:


public void initialize(ProviderMBean mbean, SecurityServices services)
  {
System.out.println("SampleAuthenticationProviderImpl.initialize");
// Cast the mbean from a generic ProviderMBean to a SampleAuthenticatorMBean.
SampleAuthenticatorMBean myMBean = (SampleAuthenticatorMBean)mbean;
// Set the description to the sample authenticator's
// mbean's description and version
description = myMBean.getDescription() + "\n" + myMBean.getVersion();

// Instantiate the helper that manages this provider's user and group definitions
database = new SampleAuthenticatorDatabase(myMBean);
// Extract the JAAS control flag from the sample authenticator's mbean.
// This flag controls how the sample authenticator's login module is used
// by the JAAS login, both for authentication and for identity assertion.
String flag = myMBean.getControlFlag();
if (flag.equalsIgnoreCase("REQUIRED")) {
controlFlag = LoginModuleControlFlag.REQUIRED;
} else if (flag.equalsIgnoreCase("OPTIONAL")) {
controlFlag = LoginModuleControlFlag.OPTIONAL;
} else if (flag.equalsIgnoreCase("REQUISITE")) {
controlFlag = LoginModuleControlFlag.REQUISITE;
} else if (flag.equalsIgnoreCase("SUFFICIENT")) {
controlFlag = LoginModuleControlFlag.SUFFICIENT;
} else {
throw new IllegalArgumentException("invalid flag value" + flag);
    }
  }

The implementation details of your custom security provider might be completely different from this one. In our example, when the provider is initialized, it reads its description from the MBean. This description will be set in the admin console when a system administrator configures this provider in the security realm. Then it initializes its internal database of usernames and groups. Another class, SampleAuthenticatorDatabase, is responsible for initializing a Properties object by either reading a preconfigured properties file (if this provider has already been utilized in this realm) or by creating one from scratch. Look at SampleAuthenticatorDatabase.java for more information about this class.

The last step any custom security provider should perform in its initialize() method is to analyze and store the configuration information. In our example, this means the getControlFlag() method is called on the MBean. The system administrator determines the value of this flag when the provider is configured and it's used by the WebLogic Server security framework to determine whether authentication from this provider is required for a user to be authenticated. This JAAS control flag is important when multiple authentication providers have been configured.


public PrincipalValidator getPrincipalValidator()
  {
return new PrincipalValidatorImpl();
  }

This provider works with WebLogic Server users and groups, so we can use the default PrincipalValidator. This default implementation class is named PrincipalValidatorImpl. It's common to reuse any WLS security classes to fill in the holes of your custom security provider implementation.


public AppConfigurationEntry getLoginModuleConfiguration()
  {
// Don't pass in any special options.
// By default, the sample authenticator's login module
// will authenticate (by checking that the passwords match).
HashMap options = new HashMap();
return getConfiguration(options);
  }

private AppConfigurationEntry getConfiguration(HashMap options)
  {
// add the "database helper" object to the options so that the
// login module can access the user and group definitions
options.put("database", database);
// make sure to specify the sample authenticator's login module
// and to use the control flag from the sample authenticator's mbean.
return new
AppConfigurationEntry(
"examples.security.providers.authentication.SampleLoginModuleImpl",
controlFlag,
options
      );
  }

The getLoginModuleConfiguration() method calls the private getConfiguration() method. Together, these two methods create an AppConfigurationEntry object and populate it with the class name of the associated login module (SampleLoginModuleImpl), the JAAS control flag, and a HashMap to hold the provider database. The associated LoginModule will use this database. The AppConfigurationEntry class is part of the JAAS API and is in the javax.security.auth.login package.

Implementing LoginModule

Every authentication provider must have one associated LoginModule, and SampleAuthenticationProviderImpl is no exception. The LoginModule interface is from the JAAS specifications and is included in the javax.security.auth.spi package. The LoginModule is responsible for determining whether client credentials are sufficient and then filling a supplied Subject object with a Principal object that meets authentication requirements.

The LoginModule interface is composed of the following methods:


public void initialize(
Subject         subject,
CallbackHandler callbackHandler,
      Map             sharedState,
      Map             options
    )

A LoginContext calls this method, passing in a Subject that will be populated with Principals, a CallbackHandler for communicating with the end user for client credentials, a sharedState for communication with other LoginModules, and options to configure this LoginModule.


public boolean login() throws LoginException

This is phase one of the login process. A LoginContext will call this method and the LoginModule performs a lookup on its internal store of users and groups. It might invoke callback handlers passed in the initialize() method if client credential information is needed. The LoginModule will attempt to authenticate and will store the Principals objects internally.


public boolean commit() throws LoginException

This is phase two of the login process. If the calling LoginContext's overall authentication succeeded, it calls this method. This is where the Subject object is populated with the authenticated Principals from the login() method.


public boolean abort() throws LoginException

This is also phase two of the login process. If the calling LoginContext's overall authentication does not succeed, it calls this method. The LoginModule should clean up any private state established in the login() method.


public boolean logout() throws LoginException

This is called by LoginContext to log out a Subject. The LoginModule could remove Principals and Credentials from the Subject object.

SampleLoginModuleImpl

SampleLoginModuleImpl implements the LoginModule interface. Here are some excerpts from SampleLoginModuleImpl.java.

From the initialize method:


// Get the object that manages the user and group definitions
database = (SampleAuthenticatorDatabase)options.get("database");

SampleLoginModuleImpl gets access to the internal database of its associated authentication provider, SampleAuthenticationProviderImpl.

From the login method:


// Verify that the user exists. The login fails if not.
if (!database.userExists(userName)) {
throwFailedLoginException("Authentication Failed: " +
"User " + userName + " doesn't exist.");
}

The database is now used to verify the username exists. Also from the login method:


// since the login succeeded, add the user and its groups to the
// list of principals we want to add to the subject.
principalsForSubject.add(new WLSUserImpl(userName));
addGroupsForSubject(userName);

An internal vector called principalsForSubject is populated with WLSUsersImpl and WLSGroupImpl objects. These represent the Principals for this user.

From the commit method:


subject.getPrincipals().addAll(principalsForSubject);

The Subject object is populated with Principals from the principalsForSubject vector.

MBean Definition Files and WebLogic MBeanMaker

Each custom security provider needs to have an associated MBean definition file (MDF). The MDF states the name, display name, package, class, persistence policy, and custom attributes of the custom security provider.

The WebLogic MBeanMaker tool takes an MDF file as input and outputs an MBean interface file, an MBean implementation file, and an MBean information file. All these files are necessary for the creation of MBeans.

NOTE

More information about MDF files and MBeanMaker is available at http://e-docs.bea.com/wls/docs81/dvspisec/design.html.


Building the Custom Security Providers

The custom providers are accompanied by an Ant build script. Open a command prompt, go to the WL_HOME\server\bin directory, and run the setWLSEnv.cmd script to set up your environmental variables. Then go to the beahome\ sample_security_providers directory and type ant. The ant command will run the build.xml script. This script invokes MBeanMaker, creates a JAR file named wlSampleSecurityProviders.jar, and copies the JAR file to the WL_HOME\server\lib\mbeantypes directory. Once this JAR file is in this directory, the providers are available to use in a security realm.

Running the Custom Security Providers

Create a new WebLogic Server domain with the Configuration Wizard. From the Windows Start Menu, select Programs, BEA WebLogic Platform 8.1, Configuration Wizard. Select a WebLogic Server domain and change the default name from mydomain to sampledomain. Proceed through the wizard pages and accept system defaults for all information except the setting for the Create System User Name and Password page. On that page, set the username to sampleuser and the password to samplepassword. The sample providers rely on this information to work unmodified.

Boot WebLogic Server in this domain. Open the admin console with your Internet browser. Log in using sampleuser and samplepassword. Create a new security realm named SampleRealm. This realm will have no providers in it. Configure the following providers as depicted in Table 28.4.

Table 28.4. Default WebLogic Server Groups

Provider Type

Provider Name

Adjudicator

Default Adjudicator

Auditor

Sample Auditor

Authentication

Sample Authentication

Identity Asserter (under Authentication Provider)

Sample Identity Asserter

Authorizers

Sample Authorizer

Credential Mapper

Default Credential Mapper

Key Store

Default Key Store

Role Mapper

Sample Role Mapper

We're now going to set the realm as the default realm for this domain. Click on the sampledomain node in the left panel, and click on the View Domain-wide Security Settings link in the right panel. Set the default realm to be SampleRealm and click the Apply button.

Now let's shut down WebLogic Server from the admin console. Expand the Servers node in the left panel and click on the myserver node. Click on control in the right panel and then click on the Shutdown This Server link.

Boot WebLogic Server in the sampledomain domain and look at the WebLogic Server command prompt. Each of the custom security providers will output initialization to this window. The output should include the following:


SampleLoginModuleImpl.initialize
SampleLoginModuleImpl.login
        userName        = sampleuser
        passwordHave    = samplepassword
        groupName       = Administrators
SampleLoginModule.commit

This output reveals that SampleLoginModuleImpl is being called with our login parameters, sampleuser and samplepassword. The custom providers also come with a client to test perimeter authentication. Please refer to the readme.pdf that accompanies the BEA sample code from the dev2dev site.

Where to Go from Here

This chapter has just covered the basics of creating custom security providers. The sample providers are just meant to introduce developers to the concept of creating customer providers. There are many issues to consider when creating a custom security provider, such as custom attributes and methods, admin console enhancements, and best practices. Please visit http://e-docs.bea.com/wls/docs81/dvspisec/index.html for much more information about this topic.

    [ Team LiB ] Previous Section Next Section