[jboss-user] [Security & JAAS/JBoss] - Re: writing new LoginModul - unable to replace Principal ??

rsoika do-not-reply at jboss.com
Wed Sep 13 18:41:22 EDT 2006


Hi

and thanks a lot for your Help!
I works!

I wrote the following new LDAPLogin Modul which converts the Login Name into the corresponding Distinguished name of the LDAP Directory the User authenticates :

package org.imixs.jboss.security;
  | 
  | import org.jboss.security.*;
  | import org.jboss.security.auth.spi.LdapLoginModule;
  | import javax.security.auth.login.LoginException;
  | import java.util.*;
  | import java.util.Map.Entry;
  | import javax.security.auth.Subject;
  | import javax.security.auth.callback.CallbackHandler;
  | import javax.naming.*;
  | import javax.naming.directory.*;
  | import javax.naming.ldap.InitialLdapContext;
  | 
  | /**
  |  * This LoginModul sublcasses the org.jboss.security.auth.spi.LdapLoginModul and
  |  * coverts a login name into the corresponding Distinguished Name of the LDAP
  |  * Object the login name belongs to. The Class replaces the CallerPricipal form
  |  * the Session Context so the new Distinguished Name will be returend by the
  |  * "getCallerPricipal()" method instead of the Login Name.
  |  * 
  |  * The Class starts a LDAP Seach inside the LDAP Context from the JNDI Context
  |  * defined by the parameters form the
  |  * org.jboss.security.auth.spi.LdapLoginModul. Additional to configuration
  |  * parameters from the LdapLoginModul the following params can be set (all these
  |  * params are optional)
  |  * 
  |  * LoginNameSeachContext the SeachContext to search for the UserObject of the
  |  * Login Name
  |  * 
  |  * LoginNameSearchAttribute the Attribute to search. A SearchFilter is generated
  |  * in the Form "(LoginNameSearchAttribute=Username)"
  |  * 
  |  * LoginNameToCompositeName if "false" the Name of the Search Result ist returnd
  |  * if "true" (not false) the Name will be converted into a ComposteName e.g.
  |  * "Ralph Soika,O=Imixs" -> "Ralph Soika/O=Imixs"
  |  * 
  |  * 
  |  * To replace the CallerPrinciapal inside the Session Context this class
  |  * overrides the commit Methode. The Commit Mehtode creates a SimpleGroup with
  |  * the name "CallerPrincipal" and adds a new SimplePricipal Objekct with the new
  |  * Distinguished Name to it. The CallerPrincipal group is then added to the
  |  * principals collection of the subject.
  |  * 
  |  * (The current Implementation did not check the principals collection first to
  |  * determine if the CallerPrincipal group was added by a LoginModule further up
  |  * the chain!)
  |  * 
  |  * @author Ralph Soika
  |  * 
  |  */
  | 
  | public class LdapLoginModuleDN extends LdapLoginModule {
  | 	private static final String PRINCIPAL_DN_PREFIX_OPT = "principalDNPrefix";
  | 
  | 	private static final String PRINCIPAL_DN_SUFFIX_OPT = "principalDNSuffix";
  | 
  | 	private static final String SEARCH_SCOPE_OPT = "searchScope";
  | 
  | 	private static final String SEARCH_TIME_LIMIT_OPT = "searchTimeLimit";
  | 
  | 	// Environment Settings for DN Search
  | 	private static final String LOGIN_NAME_SEARCH_CTX = "LoginNameSeachContext";
  | 
  | 	private static final String LOGIN_NAME_SEARCH_ATTRIBUTE = "LoginNameSearchAttribute";
  | 
  | 	private static final String LOGIN_NAME_TO_COMPOSITE_NAME = "LoginNameToCompositeName";
  | 
  | 	String sUserPasword = "";
  | 
  | 	public boolean commit() throws LoginException {
  | 
  | 		// search User DN....
  | 		String sDistinguishedName = "";
  | 		try {
  | 			sDistinguishedName = getDN(getUsername(), sUserPasword);
  | 			if (sDistinguishedName == null || "".equals(sDistinguishedName))
  | 				sDistinguishedName = getUsername();
  | 		} catch (Exception edn) {
  | 			// no dn found!
  | 			sDistinguishedName = getUsername();
  | 		}
  | 
  | 		Set principals = subject.getPrincipals();
  | 		SimpleGroup simpleGroup = new SimpleGroup("CallerPrincipal");
  | 		if (principals.contains(simpleGroup))
  | 			principals.remove(simpleGroup);
  | 		simpleGroup.addMember(new SimplePrincipal(sDistinguishedName));
  | 		principals.add(simpleGroup);
  | 
  | 		return super.commit();
  | 	}
  | 
  | 	public void initialize(Subject subject, CallbackHandler callbackHandler,
  | 			Map sharedState, Map options) {
  | 		// System.out.println("[LdapLoginModuleDN] V 7.4");
  | 
  | 		super.initialize(subject, callbackHandler, sharedState, options);
  | 	}
  | 
  | 	/**
  | 	 * This Method starts a search for the DN of the LDAP Object the Username
  | 	 * belogs to.
  | 	 * 
  | 	 * @param username
  | 	 * @param credential
  | 	 * @return
  | 	 * @throws Exception
  | 	 */
  | 	private String getDN(String username, Object credential) throws Exception {
  | 		String sDN = "";
  | 		Properties env = new Properties();
  | 		// Map all option into the JNDI InitialLdapContext env
  | 		Iterator iter = options.entrySet().iterator();
  | 		while (iter.hasNext()) {
  | 			Entry entry = (Entry) iter.next();
  | 			env.put(entry.getKey(), entry.getValue());
  | 		}
  | 
  | 		// Set defaults for key values if they are missing
  | 		String factoryName = env.getProperty(Context.INITIAL_CONTEXT_FACTORY);
  | 		if (factoryName == null) {
  | 			factoryName = "com.sun.jndi.ldap.LdapCtxFactory";
  | 			env.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
  | 		}
  | 		String authType = env.getProperty(Context.SECURITY_AUTHENTICATION);
  | 		if (authType == null)
  | 			env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
  | 		String protocol = env.getProperty(Context.SECURITY_PROTOCOL);
  | 		String providerURL = (String) options.get(Context.PROVIDER_URL);
  | 		if (providerURL == null)
  | 			providerURL = "ldap://localhost:"
  | 					+ ((protocol != null && protocol.equals("ssl")) ? "636"
  | 							: "389");
  | 
  | 		String bindDN = (String) options.get(Context.SECURITY_PRINCIPAL);
  | 		String bindCredential = (String) options
  | 				.get(Context.SECURITY_CREDENTIALS);
  | 		/*
  | 		 * String securityDomain = (String) options.get(SECURITY_DOMAIN_OPT);
  | 		 * if( securityDomain != null ) { ObjectName serviceName = new
  | 		 * ObjectName(securityDomain); char[] tmp =
  | 		 * DecodeAction.decode(bindCredential, serviceName); bindCredential =
  | 		 * new String(tmp); }
  | 		 */
  | 
  | 		String principalDNPrefix = (String) options
  | 				.get(PRINCIPAL_DN_PREFIX_OPT);
  | 		if (principalDNPrefix == null)
  | 			principalDNPrefix = "";
  | 		String principalDNSuffix = (String) options
  | 				.get(PRINCIPAL_DN_SUFFIX_OPT);
  | 		if (principalDNSuffix == null)
  | 			principalDNSuffix = "";
  | 		String userDN = principalDNPrefix + username + principalDNSuffix;
  | 		env.setProperty(Context.PROVIDER_URL, providerURL);
  | 		env.setProperty(Context.SECURITY_PRINCIPAL, userDN);
  | 		env.put(Context.SECURITY_CREDENTIALS, credential);
  | 		if (bindDN != null) {
  | 			// Rebind the ctx to the bind dn/credentials for the roles searches
  | 			env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
  | 			env.put(Context.SECURITY_CREDENTIALS, bindCredential);
  | 		}
  | 		// get Context
  | 		InitialLdapContext ctx = new InitialLdapContext(env, null);
  | 
  | 		try {
  | 
  | 			int searchScope = SearchControls.SUBTREE_SCOPE;
  | 			int searchTimeLimit = 10000;
  | 			String timeLimit = (String) options.get(SEARCH_TIME_LIMIT_OPT);
  | 			if (timeLimit != null) {
  | 				try {
  | 					searchTimeLimit = Integer.parseInt(timeLimit);
  | 				} catch (NumberFormatException e) {
  | 				}
  | 			}
  | 			String scope = (String) options.get(SEARCH_SCOPE_OPT);
  | 			if ("OBJECT_SCOPE".equalsIgnoreCase(scope))
  | 				searchScope = SearchControls.OBJECT_SCOPE;
  | 			else if ("ONELEVEL_SCOPE".equalsIgnoreCase(scope))
  | 				searchScope = SearchControls.ONELEVEL_SCOPE;
  | 			if ("SUBTREE_SCOPE".equalsIgnoreCase(scope))
  | 				searchScope = SearchControls.SUBTREE_SCOPE;
  | 
  | 			SearchControls controls = new SearchControls();
  | 			controls.setSearchScope(searchScope);
  | 
  | 			controls.setTimeLimit(searchTimeLimit);
  | 
  | 			String sContext = env.getProperty(LOGIN_NAME_SEARCH_CTX);
  | 			if (sContext == null)
  | 				sContext = "";
  | 
  | 			String sLoginAttribute = env
  | 					.getProperty(LOGIN_NAME_SEARCH_ATTRIBUTE);
  | 			if (sLoginAttribute == null)
  | 				sLoginAttribute = "uid";
  | 
  | 			// create search filter
  | 			String sFilter = "(" + sLoginAttribute + "=" + userDN + ")";
  | 
  | 			NamingEnumeration answer = ctx.search(sContext, sFilter, controls);
  | 			if (answer.hasMore()) {
  | 				SearchResult sr = (SearchResult) answer.next();
  | 				sDN = sr.getName();
  | 
  | 				// convert into a composite name?
  | 				String sConvert = env.getProperty(LOGIN_NAME_TO_COMPOSITE_NAME);
  | 				if (!"false".equals(sConvert)) {
  | 					CompositeName cn = new CompositeName();
  | 					StringTokenizer st = new StringTokenizer(sDN, ",");
  | 					while (st.hasMoreTokens())
  | 						cn.add(st.nextToken().trim());
  | 					sDN = cn.toString();
  | 				}
  | 
  | 			}
  | 
  | 		} catch (Exception e) {
  | 			System.out.println("[LdapLoginModuleDN] getDN error=" + e);
  | 
  | 		}
  | 		// System.out.println("[LdapLoginModuleDN] getDN Result='" + sDN + "");
  | 
  | 		// Close the context to release the connection
  | 		ctx.close();
  | 		return sDN;
  | 	}
  | 
  | 	/**
  | 	 * this methode is overridden to save the current user password for later
  | 	 * ldap search
  | 	 */
  | 	protected boolean validatePassword(String inputPassword,
  | 			String expectedPassword) {
  | 		sUserPasword = inputPassword;
  | 		return super.validatePassword(inputPassword, expectedPassword);
  | 	}
  | 
  | }
  | 

The Login Modul is configured as the LDAPLoginModul with 3 new optional params. So the following configuration works perfect with a IBM Domino LDAP Directory 



  | <application-policy name="imixsIX">
  |         <authentication>
  |             <login-module code="org.imixs.jboss.security.LdapLoginModuleDN"
  |                           flag="required">
  |                 <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
  |                 <module-option name="java.naming.provider.url">ldap://mydominohost:389/</module-option>         
  | 
  |                        
  |                 <module-option name="java.naming.security.authentication">simple</module-option>
  |                 <module-option name="principalDNPrefix"></module-option>
  |                 <!-- for principalDNSuffix no entry is needed for domino (e.g. o=MYDOMIAN) -->                  
  |                 <module-option name="principalDNSuffix"></module-option>
  |                 <module-option name="rolesCtxDN"></module-option>
  |                 <module-option name="uidAttributeID">member</module-option>
  |                 <module-option name="matchOnUserDN">true</module-option>
  |                 <module-option name="roleAttributeID">cn</module-option>
  |                 <module-option name="roleAttributeIsDN">false</module-option>
  |                 <module-option name="searchTimeLimit">5000</module-option>
  |      
  |                 <module-option name="searchScope">SUBTREE_SCOPE</module-option>
  |                 
  | 		<!-- Params for Distinguished Name Search (optional) -->
  |                 <module-option name="LoginNameSeachContext"></module-option>
  |                 <module-option name="LoginNameSearchAttribute">uid</module-option>
  |                 <module-option name="LoginNameToCompositeName">true</module-option>
  | 
  |             </login-module>
  |         </authentication>
  |     </application-policy>
  | 

I think this LoginModul will be usefull for J2EE Applications where the CallerPricipal is importend for business logic. So feel free to use it

Thanks again
ralph


View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3971433#3971433

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=3971433



More information about the jboss-user mailing list