As we transition to the new Hyland Community, some post links may be temporarily disrupted as we finalize redirects. We apologize for the inconvenience and are working to resolve this as quickly as possible. Details.
Common Problem #1: My application goes into an "endless loop" when I try to login, what's going on?A common user problem with infinite loop and redirecting to the login page is caused by accidently configuring the login page as a "secured" resource. Generally make sure you mark your login page as requiring ROLE_ANONYMOUS.
user = (User)session.getAttribute(AlfrescoFacesPortlet.MANAGED_BEAN_PREFIX + AUTHENTICATION_USER);
alfresco.sar |_alfresco-1.3.0.war |_ WEB-INF |_ jboss-web.xml |_ classes |_alfresco |_extension |_authentication-services-context.xml |_core-services-context.xml |_repository.properties |_conf |_ login-config.xml |_META-INF |_ jboss-service.xml
<?xml version='1.0'?> <!DOCTYPE policy PUBLIC "-//JBoss//DTD JBOSS Security Config 3.0//EN" "http://www.jboss.org/j2ee/dtd/security_config.dtd"> <policy> <application-policy name="alfresco"> <authentication> <login-module code="org.jboss.security.auth.spi.LdapAuthenticatorLoginModule" flag="sufficient"> <module-option name="java.naming.provider.url">ldap://gscodenh02.cr.usgs.gov:636/</module-option> <module-option name="java.naming.security.protocol">ssl</module-option> <module-option name="searchBase">OU=USGS,O=DOI</module-option> <module-option name="accepted.email.domains">usgs.gov</module-option> <module-option name="java.naming.security.principal">CN=Blah</module-option> <module-option name="java.naming.security.credentials">********</module-option> </login-module> </authentication> </application-policy> </policy>
<?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="org.jboss.security.auth.login.DynamicLoginConfig" name="alfresco:service=JAASLoginModule"> <depends optional-attribute-name="LoginConfigService">jboss.security:service=XMLLoginConfig</depends> <depends optional-attribute-name="SecurityManagerService">jboss.security:service=JaasSecurityManager</depends> <attribute name="AuthConfig">conf/login-config.xml</attribute> </mbean> </server>
<?xml version="1.0"?> <jboss-web> <context-root>/alfresco</context-root> </jboss-web>
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'> <beans> <!– The bean element below replaces the default authenticationComponentImpl class as configured for out-of-the-box Alfresco. See the class javadocs for an explanation as to what this implementation of AuthenticationComponent does –> <bean id="authenticationComponentImpl" class="gov.doi.usgs.alfresco.security.JBossSecurityManagerAuthCompImpl"> <property name="authenticationDao"> <ref bean="alfDaoImpl" /> </property> <property name="authenticationManager"> <ref bean="authenticationManager" /> </property> <property name="allowGuestLogin"> <value>false</value> </property> <property name="jaasSecurityManagerJndiName"> <value>java:jaas/alfresco</value> </property> </bean> </beans>
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'> <beans> <!– The bean element below replaces the default repository-properties class as configured for out-of-the-box Alfresco. –> <bean id="repository-properties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="ignoreUnresolvablePlaceholders"> <value>true</value> </property> <property name="locations"> <list> <value>classpath:alfresco/repository.properties</value> <value>classpath:alfresco/version.properties</value> <value>classpath:alfresco/domain/transaction.properties</value> <value>classpath:alfresco/extension/repository.properties</value> <!– overridecore properties –> </list> </property> </bean> </beans>
#Directory for storing alfresco data dir.root=/usr/local/alf_data
package org.jboss.security.auth.spi; import java.security.acl.Group; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.Subject; import javax.naming.InitialContext; import javax.security.auth.login.LoginException; import java.util.Arrays; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.jboss.security.SimpleGroup; import org.apache.commons.lang.StringUtils; import gov.doi.usgs.jboss.security.LoginStateModule; import gov.doi.usgs.util.AuthenticationHelper; /** * LoginModule that authenticates users based on an email and password lookup * from an LDAP server. The following options must be configured for this module: * <ul> * <li>java.naming.provider.url - the url to the ldap server (ie. ldap://ldap.myserver.gov:389)</li> * <li>searchBase - Use searchbase as the starting point for the LDAP search. [required]</li> * </ul> * The following options may be optionally configured for this module: * <ul> * <li>java.naming.security.principal - the distinguished name used to bind to the ldap server * in order to perform the orginal email query. Leave blank for anonymous bind.</li> * <li>java.naming.security.credentials - the password used to bind to the ldap sesrver in order * to perform the orginal email query. Leave blank for anonymous bind.</li> * <li>socketTimeout - How long should the module wait in millisecs for a connection to the LDAP server before it timesout.</li> * <li>accepted.email.domains - comma separated list of email domain names (for example usgs.gov, fws.giv) which must match the * given email address (username) in order for this LoginModule to attempt an LDAP bind. If not specified, the LoginModule * always tries to preform a bind connection.</li> * <li>java.naming.security.protocol - set to "ssl" to use ssl encrpytion</li> * </ul> * <p> * This LoginModule is used strictly for authentication (role assignment) and performs no authorization. * Subsequent LoginModules should perform authorization. * <p> * The module works like this: * <ol> * <li>The module will attempt to find a node of objectClass=person and mail=[given username] * by binding to the ldap server using the url, dn, and password configured in the module options</li> * <li>If a matching dn is found, the module will then attempt to bind to the ldap server using * the found dn and the given password.</li> * <li>If the second bind is successful, the module will ensure that * <ol> * <li>the user's email and password are set into the LoginModule shareMemory map under * the <tt>javax.security.auth.login.name</tt> and <tt>javax.security.auth.login.password</tt> * keys respectively. This makes sure that all subsequent LoginModules that use password-stacking * will use these values as the username/password instead of those provided by the CallbackHandler.</li> * </ol> * </li> * </ol> * * @author Jon French */ public class LdapAuthenticatorLoginModule extends UsernamePasswordLoginModule { public static final String SEARCH_BASE = "searchBase"; public static final String SOCKET_TIMEOUT = "socketTimeout"; public LdapAuthenticatorLoginModule(){} private transient SimpleGroup userRoles = new SimpleGroup("Roles"); private String[] fAcceptedDomains; public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { super.initialize(subject, callbackHandler, sharedState, options); fAcceptedDomains = StringUtils.split((String) options.get("accepted.email.domains"),','); if (fAcceptedDomains != null) Arrays.sort(fAcceptedDomains); } /** * Overriden to return an empty password string as typically one cannot * obtain a user's password. We also override the validatePassword so * this is ok. @return and empty password String */ protected String getUsersPassword() throws LoginException { return ""; } /** * Return a dummy set of userRoles. We don't care that they are not * appropriately populated. The population of the user roles will be * the responsibility of a second LoginModule. * @return Group[] containing the sets of roles */ protected Group[] getRoleSets() throws LoginException { Group[] roleSets = {userRoles}; return roleSets; } /** * Returns true if the username and password validates against the * configured LDAP options. * * @param inputPassword the password to validate. * @param expectedPassword ignored */ protected boolean validatePassword(String inputPassword, String expectedPassword) { boolean isValid = false; if (inputPassword != null) { // See if this is an empty password that should be disallowed if (inputPassword.trim().length() == 0) { return false; //never allow empty passwords } try { // Validate the password by trying to create an initial context String username = getUsername(); /* If the username (which should be an email) does not match any of the email domains in the accepted.email.domains options, return false */ int ampIndex = username.indexOf('@'); if (ampIndex < 0 || // if the username is not an email, forget the authentication entirely. (fAcceptedDomains != null && Arrays.binarySearch(fAcceptedDomains,username.substring(ampIndex + 1)) < 0)) return false; verifyLdapBind(username, inputPassword); sharedState.put("javax.security.auth.login.name",username); sharedState.put("javax.security.auth.login.password",inputPassword); isValid = true; } catch (Exception e) { super.log.debug("Failed to validate password", e); } } return isValid; } /** * Attempt to bind to the LDAP server configured in the module options and * confirm that there [1] is a user with an mail attribute matching the given * username and [2] the DN of that user will allow a success bind with the * given credential as a String password. * * @throws Exception if either step [1] or [2] fails */ private void verifyLdapBind(String username, Object credential) throws Exception{ Hashtable env = new Hashtable(); // 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 the timeout in case an LDAP Server may not be connected */ String timeout = (String) options.get(SOCKET_TIMEOUT); if (timeout == null) timeout = "2000"; env.put("com.sun.jndi.ldap.connect.timeout", timeout); switch(AuthenticationHelper.verifyLdapBind(env,username,(String) credential,(String)options.get(SEARCH_BASE))) { case AuthenticationHelper.USERNAME_PASSWORD_AUTHENTICATED: return; case AuthenticationHelper.NO_USERNAME_FOUND: throw new Exception("Failed to find dn for : " + username); case AuthenticationHelper.INVALID_PASSWORD: InitialContext _ctx = new InitialContext(); LoginStateModule module = (LoginStateModule) _ctx.lookup("java:LoginStateModule"); module.trySetLoginState(LoginStateModule.USERNAME_IN_LDAP_BUT_BAD_CREDENTIAL); throw new Exception("Failed to validate password for : " + username); } } }
package gov.doi.usgs.alfresco.security; import org.alfresco.repo.security.authentication.AuthenticationComponentImpl; import org.alfresco.repo.security.authentication.AuthenticationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.naming.Context; import javax.naming.InitialContext; import org.jboss.security.plugins.JaasSecurityManager; import org.jboss.security.SimplePrincipal; /** * Implementation of AuthenticationComponent that attempts to authenticate against * a JBoss JaasSecurityManager found at the JNDI name specified by the <tt>jaasSecurityManagerJndiName</tt> * property. * <p> * The strategy that the class takes is to first try to authenticate against the JaasSecurityManager * and if that fails, to pass the authenticate request back to the super class. * * @author Jon French */ public class JBossSecurityManagerAuthCompImpl extends AuthenticationComponentImpl { private static final Log LOG = LogFactory.getLog(JBossSecurityManagerAuthCompImpl.class); private String fJaasSecurityManagerJndiName = null; public void setJaasSecurityManagerJndiName(String name){ fJaasSecurityManagerJndiName=name; } public void authenticate(String userName, char[] password) throws AuthenticationException { try{ Context initContext = new InitialContext(); JaasSecurityManager m = (JaasSecurityManager) initContext.lookup( fJaasSecurityManagerJndiName); SimplePrincipal userNameP = new SimplePrincipal(userName); if (LOG.isDebugEnabled()) { LOG.debug("userName: " + userName + " , " + new String(password)); } boolean success = m.isValid(userNameP,password); if (LOG.isDebugEnabled()) { LOG.debug("success:" + success); } if (!success) { /* If we weren't able to log in via the JAAS Security Manager, this means that the user wasn't found on the Ldap servers. Try to authenticate via standard Alfresco lookup via the super class */ super.authenticate(userName,password); } else { setCurrentUser(userName); } } catch(javax.naming.NamingException e){ throw new AuthenticationException("Problem obtaining JaasSecurityManager",e); } } }
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'> <beans> <!– The bean element below replaces the default daoAuthenticationProvider class as configured for out-of-the-box Alfresco. –> <bean id="daoAuthenticationProvider" class="gov.doi.usgs.alfresco.security.JBossSecurityManagerDaoAuthenticationProvider"> <property name="authenticationDao"> <ref bean="authenticationDao" /> </property> <property name="saltSource"> <ref bean="saltSource" /> </property> <property name="passwordEncoder"> <ref bean="passwordEncoder" /> </property> <property name="jaasSecurityManagerJndiName"> <value>java:jaas/alfresco</value> </property> </bean> </beans>
package gov.doi.usgs.alfresco.security; import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.UserDetails; import javax.naming.Context; import javax.naming.InitialContext; import org.jboss.security.plugins.JaasSecurityManager; import org.jboss.security.SimplePrincipal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * */ public class JBossSecurityManagerDaoAuthenticationProvider extends net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider { private JaasSecurityManager fManager; private static final Log LOG = LogFactory.getLog(RemoteUserDaoAuthenticationProvider.class); private String fJaasSecurityManagerJndiName = null; public void setJaasSecurityManagerJndiName(String name){ fJaasSecurityManagerJndiName=name; } protected boolean isPasswordCorrect(Authentication authentication, UserDetails user) { if (fManager == null) { try{ Context initContext = new InitialContext(); fManager = (JaasSecurityManager) initContext.lookup( fJaasSecurityManagerJndiName); } catch(javax.naming.NamingException e){ throw new RuntimeException("Problem obtaining JaasSecurityManager",e); } } SimplePrincipal userNameP = new SimplePrincipal((String)authentication.getPrincipal()); if (LOG.isDebugEnabled()) { LOG.debug("userName: " + userNameP + " , " + authentication.getCredentials()); } /* * First try to authenticate via the user via the JaasSecurityManager, if that fails, * authenitcate them through the Alfresco store. */ if (fManager.isValid(userNameP,authentication.getCredentials())) { return true; } else { return super.isPasswordCorrect(authentication,user); } } }