-
1. Re: writing new LoginModul - unable to replace Principal ??
markash Sep 13, 2006 9:12 AM (in response to rsoika)Good Day,
There are two parts this:
1. Customise the Principal class
2. Retrieving the CallingPrincipal
1. Customise the Principal class
By default the LoginModules in JBoss make use of the SimplePrincipal class to denote principals in the system. It is easy to change the principal that the LoginModules create by setting the principalClass of the LDAPLoginModule in login-conf.xml
Example
<module-option name="principalClass">za.co.connext.jboss.LDAPPrincipal</module-option>
Just remember that the Principal class needs a constructor that takes the name of the user as a parameter.
2. Retrieving the CallingPrincipal
To enable to session context to retrieve the caller principal, the login module should save the information in the Subject.
The easiest way is to create a SimpleGroup with the name CallerPrincipal and add your principal object to it. Add the CallerPrincipal group to the principals collection of the subject. You should check the principals collection first to determine if the CallerPrincipal group was added by a LoginModule further up the chain. All of this should be done in the commit method of the LoginModule. In my implementations when using JBoss is to always to do a super.commit() before adding my custom logic in the commit method.
Hope this helps. -
2. Re: writing new LoginModul - unable to replace Principal ??
rsoika Sep 13, 2006 6:41 PM (in response to rsoika)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 -
3. Re: writing new LoginModul - unable to replace Principal ??
markash Sep 13, 2006 11:11 PM (in response to rsoika)Good Day,
Your commit should only do your logic if the login was OK. Look at the LDAPLoginModule's parent class, you'll see that there is a protected variable `loginOK` which is set to true in the login() method and should be checked in the commit() method. -
4. Re: writing new LoginModul - unable to replace Principal ??
rsoika Sep 15, 2006 10:27 AM (in response to rsoika)hi, yes I have changed this. I also implememented a new version with extended functionality. Documentation and also source is available at:
http://www.imixs.org/websites/imixs-org.nsf/chapter/0300.0100.0020.?OpenDocument
This Modul works perfect with an IBM Lotus Domino LDAP and is able to resolve encapsulated roles! -
5. Re: writing new LoginModul - unable to replace Principal ??
patwary_shiva Oct 9, 2007 11:53 AM (in response to rsoika)Does it work with sun one ldap or active directory?????
-
6. Re: writing new LoginModul - unable to replace Principal ??
rsoika Oct 9, 2007 12:11 PM (in response to rsoika)hi
sorry I had only tested it with IBM Lotus Domino LDAP Directory -
7. Re: writing new LoginModul - unable to replace Principal ??
patwary_shiva Oct 9, 2007 4:04 PM (in response to rsoika)I am trying to use with sun one ldap it is not working.Is there any Mbean similar to weblogic LDAPAuthenticatorMBean which can give me information about the ldap
-
8. Re: writing new LoginModul - unable to replace Principal ??
patwary_shiva Oct 9, 2007 4:26 PM (in response to rsoika)I am getting Bad password for username=admin
Below is the stack trace of the exception :
authInfo=AppConfigurationEntry[]:
[0]
LoginModule Class: org.jboss.security.ClientLoginModule
ControlFlag: LoginModuleControlFlag: required
Options:[1]
LoginModule Class: org.imixs.jboss.security.LdapLoginModuleExt
ControlFlag: LoginModuleControlFlag: required
Options:name=RoleSeachContext, value=ou=Groups, dc=axeda, dc=com
name=java.naming.security.principal, value=uid=admin, ou=Administrators, ou=TopologyManagement, o=NetscapeRoot
name=LoginNameSearch, value=(&(uid={0})(objectclass=person))
name=java.naming.factory.initial, value=com.sun.jndi.ldap.LdapCtxFactory
name=java.naming.security.credentials, value=admin
name=roleNameAttributeID, value=cn
name=roleFilter, value=(&(cn={0})(objectclass=groupofUniqueNames))]]</module-option>
<module-option name="baseFilter"><![CDATA[(&(uid={0})(objectclass=person))
name=java.naming.security.authentication, value=simple
name=java.naming.provider.url, value=ldap://leda.axeda.com:389/
name=roleAttributeID, value=cn
name=RoleSearch, value=(&(cn={0})(objectclass=groupofUniqueNames))
name=LoginNameSeachContext, value=ou=People, dc=axeda, dc=com
name=uidAttributeID, value=uniqueMember
name=LoginNameToCompositeName, value=true
name=roleAttributeIsDN, value=false
2007-10-09 16:19:42,694 TRACE [org.jboss.security.ClientLoginModule] Security domain: servicelinkdomain
2007-10-09 16:19:42,694 TRACE [org.jboss.security.ClientLoginModule] Enabling restore-login-identity mode
2007-10-09 16:19:42,694 TRACE [org.jboss.security.ClientLoginModule] Begin login
2007-10-09 16:19:42,694 TRACE [org.jboss.security.ClientLoginModule] Obtained login: admin, credential.class: [C
2007-10-09 16:19:42,694 TRACE [org.jboss.security.ClientLoginModule] End login
2007-10-09 16:19:42,725 DEBUG [org.imixs.jboss.security.LdapLoginModuleExt] Bad password for username=admin
2007-10-09 16:19:42,725 TRACE [org.jboss.security.ClientLoginModule] abort
2007-10-09 16:19:42,725 TRACE [org.jboss.security.SecurityAssociation] clear, server=true
2007-10-09 16:19:42,725 TRACE [org.jboss.security.plugins.JaasSecurityManager.servicelinkdomain] Login failure
javax.security.auth.login.FailedLoginException: Password Incorrect/Password Required
at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:213)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:769)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
at javax.security.auth.login.LoginContext.login(LoginContext.java:579)
at org.jboss.security.plugins.JaasSecurityManager.defaultLogin(JaasSecurityManager.java:603)
at org.jboss.security.plugins.JaasSecurityManager.authenticate(JaasSecurityManager.java:537)
at org.jboss.security.plugins.JaasSecurityManager.isValid(JaasSecurityManager.java:344)
at org.jboss.web.tomcat.security.JBossSecurityMgrRealm.authenticate(JBossSecurityMgrRealm.java:491)
at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:258)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:417)
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:595)
2007-10-09 16:19:42,725 TRACE [org.jboss.security.plugins.JaasSecurityManager.servicelinkdomain] End isValid, false
below is the configuration:
<login-module code="org.jboss.security.ClientLoginModule" flag="required"/>
<login-module code="org.imixs.jboss.security.LdapLoginModuleExt" flag="required">
<module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
<module-option name="java.naming.provider.url">ldap://sample.sample.sample:389/</module-option>
<module-option name="java.naming.security.authentication">simple</module-option>
<module-option name="searchTimeLimit">5000</module-option>
<!-- searchScope is neccesary for Domino SUBTREE_SCOPE -->
<module-option name="searchScope"></module-option>
<!-- Params for Distinguished Name Search -->
<module-option name="LoginNameToCompositeName">false</module-option>
<module-option name="LoginNameSeachContext">ou=People, dc=axeda, dc=com</module-option>
<module-option name="LoginNameSearch"><![CDATA[(&(uid={0})(objectclass=person))]]></module-option>
<!-- Params for Role Search -->
<module-option name="roleAttributeID">cn</module-option>
<module-option name="RoleSeachContext">ou=Groups, dc=axeda, dc=com</module-option>
<module-option name="RoleSearch"><![CDATA[(&(cn={0})(objectclass=groupofUniqueNames))]]></module-option>
<!-- Principal und Credentials for ldap lookups -->
<module-option name="java.naming.security.principal">uid=admin, ou=Administrators, ou=TopologyManagement, o=NetscapeRoot</module-option>
<module-option name="java.naming.security.credentials">sample</module-option>
</login-module>