[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