Index: lams_central/build.xml =================================================================== diff -u -r9890a029ab3270cf7750dec1120a0d71e6f4f86f -rca68b26281fd59bce02a7c78f8ff9c3db534d2ae --- lams_central/build.xml (.../build.xml) (revision 9890a029ab3270cf7750dec1120a0d71e6f4f86f) +++ lams_central/build.xml (.../build.xml) (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -140,6 +140,7 @@ + Index: lams_central/conf/xdoclet/struts-plugins.xml =================================================================== diff -u --- lams_central/conf/xdoclet/struts-plugins.xml (revision 0) +++ lams_central/conf/xdoclet/struts-plugins.xml (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,7 @@ + + + + + + Index: lams_central/src/java/org/lamsfoundation/lams/security/AbstractServerLoginModule.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/AbstractServerLoginModule.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/AbstractServerLoginModule.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,284 @@ +package org.lamsfoundation.lams.security; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.lang.reflect.Constructor; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.apache.log4j.Logger; + +public class AbstractServerLoginModule implements LoginModule +{ + protected Subject subject; + protected CallbackHandler callbackHandler; + protected Map sharedState; + protected Map options; + protected Logger log = Logger.getLogger(AbstractServerLoginModule.class); + /** Flag indicating if the shared credential should be used */ + protected boolean useFirstPass; + /** Flag indicating if the login phase succeeded. Subclasses that override + the login method must set this to true on successful completion of login + */ + protected boolean loginOk; + /** An optional custom Principal class implementation */ + protected String principalClassName; + /** the principal to use when a null username and password are seen */ + protected Principal unauthenticatedIdentity; + + /** Initialize the login module. This stores the subject, callbackHandler + * and sharedState and options for the login session. Subclasses should override + * if they need to process their own options. A call to super.initialize(...) + * must be made in the case of an override. + *

+ * @option password-stacking: If this is set to "useFirstPass", the login + * identity will be taken from the javax.security.auth.login.name + * value of the sharedState map, and the proof of identity from the + * javax.security.auth.login.password value of the sharedState + * map. + * @option principalClass: A Principal implementation that support a ctor + * taking a String argument for the princpal name. + * @option unauthenticatedIdentity: the name of the principal to asssign + * and authenticate when a null username and password are seen. + * + * @param subject the Subject to update after a successful login. + * @param callbackHandler the CallbackHandler that will be used to obtain the + * the user identity and credentials. + * @param sharedState a Map shared between all configured login module instances + * @param options the parameters passed to the login module. + */ + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) + { + this.subject = subject; + this.callbackHandler = callbackHandler; + this.sharedState = sharedState; + this.options = options; + log = Logger.getLogger(getClass()); + log.info("initialize"); + /* Check for password sharing options. Any non-null value for + password_stacking sets useFirstPass as this module has no way to + validate any shared password. + */ + String passwordStacking = (String) options.get("password-stacking"); + if( passwordStacking != null && passwordStacking.equalsIgnoreCase("useFirstPass") ) + useFirstPass = true; + + // Check for a custom Principal implementation + principalClassName = (String) options.get("principalClass"); + + // Check for unauthenticatedIdentity option. + String name = (String) options.get("unauthenticatedIdentity"); + if( name != null ) + { + try + { + unauthenticatedIdentity = createIdentity(name); + log.info("Saw unauthenticatedIdentity="+name); + } + catch(Exception e) + { + log.warn("Failed to create custom unauthenticatedIdentity", e); + } + } + } + + /** Looks for javax.security.auth.login.name and javax.security.auth.login.password + values in the sharedState map if the useFirstPass option was true and returns + true if they exist. If they do not or are null this method returns false. + + Note that subclasses that override the login method must set the loginOk + ivar to true if the login succeeds in order for the commit phase to + populate the Subject. This implementation sets loginOk to true if the + login() method returns true, otherwise, it sets loginOk to false. + */ + public boolean login() throws LoginException + { + log.info("login"); + loginOk = false; + // If useFirstPass is true, look for the shared password + if( useFirstPass == true ) + { + try + { + Object identity = sharedState.get("javax.security.auth.login.name"); + Object credential = sharedState.get("javax.security.auth.login.password"); + if( identity != null && credential != null ) + { + loginOk = true; + return true; + } + // Else, fall through and perform the login + } + catch(Exception e) + { // Dump the exception and continue + log.error("login failed", e); + } + } + return false; + } + + /** Method to commit the authentication process (phase 2). If the login + method completed successfully as indicated by loginOk == true, this + method adds the getIdentity() value to the subject getPrincipals() Set. + It also adds the members of each Group returned by getRoleSets() + to the subject getPrincipals() Set. + + @see javax.security.auth.Subject; + @see java.security.acl.Group; + @return true always. + */ + public boolean commit() throws LoginException + { + log.info("commit, loginOk="+loginOk); + if( loginOk == false ) + return false; + + Set principals = subject.getPrincipals(); + Principal identity = getIdentity(); + principals.add(identity); + Group[] roleSets = getRoleSets(); + for(int g = 0; g < roleSets.length; g ++) + { + Group group = roleSets[g]; + String name = group.getName(); + Group subjectGroup = createGroup(name, principals); + if( subjectGroup instanceof NestableGroup ) + { + /* A NestableGroup only allows Groups to be added to it so we + need to add a SimpleGroup to subjectRoles to contain the roles + */ + SimpleGroup tmp = new SimpleGroup("Roles"); + subjectGroup.addMember(tmp); + subjectGroup = tmp; + } + // Copy the group members to the Subject group + Enumeration members = group.members(); + while( members.hasMoreElements() ) + { + Principal role = (Principal) members.nextElement(); + subjectGroup.addMember(role); + } + } + return true; + } + + /** Method to abort the authentication process (phase 2). + @return true alaways + */ + public boolean abort() throws LoginException + { + log.info("abort"); + return true; + } + + /** Remove the user identity and roles added to the Subject during commit. + @return true always. + */ + public boolean logout() throws LoginException + { + log.info("logout"); + // Remove the user identity + Principal identity = getIdentity(); + Set principals = subject.getPrincipals(); + principals.remove(identity); + // Remove any added Groups... + return true; + } + //--- End LoginModule interface methods + + // --- Protected methods + + /** Overriden by subclasses to return the Principal that corresponds to + the user primary identity. + */ + protected Principal getIdentity() + { + return null; + } + /** Overriden by subclasses to return the Groups that correspond to the + to the role sets assigned to the user. Subclasses should create at + least a Group named "Roles" that contains the roles assigned to the user. + A second common group is "CallerPrincipal" that provides the application + identity of the user rather than the security domain identity. + @return Group[] containing the sets of roles + */ + protected Group[] getRoleSets() throws LoginException + { + return null; + } + + protected boolean getUseFirstPass() + { + return useFirstPass; + } + protected Principal getUnauthenticatedIdentity() + { + return unauthenticatedIdentity; + } + + /** 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. + */ + protected Group createGroup(String name, Set principals) + { + Group roles = null; + Iterator iter = principals.iterator(); + while( iter.hasNext() ) + { + Object next = iter.next(); + if( (next instanceof Group) == false ) + continue; + Group grp = (Group) next; + if( grp.getName().equals(name) ) + { + roles = grp; + break; + } + } + // If we did not find a group create one + if( roles == null ) + { + roles = new SimpleGroup(name); + principals.add(roles); + } + return roles; + } + + /** Utility method to create a Principal for the given username. This + * creates an instance of the principalClassName type if this option was + * specified using the class constructor matching: ctor(String). If + * principalClassName was not specified, a SimplePrincipal is created. + * + * @param username the name of the principal + * @return the principal instance + * @throws java.lang.Exception thrown if the custom principal type cannot be created. + */ + protected Principal createIdentity(String username) + throws Exception + { + Principal p = null; + if( principalClassName == null ) + { + p = new SimplePrincipal(username); + } + else + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class clazz = loader.loadClass(principalClassName); + Class[] ctorSig = {String.class}; + Constructor ctor = clazz.getConstructor(ctorSig); + Object[] ctorArgs = {username}; + p = (Principal) ctor.newInstance(ctorArgs); + } + return p; + } +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/AuthenticationMethodConfigurer.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/AuthenticationMethodConfigurer.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/AuthenticationMethodConfigurer.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,129 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ +package org.lamsfoundation.lams.security; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import org.lamsfoundation.lams.usermanagement.AuthenticationMethodParameter; +import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; +import org.lamsfoundation.lams.util.XmlFileLoader; + +/** + *

+ * View Source + *

+ * + * @author Fei Yang + */ +public class AuthenticationMethodConfigurer { + + private static Document authConfigureDoc = null; + + private static String configFilePath; + + /** + * @return Returns the configFilePath. + */ + public static String getConfigFilePath() { + return configFilePath; + } + /** + * @param configFilePath The configFilePath to set. + */ + public static void setConfigFilePath(String configFilePath) { + AuthenticationMethodConfigurer.configFilePath = configFilePath; + } + + private static void loadConfiguration() + throws IOException,SAXException,SAXParseException,ParserConfigurationException{ + + if(authConfigureDoc==null){ + authConfigureDoc = XmlFileLoader.getDocumentFromFilePath(configFilePath); + } + } + + private static Element findMethodElement(String methodName) + throws IOException,SAXException,SAXParseException,ParserConfigurationException + { + NodeList nodeList = authConfigureDoc.getElementsByTagName("Method"); + for(int i=0; i + * View Source + *

+ * + * @author Kevin Han + * Fei Yang + */ +public class JspRedirectStrategy { + + public static final String AUTH_OBJECT_NAME = "authorizationObjectName"; + + public static final String SECURITY_CHECK_NOT_PASSED = "SecurityCheckNotPassed"; + + public static final String SECURITY_CHECK_PASSED = "securityCheckPassed"; + + public static final String WELCOME_PAGE = "index.jsp "; + + private static Logger log = Logger.getLogger(JspRedirectStrategy.class); + + /** + * login page (defined in web.xml) calls this method to have its response + * redirected whenever necessary, and have its authorization object updated + * as well. + * + * @param request + * @param response + * @return true if response has been redirected; false if login page should + * continue. + * + */ + public static boolean loginPageRedirected(HttpServletRequest request,HttpServletResponse response) throws java.io.IOException { + HttpSession session = request.getSession(false); + if (session == null) { + log.debug("===>LOGIN PAGE: session not found, simply stop here."); + return true; + } + String checkStatus = (String) session.getAttribute(AUTH_OBJECT_NAME); + String id = session.getId(); + + if (checkStatus == null) { + log.debug("===>LOGIN PAGE: there is no auth obj in session, auth obj created. session id: " + id); + session.setAttribute(AUTH_OBJECT_NAME, SECURITY_CHECK_NOT_PASSED); + response.sendRedirect(WELCOME_PAGE); + return true; + } else if (checkStatus.equals(SECURITY_CHECK_NOT_PASSED)) { + log.debug("===>LOGIN PAGE: accessing login page before login succeed, display login page. session id: "+ id); + return false; + } else if (checkStatus.equals(SECURITY_CHECK_PASSED)) { + log.debug("===>LOGIN PAGE: accessing login after login succeed. Invalidate the session: " + id + " and redirect to "+ WELCOME_PAGE); + session.invalidate(); + response.sendRedirect(WELCOME_PAGE); + return true; + } else { + log.debug("===>LOGIN PAGE: logically impossible to be here, no valid status found : "+ id); + session.invalidate(); + response.sendRedirect(WELCOME_PAGE); + return true; + } + + } + + public static void welcomePageStatusUpdate(HttpServletRequest request, + HttpServletResponse response) { + HttpSession session = request.getSession(false); + if (session == null) { + log.debug("===>INDEX PAGE: session not found, simply stop here."); + return; + } + if (!session.getAttribute(AUTH_OBJECT_NAME).equals(SECURITY_CHECK_PASSED)) + session.setAttribute(AUTH_OBJECT_NAME, new String(SECURITY_CHECK_PASSED)); + + } + +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,96 @@ +package org.lamsfoundation.lams.security; + +import java.util.Properties; +import javax.naming.Context; +import javax.naming.ldap.InitialLdapContext; + +import org.apache.log4j.Logger; + +import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; + +public class LDAPAuthenticator { + private static Logger log = Logger.getLogger(LDAPAuthenticator.class); + + private static final String USE_OBJECT_CREDENTIAL_OPT = "useObjectCredential"; + + private static final String PRINCIPAL_DN_PREFIX_OPT = "principalDNPrefix"; + + private static final String PRINCIPAL_DN_SUFFIX_OPT = "principalDNSuffix"; + + private static final String ROLES_CTX_DN_OPT = "rolesCtxDN"; + + private static final String USER_ROLES_CTX_DN_ATTRIBUTE_ID_OPT = "userRolesCtxDNAttributeName"; + + private static final String UID_ATTRIBUTE_ID_OPT = "uidAttributeID"; + + private static final String ROLE_ATTRIBUTE_ID_OPT = "roleAttributeID"; + + private static final String MATCH_ON_USER_DN_OPT = "matchOnUserDN"; + + private static final String ROLE_ATTRIBUTE_IS_DN_OPT = "roleAttributeIsDN"; + + private static final String ROLE_NAME_ATTRIBUTE_ID_OPT = "roleNameAttributeID"; + + private AuthenticationMethod method; + + public LDAPAuthenticator(AuthenticationMethod method) { + this.method = method; + } + + public boolean authenticate(String username, String inputPassword) { + return authentication(username, inputPassword); + } + + private boolean authentication(String username, Object credential) { + Properties env = new Properties(); + + // Load all authentication method parameters into env + env.setProperty(Context.INITIAL_CONTEXT_FACTORY, method.getParameterByName(Context.INITIAL_CONTEXT_FACTORY).getValue()); + env.setProperty(Context.SECURITY_AUTHENTICATION, method.getParameterByName(Context.SECURITY_AUTHENTICATION).getValue()); + env.setProperty(Context.SECURITY_PROTOCOL, method.getParameterByName(Context.SECURITY_PROTOCOL).getValue()); + + String principalDNPrefix = method.getParameterByName(PRINCIPAL_DN_PREFIX_OPT).getValue(); + String principalDNSuffix = method.getParameterByName(PRINCIPAL_DN_SUFFIX_OPT).getValue(); + String userDN = principalDNPrefix + username + principalDNSuffix; + env.setProperty(Context.SECURITY_PRINCIPAL, userDN); + + env.setProperty(Context.PROVIDER_URL, method.getParameterByName(Context.PROVIDER_URL).getValue()); + env.put(Context.SECURITY_CREDENTIALS, credential); + + Object originalTrustStore = System.getProperty("javax.net.ssl.trustStore"); + Object originalTrustPass = System.getProperty("javax.net.ssl.trustStorePassword"); + //FIXME: synchronization issue: dynamically load certificate into + // system instead of overwritting it. + System.setProperty("javax.net.ssl.trustStore", method.getParameterByName("truststore.path").getValue()); + System.setProperty("javax.net.ssl.trustStorePassword", method.getParameterByName("truststore.password").getValue()); + + log.debug("===> LDAP authenticator: " + env); + + InitialLdapContext ctx = null; + try { + ctx = new InitialLdapContext(env, null); + log.debug("===> ldap context created: "+ctx); + return true; + } catch (Exception e) { + log.error("===> Ldap exception: " + e); + return false; + } finally { + + try { + //FIXME: synchronization issue -- dynamically load certificate + // instead of overwritting system properties + //System.setProperty("javax.net.ssl.trustStore",(String)originalTrustStore + // ); + //System.setProperty("javax.net.ssl.trustStorePassword",(String)originalTrustPass + // ); + + if (ctx != null) + ctx.close(); + } catch (Exception e) { + log.error("===> gettting problem when closing context. Excetion: "+e); + } + } + + } + +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/NestableGroup.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/NestableGroup.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/NestableGroup.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,116 @@ +package org.lamsfoundation.lams.security; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Enumeration; +import java.util.LinkedList; + +public class NestableGroup extends SimplePrincipal implements Group +{ + /** The stack of the Groups. Elements are pushed/poped by + inserting/removing element 0. + */ + private LinkedList rolesStack; + + /** Creates new NestableGroup with the given name + */ + public NestableGroup(String name) + { + super(name); + rolesStack = new LinkedList(); + } + +// --- Begin Group interface methods + /** Returns an enumeration that contains the single active Principal. + @return an Enumeration of the single active Principal. + */ + public Enumeration members() + { + return new IndexEnumeration(); + } + + /** Removes the first occurence of user from the Principal stack. + + @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) + { + return rolesStack.remove(user); + } + + /** Pushes the group onto the Group stack and makes it the active + Group. + @param group, the instance of Group that contains the roles to set as the + active Group. + @exception IllegalArgumentException, thrown if group is not an instance of Group. + @return true always. + */ + public boolean addMember(Principal group) throws IllegalArgumentException + { + if( (group instanceof Group) == false ) + throw new IllegalArgumentException("The addMember argument must be a Group"); + + rolesStack.addFirst(group); + return true; + } + + /** Returns true if the passed principal is a member of the active 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. + + @param member the principal whose membership is to be checked. + + @return true if the principal is a member of this group, false otherwise. + */ + public boolean isMember(Principal member) + { + if( rolesStack.size() == 0 ) + return false; + Group activeGroup = (Group) rolesStack.getFirst(); + boolean isMember = activeGroup.isMember(member); + return isMember; + } + + public String toString() + { + StringBuffer tmp = new StringBuffer(getName()); + tmp.append("(members:"); + Enumeration iter = members(); + while( iter.hasMoreElements() ) + { + tmp.append(iter.nextElement()); + tmp.append(','); + } + tmp.setCharAt(tmp.length()-1, ')'); + return tmp.toString(); + } +// --- End Group interface methods + + private class IndexEnumeration implements Enumeration + { + private Enumeration iter; + + IndexEnumeration() + { + if( rolesStack.size() > 0 ) + { + Group grp = (Group) rolesStack.get(0); + iter = grp.members(); + } + } + public boolean hasMoreElements() + { + boolean hasMore = iter != null && iter.hasMoreElements(); + return hasMore; + } + public Object nextElement() + { + Object next = null; + if( iter != null ) + next = iter.nextElement(); + return next; + } + } +} Index: lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,113 @@ +package org.lamsfoundation.lams.security; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.HashMap; + +public class SimpleGroup extends SimplePrincipal implements Group +{ + private HashMap members; + + 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. + */ + public boolean addMember(Principal user) + { + boolean isMember = members.containsKey(user); + if( isMember == false ) + members.put(user, user); + return isMember == false; + } + /** 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. + + 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. + */ + + 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; + */ + } + + /** 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()); + } + + /** 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; + } + + 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(); + } +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,44 @@ +package org.lamsfoundation.lams.security; + +import java.security.Principal; + +public class SimplePrincipal implements Principal, java.io.Serializable +{ + private 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(); + boolean equals = false; + if( name == null ) + equals = anotherName == null; + else + equals = name.equals(anotherName); + return equals; + } + + public int hashCode() + { + return (name == null ? 0 : name.hashCode()); + } + + public String toString() + { + return name; + } + + public String getName() + { + return name; + } +} Index: lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,257 @@ +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.security.acl.Group; +import java.security.Principal; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.FailedLoginException; + +import javax.naming.NamingException; +import javax.security.auth.login.LoginException; +import javax.naming.InitialContext; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; +import java.util.*; + +import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; + +import org.apache.log4j.Logger; +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.context.WebApplicationContext; +import org.lamsfoundation.lams.usermanagement.service.UserManagementService; +import org.lamsfoundation.lams.web.HttpSessionManager; +import org.lamsfoundation.lams.usermanagement.*; + +public class UniversalLoginModule extends UsernamePasswordLoginModule { + private static Logger log = Logger.getLogger(UniversalLoginModule.class); + + private static final String USE_OBJECT_CREDENTIAL_OPT = "useObjectCredential"; + + private static final String PRINCIPAL_DN_PREFIX_OPT = "principalDNPrefix"; + + private static final String PRINCIPAL_DN_SUFFIX_OPT = "principalDNSuffix"; + + private static final String ROLES_CTX_DN_OPT = "rolesCtxDN"; + + private static final String USER_ROLES_CTX_DN_ATTRIBUTE_ID_OPT = "userRolesCtxDNAttributeName"; + + private static final String UID_ATTRIBUTE_ID_OPT = "uidAttributeID"; + + private static final String ROLE_ATTRIBUTE_ID_OPT = "roleAttributeID"; + + private static final String MATCH_ON_USER_DN_OPT = "matchOnUserDN"; + + private static final String ROLE_ATTRIBUTE_IS_DN_OPT = "roleAttributeIsDN"; + + private static final String ROLE_NAME_ATTRIBUTE_ID_OPT = "roleNameAttributeID"; + + public UniversalLoginModule() { + } + + private transient SimpleGroup userRoles = new SimpleGroup("Roles"); + + protected String dsJndiName; + + protected String rolesQuery; + + protected String propertyFilePath; + + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) { + super.initialize(subject, callbackHandler, sharedState, options); + + //from options to get path to property file -> authentication.xml + propertyFilePath = (String) options.get("authenticationPropertyFile"); + //load authentication property file + AuthenticationMethodConfigurer.setConfigFilePath(propertyFilePath); + + } + + protected boolean validatePassword(String inputPassword, + String expectedPassword) { + boolean isValid = false; + if (inputPassword != null) { + // empty password not allowed + if (inputPassword.length() == 0) + return false; + + log.debug("===> validatePassword() called: " + inputPassword + + " : " + expectedPassword); + + try { + String username = getUsername(); + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(HttpSessionManager.getInstance().getServletContext()); + UserManagementService service = (UserManagementService) ctx.getBean("userManagementServiceTarget"); + User user = service.getUserByLogin(username); + + log.debug("===> authenticating user: " + username); + if (user == null) + return false; + + AuthenticationMethod method = null; + try { + method = service.getAuthenticationMethodForUser(username); + AuthenticationMethodConfigurer.configure(method); + + this.dsJndiName = method.getParameterByName("dsJndiName").getValue(); + this.rolesQuery = method.getParameterByName("rolesQuery").getValue(); + } catch (Exception e) { + log.debug("===>Exception : " + e); + return false; + } + + List parameters = method.getAuthenticationMethodParameters(); + + //for debug purpose only + for (int i = 0; i < parameters.size(); i++) { + AuthenticationMethodParameter mp = (AuthenticationMethodParameter) parameters.get(i); + log.debug("===>" + mp.getName() + " = " + mp.getValue()); + } + + String type = method.getAuthenticationMethodType().getDescription(); + log.debug("===> authentication type :" + type); + if ("LDAP".equals(type)) { + LDAPAuthenticator authenticator = new LDAPAuthenticator(method); + isValid = authenticator.authenticate(username,inputPassword); + log.debug("===> LDAP :: user:" + username + ":" + + inputPassword + " authenticated! "); + } else if ("LAMS".equals(type)) { + DatabaseAuthenticator authenticator = new DatabaseAuthenticator(method); + isValid = authenticator.authenticate(username,inputPassword); + log.debug("===> LAMS:: user:" + username + ":" + + inputPassword + " authenticated! "); + } else if ("WEB_AUTH".equals(type)) { + log.debug("===> WEBAUTH: " + username + " type: " + type); + WebAuthAuthenticator authenticator = new WebAuthAuthenticator(); + log.debug("===> webauth authenticator is:" + authenticator); + isValid = authenticator.authenticate(username,inputPassword); + log.debug("===> WEBAUTH :: user:" + username + ":" + + inputPassword + " authenticated! "); + + } else { + log.debug("Unexpected authentication type!"); + return false; + } + } catch (Exception e) { + e.printStackTrace(); + log.debug("===> exception: " + e); + } + } + return isValid; + } + + /** + * According to Lams's security policy, all the authorization + * must be done locally, in other word, through Lams database + * or other "local"(logically) data resource. + * + @return Group[] containing the sets of roles + */ + protected Group[] getRoleSets() throws LoginException { + String username = getUsername(); + Connection conn = null; + HashMap setsMap = new HashMap(); + PreparedStatement ps = null; + ResultSet rs = null; + + try { + + 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 + } + 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; + } + + do { + String name = rs.getString(1); + String groupName = rs.getString(2); + if (groupName == null || groupName.length() == 0) + groupName = "Roles"; + Group group = (Group) setsMap.get(groupName); + if (group == null) { + group = new SimpleGroup(groupName); + setsMap.put(groupName, group); + } + + try { + Principal p = super.createIdentity(name); + log.info("Assign user to role " + name); + group.addMember(p); + } catch (Exception e) { + log.debug("Failed to create principal: " + name, 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()); + } 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 (Exception ex) { + } + } + } + + Group[] roleSets = new Group[setsMap.size()]; + 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 + */ + protected String getUsersPassword() throws LoginException { + return ""; + } + +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/UsernamePasswordLoginModule.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/UsernamePasswordLoginModule.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/UsernamePasswordLoginModule.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,317 @@ +package org.lamsfoundation.lams.security; + +import java.security.Principal; +import java.util.Map; + +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; + +public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule +{ + /** The login identity */ + private Principal identity; + /** The proof of login identity */ + private char[] credential; + /** the message digest algorithm used to hash passwords. If null then + plain passwords will be used. */ + private String hashAlgorithm = null; + /** the name of the charset/encoding to use when converting the password + String to a byte array. Default is the platform's default encoding. + */ + private String hashCharset = null; + /** the string encoding format to use. Defaults to base64. */ + private String hashEncoding = null; + /** A flag indicating if the password comparison should ignore case */ + private boolean ignorePasswordCase; + + /** Override the superclass method to look for the following options after + first invoking the super version. + @param options : + @option hashAlgorithm: the message digest algorithm used to hash passwords. + If null then plain passwords will be used. + @option hashCharset: the name of the charset/encoding to use when converting + the password String to a byte array. Default is the platform's default + encoding. + @option hashEncoding: the string encoding format to use. Defaults to base64. + @option ignorePasswordCase: A flag indicating if the password comparison + should ignore case + */ + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) + { + super.initialize(subject, callbackHandler, sharedState, options); + + // Check to see if password hashing has been enabled. + // If an algorithm is set, check for a format and charset. + hashAlgorithm = (String) options.get("hashAlgorithm"); + if( hashAlgorithm != null ) + { + hashEncoding = (String) options.get("hashEncoding"); + if( hashEncoding == null ) + hashEncoding = "BASE64"; //Util.BASE64_ENCODING; + hashCharset = (String) options.get("hashCharset"); + log.debug("Passworg hashing activated: algorithm = " + hashAlgorithm + + ", encoding = " + hashEncoding+ (hashCharset == null ? "" : "charset = " + hashCharset)); + } + String flag = (String) options.get("ignorePasswordCase"); + ignorePasswordCase = Boolean.valueOf(flag).booleanValue(); + } + + /** Perform the authentication of the username and password. + */ + public boolean login() throws LoginException + { + // See if shared credentials exist + if( super.login() == true ) + { + // Setup our view of the user + Object username = sharedState.get("javax.security.auth.login.name"); + if( username instanceof Principal ) + identity = (Principal) username; + else + { + String name = username.toString(); + try + { + identity = createIdentity(name); + } + catch(Exception e) + { + log.debug("Failed to create principal", e); + throw new LoginException("Failed to create principal: "+ e.getMessage()); + } + } + Object password = sharedState.get("javax.security.auth.login.password"); + if( password instanceof char[] ) + credential = (char[]) password; + else if( password != null ) + { + String tmp = password.toString(); + credential = tmp.toCharArray(); + } + return true; + } + + super.loginOk = false; + String[] info = getUsernameAndPassword(); + String username = info[0]; + String password = info[1]; + if( username == null && password == null ) + { + identity = unauthenticatedIdentity; + super.log.info("Authenticating as unauthenticatedIdentity="+identity); + } + + if( identity == null ) + { + try + { + identity = createIdentity(username); + } + catch(Exception e) + { + log.debug("Failed to create principal", e); + throw new LoginException("Failed to create principal: "+ e.getMessage()); + } + + // Hash the user entered password if password hashing is in use + if( hashAlgorithm != null ) + password = createPasswordHash(username, password); + // Validate the password supplied by the subclass + String expectedPassword = getUsersPassword(); + if( validatePassword(password, expectedPassword) == false ) + { + super.log.debug("Bad password for username="+username); + throw new FailedLoginException("Password Incorrect/Password Required"); + } + } + + if( getUseFirstPass() == true ) + { // Add the username and password to the shared state map + sharedState.put("javax.security.auth.login.name", username); + sharedState.put("javax.security.auth.login.password", credential); + } + super.loginOk = true; + super.log.info("User '" + identity + "' authenticated, loginOk="+loginOk); + return true; + } + + protected Principal getIdentity() + { + return identity; + } + protected Principal getUnauthenticatedIdentity() + { + return unauthenticatedIdentity; + } + + protected Object getCredentials() + { + return credential; + } + protected String getUsername() + { + String username = null; + if( getIdentity() != null ) + username = getIdentity().getName(); + return username; + } + + /** 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 + @exception LoginException thrown if CallbackHandler is not set or fails. + */ + protected String[] getUsernameAndPassword() throws LoginException + { + String[] info = {null, null}; + // prompt for a username and password + if( callbackHandler == null ) + { + throw new LoginException("Error: 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(java.io.IOException ioe) + { + throw new LoginException(ioe.toString()); + } + catch(UnsupportedCallbackException uce) + { + throw new LoginException("CallbackHandler does not support: " + uce.getCallback()); + } + info[0] = username; + info[1] = password; + return info; + } + + /** + * If hashing is enabled, this method is called from login() + * prior to password validation. + *

+ * Subclasses may override it to provide customized password hashing, + * for example by adding user-specific information or salting. + *

+ * The default version calculates the hash based on the following options: + *

    + *
  • hashAlgorithm: The digest algorithm to use. + *
  • hashEncoding: The format used to store the hashes (base64 or hex) + *
  • hashCharset: The encoding used to convert the password to bytes + * for hashing. + *
+ * It will return null if the hash fails for any reason, which will in turn + * cause validatePassword() to fail. + * + * @param username ignored in default version + * @param password the password string to be hashed + */ + protected String createPasswordHash(String username, String password) + { + String passwordHash = createPasswordHash(hashAlgorithm, hashEncoding, + hashCharset, username, password); + return passwordHash; + } + + /** A hook that allows subclasses to change the validation of the input + password against the expected password. This version checks that + neither inputPassword or expectedPassword are null that that + inputPassword.equals(expectedPassword) is true; + @return true if the inputPassword is valid, false otherwise. + */ + protected boolean validatePassword(String inputPassword, String expectedPassword) + { + if( inputPassword == null || expectedPassword == null ) + return false; + boolean valid = false; + if( ignorePasswordCase == true ) + valid = inputPassword.equalsIgnoreCase(expectedPassword); + else + valid = inputPassword.equals(expectedPassword); + return valid; + } + + + /** Get the expected password for the current username available via + the getUsername() method. This is called from within the login() + method after the CallbackHandler has returned the username and + candidate password. + @return the valid password String + */ + abstract protected String getUsersPassword() throws LoginException; + + public static String createPasswordHash(String hashAlgorithm, String hashEncoding, + String hashCharset, String username, String password) + { + //currently no implementation for password hashing. + return password; + /* + byte[] passBytes; + String passwordHash = null; + + // convert password to byte data + try + { + if(hashCharset == null) + passBytes = password.getBytes(); + else + passBytes = password.getBytes(hashCharset); + } + catch(Exception uee) + { + Logger log = Logger.getLogger(UsernamePasswordLoginModule.class); + log.error("charset " + hashCharset + " not found. Using platform default.", uee); + passBytes = password.getBytes(); + } + + // calculate the hash and apply the encoding. + try + { + byte[] hash = MessageDigest.getInstance(hashAlgorithm).digest(passBytes); + if(hashEncoding.equalsIgnoreCase("BASE64")) + { + passwordHash = Util.encodeBase64(hash); + } + else if(hashEncoding.equalsIgnoreCase("HEX")) + { + passwordHash = Util.encodeBase16(hash); + } + else + { + Logger log = Logger.getLogger(UsernamePasswordLoginModule.class); + log.error("Unsupported hash encoding format " + hashEncoding); + } + } + catch(Exception e) + { + Logger log = Logger.getLogger(UsernamePasswordLoginModule.class); + log.error("Password hash calculation failed ", e); + } + return passwordHash; + */ + + } + +} Index: lams_central/src/java/org/lamsfoundation/lams/security/WebAuthAuthenticator.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/WebAuthAuthenticator.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/WebAuthAuthenticator.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,25 @@ +/* + * Created on 2004-12-15 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package org.lamsfoundation.lams.security; + +/** + * @author kevin + * + */ +public class WebAuthAuthenticator +{ + public WebAuthAuthenticator() + { + } + + public boolean authenticate(String username, String inputPassword) + { + //as for now, alway return true for webauth authenticated users + //TODO check session to see if the user is from webauth + return true; + } +} Index: lams_central/src/java/org/lamsfoundation/lams/security/WebAuthServlet.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/security/WebAuthServlet.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/security/WebAuthServlet.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,63 @@ +package org.lamsfoundation.lams.security; + +import java.io.*; +import javax.servlet.*; +import javax.servlet.http.*; + +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.context.WebApplicationContext; +import org.lamsfoundation.lams.usermanagement.service.UserManagementService; +import org.lamsfoundation.lams.usermanagement.*; + +/** + * @author kevin + * + */ +public class WebAuthServlet extends HttpServlet { + + private static final String WEBAUTH_TOKEN = "WEBAUTH_USER"; + + public void init() throws ServletException + { + } + + public void service(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException + { + HttpSession oldSession = request.getSession(false); + if ( oldSession != null ) + { + oldSession.invalidate(); + } + + String webAuthUserID = (String)request.getAttribute(WEBAUTH_TOKEN); + HttpSession session = request.getSession(true); + if ( webAuthUserID == null ) + //there is no valid WebAuth authenticated user + { + session.removeAttribute(WEBAUTH_TOKEN); + } + else + { + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext()); + UserManagementService service = (UserManagementService)ctx.getBean("userManagementServiceTarget"); + User webAuthUser = service.getUserByLogin(webAuthUserID); + + if (webAuthUser != null) + //valid webauth user also is registered in Lams as well + { + session.setAttribute(WEBAUTH_TOKEN, webAuthUserID); + } + else + //though webauth authenticated, but not registered. + { + session.removeAttribute(WEBAUTH_TOKEN); + } + } + + //In anycase, goto Lams welcome page + response.sendRedirect("index.jsp"); + + } + +} Index: lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,222 @@ +package org.lamsfoundation.lams.web; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.actions.DispatchAction; + +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.context.WebApplicationContext; + +import org.lamsfoundation.lams.usermanagement.service.UserManagementService; +import org.lamsfoundation.lams.web.PasswordChangeActionForm; +import org.lamsfoundation.lams.usermanagement.Role; +import org.lamsfoundation.lams.usermanagement.Organisation; +import org.lamsfoundation.lams.usermanagement.util.AdminPreparer; + +import org.lamsfoundation.lams.util.Configuration; +import org.lamsfoundation.lams.util.ConfigurationKeys; + + +/** + * this is an action where all lams client environments launch. + * initial configuration of the individual environment setting is done here. + * + * @struts:action path="/home" + * validate="false" + * parameter="method" + * @struts:action-forward name="admin" path=".admin" + * @struts:action-forward name="learner" path="/learner.jsp" + * @struts:action-forward name="author" path="/author.jsp" + * @struts:action-forward name="staff" path="/staff.jsp" + * @struts:action-forward name="error" path=".error" + * @struts:action-forward name="passwordChange" path=".passwordChange" + * + */ +public class HomeAction extends DispatchAction { + + private static Logger log = Logger.getLogger(HomeAction.class); + private static WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(HttpSessionManager.getInstance().getServletContext()); + private static UserManagementService service = (UserManagementService) ctx.getBean("userManagementServiceTarget"); + + + private boolean isUserInRole(String login,int orgId, String roleName) + { + if (service.getUserOrganisationRole(login, new Integer(orgId),roleName)==null) + return false; + return true; + } + + /** + * request for sysadmin environment + */ + public ActionForward admin(ActionMapping mapping, ActionForm form, + HttpServletRequest req, HttpServletResponse res) + throws IOException, ServletException { + + try { + log.debug("request admin"); + + String login = req.getRemoteUser(); + + int orgId = new Integer(req.getParameter("orgId")).intValue(); + + if ( isUserInRole(login,orgId,Role.ADMIN)) + { + log.debug("user is admin"); + Organisation org = service.getOrganisationById(new Integer(orgId)); + AdminPreparer.prepare(org,req,service); + return mapping.findForward("admin"); + } + else + { + log.error("User "+login+" tried to get admin screen but isn't admin in organisation: "+orgId); + return mapping.findForward("error"); + } + + } catch (Exception e) { + e.printStackTrace(); + return mapping.findForward("error"); + } + } + + /** + * request for learner environment + */ + public ActionForward learner(ActionMapping mapping, ActionForm form, + HttpServletRequest req, HttpServletResponse res) + throws IOException, ServletException { + + try { + log.debug("request learner"); + + String login = req.getRemoteUser(); + + int orgId = new Integer(req.getParameter("orgId")).intValue(); + + if ( isUserInRole(login,orgId,Role.LEARNER) ) + { + log.debug("user is learner"); + + String serverUrl = Configuration.get(ConfigurationKeys.SERVER_URL); + + req.setAttribute("serverUrl", serverUrl); + return mapping.findForward("learner"); + } + else + { + log.error("User "+login+" tried to get learner screen but isn't learner in organisation: "+orgId); + return mapping.findForward("error"); + } + + } catch (Exception e) { + e.printStackTrace(); + return mapping.findForward("error"); + } + } + + + /** + * request for author environment + */ + public ActionForward author(ActionMapping mapping, ActionForm form, + HttpServletRequest req, HttpServletResponse res) + throws IOException, ServletException { + + try { + log.debug("request author"); + + String login = req.getRemoteUser(); + + int orgId = new Integer(req.getParameter("orgId")).intValue(); + + if ( isUserInRole(login,orgId,Role.AUTHOR) ) + { + log.debug("user is author"); + + String serverUrl = Configuration.get(ConfigurationKeys.SERVER_URL); + + req.setAttribute("serverUrl", serverUrl); + return mapping.findForward("author"); + } + else + { + log.error("User "+login+" tried to get author screen but isn't author in organisation: "+orgId); + return mapping.findForward("error"); + } + + } catch (Exception e) { + e.printStackTrace(); + return mapping.findForward("error"); + } + } + + + /** + * request for staff environment + */ + public ActionForward staff(ActionMapping mapping, ActionForm form, + HttpServletRequest req, HttpServletResponse res) + throws IOException, ServletException { + + try { + log.debug("request staff"); + + String login = req.getRemoteUser(); + + int orgId = new Integer(req.getParameter("orgId")).intValue(); + + if (isUserInRole(login, orgId, Role.STAFF)) { + log.debug("user is staff"); + + String serverUrl = Configuration + .get(ConfigurationKeys.SERVER_URL); + + req.setAttribute("serverUrl", serverUrl); + return mapping.findForward("staff"); + } else { + log + .error("User " + + login + + " tried to get staff screen but isn't staff in organisation: " + + orgId); + return mapping.findForward("error"); + } + + } catch (Exception e) { + e.printStackTrace(); + return mapping.findForward("error"); + } + } + + /** + * Loads up the user password change form + * @return screen reference name - "passwordChange" + */ + public ActionForward passwordChange( + ActionMapping mapping, + ActionForm form, + HttpServletRequest request, + HttpServletResponse response) + throws IOException, ServletException { + + String login = request.getRemoteUser(); + + PasswordChangeActionForm newForm = new PasswordChangeActionForm(); + newForm.setLogin(login); + + request.getSession(true).setAttribute( + PasswordChangeActionForm.formName, + newForm); + + return mapping.findForward("passwordChange"); + } + +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/web/PasswordChangeAction.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/PasswordChangeAction.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/PasswordChangeAction.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,112 @@ +package org.lamsfoundation.lams.web; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.lamsfoundation.lams.usermanagement.service.UserManagementService; + +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.context.WebApplicationContext; + + +import org.apache.log4j.Logger; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.action.ActionMessages; +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.Action; + +/** + * @author Fei Yang + * + * @struts:action path="/passwordChanged" + * name="PasswordChangeActionForm" + * input=".passwordChange" + * validate="true" + * + * @struts:action-forward name="okay" path=".passwordChangeOk" + * @struts:action-forward name="cancelled" path="/index.jsp" + */ +public class PasswordChangeAction extends Action { + + private static Logger log = Logger.getLogger(PasswordChangeAction.class); + + /** + * @param mapping The ActionMapping used to select this instance + * @param actionForm The optional ActionForm bean for this request (if any) + * @param request The HTTP request we are processing + * @param response The HTTP response we are creating + * + */ + public ActionForward execute(ActionMapping mapping, + ActionForm form, + HttpServletRequest request, + HttpServletResponse response) + throws Exception + { + // -- isCancelled? + if (isCancelled(request)) { + request.getSession().removeAttribute(PasswordChangeActionForm.formName); + return mapping.findForward("cancelled"); + } + + + ActionErrors errors = new ActionErrors(); + + PasswordChangeActionForm passwordChangeForm = (PasswordChangeActionForm) form; + + if (errors.isEmpty()) + { + try { + + String loggedInUser = request.getRemoteUser(); + String login = passwordChangeForm.getLogin(); + String oldPassword = passwordChangeForm.getOldPassword(); + String password = passwordChangeForm.getPassword(); + + if ( loggedInUser == null || ! loggedInUser.equals(login) ) + { + errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.authorisation")); + } + else + { + + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(request.getSession(true).getServletContext()); + UserManagementService service = (UserManagementService)ctx.getBean("userManagementServiceTarget"); + if(!service.getUserByLogin(login).getPassword().equals(oldPassword)) + { + errors.add("oldPassword", new ActionMessage("error.oldpassword.mismatch")); + } + else + { + service.updatePassword(login, password ); + } + } + + } catch (Exception e) { + log.error("Exception occured ",e); + errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(e.getMessage())); + } + + } // end if no errors + + // -- Report any errors + if (!errors.isEmpty()) { + addErrors(request, errors); + if (mapping.getInput()!=null) + { + passwordChangeForm.reset(mapping,request); + return (new ActionForward(mapping.getInput())); + } + // If no input page, use error forwarding + return (mapping.findForward("error")); + } + return mapping.findForward("okay"); + + } +} + + Index: lams_central/src/java/org/lamsfoundation/lams/web/PasswordChangeActionForm.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/PasswordChangeActionForm.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/PasswordChangeActionForm.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,145 @@ +package org.lamsfoundation.lams.web; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionMapping; + +/** + * @author fmalikoff + * @Modified by Fei Yang + * + * @struts:form name="PasswordChangeActionForm" + * include-pk="true" + * include-all="true" + */ +public class PasswordChangeActionForm extends ActionForm { + + public static final String formName = "PasswordChangeActionForm"; // must match name in @struts:action section above + + private static Logger log = Logger.getLogger(PasswordChangeActionForm.class); + + private String oldPassword; + + private String password; + + private String passwordConfirm; + + private String login; + + public PasswordChangeActionForm() { + } + + /** + * Returns the login. + * @return String + */ + public String getLogin() { + return login; + } + + /** + * Returns the password. + * @return String + */ + public String getPassword() { + return password; + } + + /** + * Returns the passwordConfirm. + * @return String + */ + public String getPasswordConfirm() { + return passwordConfirm; + } + + /** + * Sets the login. + * @param login The login to set + */ + public void setLogin(String login) { + this.login = StringUtils.trimToEmpty(login); + } + + /** + * Sets the password. + * @param password The password to set + */ + public void setPassword(String password) { + this.password = StringUtils.trimToEmpty(password); + } + + /** + * Sets the passwordConfirm. + * @param passwordConfirm The passwordConfirm to set + */ + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = StringUtils.trimToEmpty(passwordConfirm); + } + + /** + * @return Returns the oldPassword. + */ + public String getOldPassword() { + return oldPassword; + } + + /** + * @param oldPassword The oldPassword to set. + */ + public void setOldPassword(String oldPassword) { + this.oldPassword = StringUtils.trimToEmpty(oldPassword); + } + + /** + * Reset all properties to their default values. + * + * @param mapping The mapping used to select this instance + * @param request The servlet request we are processing + */ + public void reset(ActionMapping mapping, HttpServletRequest request) { + setOldPassword(null); + setPassword(null); + setPasswordConfirm(null); + } + + /** + * Validate the properties that have been set from this HTTP request, + * and return an ActionErrors object that encapsulates any + * validation errors that have been found. If no errors are found, return + * null or an ActionErrors object with no + * recorded error messages. + *

+ * @param mapping The mapping used to select this instance + * @param request The servlet request we are processing + */ + public ActionErrors validate(ActionMapping mapping, + HttpServletRequest request) { + ActionErrors errors = super.validate(mapping, request); + + if (errors == null) + errors = new ActionErrors(); + + if (getPassword() == null || getPassword().length() == 0 + || !getPassword().equals(getPasswordConfirm())) { + errors.add("password", new ActionMessage("error.newpassword.mismatch")); + } + + if (errors.isEmpty()) + return null; + else { + // don't want to pass back what they had as the password + setOldPassword(null); + setPassword(null); + setPasswordConfirm(null); + return errors; + } + + } + +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/web/SessionListener.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/SessionListener.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/SessionListener.java (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,39 @@ +package org.lamsfoundation.lams.web; + +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; + +import org.apache.log4j.Logger; + + +/** + * Listens for the creation and destruction of http sessions, and reports back to + * the ClientSessionInfoManager. + * + * @web.listener + */ +/* Should come out in web.xml as: + * + * + * + * com.webmcq.ld.controller.web.SessionListener + * + * + */ +public class SessionListener implements HttpSessionListener +{ + private static Logger log = Logger.getLogger(SessionListener.class); + + /** HttpSessionListener interface */ + public void sessionCreated(HttpSessionEvent se) + { + HttpSessionManager.getInstance().sessionCreated(se); + } + + /** HttpSessionListener interface */ + public void sessionDestroyed(HttpSessionEvent se) + { + //nothing to do + } + +} Index: lams_central/web/WEB-INF/struts/struts-config.xml =================================================================== diff -u -r9bef1de27d0d527d75191115a535bc2c4311ade9 -rca68b26281fd59bce02a7c78f8ff9c3db534d2ae --- lams_central/web/WEB-INF/struts/struts-config.xml (.../struts-config.xml) (revision 9bef1de27d0d527d75191115a535bc2c4311ade9) +++ lams_central/web/WEB-INF/struts/struts-config.xml (.../struts-config.xml) (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -34,6 +34,64 @@ + + + + + + + + + + + + - + + + + + + Index: lams_central/web/WEB-INF/tiles/tiles-defs.xml =================================================================== diff -u --- lams_central/web/WEB-INF/tiles/tiles-defs.xml (revision 0) +++ lams_central/web/WEB-INF/tiles/tiles-defs.xml (revision ca68b26281fd59bce02a7c78f8ff9c3db534d2ae) @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file