Index: lams_build/conf/slim/standalone.xml =================================================================== diff -u -r8636b175edec8304175215f79a5deaa3dae5e361 -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_build/conf/slim/standalone.xml (.../standalone.xml) (revision 8636b175edec8304175215f79a5deaa3dae5e361) +++ lams_build/conf/slim/standalone.xml (.../standalone.xml) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -411,14 +411,6 @@ - - Index: lams_build/conf/standalone.xml =================================================================== diff -u -rdf88f5482a95a646b341cfe39e6853cca37c1162 -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_build/conf/standalone.xml (.../standalone.xml) (revision df88f5482a95a646b341cfe39e6853cca37c1162) +++ lams_build/conf/standalone.xml (.../standalone.xml) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -447,14 +447,6 @@ - - Index: lams_build/lib/lams/lams-central.jar =================================================================== diff -u -rcb6ed4ff4960be0fcfc57b3d161505d07929c00a -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b Binary files differ Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r613b476be08c495e3da27743a90b9b932782179a -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b Binary files differ Fisheye: Tag 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/security/AbstractServerLoginModule.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_central/src/java/org/lamsfoundation/lams/security/DatabaseAuthenticator.java =================================================================== diff -u -r5e828bbc5f2a11fa8b55f616b1eb0e4c0489c68a -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_central/src/java/org/lamsfoundation/lams/security/DatabaseAuthenticator.java (.../DatabaseAuthenticator.java) (revision 5e828bbc5f2a11fa8b55f616b1eb0e4c0489c68a) +++ lams_central/src/java/org/lamsfoundation/lams/security/DatabaseAuthenticator.java (.../DatabaseAuthenticator.java) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -32,89 +32,78 @@ import javax.naming.NamingException; import javax.sql.DataSource; +import org.apache.log4j.Logger; +/** + * Validates user password against an entry in the LAMS database. + */ +public class DatabaseAuthenticator { + private static Logger log = Logger.getLogger(DatabaseAuthenticator.class); -public class DatabaseAuthenticator -{ - private String dsJndiName; - private String principalsQuery; - - public DatabaseAuthenticator(String dsJndiName, String principalsQuery) { - this.dsJndiName = dsJndiName; - this.principalsQuery = principalsQuery; - } + private String dsJndiName; + private static final String PRINCIPAL_QUERY = "SELECT password FROM lams_user WHERE login=?"; - public boolean authenticate( String username, String inputPassword ) - { - boolean isValid = false; - if ( (inputPassword == null ) && (inputPassword.trim().length()==0) ) - return isValid; - Connection conn = null; - PreparedStatement ps = null; - ResultSet rs = null; + public DatabaseAuthenticator(String dsJndiName) { + this.dsJndiName = dsJndiName; + } - String databasePassword = null; - try - { - InitialContext ctx = new InitialContext(); - DataSource ds = (DataSource) ctx.lookup(dsJndiName); - conn = ds.getConnection(); - // Get the password - ps = conn.prepareStatement(principalsQuery); - ps.setString(1, username); - rs = ps.executeQuery(); - if( rs.next() == false ) - isValid = false; - - databasePassword = rs.getString(1); - if ( inputPassword.equals(databasePassword.trim()) ) - isValid = true; - - } - catch(NamingException ex) - { - System.out.println(ex); - } - catch(SQLException ex) - { - System.out.println(ex); - } - finally - { - if (rs != null) - { - try - { - rs.close(); - } - catch(SQLException e) - {} - } - if( ps != null ) - { - try - { - ps.close(); - } - catch(SQLException e) - {} - } - if( conn != null ) - { - try - { - conn.close(); - } - catch (SQLException ex) - {} - } - } - return isValid; - + public boolean authenticate(String username, String inputPassword) { + if ((inputPassword == null) || (inputPassword.trim().length() == 0)) { + return false; } -} + boolean isValid = false; + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + String databasePassword = null; + try { + InitialContext ctx = new InitialContext(); + DataSource ds = (DataSource) ctx.lookup(dsJndiName); + conn = ds.getConnection(); + // Get the password + ps = conn.prepareStatement(PRINCIPAL_QUERY); + ps.setString(1, username); + rs = ps.executeQuery(); + if (rs.next() == false) { + isValid = false; + } + databasePassword = rs.getString(1); + if (inputPassword.equals(databasePassword.trim())) { + isValid = true; + } + } catch (NamingException e) { + DatabaseAuthenticator.log.error(e); + } catch (SQLException e) { + DatabaseAuthenticator.log.error(e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + DatabaseAuthenticator.log.error(e); + } + } + if (ps != null) { + try { + ps.close(); + } catch (SQLException e) { + DatabaseAuthenticator.log.error(e); + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + DatabaseAuthenticator.log.error(e); + } + } + } + + return isValid; + } +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java =================================================================== diff -u -r5806f7835e004132c37781df113fa0c879287065 -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java (.../LDAPAuthenticator.java) (revision 5806f7835e004132c37781df113fa0c879287065) +++ lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java (.../LDAPAuthenticator.java) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -38,60 +38,41 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.lamsfoundation.lams.usermanagement.User; -import org.lamsfoundation.lams.usermanagement.service.LdapService; -import org.lamsfoundation.lams.usermanagement.service.UserManagementService; +import org.lamsfoundation.lams.usermanagement.service.ILdapService; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.web.session.SessionManager; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * Validates user password against an entry in a LDAP database. + */ public class LDAPAuthenticator { private static Logger log = Logger.getLogger(LDAPAuthenticator.class); - private static UserManagementService service; + private static IUserManagementService userManagementService; + private static ILdapService ldapService; - private static LdapService ldapService; - private static final String INITIAL_CONTEXT_FACTORY_VALUE = "com.sun.jndi.ldap.LdapCtxFactory"; private Attributes attrs = null; - public LDAPAuthenticator() { - } - - private UserManagementService getService() { - if (LDAPAuthenticator.service == null) { - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(SessionManager - .getServletContext()); - LDAPAuthenticator.service = (UserManagementService) ctx.getBean("userManagementService"); + public LDAPAuthenticator(IUserManagementService userManagementService) { + if (LDAPAuthenticator.userManagementService == null) { + LDAPAuthenticator.userManagementService = userManagementService; } - return LDAPAuthenticator.service; - } - - private LdapService getLdapService() { if (LDAPAuthenticator.ldapService == null) { - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(SessionManager - .getServletContext()); - LDAPAuthenticator.ldapService = (LdapService) ctx.getBean("ldapService"); + WebApplicationContext ctx = WebApplicationContextUtils + .getWebApplicationContext(SessionManager.getServletContext()); + LDAPAuthenticator.ldapService = (ILdapService) ctx.getBean("ldapService"); } - return LDAPAuthenticator.ldapService; } - public Attributes getAttrs() { - return attrs; - } - - public void setAttrs(Attributes attrs) { - this.attrs = attrs; - } - - public boolean authenticate(String username, String inputPassword) { - return authentication(username, inputPassword); - } - - private boolean authentication(String username, Object credential) { + public boolean authenticate(String userName, String credential) { Properties env = new Properties(); // setup initial connection to search for user's dn @@ -126,36 +107,35 @@ // search for the user's cn String filter = Configuration.get(ConfigurationKeys.LDAP_SEARCH_FILTER); String baseDN = Configuration.get(ConfigurationKeys.LDAP_BASE_DN); - Object[] filterArgs = { username }; + Object[] filterArgs = { userName }; NamingEnumeration results = ctx.search(baseDN, filter, filterArgs, ctrl); while (results.hasMore()) { SearchResult result = results.next(); if (LDAPAuthenticator.log.isDebugEnabled()) { - LDAPAuthenticator.log.debug("===> found matching object..."); - LDAPAuthenticator.log.debug("name: " + result.getName()); - LDAPAuthenticator.log.debug("namespace name: " + result.getNameInNamespace()); + LDAPAuthenticator.log.debug("Found matching object. Name: " + result.getName() + ". Namespace: " + + result.getNameInNamespace()); } Attributes attrs = result.getAttributes(); Attribute attr = attrs.get(Configuration.get(ConfigurationKeys.LDAP_LOGIN_ATTR)); - login = getLdapService().getSingleAttributeString(attr); + login = LDAPAuthenticator.ldapService.getSingleAttributeString(attr); if (attr != null) { Object attrValue = attr.get(); if (attrValue != null) { login = attrValue.toString(); } } - if (StringUtils.equals(login, username)) { + if (StringUtils.equals(login, userName)) { // now we can try to authenticate dn = result.getNameInNamespace(); - setAttrs(attrs); + this.attrs = attrs; ctx.close(); break; } } if (StringUtils.isBlank(login)) { - LDAPAuthenticator.log.error("===> No LDAP user found with username: " + username + LDAPAuthenticator.log.error("No LDAP user found with name: " + userName + ". This could mean that the the login attribute is incorrect," - + " the user doesn't exist, or that an initial bind user is required."); + + " the user does not exist, or that an initial bind user is required."); return false; } @@ -165,57 +145,60 @@ ctx = new InitialLdapContext(env, null); // if no exception, success - LDAPAuthenticator.log.debug("===> LDAP context created using DN: " + dn); + LDAPAuthenticator.log.debug("LDAP context created using DN: " + dn); isValid = true; // start checking whether we need to update user depending on its // attributes if (LDAPAuthenticator.log.isDebugEnabled()) { - NamingEnumeration enumAttrs = this.attrs.getAll(); + NamingEnumeration enumAttrs = this.attrs.getAll(); while (enumAttrs.hasMoreElements()) { LDAPAuthenticator.log.debug(enumAttrs.next()); } } // check user is disabled in ldap - if (getLdapService().getDisabledBoolean(this.attrs)) { - LDAPAuthenticator.log.debug("===> User is disabled in LDAP."); - User user = getService().getUserByLogin(username); + if (LDAPAuthenticator.ldapService.getDisabledBoolean(this.attrs)) { + LDAPAuthenticator.log.info("User " + userName + "is disabled in LDAP."); + User user = LDAPAuthenticator.userManagementService.getUserByLogin(userName); if (user != null) { - getService().disableUser(user.getUserId()); + LDAPAuthenticator.userManagementService.disableUser(user.getUserId()); } return false; } if (Configuration.getAsBoolean(ConfigurationKeys.LDAP_UPDATE_ON_LOGIN)) { - User user = getService().getUserByLogin(username); + User user = LDAPAuthenticator.userManagementService.getUserByLogin(userName); if (user != null) { // update user's attributes and org membership - getLdapService().updateLDAPUser(user, this.attrs); - getLdapService().addLDAPUser(this.attrs, user.getUserId()); + LDAPAuthenticator.ldapService.updateLDAPUser(user, this.attrs); + LDAPAuthenticator.ldapService.addLDAPUser(this.attrs, user.getUserId()); } } return true; } catch (AuthenticationNotSupportedException e) { - LDAPAuthenticator.log.error("===> Authentication mechanism not supported. Check your " + LDAPAuthenticator.log.error("Authentication mechanism not supported. Check your " + ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION + " parameter: " + Configuration.get(ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION)); } catch (AuthenticationException e) { - LDAPAuthenticator.log.info("===> Incorrect username (" + dn + ") or password. " + e.getMessage()); + LDAPAuthenticator.log.info("Incorrect username (" + dn + ") or password. " + e.getMessage()); } catch (Exception e) { - LDAPAuthenticator.log.error("===> LDAP exception: " + e, e); + LDAPAuthenticator.log.error("LDAP exception", e); } finally { try { if (ctx != null) { ctx.close(); } } catch (Exception e) { - LDAPAuthenticator.log.error("===> gettting problem when closing context. Exception: " + e); + LDAPAuthenticator.log.error("Exception when closing context.", e); } } return isValid; } + public Attributes getAttrs() { + return attrs; + } } \ No newline at end of file Fisheye: Tag 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/security/NestableGroup.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java (.../SimpleGroup.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java (.../SimpleGroup.java) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -27,110 +27,82 @@ import java.security.acl.Group; import java.util.Collections; import java.util.Enumeration; -import java.util.Iterator; import java.util.HashMap; +import java.util.Map; -public class SimpleGroup extends SimplePrincipal implements Group -{ - private HashMap members; +public class SimpleGroup extends SimplePrincipal implements Group { + private static final long serialVersionUID = -875843422197596410L; - public SimpleGroup(String groupName) - { - super(groupName); - members = new HashMap(3); - } + private Map members; - /** Adds the specified member to the group. - @param user the principal to add to this group. - @return true if the member was successfully added, - false if the principal was already a member. - */ - public boolean addMember(Principal user) - { - boolean isMember = members.containsKey(user); - if( isMember == false ) - members.put(user, user); - return isMember == false; + public SimpleGroup(String groupName) { + super(groupName); + members = new HashMap(3); + } + + /** + * Adds the specified member to the group. + * + * @param user + * the principal to add to this group. + * @return true if the member was successfully added, false if the principal was already a member. + */ + @Override + public boolean addMember(Principal user) { + boolean isMember = members.containsKey(user); + if (!isMember) { + members.put(user, user); } - /** Returns true if the passed principal is a member of the group. - This method does a recursive search, so if a principal belongs to a - group which is a member of this group, true is returned. + return !isMember; + } - A special check is made to see if the member is an instance of - org.jboss.security.AnybodyPrincipal or org.jboss.security.NobodyPrincipal - since these classes do not hash to meaningful values. - @param member the principal whose membership is to be checked. - @return true if the principal is a member of this group, - false otherwise. - */ + /** + * Returns true if the passed principal is a member of the group. This method does a recursive search, so if a + * principal belongs to a group which is a member of this group, true is returned. - public boolean isMember(Principal member) - { - //not implemented. - return false; - - /* - // First see if there is a key with the member name - boolean isMember = members.containsKey(member); - if( isMember == false ) - { // Check the AnybodyPrincipal & NobodyPrincipal special cases - isMember = (member instanceof org.jboss.security.AnybodyPrincipal); - if( isMember == false ) - { - if( member instanceof org.jboss.security.NobodyPrincipal ) - return false; - } - } - if( isMember == false ) - { // Check any Groups for membership - Collection values = members.values(); - Iterator iter = values.iterator(); - while( isMember == false && iter.hasNext() ) - { - Object next = iter.next(); - if( next instanceof Group ) - { - Group group = (Group) next; - isMember = group.isMember(member); - } - } - } - return isMember; - */ - } + * @param member + * the principal whose membership is to be checked. + * @return true if the principal is a member of this group, false otherwise. + */ - /** Returns an enumeration of the members in the group. - The returned objects can be instances of either Principal - or Group (which is a subinterface of Principal). - @return an enumeration of the group members. - */ - public Enumeration members() - { - return Collections.enumeration(members.values()); - } + @Override + public boolean isMember(Principal member) { + return false; + } - /** Removes the specified member from the group. - @param user the principal to remove from this group. - @return true if the principal was removed, or - false if the principal was not a member. - */ - public boolean removeMember(Principal user) - { - Object prev = members.remove(user); - return prev != null; - } + /** + * Returns an enumeration of the members in the group. The returned objects can be instances of either Principal or + * Group (which is a subinterface of Principal). + * + * @return an enumeration of the group members. + */ + @Override + public Enumeration members() { + return Collections.enumeration(members.values()); + } - public String toString() - { - StringBuffer tmp = new StringBuffer(getName()); - tmp.append("(members:"); - Iterator iter = members.keySet().iterator(); - while( iter.hasNext() ) - { - tmp.append(iter.next()); - tmp.append(','); - } - tmp.setCharAt(tmp.length()-1, ')'); - return tmp.toString(); - } + /** + * Removes the specified member from the group. + * + * @param user + * the principal to remove from this group. + * @return true if the principal was removed, or false if the principal was not a member. + */ + @Override + public boolean removeMember(Principal user) { + Object prev = members.remove(user); + return prev != null; + } + + @Override + public String toString() { + StringBuffer tmp = new StringBuffer(getName()); + tmp.append("(members:"); + for (Principal principal : members.keySet()) { + tmp.append(principal); + tmp.append(','); + } + tmp.setCharAt(tmp.length() - 1, ')'); + return tmp.toString(); + } } \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java (.../SimplePrincipal.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java (.../SimplePrincipal.java) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -25,43 +25,47 @@ import java.security.Principal; -public class SimplePrincipal implements Principal, java.io.Serializable -{ - private String name; +public class SimplePrincipal implements Principal, java.io.Serializable { + private static final long serialVersionUID = 8351373842078910512L; + + private String name; - public SimplePrincipal(String name) - { + public SimplePrincipal(String name) { this.name = name; - } + } - /** Compare this SimplePrincipal's name against another Principal - @return true if name equals another.getName(); - */ - public boolean equals(Object another) - { - if( !(another instanceof Principal) ) - return false; - String anotherName = ((Principal)another).getName(); + /** + * Compare this SimplePrincipal's name against another Principal + * + * @return true if name equals another.getName() + */ + @Override + public boolean equals(Object another) { + if (!(another instanceof Principal)) { + return false; + } + String anotherName = ((Principal) another).getName(); boolean equals = false; - if( name == null ) - equals = anotherName == null; - else - equals = name.equals(anotherName); + if (name == null) { + equals = anotherName == null; + } else { + equals = name.equals(anotherName); + } return equals; - } + } - public int hashCode() - { + @Override + public int hashCode() { return (name == null ? 0 : name.hashCode()); - } + } - public String toString() - { + @Override + public String toString() { return name; - } + } - public String getName() - { + @Override + public String getName() { return name; - } -} + } +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java =================================================================== diff -u -r1179f02836b1cb2d7e24efed438336006896267a -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (.../UniversalLoginModule.java) (revision 1179f02836b1cb2d7e24efed438336006896267a) +++ lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (.../UniversalLoginModule.java) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -20,34 +20,32 @@ * http://www.gnu.org/licenses/gpl.txt * **************************************************************** */ -/* $$Id$$ */ package org.lamsfoundation.lams.security; -/** - * UniversalLoginModule is LAMS's own implementation of login module based on - * JBoss 3.0.*, 3.2.* and possibly higher versions. - * - * It's named "universal" as currently it supports WebAuth, LDAP and database - * based authentication mechanisms. - * - */ - +import java.io.IOException; import java.security.Principal; import java.security.acl.Group; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import java.util.Set; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; import javax.sql.DataSource; import org.apache.log4j.Logger; @@ -58,8 +56,8 @@ import org.lamsfoundation.lams.usermanagement.Role; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.usermanagement.service.LdapService; -import org.lamsfoundation.lams.usermanagement.service.UserManagementService; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.HashUtil; @@ -70,189 +68,344 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; -public class UniversalLoginModule extends UsernamePasswordLoginModule { - +/** + * Covers all LAMS authentication. + */ +public class UniversalLoginModule implements LoginModule { private static Logger log = Logger.getLogger(UniversalLoginModule.class); - public UniversalLoginModule() { + private Subject subject; + private CallbackHandler callbackHandler; + /** + * Flag indicating if the login 1st phase succeeded. + */ + private boolean loginOK; + private Principal identity; + private char[] credential; + private String dsJndiName; + + private static IThemeService themeService; + private static IUserManagementService userManagementService; + + private static final String ROLES_QUERY = "SELECT DISTINCT r.name,'Roles' FROM lams_user u " + + "LEFT OUTER JOIN lams_user_organisation uo USING(user_id) " + + "LEFT OUTER JOIN lams_user_organisation_role urr USING(user_organisation_id) " + + "LEFT OUTER JOIN lams_role r USING (role_id) " + "WHERE u.login=?"; + + /** + * Method to commit the authentication process (phase 2). + */ + @Override + public boolean commit() throws LoginException { + if (loginOK == false) { + return false; + } + + /* + If the login method completed successfully as indicated by + loginOK == true, this method adds the identity value to the subject's principals set. It also adds the members of + each Group returned by getRoleSets() to the subject's principals Set. + */ + Set principals = subject.getPrincipals(); + principals.add(identity); + for (Group group : getRoleSets()) { + String name = group.getName(); + Group subjectGroup = createGroup(name, principals); + // Copy the group members to the Subject group + Enumeration members = group.members(); + while (members.hasMoreElements()) { + Principal role = members.nextElement(); + subjectGroup.addMember(role); + } + } + + UniversalLoginModule.log.info("User logged in: " + getUserName()); + return true; } - protected String dsJndiName; + /** + * Method to abort the authentication process (phase 2). + * + * @return true alaways + */ + @Override + public boolean abort() throws LoginException { + UniversalLoginModule.log.info("Abort log in for user: " + getUserName()); + return true; + } - protected String rolesQuery; + /** + * Remove the user identity and roles added to the Subject during commit. + * + * @return true always. + */ + @Override + public boolean logout() throws LoginException { + UniversalLoginModule.log.info("User logged out: " + getUserName()); + // Remove the user identity + Set principals = subject.getPrincipals(); + principals.remove(identity); + return true; + } - protected String principalsQuery; + /** + * Find or create a Group with the given name. Subclasses should use this method to locate the 'Roles' group or + * create additional types of groups. + * + * @return A named Group from the principals set. + */ + private Group createGroup(String name, Set principals) { + Group roles = null; + for (Principal principal : principals) { + if (principal instanceof Group) { + Group grp = (Group) principal; + if (grp.getName().equals(name)) { + roles = grp; + break; + } + } + } - private IThemeService themeService; - private UserManagementService service; + // If we did not find a group create one + if (roles == null) { + roles = new SimpleGroup(name); + principals.add(roles); + } + return roles; + } + /** + * Perform the authentication of the username and password. + */ @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { - super.initialize(subject, callbackHandler, sharedState, options); - dsJndiName = (String) options.get("dsJndiName"); - principalsQuery = (String) options.get("principalsQuery"); - rolesQuery = (String) options.get("rolesQuery"); + public boolean login() throws LoginException { + loginOK = false; + String[] info = getUsernameAndPassword(); + String userName = info[0]; + String password = info[1]; + + UniversalLoginModule.log.info("Authenticate user: " + userName); + + if (identity == null) { + try { + identity = new SimplePrincipal(userName); + } catch (Exception e) { + throw new LoginException("Failed to create principal: " + e.getMessage()); + } + + if (!validatePassword(password)) { + if (UniversalLoginModule.log.isDebugEnabled()) { + UniversalLoginModule.log.debug("Bad password for user " + userName); + } + throw new FailedLoginException("Incorrect password"); + } + } + + loginOK = true; + if (UniversalLoginModule.log.isDebugEnabled()) { + UniversalLoginModule.log.debug("User authenticated: " + identity); + } + return true; } + /** + * Return user name from existing identity. + */ + private String getUserName() { + return identity == null ? null : identity.getName(); + } + + /** + * Called by login() to acquire the username and password strings for authentication. This method does no validation + * of either. + * + * @return String[], [0] = username, [1] = password + */ + private String[] getUsernameAndPassword() throws LoginException { + if (callbackHandler == null) { + throw new LoginException("No CallbackHandler available to collect authentication information"); + } + NameCallback nc = new NameCallback("User name: ", "guest"); + PasswordCallback pc = new PasswordCallback("Password: ", false); + Callback[] callbacks = { nc, pc }; + String username = null; + String password = null; + try { + callbackHandler.handle(callbacks); + username = nc.getName(); + char[] tmpPassword = pc.getPassword(); + if (tmpPassword != null) { + credential = new char[tmpPassword.length]; + System.arraycopy(tmpPassword, 0, credential, 0, tmpPassword.length); + pc.clearPassword(); + password = new String(credential); + } + } catch (IOException ioe) { + throw new LoginException(ioe.toString()); + } catch (UnsupportedCallbackException uce) { + throw new LoginException("CallbackHandler does not support: " + uce.getCallback()); + } + return new String[] { username, password }; + } + @Override - protected boolean validatePassword(String inputPassword, String expectedPassword) { + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, + Map options) { + this.subject = subject; + this.callbackHandler = callbackHandler; + + dsJndiName = (String) options.get("dsJndiName"); + } + + private boolean validatePassword(String inputPassword) { + WebApplicationContext ctx = WebApplicationContextUtils + .getWebApplicationContext(SessionManager.getServletContext()); + if (UniversalLoginModule.userManagementService == null) { + UniversalLoginModule.userManagementService = (IUserManagementService) ctx.getBean("userManagementService"); + UniversalLoginModule.themeService = (IThemeService) ctx.getBean("themeService"); + } + + // allow sysadmin to login as another user; in this case, the LAMS shared session will be present, + // allowing the following check to work + if (UniversalLoginModule.userManagementService.isUserSysAdmin()) { + return true; + } + + // empty password not allowed + if ((inputPassword == null) || (inputPassword.length() == 0)) { + return false; + } + boolean isValid = false; - if (inputPassword != null) { - // empty password not allowed - if (inputPassword.length() == 0) { - return false; + + try { + String userName = getUserName(); + if (UniversalLoginModule.log.isDebugEnabled()) { + UniversalLoginModule.log.debug("Authenticating user " + userName + "."); } - try { - String username = getUsername(); - UniversalLoginModule.log.debug("===> authenticating user: " + username); + User user = UniversalLoginModule.userManagementService.getUserByLogin(userName); - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(SessionManager - .getServletContext()); + // LDAP user provisioning + if (user == null) { + if (!Configuration.getAsBoolean(ConfigurationKeys.LDAP_PROVISIONING_ENABLED)) { + return false; + } - if (service == null) { - service = (UserManagementService) ctx.getBean("userManagementService"); + // provision a new user by checking LDAP server + LdapService ldapService = null; + try { + ldapService = (LdapService) ctx.getBean("ldapService"); + } catch (NoSuchBeanDefinitionException e) { + // LDEV-1937 + UniversalLoginModule.log.warn("No ldapService bean found, trying another method to fetch it.", e); + @SuppressWarnings("resource") + ApplicationContext context = new ClassPathXmlApplicationContext( + "org/lamsfoundation/lams/usermanagement/ldapContext.xml"); + ldapService = (LdapService) context.getBean("ldapService"); } - User user = service.getUserByLogin(username); - if (themeService == null) { - themeService = (IThemeService) ctx.getBean("themeService"); + if (UniversalLoginModule.log.isDebugEnabled()) { + UniversalLoginModule.log + .debug("LDAP provisioning is enabled, checking user " + userName + " against LDAP server."); } + LDAPAuthenticator ldap = new LDAPAuthenticator(UniversalLoginModule.userManagementService); + isValid = ldap.authenticate(userName, inputPassword); + if (!isValid) { + return false; + } - // LDAP user provisioning - if (user == null) { - // provision a new user by checking ldap server - if (Configuration.getAsBoolean(ConfigurationKeys.LDAP_PROVISIONING_ENABLED)) { - LdapService ldapService; - try { - ldapService = (LdapService) ctx.getBean("ldapService"); - } catch (NoSuchBeanDefinitionException e) { - // LDEV-1937 - UniversalLoginModule.log - .error("NoSuchBeanDefinitionException while getting ldapService bean, will try another method...", - e); - ApplicationContext context = new ClassPathXmlApplicationContext( - "org/lamsfoundation/lams/usermanagement/ldapContext.xml"); - ldapService = (LdapService) context.getBean("ldapService"); - } - UniversalLoginModule.log - .debug("===> LDAP provisioning is enabled, checking username against LDAP server..."); - LDAPAuthenticator ldap = new LDAPAuthenticator(); - isValid = ldap.authenticate(username, inputPassword); - if (isValid) { // create a new user - UniversalLoginModule.log.info("===> Creating new user for LDAP username: " + username); - if (ldapService.createLDAPUser(ldap.getAttrs())) { - user = service.getUserByLogin(username); - if (!ldapService.addLDAPUser(ldap.getAttrs(), user.getUserId())) { - UniversalLoginModule.log.error("===> Couldn't add LDAP user: " + username - + " to organisation."); - } - } else { - UniversalLoginModule.log.error("===> Couldn't create new user for LDAP username: " - + username); - return false; - } - } else { // didn't authenticate successfully with - // ldap - return false; - } - } else { - return false; + // create a new user + UniversalLoginModule.log.info("Creating new user using LDAP: " + userName); + if (ldapService.createLDAPUser(ldap.getAttrs())) { + user = UniversalLoginModule.userManagementService.getUserByLogin(userName); + if (!ldapService.addLDAPUser(ldap.getAttrs(), user.getUserId())) { + UniversalLoginModule.log.error("Could not add LDAP user " + userName + " to organisation."); } + } else { + UniversalLoginModule.log.error("Could not create new user for LDAP user: " + userName); + return false; } + } - // allow sysadmin to login as another user; in this case, the - // LAMS shared session - // will be present, allowing the following check to work - if (service.isUserSysAdmin()) { - isValid = true; - } + // perform password checking according to user's authentication method + if (!isValid) { + String type = user.getAuthenticationMethod().getAuthenticationMethodType().getDescription(); + UniversalLoginModule.log.debug("Authentication type: " + type); + if (AuthenticationMethodType.LDAP.equals(type)) { + LDAPAuthenticator authenticator = new LDAPAuthenticator(UniversalLoginModule.userManagementService); + isValid = authenticator.authenticate(userName, inputPassword); + // if LDAP user profile has updated, udpate user object for dto below + user = UniversalLoginModule.userManagementService.getUserByLogin(userName); - // perform password checking according to user's authentication - // method - if (!isValid) { - String type = user.getAuthenticationMethod().getAuthenticationMethodType().getDescription(); - UniversalLoginModule.log.debug("===> authentication type: " + type); - if (AuthenticationMethodType.LDAP.equals(type)) { - LDAPAuthenticator authenticator = new LDAPAuthenticator(); - isValid = authenticator.authenticate(username, inputPassword); - // if ldap user profile has updated, udpate user object - // for dto below - user = service.getUserByLogin(username); - } else if (AuthenticationMethodType.LAMS.equals(type)) { - DatabaseAuthenticator authenticator = new DatabaseAuthenticator(dsJndiName, principalsQuery); - // if the password is not encrypted when sent from the - // jsp (e.g. when it is passed - // unencrypted to say, ldap) then encrypt it here when - // authenticating against local db - if (!Configuration.getAsBoolean(ConfigurationKeys.LDAP_ENCRYPT_PASSWORD_FROM_BROWSER)) { - // try the passed in password first, - // LoginRequestServlet always passes in encrypted - // passwords - isValid = authenticator.authenticate(username, inputPassword); - if (!isValid) { - inputPassword = HashUtil.sha1(inputPassword); - } - isValid = authenticator.authenticate(username, inputPassword); - } else { - isValid = authenticator.authenticate(username, inputPassword); - } - } else { - UniversalLoginModule.log.error("===> Unexpected authentication type: " + type); - return false; + } else if (AuthenticationMethodType.LAMS.equals(type)) { + DatabaseAuthenticator authenticator = new DatabaseAuthenticator(dsJndiName); + isValid = authenticator.authenticate(userName, inputPassword); + // if the password is not encrypted when sent from the + // jsp (e.g. when it is passed + // unencrypted to say, ldap) then encrypt it here when + // authenticating against local db + if (!isValid && !Configuration.getAsBoolean(ConfigurationKeys.LDAP_ENCRYPT_PASSWORD_FROM_BROWSER)) { + inputPassword = HashUtil.sha1(inputPassword); + isValid = authenticator.authenticate(userName, inputPassword); } - } - // disabled users can't login; - // check after authentication to give non-db authentication - // methods - // a chance to update disabled flag - if (user.getDisabledFlag()) { - UniversalLoginModule.log.debug("===> user is disabled."); + } else { + UniversalLoginModule.log.error("Unexpected authentication type: " + type); return false; } + } - // if login is valid, register userDTO into session. - if (isValid) { - UserDTO userDTO = user.getUserDTO(); + // disabled users can't login; + // check after authentication to give non-db authentication methods + // a chance to update disabled flag + if (user.getDisabledFlag()) { + UniversalLoginModule.log.debug("User is disabled: " + user.getLogin()); + return false; + } - // If the user's css theme has been deleted, use the default - // as fallback - CSSThemeBriefDTO userCSSTheme = userDTO.getHtmlTheme(); - if (userCSSTheme != null) { - boolean themeExists = false; - for (Theme theme : themeService.getAllCSSThemes()) { - if (userCSSTheme.getId().equals(theme.getThemeId())) { - themeExists = true; - break; - } - } + // if login is valid, register userDTO into session. + if (isValid) { + UserDTO userDTO = user.getUserDTO(); - if (!themeExists) { - userDTO.setHtmlTheme(new CSSThemeBriefDTO(themeService.getDefaultCSSTheme())); + // If the user's css theme has been deleted, use the default + // as fallback + CSSThemeBriefDTO userCSSTheme = userDTO.getHtmlTheme(); + if (userCSSTheme != null) { + boolean themeExists = false; + for (Theme theme : UniversalLoginModule.themeService.getAllCSSThemes()) { + if (userCSSTheme.getId().equals(theme.getThemeId())) { + themeExists = true; + break; } } - // If the user's flash theme has been deleted, use the - // default as fallback - CSSThemeBriefDTO userFlashTheme = userDTO.getFlashTheme(); - if (userFlashTheme != null) { - boolean themeExists = false; - for (Theme theme : themeService.getAllFlashThemes()) { - if (userFlashTheme.getId().equals(theme.getThemeId())) { - themeExists = true; - break; - } - } + if (!themeExists) { + userDTO.setHtmlTheme( + new CSSThemeBriefDTO(UniversalLoginModule.themeService.getDefaultCSSTheme())); + } + } - if (!themeExists) { - userDTO.setFlashTheme(new CSSThemeBriefDTO(themeService.getDefaultFlashTheme())); + // If the user's flash theme has been deleted, use the default as fallback + CSSThemeBriefDTO userFlashTheme = userDTO.getFlashTheme(); + if (userFlashTheme != null) { + boolean themeExists = false; + for (Theme theme : UniversalLoginModule.themeService.getAllFlashThemes()) { + if (userFlashTheme.getId().equals(theme.getThemeId())) { + themeExists = true; + break; } } + + if (!themeExists) { + userDTO.setFlashTheme( + new CSSThemeBriefDTO(UniversalLoginModule.themeService.getDefaultFlashTheme())); + } } - } catch (Exception e) { - UniversalLoginModule.log.error("Error while validating password", e); } + } catch (Exception e) { + UniversalLoginModule.log.error("Error while validating password", e); + return false; } return isValid; } @@ -263,11 +416,10 @@ * * @return Group[] containing the sets of roles */ - @Override - protected Group[] getRoleSets() throws LoginException { - String username = getUsername(); + private Group[] getRoleSets() throws LoginException { + String userName = getUserName(); + Map setsMap = new HashMap(); Connection conn = null; - HashMap setsMap = new HashMap(); PreparedStatement ps = null; ResultSet rs = null; @@ -276,27 +428,13 @@ InitialContext ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup(this.dsJndiName); - // log.debug("===> getRoleSets() called: " + dsJndiName + ": " + - // rolesQuery); conn = ds.getConnection(); // Get the user role names - ps = conn.prepareStatement(this.rolesQuery); - try { - ps.setString(1, username); - } catch (ArrayIndexOutOfBoundsException ignore) { - // The query may not have any parameters so just try it - } + ps = conn.prepareStatement(UniversalLoginModule.ROLES_QUERY); + ps.setString(1, userName); rs = ps.executeQuery(); if (rs.next() == false) { - if (getUnauthenticatedIdentity() == null) { - throw new FailedLoginException("No matching username found in Roles"); - } - /* - * We are running with an unauthenticatedIdentity so create an - * empty Roles set and return. - */ - Group[] roleSets = { new SimpleGroup("Roles") }; - return roleSets; + throw new FailedLoginException("No matching user name found in roles: " + userName); } ArrayList groupMembers = new ArrayList(); @@ -306,60 +444,62 @@ if ((groupName == null) || (groupName.length() == 0)) { groupName = "Roles"; } - Group group = (Group) setsMap.get(groupName); + Group group = setsMap.get(groupName); if (group == null) { group = new SimpleGroup(groupName); setsMap.put(groupName, group); } try { - Principal p; + Principal p = null; // Assign minimal role if user has none if (name == null) { name = Role.LEARNER; - UniversalLoginModule.log.info("===> Found no roles"); + UniversalLoginModule.log.info("Found no roles for user: " + userName + ", assigning: " + name); } - p = super.createIdentity(name); + p = new SimplePrincipal(name); if (!groupMembers.contains(name)) { - UniversalLoginModule.log.info("===> Assign user to role " + p.getName()); + UniversalLoginModule.log.info("Assign user: " + userName + " to role " + p.getName()); group.addMember(p); groupMembers.add(name); } if (name.equals(Role.SYSADMIN)) { - p = super.createIdentity(Role.AUTHOR); - UniversalLoginModule.log.info("===> Found " + name); + p = new SimplePrincipal(Role.AUTHOR); + UniversalLoginModule.log.info("Found role " + name); if (!groupMembers.contains(Role.AUTHOR)) { - UniversalLoginModule.log.info("===> Assign user to role " + Role.AUTHOR); + UniversalLoginModule.log.info("Assign user: " + userName + " to role " + Role.AUTHOR); group.addMember(p); groupMembers.add(Role.AUTHOR); } } } catch (Exception e) { - UniversalLoginModule.log.debug("===> Failed to create principal: " + name, e); + UniversalLoginModule.log.info("Failed to create principal: " + name + " for user: " + userName, e); } } while (rs.next()); - } catch (NamingException ex) { - throw new LoginException(ex.toString(true)); - } catch (SQLException ex) { - super.log.error("SQL failure", ex); - throw new LoginException(ex.toString()); + } catch (NamingException e) { + throw new LoginException(e.toString(true)); + } catch (SQLException e) { + throw new LoginException(e.toString()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { + UniversalLoginModule.log.error(e); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { + UniversalLoginModule.log.error(e); } } if (conn != null) { try { conn.close(); - } catch (Exception ex) { + } catch (Exception e) { + UniversalLoginModule.log.error(e); } } } @@ -368,16 +508,4 @@ setsMap.values().toArray(roleSets); return roleSets; } - - /** - * 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 - */ - @Override - protected String getUsersPassword() throws LoginException { - return ""; - } - } \ No newline at end of file Fisheye: Tag 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/security/UsernamePasswordLoginModule.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/ILdapService.java =================================================================== diff -u -r053b3694d8631a983eb8e95e3f0374a22540b43a -r3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/ILdapService.java (.../ILdapService.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/ILdapService.java (.../ILdapService.java) (revision 3a46dc5ecae9d98d6b93744fc5e0dcec75f6008b) @@ -23,6 +23,7 @@ /* $Id$ */ package org.lamsfoundation.lams.usermanagement.service; +import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import org.lamsfoundation.lams.usermanagement.User; @@ -33,51 +34,58 @@ * */ public interface ILdapService { - - public static final String SYNC_RESULTS = "syncResults"; - - /** - * Updates a LAMS user's profile with LDAP attributes. - * @param user - * @param attrs - */ - public void updateLDAPUser(User user, Attributes attrs); - /** - * Creates a LAMS user from LDAP attributes. Returns false on failure. - * @param attrs - * @return boolean - */ - public boolean createLDAPUser(Attributes attrs); - - /** - * Returns LDAP attribute name, removing prefixed '!' char if necessary, - * which is used to toggle the enabled/disabled meaning of the ldap attribute. - * @param ldapAttr - * @return ldapAttr - */ - public String getLdapAttr(String ldapAttr); - - /** - * Convert the LDAP disabled attribute value string to a boolean. - * @param attrs - * @return boolean - */ - public boolean getDisabledBoolean(Attributes attrs); - - /** - * Adds user to organisation with roles specified by the LDAPOrgField, - * LDAPOrgAttr, LDAPRolesAttr attributes. Returns false if it can't do - * one of these tasks. - * @param attrs - * @param userId - * @return boolean - */ - public boolean addLDAPUser(Attributes attrs, Integer userId); - - /** - * Bulk updates LAMS with LDAP users. - * @return stats on result of bulk update. - */ - public BulkUpdateResultDTO bulkUpdate(); + public static final String SYNC_RESULTS = "syncResults"; + + /** + * Updates a LAMS user's profile with LDAP attributes. + * + * @param user + * @param attrs + */ + public void updateLDAPUser(User user, Attributes attrs); + + /** + * Creates a LAMS user from LDAP attributes. Returns false on failure. + * + * @param attrs + * @return boolean + */ + public boolean createLDAPUser(Attributes attrs); + + /** + * Returns LDAP attribute name, removing prefixed '!' char if necessary, which is used to toggle the + * enabled/disabled meaning of the ldap attribute. + * + * @param ldapAttr + * @return ldapAttr + */ + public String getLdapAttr(String ldapAttr); + + public String getSingleAttributeString(Attribute attr); + + /** + * Convert the LDAP disabled attribute value string to a boolean. + * + * @param attrs + * @return boolean + */ + public boolean getDisabledBoolean(Attributes attrs); + + /** + * Adds user to organisation with roles specified by the LDAPOrgField, LDAPOrgAttr, LDAPRolesAttr attributes. + * Returns false if it can't do one of these tasks. + * + * @param attrs + * @param userId + * @return boolean + */ + public boolean addLDAPUser(Attributes attrs, Integer userId); + + /** + * Bulk updates LAMS with LDAP users. + * + * @return stats on result of bulk update. + */ + public BulkUpdateResultDTO bulkUpdate(); }