FileDocCategorySizeDatePackage
LdapLoginModule.javaAPI DocJava SE 6 API34058Tue Jun 10 00:23:22 BST 2008com.sun.security.auth.module

LdapLoginModule

public class LdapLoginModule extends Object implements LoginModule
This {@link LoginModule} performs LDAP-based authentication. A username and password is verified against the corresponding user credentials stored in an LDAP directory. This module requires the supplied {@link CallbackHandler} to support a {@link NameCallback} and a {@link PasswordCallback}. If authentication is successful then a new {@link LdapPrincipal} is created using the user's distinguished name and a new {@link UserPrincipal} is created using the user's username and both are associated with the current {@link Subject}.

This module operates in one of three modes: search-first, authentication-first or authentication-only. A mode is selected by specifying a particular set of options.

In search-first mode, the LDAP directory is searched to determine the user's distinguished name and then authentication is attempted. An (anonymous) search is performed using the supplied username in conjunction with a specified search filter. If successful then authentication is attempted using the user's distinguished name and the supplied password. To enable this mode, set the userFilter option and omit the authIdentity option. Use search-first mode when the user's distinguished name is not known in advance.

In authentication-first mode, authentication is attempted using the supplied username and password and then the LDAP directory is searched. If authentication is successful then a search is performed using the supplied username in conjunction with a specified search filter. To enable this mode, set the authIdentity and the userFilter options. Use authentication-first mode when accessing an LDAP directory that has been configured to disallow anonymous searches.

In authentication-only mode, authentication is attempted using the supplied username and password. The LDAP directory is not searched because the user's distinguished name is already known. To enable this mode, set the authIdentity option to a valid distinguished name and omit the userFilter option. Use authentication-only mode when the user's distinguished name is known in advance.

The following option is mandatory and must be specified in this module's login {@link Configuration}:

userProvider=ldap_urls
This option identifies the LDAP directory that stores user entries. ldap_urls is a list of space-separated LDAP URLs (RFC 2255) that identifies the LDAP server to use and the position in its directory tree where user entries are located. When several LDAP URLs are specified then each is attempted, in turn, until the first successful connection is established. Spaces in the distinguished name component of the URL must be escaped using the standard mechanism of percent character ('%') followed by two hexadecimal digits (see {@link java.net.URI}). Query components must also be omitted from the URL.

Automatic discovery of the LDAP server via DNS (RFC 2782) is supported (once DNS has been configured to support such a service). It is enabled by omitting the hostname and port number components from the LDAP URL.

This module also recognizes the following optional {@link Configuration} options:

userFilter=ldap_filter
This option specifies the search filter to use to locate a user's entry in the LDAP directory. It is used to determine a user's distinguished name. ldap_filter is an LDAP filter string (RFC 2254). If it contains the special token "{USERNAME}" then that token will be replaced with the supplied username value before the filter is used to search the directory.
authIdentity=auth_id
This option specifies the identity to use when authenticating a user to the LDAP directory. auth_id may be an LDAP distinguished name string (RFC 2253) or some other string name. It must contain the special token "{USERNAME}" which will be replaced with the supplied username value before the name is used for authentication. Note that if this option does not contain a distinguished name then the userFilter option must also be specified.
authzIdentity=authz_id
This option specifies an authorization identity for the user. authz_id is any string name. If it comprises a single special token with curly braces then that token is treated as a attribute name and will be replaced with a single value of that attribute from the user's LDAP entry. If the attribute cannot be found then the option is ignored. When this option is supplied and the user has been successfully authenticated then an additional {@link UserPrincipal} is created using the authorization identity and it is assocated with the current {@link Subject}.
useSSL
if false, this module does not establish an SSL connection to the LDAP server before attempting authentication. SSL is used to protect the privacy of the user's password because it is transmitted in the clear over LDAP. By default, this module uses SSL.
useFirstPass
if true, this module retrieves the username and password from the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. The retrieved values are used for authentication. If authentication fails, no attempt for a retry is made, and the failure is reported back to the calling application.
tryFirstPass
if true, this module retrieves the username and password from the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. The retrieved values are used for authentication. If authentication fails, the module uses the {@link CallbackHandler} to retrieve a new username and password, and another attempt to authenticate is made. If the authentication fails, the failure is reported back to the calling application.
storePass
if true, this module stores the username and password obtained from the {@link CallbackHandler} in the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. This is not performed if existing values already exist for the username and password in the shared state, or if authentication fails.
clearPass
if true, this module clears the username and password stored in the module's shared state after both phases of authentication (login and commit) have completed.
debug
if true, debug messages are displayed on the standard output stream.

Arbitrary JNDI properties may also be specified in the {@link Configuration}. They are added to the environment and passed to the LDAP provider. Note that the following four JNDI properties are set by this module directly and are ignored if also present in the configuration:

  • java.naming.provider.url
  • java.naming.security.principal
  • java.naming.security.credentials
  • java.naming.security.protocol

Three sample {@link Configuration}s are shown below. The first one activates search-first mode. It identifies the LDAP server and specifies that users' entries be located by their uid and objectClass attributes. It also specifies that an identity based on the user's employeeNumber attribute should be created. The second one activates authentication-first mode. It requests that the LDAP server be located dynamically, that authentication be performed using the supplied username directly but without the protection of SSL and that users' entries be located by one of three naming attributes and their objectClass attribute. The third one activates authentication-only mode. It identifies alternative LDAP servers, it specifies the distinguished name to use for authentication and a fixed identity to use for authorization. No directory search is performed.


ExampleApplication {
com.sun.security.auth.module.LdapLoginModule REQUIRED
userProvider="ldap://ldap-svr/ou=people,dc=example,dc=com"
userFilter="(&(uid={USERNAME})(objectClass=inetOrgPerson))"
authzIdentity="{EMPLOYEENUMBER}"
debug=true;
};

ExampleApplication {
com.sun.security.auth.module.LdapLoginModule REQUIRED
userProvider="ldap:///cn=users,dc=example,dc=com"
authIdentity="{USERNAME}"
userFilter="(&(|(samAccountName={USERNAME})(userPrincipalName={USERNAME})(cn={USERNAME}))(objectClass=user))"
useSSL=false
debug=true;
};

ExampleApplication {
com.sun.security.auth.module.LdapLoginModule REQUIRED
userProvider="ldap://ldap-svr1 ldap://ldap-svr2"
authIdentity="cn={USERNAME},ou=people,dc=example,dc=com"
authzIdentity="staff"
debug=true;
};

Note:
When a {@link SecurityManager} is active then an application that creates a {@link LoginContext} and uses a {@link LoginModule} must be granted certain permissions.

If the application creates a login context using an installed {@link Configuration} then the application must be granted the {@link AuthPermission} to create login contexts. For example, the following security policy allows an application in the user's current directory to instantiate any login context:


grant codebase "file:${user.dir}/" {
permission javax.security.auth.AuthPermission "createLoginContext.*";
};
Alternatively, if the application creates a login context using a caller-specified {@link Configuration} then the application must be granted the permissions required by the {@link LoginModule}. This module requires the following two permissions:

  • The {@link SocketPermission} to connect to an LDAP server.
  • The {@link AuthPermission} to modify the set of {@link Principal}s associated with a {@link Subject}.

For example, the following security policy grants an application in the user's current directory all the permissions required by this module:


grant codebase "file:${user.dir}/" {
permission java.net.SocketPermission "*:389", "connect";
permission java.net.SocketPermission "*:636", "connect";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
since
1.6

Fields Summary
private static final ResourceBundle
rb
private static final String
USERNAME_KEY
private static final String
PASSWORD_KEY
private static final String
USER_PROVIDER
private static final String
USER_FILTER
private static final String
AUTHC_IDENTITY
private static final String
AUTHZ_IDENTITY
private static final String
USERNAME_TOKEN
private static final Pattern
USERNAME_PATTERN
private String
userProvider
private String
userFilter
private String
authcIdentity
private String
authzIdentity
private String
authzIdentityAttr
private boolean
useSSL
private boolean
authFirst
private boolean
authOnly
private boolean
useFirstPass
private boolean
tryFirstPass
private boolean
storePass
private boolean
clearPass
private boolean
debug
private boolean
succeeded
private boolean
commitSucceeded
private String
username
private char[]
password
private LdapPrincipal
ldapPrincipal
private UserPrincipal
userPrincipal
private UserPrincipal
authzPrincipal
private Subject
subject
private CallbackHandler
callbackHandler
private Map
sharedState
private Map
options
private LdapContext
ctx
private Matcher
identityMatcher
private Matcher
filterMatcher
private Hashtable
ldapEnvironment
private SearchControls
constraints
Constructors Summary
Methods Summary
public booleanabort()
Abort user authentication.

This method is called if the overall authentication failed. (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed).

If this LoginModule's own authentication attempt succeeded (checked by retrieving the private state saved by the login and commit methods), then this method cleans up any state that was originally saved.

exception
LoginException if the abort fails.
return
false if this LoginModule's own login and/or commit attempts failed, and true otherwise.

	if (debug)
	    System.out.println("\t\t[LdapLoginModule] " +
		"aborted authentication");

	if (succeeded == false) {
	    return false;
	} else if (succeeded == true && commitSucceeded == false) {

	    // Clean out state
	    succeeded = false;
	    cleanState();

	    ldapPrincipal = null;
	    userPrincipal = null;
	    authzPrincipal = null;
	} else {
	    // overall authentication succeeded and commit succeeded,
	    // but someone else's commit failed
	    logout();
	}
	return true;
    
private voidattemptAuthentication(boolean getPasswdFromSharedState)
Attempt authentication

param
getPasswdFromSharedState boolean that tells this method whether to retrieve the password from the sharedState.
exception
LoginException if the authentication attempt fails.


	// first get the username and password
	getUsernamePassword(getPasswdFromSharedState);

	if (password == null || password.length == 0) {
	    throw (LoginException)
		new FailedLoginException("No password was supplied");
	}

	String dn = "";

	if (authFirst || authOnly) {

	    String id = replaceUsernameToken(identityMatcher, authcIdentity);

	    // Prepare to bind using user's username and password
	    ldapEnvironment.put(Context.SECURITY_CREDENTIALS, password);
	    ldapEnvironment.put(Context.SECURITY_PRINCIPAL, id);

	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
		    "attempting to authenticate user: " + username);
	    }

	    try {
		// Connect to the LDAP server (using simple bind)
		ctx = new InitialLdapContext(ldapEnvironment, null);

	    } catch (NamingException e) {
		throw (LoginException)
		    new FailedLoginException("Cannot bind to LDAP server")
			.initCause(e);
	    }

	    // Authentication has succeeded

	    // Locate the user's distinguished name
	    if (userFilter != null) {
		dn = findUserDN(ctx);
	    } else {
		dn = id;
	    }

	} else {

	    try {
		// Connect to the LDAP server (using anonymous bind)
		ctx = new InitialLdapContext(ldapEnvironment, null);

	    } catch (NamingException e) {
		throw (LoginException)
		    new FailedLoginException("Cannot connect to LDAP server")
			.initCause(e);
	    }

	    // Locate the user's distinguished name
	    dn = findUserDN(ctx);

	    try {

		// Prepare to bind using user's distinguished name and password
		ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
		ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
		ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);

		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] " +
			"attempting to authenticate user: " + username);
		}
		// Connect to the LDAP server (using simple bind)
		ctx.reconnect(null);

		// Authentication has succeeded

	    } catch (NamingException e) {
		throw (LoginException)
		    new FailedLoginException("Cannot bind to LDAP server")
			.initCause(e);
	    }
	}

	// Save input as shared state only if authentication succeeded
	if (storePass &&
	    !sharedState.containsKey(USERNAME_KEY) &&
	    !sharedState.containsKey(PASSWORD_KEY)) {
	    sharedState.put(USERNAME_KEY, username);
	    sharedState.put(PASSWORD_KEY, password);
	}

	// Create the user principals
	userPrincipal = new UserPrincipal(username);
	if (authzIdentity != null) {
	    authzPrincipal = new UserPrincipal(authzIdentity);
	}

	try {

	    ldapPrincipal = new LdapPrincipal(dn);

	} catch (InvalidNameException e) {
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
				   "cannot create LdapPrincipal: bad DN");
	    }
	    throw (LoginException)
		new FailedLoginException("Cannot create LdapPrincipal")
		    .initCause(e);
	}
    
private voidcleanState()
Clean out state because of a failed authentication attempt

	username = null;
	if (password != null) {
	    Arrays.fill(password, ' ");
	    password = null;
	}
	try {
	    if (ctx != null) {
		ctx.close();
	    }
	} catch (NamingException e) {
	    // ignore
	}
	ctx = null;

	if (clearPass) {
	    sharedState.remove(USERNAME_KEY);
	    sharedState.remove(PASSWORD_KEY);
	}
    
public booleancommit()
Complete user authentication.

This method is called if the LoginContext's overall authentication succeeded (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules succeeded).

If this LoginModule's own authentication attempt succeeded (checked by retrieving the private state saved by the login method), then this method associates an LdapPrincipal and one or more UserPrincipals with the Subject located in the LoginModule. If this LoginModule's own authentication attempted failed, then this method removes any state that was originally saved.

exception
LoginException if the commit fails
return
true if this LoginModule's own login and commit attempts succeeded, or false otherwise.


	if (succeeded == false) {
	    return false;
	} else {
	    if (subject.isReadOnly()) {
		cleanState();
		throw new LoginException ("Subject is read-only");
	    }
	    // add Principals to the Subject
	    Set principals = subject.getPrincipals();
	    if (! principals.contains(ldapPrincipal)) {
		principals.add(ldapPrincipal);
	    }
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
				   "added LdapPrincipal \"" +
				   ldapPrincipal +
				   "\" to Subject");
	    }

	    if (! principals.contains(userPrincipal)) {
		principals.add(userPrincipal);
	    }
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
				   "added UserPrincipal \"" +
				   userPrincipal +
				   "\" to Subject");
	    }

	    if (authzPrincipal != null &&
		(! principals.contains(authzPrincipal))) {
		principals.add(authzPrincipal);

		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] " +
				   "added UserPrincipal \"" +
				   authzPrincipal +
				   "\" to Subject");
		}
	    }
	}
	// in any case, clean out state
	cleanState();
	commitSucceeded = true;
	return true;
    
private java.lang.StringfindUserDN(javax.naming.ldap.LdapContext ctx)
Search for the user's entry. Determine the distinguished name of the user's entry and optionally an authorization identity for the user.

param
ctx an LDAP context to use for the search
return
the user's distinguished name or an empty string if none was found.
exception
LoginException if the user's entry cannot be found.


	String userDN = "";

	// Locate the user's LDAP entry
	if (userFilter != null) {
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
		    "searching for entry belonging to user: " + username);
	    }
	} else {
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
		    "cannot search for entry belonging to user: " + username);
	    }
	    throw (LoginException)
		new FailedLoginException("Cannot find user's LDAP entry");
	}

	try {
	    NamingEnumeration results = ctx.search("",
		replaceUsernameToken(filterMatcher, userFilter), constraints);

	    // Extract the distinguished name of the user's entry
	    // (Use the first entry if more than one is returned)
	    if (results.hasMore()) {
		SearchResult entry = (SearchResult) results.next();

		// %%% - use the SearchResult.getNameInNamespace method
		//        available in JDK 1.5 and later.
		//        (can remove call to constraints.setReturningObjFlag)
		userDN = ((Context)entry.getObject()).getNameInNamespace();

		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] found entry: " +
			userDN);
		}

		// Extract a value from user's authorization identity attribute
		if (authzIdentityAttr != null) {
		    Attribute attr =
			entry.getAttributes().get(authzIdentityAttr);
		    if (attr != null) {
			Object val = attr.get();
			if (val instanceof String) {
			    authzIdentity = (String) val;
			}
		    }
		}

		results.close();

	    } else {
		// Bad username
		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] user's entry " +
			"not found");
		}
	    }

	} catch (NamingException e) {
	    // ignore
	}

	if (userDN.equals("")) {
	    throw (LoginException)
		new FailedLoginException("Cannot find user's LDAP entry");
	} else {
	    return userDN;
	}
    
private voidgetUsernamePassword(boolean getPasswdFromSharedState)
Get the username and password. This method does not return any value. Instead, it sets global name and password variables.

Also note that this method will set the username and password values in the shared state in case subsequent LoginModules want to use them via use/tryFirstPass.

param
getPasswdFromSharedState boolean that tells this method whether to retrieve the password from the sharedState.
exception
LoginException if the username/password cannot be acquired.


	if (getPasswdFromSharedState) {
	    // use the password saved by the first module in the stack
	    username = (String)sharedState.get(USERNAME_KEY);
	    password = (char[])sharedState.get(PASSWORD_KEY);
	    return;
	}

	// prompt for a username and password
        if (callbackHandler == null)
	    throw new LoginException("No CallbackHandler available " +
		"to acquire authentication information from the user");

	Callback[] callbacks = new Callback[2];
	callbacks[0] = new NameCallback(rb.getString("username: "));
	callbacks[1] = new PasswordCallback(rb.getString("password: "), false);

	try {
	    callbackHandler.handle(callbacks);
	    username = ((NameCallback)callbacks[0]).getName();
	    char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();
	    password = new char[tmpPassword.length];
	    System.arraycopy(tmpPassword, 0,
				password, 0, tmpPassword.length);
	    ((PasswordCallback)callbacks[1]).clearPassword();

	} catch (java.io.IOException ioe) {
	    throw new LoginException(ioe.toString());

	} catch (UnsupportedCallbackException uce) {
	    throw new LoginException("Error: " + uce.getCallback().toString() +
			" not available to acquire authentication information" +
			" from the user");
	}
    
public voidinitialize(javax.security.auth.Subject subject, javax.security.auth.callback.CallbackHandler callbackHandler, java.util.Map sharedState, java.util.Map options)
Initialize this LoginModule.

param
subject the Subject to be authenticated.
param
callbackHandler a CallbackHandler to acquire the username and password.
param
sharedState shared LoginModule state.
param
options options specified in the login Configuration for this particular LoginModule.


                                                     			   			     
         
			      

	this.subject = subject;
	this.callbackHandler = callbackHandler;
	this.sharedState = sharedState;
	this.options = options;

	ldapEnvironment = new Hashtable(9);
	ldapEnvironment.put(Context.INITIAL_CONTEXT_FACTORY,
	    "com.sun.jndi.ldap.LdapCtxFactory");

	// Add any JNDI properties to the environment
	Set keys = options.keySet();
	String key;
	for (Iterator i = keys.iterator(); i.hasNext(); ) {
	    key = (String) i.next();
	    if (key.indexOf(".") > -1) {
		ldapEnvironment.put(key, options.get(key));
	    }
	}

	// initialize any configured options

	userProvider = (String)options.get(USER_PROVIDER);
	if (userProvider != null) {
	    ldapEnvironment.put(Context.PROVIDER_URL, userProvider);
	}

	authcIdentity = (String)options.get(AUTHC_IDENTITY);
	if (authcIdentity != null &&
	    (authcIdentity.indexOf(USERNAME_TOKEN) != -1)) {
	    identityMatcher = USERNAME_PATTERN.matcher(authcIdentity);
	}

	userFilter = (String)options.get(USER_FILTER);
	if (userFilter != null) {
	    if (userFilter.indexOf(USERNAME_TOKEN) != -1) {
		filterMatcher = USERNAME_PATTERN.matcher(userFilter);
	    }
	    constraints = new SearchControls();
	    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
	    constraints.setReturningAttributes(new String[0]); //return no attrs
	    constraints.setReturningObjFlag(true); // to get the full DN
	}

	authzIdentity = (String)options.get(AUTHZ_IDENTITY);
	if (authzIdentity != null &&
	    authzIdentity.startsWith("{") && authzIdentity.endsWith("}")) {
	    if (constraints != null) {
		authzIdentityAttr =
		    authzIdentity.substring(1, authzIdentity.length() - 1);
		constraints.setReturningAttributes(
		    new String[]{authzIdentityAttr});
	    }
	    authzIdentity = null; // set later, from the specified attribute
	}

	// determine mode
	if (authcIdentity != null) {
	    if (userFilter != null) {
		authFirst = true; // authentication-first mode
	    } else {
		authOnly = true; // authentication-only mode
	    }
	}

	if ("false".equalsIgnoreCase((String)options.get("useSSL"))) {
	    useSSL = false;
	    ldapEnvironment.remove(Context.SECURITY_PROTOCOL);
	} else {
	    ldapEnvironment.put(Context.SECURITY_PROTOCOL, "ssl");
	}

	tryFirstPass =
		"true".equalsIgnoreCase((String)options.get("tryFirstPass"));

	useFirstPass =
		"true".equalsIgnoreCase((String)options.get("useFirstPass"));

	storePass = "true".equalsIgnoreCase((String)options.get("storePass"));

	clearPass = "true".equalsIgnoreCase((String)options.get("clearPass"));

	debug = "true".equalsIgnoreCase((String)options.get("debug"));

	if (debug) {
	    if (authFirst) {
		System.out.println("\t\t[LdapLoginModule] " +
		    "authentication-first mode; " +
		    (useSSL ? "SSL enabled" : "SSL disabled"));
	    } else if (authOnly) {
		System.out.println("\t\t[LdapLoginModule] " +
		    "authentication-only mode; " +
		    (useSSL ? "SSL enabled" : "SSL disabled"));
	    } else {
		System.out.println("\t\t[LdapLoginModule] " +
		    "search-first mode; " +
		    (useSSL ? "SSL enabled" : "SSL disabled"));
	    }
	}
    
public booleanlogin()
Begin user authentication.

Acquire the user's credentials and verify them against the specified LDAP directory.

return
true always, since this LoginModule should not be ignored.
exception
FailedLoginException if the authentication fails.
exception
LoginException if this LoginModule is unable to perform the authentication.


	if (userProvider == null) {
	    throw new LoginException
		("Unable to locate the LDAP directory service");
	}

	if (debug) {
	    System.out.println("\t\t[LdapLoginModule] user provider: " +
		userProvider);
	}

	// attempt the authentication
	if (tryFirstPass) {

	    try {
		// attempt the authentication by getting the
		// username and password from shared state
		attemptAuthentication(true);

		// authentication succeeded
		succeeded = true;
		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] " +
				"tryFirstPass succeeded");
		}
		return true;

	    } catch (LoginException le) {
		// authentication failed -- try again below by prompting
		cleanState();
		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] " +
				"tryFirstPass failed: " + le.toString());
		}
	    }

	} else if (useFirstPass) {

	    try {
		// attempt the authentication by getting the
		// username and password from shared state
		attemptAuthentication(true);

		// authentication succeeded
		succeeded = true;
		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] " +
				"useFirstPass succeeded");
		}
		return true;

	    } catch (LoginException le) {
		// authentication failed
		cleanState();
		if (debug) {
		    System.out.println("\t\t[LdapLoginModule] " +
				"useFirstPass failed");
		}
		throw le;
	    }
	}

	// attempt the authentication by prompting for the username and pwd
	try {
	    attemptAuthentication(false);

	    // authentication succeeded
	   succeeded = true;
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
				"authentication succeeded");
	    }
	    return true;

	} catch (LoginException le) {
	    cleanState();
	    if (debug) {
		System.out.println("\t\t[LdapLoginModule] " +
				"authentication failed");
	    }
	    throw le;
	}
    
public booleanlogout()
Logout a user.

This method removes the Principals that were added by the commit method.

exception
LoginException if the logout fails.
return
true in all cases since this LoginModule should not be ignored.

	if (subject.isReadOnly()) {
	    cleanState();
	    throw new LoginException ("Subject is read-only");
	}
	Set principals = subject.getPrincipals();
	principals.remove(ldapPrincipal);
	principals.remove(userPrincipal);
	if (authzIdentity != null) {
	    principals.remove(authzPrincipal);
	}

	// clean out state
	cleanState();
	succeeded = false;
	commitSucceeded = false;

	ldapPrincipal = null;
	userPrincipal = null;
	authzPrincipal = null;

	if (debug) {
	    System.out.println("\t\t[LdapLoginModule] logged out Subject");
	}
	return true;
    
private java.lang.StringreplaceUsernameToken(java.util.regex.Matcher matcher, java.lang.String string)
Replace the username token

param
string the target string
return
the modified string

	return matcher != null ? matcher.replaceAll(username) : string;