Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/action/LdapConfigAction.java =================================================================== diff -u -r18df5a739d3e9dff1734cf64ee63595acff7a71e -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/action/LdapConfigAction.java (.../LdapConfigAction.java) (revision 18df5a739d3e9dff1734cf64ee63595acff7a71e) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/action/LdapConfigAction.java (.../LdapConfigAction.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -23,10 +23,9 @@ /* $Id$ */ package org.lamsfoundation.lams.admin.web.action; -import java.util.List; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; @@ -36,11 +35,13 @@ import org.apache.struts.action.ActionMapping; import org.lamsfoundation.lams.admin.service.AdminServiceProxy; import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; -import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.usermanagement.dto.BulkUpdateResultDTO; +import org.lamsfoundation.lams.usermanagement.service.ILdapService; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.usermanagement.service.LdapService; import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.WebUtil; +import org.lamsfoundation.lams.web.session.SessionManager; /** * @author jliew @@ -85,28 +86,15 @@ HttpServletRequest request, HttpServletResponse response) throws Exception { - // check if url contains request for refresh folder sizes only String action = WebUtil.readStrParam(request, "action", true); - if (action != null && StringUtils.equals(action, "sync")) { - return sync(mapping, form, request, response); + if (action != null) { + if (StringUtils.equals(action, "sync")) return sync(mapping, form, request, response); + if (StringUtils.equals(action, "waiting")) return waiting(mapping, form, request, response); + if (StringUtils.equals(action, "results")) return results(mapping, form, request, response); } - // get number of ldap users - List ldapUsers = getService().findByProperty( - User.class, - "authenticationMethod.authenticationMethodId", - AuthenticationMethod.LDAP - ); - if (ldapUsers != null) { - int numLdapUsers = ldapUsers.size(); - request.setAttribute( - "numLdapUsersMsg", - getMessageService().getMessage( - "msg.num.ldap.users", - getNumLdapUsersMessage(numLdapUsers) - ) - ); - } + int numLdapUsers = getNumLdapUsers(); + request.setAttribute("numLdapUsersMsg", getNumLdapUsersMsg(numLdapUsers)); return mapping.findForward("ldap"); } @@ -116,24 +104,99 @@ HttpServletRequest request, HttpServletResponse response) throws Exception { - log.info("=== Beginning LDAP user sync ==="); - int numLdapUsers = getLdapService().updateLAMSFromLdap(); - log.info("=== Finished LDAP user sync ==="); - request.setAttribute( - "numLdapUsersMsg", - getMessageService().getMessage( - "msg.num.ldap.users", - getNumLdapUsersMessage(numLdapUsers) - ) - ); + String sessionId = (String)SessionManager.getSession().getId(); + Thread t = new Thread(new LdapSyncThread(sessionId)); + t.start(); + + request.setAttribute("wait", getMessageService().getMessage("msg.ldap.synchronise.wait")); + + return mapping.findForward("ldap"); + } + + public ActionForward waiting(ActionMapping mapping, + ActionForm form, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + + request.setAttribute("wait", getMessageService().getMessage("msg.ldap.synchronise.wait")); + + return mapping.findForward("ldap"); + } + + public ActionForward results(ActionMapping mapping, + ActionForm form, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + + HttpSession ss = SessionManager.getSession(); + BulkUpdateResultDTO dto = (BulkUpdateResultDTO)ss.getAttribute(ILdapService.SYNC_RESULTS); + + int numLdapUsers = getNumLdapUsers(); + request.setAttribute("numLdapUsersMsg", getNumLdapUsersMsg(numLdapUsers)); + + request.setAttribute("numSearchResults", getNumSearchResultsUsersMsg(dto.getNumSearchResults())); + request.setAttribute("numLdapUsersCreated", getNumCreatedUsersMsg(dto.getNumUsersCreated())); + request.setAttribute("numLdapUsersUpdated", getNumUpdatedUsersMsg(dto.getNumUsersUpdated())); + request.setAttribute("numLdapUsersDisabled", getNumDisabledUsersMsg(dto.getNumUsersDisabled())); + request.setAttribute("messages", dto.getMessages()); request.setAttribute("done", getMessageService().getMessage("msg.done")); + // remove session variable that flags bulk update as done + ss.removeAttribute(ILdapService.SYNC_RESULTS); + return mapping.findForward("ldap"); } - private String[] getNumLdapUsersMessage(int numLdapUsers) { + private int getNumLdapUsers() { + Integer count = getService().getCountUsers(AuthenticationMethod.LDAP); + return (count != null ? count.intValue() : -1); + } + + private String getNumLdapUsersMsg(int numLdapUsers) { String[] args = new String[1]; args[0] = String.valueOf(numLdapUsers); - return args; + return getMessageService().getMessage("msg.num.ldap.users", args); } + + private String getNumSearchResultsUsersMsg(int searchResults) { + String[] args = new String[1]; + args[0] = String.valueOf(searchResults); + return getMessageService().getMessage("msg.num.search.results.users", args); + } + + private String getNumCreatedUsersMsg(int created) { + String[] args = new String[1]; + args[0] = String.valueOf(created); + return getMessageService().getMessage("msg.num.created.users", args); + } + + private String getNumUpdatedUsersMsg(int updated) { + String[] args = new String[1]; + args[0] = String.valueOf(updated); + return getMessageService().getMessage("msg.num.updated.users", args); + } + + private String getNumDisabledUsersMsg(int disabled) { + String[] args = new String[1]; + args[0] = String.valueOf(disabled); + return getMessageService().getMessage("msg.num.disabled.users", args); + } + + private class LdapSyncThread implements Runnable { + private String sessionId; + + public LdapSyncThread(String sessionId) { + this.sessionId = sessionId; + } + + public void run() { + log.info("=== Beginning LDAP user sync ==="); + long start = System.currentTimeMillis(); + BulkUpdateResultDTO dto = getLdapService().bulkUpdate(); + long end = System.currentTimeMillis(); + log.info("=== Finished LDAP user sync ==="); + log.info("Bulk update took " + (end-start)/1000 + " seconds."); + SessionManager.getSession(sessionId).setAttribute(ILdapService.SYNC_RESULTS, dto); + } + } } Index: lams_admin/web/ldap.jsp =================================================================== diff -u -rfec1d7a65d1a4d66c7ddccef5d0b79ff20c0da61 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_admin/web/ldap.jsp (.../ldap.jsp) (revision fec1d7a65d1a4d66c7ddccef5d0b79ff20c0da61) +++ lams_admin/web/ldap.jsp (.../ldap.jsp) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -1,5 +1,16 @@ <%@ include file="/taglibs.jsp"%> +<%@ page import="org.lamsfoundation.lams.web.session.SessionManager" %> +<%@ page import="org.lamsfoundation.lams.usermanagement.service.ILdapService" %> + +

:

@@ -19,15 +30,44 @@

- -

-
-

+ +

 

+ + +

+ +
+ + +

+ +
+ Index: lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java =================================================================== diff -u -r109725d30c92dd25ac9cec693a233d6592cfe0e6 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java (.../LDAPAuthenticator.java) (revision 109725d30c92dd25ac9cec693a233d6592cfe0e6) +++ lams_central/src/java/org/lamsfoundation/lams/security/LDAPAuthenticator.java (.../LDAPAuthenticator.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -92,9 +92,7 @@ env.setProperty(Context.SECURITY_AUTHENTICATION, Configuration.get(ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION)); String principalDNPrefix = Configuration.get(ConfigurationKeys.LDAP_PRINCIPAL_DN_PREFIX); - String principalDNSuffix = Configuration.get(ConfigurationKeys.LDAP_PRINCIPAL_DN_SUFFIX); - String userDN = principalDNPrefix + username + principalDNSuffix; - env.setProperty(Context.SECURITY_PRINCIPAL, userDN); + String[] principalDNSuffixes = Configuration.get(ConfigurationKeys.LDAP_PRINCIPAL_DN_SUFFIX).split(";"); env.setProperty(Context.PROVIDER_URL, Configuration.get(ConfigurationKeys.LDAP_PROVIDER_URL)); env.put(Context.SECURITY_CREDENTIALS, credential); @@ -114,61 +112,69 @@ log.debug("===> LDAP authenticator: " + env); InitialLdapContext ctx = null; - try { - ctx = new InitialLdapContext(env, null); - log.debug("===> LDAP context created: "+ctx); - Attributes attrs = ctx.getAttributes(userDN); - setAttrs(attrs); - - if (log.isDebugEnabled()) { - NamingEnumeration enumAttrs = attrs.getAll(); - while (enumAttrs.hasMoreElements()) { - log.debug(enumAttrs.next()); - } + + for (String principalDNSuffix : principalDNSuffixes) { + if (!principalDNSuffix.startsWith(",")) { + principalDNSuffix = "," + principalDNSuffix; } + String userDN = principalDNPrefix + username + principalDNSuffix; + env.setProperty(Context.SECURITY_PRINCIPAL, userDN); + try { + ctx = new InitialLdapContext(env, null); + log.debug("===> LDAP context created using DN: "+userDN); + Attributes attrs = ctx.getAttributes(userDN); + setAttrs(attrs); + + if (log.isDebugEnabled()) { + NamingEnumeration enumAttrs = attrs.getAll(); + while (enumAttrs.hasMoreElements()) { + log.debug(enumAttrs.next()); + } + } - // check user is disabled in ldap - if (getLdapService().getDisabledBoolean(attrs)) { - log.debug("===> User is disabled in LDAP."); - User user = getService().getUserByLogin(username); - if (user != null) { - getService().disableUser(user.getUserId()); + // check user is disabled in ldap + if (getLdapService().getDisabledBoolean(attrs)) { + log.debug("===> User is disabled in LDAP."); + User user = getService().getUserByLogin(username); + if (user != null) { + getService().disableUser(user.getUserId()); + } + return false; } - return false; - } - if (Configuration.getAsBoolean(ConfigurationKeys.LDAP_UPDATE_ON_LOGIN)) { - User user = getService().getUserByLogin(username); - if (user != null) { - // update user's attributes and org membership - getLdapService().updateLDAPUser(user, attrs); - getLdapService().addLDAPUser(attrs, user.getUserId()); + if (Configuration.getAsBoolean(ConfigurationKeys.LDAP_UPDATE_ON_LOGIN)) { + User user = getService().getUserByLogin(username); + if (user != null) { + // update user's attributes and org membership + getLdapService().updateLDAPUser(user, attrs); + getLdapService().addLDAPUser(attrs, user.getUserId()); + } } - } - return true; - } catch (AuthenticationNotSupportedException e) { - log.error("===> Authentication mechanism not supported. Check your " - +ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION+" parameter: " - +Configuration.get(ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION)); - } catch (AuthenticationException e) { - log.info("===> Incorrect username ("+userDN+") or password ("+credential+"): "+e.getMessage()); - } catch (Exception e) { - log.error("===> LDAP exception: " + e, e); - } finally { + return true; + } catch (AuthenticationNotSupportedException e) { + log.error("===> Authentication mechanism not supported. Check your " + +ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION+" parameter: " + +Configuration.get(ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION)); + } catch (AuthenticationException e) { + log.info("===> Incorrect username ("+userDN+") or password ("+credential+"): "+e.getMessage()); + } catch (Exception e) { + log.error("===> LDAP exception: " + e, e); + } 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 - // ); + 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); + if (ctx != null) + ctx.close(); + } catch (Exception e) { + log.error("===> gettting problem when closing context. Excetion: "+e); + } } } Index: lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java =================================================================== diff -u -r2cdc9593bafb191bcc07439fe37d41d3cd718651 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (.../UniversalLoginModule.java) (revision 2cdc9593bafb191bcc07439fe37d41d3cd718651) +++ lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (.../UniversalLoginModule.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -150,9 +150,16 @@ // 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)) { - inputPassword = HashUtil.sha1(inputPassword); + // 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); } - isValid = authenticator.authenticate(username,inputPassword); } else if (AuthenticationMethodType.WEB_AUTH.equals(type)) { WebAuthAuthenticator authenticator = new WebAuthAuthenticator(); isValid = authenticator.authenticate(username,inputPassword); Index: lams_common/db/sql/insert_lams_unix_config_data.sql =================================================================== diff -u -r109725d30c92dd25ac9cec693a233d6592cfe0e6 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/db/sql/insert_lams_unix_config_data.sql (.../insert_lams_unix_config_data.sql) (revision 109725d30c92dd25ac9cec693a233d6592cfe0e6) +++ lams_common/db/sql/insert_lams_unix_config_data.sql (.../insert_lams_unix_config_data.sql) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -32,7 +32,7 @@ insert into lams_configuration (config_key, config_value) values ('AllowDirectLessonLaunch','false'); insert into lams_configuration (config_key, config_value) values ('LAMS_Community_enable','false'); insert into lams_configuration (config_key, config_value) values ('AllowLiveEdit','true'); -insert into lams_configuration (config_key, config_value) values ('LDAPProvisioningEnabled','true'); +insert into lams_configuration (config_key, config_value) values ('LDAPProvisioningEnabled','false'); insert into lams_configuration (config_key, config_value) values ('LDAPProviderURL','ldap://192.168.111.15'); insert into lams_configuration (config_key, config_value) values ('LDAPSecurityAuthentication','simple'); insert into lams_configuration (config_key, config_value) values ('LDAPPrincipalDNPrefix','cn='); @@ -67,4 +67,5 @@ insert into lams_configuration (config_key, config_value) values ('LDAPUpdateOnLogin', 'true'); insert into lams_configuration (config_key, config_value) values ('LDAPOrgField', 'code'); insert into lams_configuration (config_key, config_value) values ('LDAPOnlyOneOrg', 'true'); -insert into lams_configuration (config_key, config_value) values ('LDAPEncryptPasswordFromBrowser', 'false'); \ No newline at end of file +insert into lams_configuration (config_key, config_value) values ('LDAPEncryptPasswordFromBrowser', 'true'); +insert into lams_configuration (config_key, config_value) values ('LDAPSearchResultsPageSize', '100'); \ No newline at end of file Index: lams_common/db/sql/insert_lams_windows_config_data.sql =================================================================== diff -u -r109725d30c92dd25ac9cec693a233d6592cfe0e6 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/db/sql/insert_lams_windows_config_data.sql (.../insert_lams_windows_config_data.sql) (revision 109725d30c92dd25ac9cec693a233d6592cfe0e6) +++ lams_common/db/sql/insert_lams_windows_config_data.sql (.../insert_lams_windows_config_data.sql) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -1,6 +1,6 @@ insert into lams_configuration (config_key, config_value) values ('ServerURL','http://localhost:8080/lams/'); insert into lams_configuration (config_key, config_value) values ('ServerURLContextPath','lams/'); -insert into lams_configuration (config_key, config_value) values ('Version','2.0.3'); +insert into lams_configuration (config_key, config_value) values ('Version','2.0.4'); insert into lams_configuration (config_key, config_value) values ('TempDir','C:/lams/temp'); insert into lams_configuration (config_key, config_value) values ('DumpDir','C:/lams/dump'); insert into lams_configuration (config_key, config_value) values ('EARDir','C:/jboss-4.0.2/server/default/deploy/lams.ear'); @@ -15,10 +15,10 @@ insert into lams_configuration (config_key, config_value) values ('UseCacheDebugListener','false'); insert into lams_configuration (config_key, config_value) values ('CleanupPreviewOlderThanDays','7'); insert into lams_configuration (config_key, config_value) values ('AuthoringActivitiesColour', 'true'); -insert into lams_configuration (config_key, config_value) values ('AuthoringClientVersion','2.0.3.@datetimestamp@'); -insert into lams_configuration (config_key, config_value) values ('MonitorClientVersion','2.0.3.@datetimestamp@'); -insert into lams_configuration (config_key, config_value) values ('LearnerClientVersion','2.0.3.@datetimestamp@'); -insert into lams_configuration (config_key, config_value) values ('ServerVersionNumber','2.0.3.@datetimestamp@'); +insert into lams_configuration (config_key, config_value) values ('AuthoringClientVersion','2.0.4.@datetimestamp@'); +insert into lams_configuration (config_key, config_value) values ('MonitorClientVersion','2.0.4.@datetimestamp@'); +insert into lams_configuration (config_key, config_value) values ('LearnerClientVersion','2.0.4.@datetimestamp@'); +insert into lams_configuration (config_key, config_value) values ('ServerVersionNumber','2.0.4.@datetimestamp@'); insert into lams_configuration (config_key, config_value) values ('ServerLanguage','en_AU'); insert into lams_configuration (config_key, config_value) values ('ServerPageDirection','LTR'); insert into lams_configuration (config_key, config_value) values ('DictionaryDateCreated','2007-05-24'); @@ -32,7 +32,7 @@ insert into lams_configuration (config_key, config_value) values ('AllowDirectLessonLaunch','false'); insert into lams_configuration (config_key, config_value) values ('LAMS_Community_enable','false'); insert into lams_configuration (config_key, config_value) values ('AllowLiveEdit','true'); -insert into lams_configuration (config_key, config_value) values ('LDAPProvisioningEnabled','true'); +insert into lams_configuration (config_key, config_value) values ('LDAPProvisioningEnabled','false'); insert into lams_configuration (config_key, config_value) values ('LDAPProviderURL','ldap://192.168.111.15'); insert into lams_configuration (config_key, config_value) values ('LDAPSecurityAuthentication','simple'); insert into lams_configuration (config_key, config_value) values ('LDAPPrincipalDNPrefix','cn='); @@ -67,4 +67,5 @@ insert into lams_configuration (config_key, config_value) values ('LDAPUpdateOnLogin', 'true'); insert into lams_configuration (config_key, config_value) values ('LDAPOrgField', 'code'); insert into lams_configuration (config_key, config_value) values ('LDAPOnlyOneOrg', 'true'); -insert into lams_configuration (config_key, config_value) values ('LDAPEncryptPasswordFromBrowser', 'false'); \ No newline at end of file +insert into lams_configuration (config_key, config_value) values ('LDAPEncryptPasswordFromBrowser', 'false'); +insert into lams_configuration (config_key, config_value) values ('LDAPSearchResultsPageSize', '100'); \ No newline at end of file Index: lams_common/db/sql/updatescripts/alter_205_ldap.sql =================================================================== diff -u -r109725d30c92dd25ac9cec693a233d6592cfe0e6 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/db/sql/updatescripts/alter_205_ldap.sql (.../alter_205_ldap.sql) (revision 109725d30c92dd25ac9cec693a233d6592cfe0e6) +++ lams_common/db/sql/updatescripts/alter_205_ldap.sql (.../alter_205_ldap.sql) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -35,4 +35,5 @@ insert into lams_configuration (config_key, config_value) values ('LDAPUpdateOnLogin', 'true'); insert into lams_configuration (config_key, config_value) values ('LDAPOrgField', 'code'); insert into lams_configuration (config_key, config_value) values ('LDAPOnlyOneOrg', 'true'); -insert into lams_configuration (config_key, config_value) values ('LDAPEncryptPasswordFromBrowser', 'false'); \ No newline at end of file +insert into lams_configuration (config_key, config_value) values ('LDAPEncryptPasswordFromBrowser', 'false'); +insert into lams_configuration (config_key, config_value) values ('LDAPSearchResultsPageSize', '100'); \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/dto/BulkUpdateResultDTO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/dto/BulkUpdateResultDTO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/dto/BulkUpdateResultDTO.java (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -0,0 +1,69 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.usermanagement.dto; + +import java.util.List; + +/** + * @author jliew + * + */ +public class BulkUpdateResultDTO { + + private int numSearchResults; + private int numUsersCreated; + private int numUsersUpdated; + private int numUsersDisabled; + private List messages; + + public BulkUpdateResultDTO(int results, int created, + int updated, int disabled, List messages) { + this.numSearchResults = results; + this.numUsersCreated = created; + this.numUsersUpdated = updated; + this.numUsersDisabled = disabled; + this.messages = messages; + } + + public int getNumSearchResults() { + return numSearchResults; + } + + public int getNumUsersCreated() { + return numUsersCreated; + } + + public int getNumUsersUpdated() { + return numUsersUpdated; + } + + public int getNumUsersDisabled() { + return numUsersDisabled; + } + + public List getMessages() { + return messages; + } +} + \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/ldapContext.xml =================================================================== diff -u -r43dfd9f833c87c58f4dc4a61f386b6c2cf961720 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/ldapContext.xml (.../ldapContext.xml) (revision 43dfd9f833c87c58f4dc4a61f386b6c2cf961720) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/ldapContext.xml (.../ldapContext.xml) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -15,9 +15,9 @@ true - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/ILdapService.java =================================================================== diff -u -r109725d30c92dd25ac9cec693a233d6592cfe0e6 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/ILdapService.java (.../ILdapService.java) (revision 109725d30c92dd25ac9cec693a233d6592cfe0e6) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/ILdapService.java (.../ILdapService.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -23,17 +23,19 @@ /* $Id$ */ package org.lamsfoundation.lams.usermanagement.service; -import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.usermanagement.dto.BulkUpdateResultDTO; /** * @author jliew * */ public interface ILdapService { + public static final String SYNC_RESULTS = "syncResults"; + /** * Updates a LAMS user's profile with LDAP attributes. * @param user @@ -75,7 +77,7 @@ /** * Bulk updates LAMS with LDAP users. - * @return number of LDAP users received. + * @return stats on result of bulk update. */ - public int updateLAMSFromLdap(); + public BulkUpdateResultDTO bulkUpdate(); } Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/IUserManagementService.java =================================================================== diff -u -r9b9a8c320a611027445fe94db5cd458c2968700d -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/IUserManagementService.java (.../IUserManagementService.java) (revision 9b9a8c320a611027445fe94db5cd458c2968700d) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/IUserManagementService.java (.../IUserManagementService.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -385,4 +385,8 @@ public void auditPasswordChanged(User user, String moduleName); public void auditUserCreated(User user, String moduleName); + + public Integer getCountUsers(); + + public Integer getCountUsers(Integer authenticationMethodId); } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/LdapService.java =================================================================== diff -u -r4d72f55fbac086d72edbf3b2975fc0bb2b464213 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/LdapService.java (.../LdapService.java) (revision 4d72f55fbac086d72edbf3b2975fc0bb2b464213) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/LdapService.java (.../LdapService.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -37,13 +37,17 @@ import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import javax.naming.ldap.Control; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; +import javax.naming.ldap.PagedResultsControl; +import javax.naming.ldap.PagedResultsResponseControl; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.integration.security.RandomPasswordGenerator; import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.OrganisationState; @@ -52,8 +56,10 @@ import org.lamsfoundation.lams.usermanagement.SupportedLocale; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.UserOrganisation; +import org.lamsfoundation.lams.usermanagement.dto.BulkUpdateResultDTO; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; +import org.lamsfoundation.lams.util.HashUtil; import org.lamsfoundation.lams.util.LanguageUtil; /** @@ -64,6 +70,9 @@ private Logger log = Logger.getLogger(LdapService.class); private IUserManagementService service; + private static final int BULK_UPDATE_CREATED = 0; + private static final int BULK_UPDATE_UPDATED = 1; + private static final int BULK_UPDATE_DISABLED = 2; public IUserManagementService getService() { return service; @@ -117,12 +126,26 @@ if (map.get("login")!=null && map.get("login").trim().length()>0) { if (log.isDebugEnabled()) { log.debug("===> using LDAP attributes: " - +map.get("login")+","+map.get("fname")+","+map.get("lname")+"," - +map.get("email")+","+map.get("phone")+","+map.get("fax")+"," - +map.get("mobile")); + +map.get("login")+"," + +map.get("fname")+"," + +map.get("lname")+"," + +map.get("email")+"," + +map.get("address1")+"," + +map.get("address2")+"," + +map.get("address3")+"," + +map.get("city")+"," + +map.get("state")+"," + +map.get("postcode")+"," + +map.get("country")+"," + +map.get("dayphone")+"," + +map.get("eveningphone")+"," + +map.get("fax")+"," + +map.get("mobile")+"," + +map.get("locale") + ); } user.setLogin(map.get("login")); - user.setPassword("dummy"); // password column is not-null + user.setPassword(HashUtil.sha1(RandomPasswordGenerator.nextPassword(10))); user.setFirstName(map.get("fname")); user.setLastName(map.get("lname")); user.setEmail(map.get("email")); @@ -181,6 +204,54 @@ } catch (Exception e) { log.error("===> Exception occurred while getting LDAP user attributes: ", e); } + + // field validation; trim values before they get to database + if (map.get("login") != null && map.get("login").trim().length() > 255) { + map.put("login", map.get("login").substring(0, 255)); + } + if (map.get("fname") != null && map.get("fname").trim().length() > 128) { + map.put("fname", map.get("fname").substring(0, 128)); + } + if (map.get("lname") != null && map.get("lname").trim().length() > 128) { + map.put("lname", map.get("lname").substring(0, 128)); + } + if (map.get("email") != null && map.get("email").trim().length() > 128) { + map.put("email", map.get("email").substring(0, 128)); + } + if (map.get("address1") != null && map.get("address1").trim().length() > 64) { + map.put("address1", map.get("address1").substring(0, 64)); + } + if (map.get("address2") != null && map.get("address2").trim().length() > 64) { + map.put("address2", map.get("address2").substring(0, 64)); + } + if (map.get("address3") != null && map.get("address3").trim().length() > 64) { + map.put("address3", map.get("address3").substring(0, 64)); + } + if (map.get("city") != null && map.get("city").trim().length() > 64) { + map.put("city", map.get("city").substring(0, 64)); + } + if (map.get("state") != null && map.get("state").trim().length() > 64) { + map.put("state", map.get("state").substring(0, 64)); + } + if (map.get("postcode") != null && map.get("postcode").trim().length() > 10) { + map.put("postcode", map.get("postcode").substring(0, 10)); + } + if (map.get("country") != null && map.get("country").trim().length() > 64) { + map.put("country", map.get("country").substring(0, 64)); + } + if (map.get("dayphone") != null && map.get("dayphone").trim().length() > 64) { + map.put("dayphone", map.get("dayphone").substring(0, 64)); + } + if (map.get("eveningphone") != null && map.get("eveningphone").trim().length() > 64) { + map.put("eveningphone", map.get("eveningphone").substring(0, 64)); + } + if (map.get("fax") != null && map.get("fax").trim().length() > 64) { + map.put("fax", map.get("fax").substring(0, 64)); + } + if (map.get("mobile") != null && map.get("mobile").trim().length() > 64) { + map.put("mobile", map.get("mobile").substring(0, 64)); + } + return map; } @@ -357,11 +428,13 @@ return null; } - public int updateLAMSFromLdap() { + public BulkUpdateResultDTO bulkUpdate() { // setup ldap context Properties env = new Properties(); env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.setProperty(Context.SECURITY_AUTHENTICATION, Configuration.get(ConfigurationKeys.LDAP_SECURITY_AUTHENTICATION)); + // make java ldap provider return 10 results at a time instead of default 1 + env.setProperty(Context.BATCHSIZE, "10"); env.setProperty(Context.PROVIDER_URL, Configuration.get(ConfigurationKeys.LDAP_PROVIDER_URL)); String securityProtocol = Configuration.get(ConfigurationKeys.LDAP_SECURITY_PROTOCOL); if (StringUtils.equals("ssl", securityProtocol)) { @@ -372,71 +445,145 @@ System.setProperty("javax.net.ssl.trustStorePassword", Configuration.get(ConfigurationKeys.LDAP_TRUSTSTORE_PASSWORD)); } - // get base dn - String baseDN = Configuration.get(ConfigurationKeys.LDAP_PRINCIPAL_DN_SUFFIX); - if (baseDN.startsWith(",")) { - baseDN = baseDN.substring(1); - } + // get base DN/s to search on + String[] baseDNs = Configuration.get(ConfigurationKeys.LDAP_PRINCIPAL_DN_SUFFIX).split(";"); // get search filter String filter = Configuration.get(ConfigurationKeys.LDAP_PRINCIPAL_DN_PREFIX); filter = "(" + filter + (filter.endsWith("=") ? "" : "=") + "*)"; - int numResults = 0; + // get page size + int pageSize = 100; try { - DirContext ctx = new InitialDirContext(env); - - // set search to subtree of base dn - SearchControls ctrl = new SearchControls(); - ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); + pageSize = new Integer(Configuration.get(ConfigurationKeys.LDAP_SEARCH_RESULTS_PAGE_SIZE)).intValue(); + } catch (Exception e) { + log.error("Couldn't read " + ConfigurationKeys.LDAP_SEARCH_RESULTS_PAGE_SIZE + ", using default page size of 100."); + } + + int totalResults = 0; + int createdUsers = 0; + int updatedUsers = 0; + int disabledUsers = 0; + List messages = new ArrayList(); + + for (String baseDN : baseDNs) { + int contextResults = 0; + if (baseDN.startsWith(",")) { + baseDN = baseDN.substring(1); + } + try { + // open LDAP connection + LdapContext ctx = null; + try { + ctx = new InitialLdapContext(env, null); + // ask ldap server to return results in pages of PAGE_SIZE, if supported + ctx.setRequestControls(new Control[] { + new PagedResultsControl(pageSize, Control.NONCRITICAL) }); + } catch (Exception e) { + messages.add("Error creating control."); + log.error(e, e); + } + + // perform ldap search, in batches + byte[] cookie = null; + do { + // set search to subtree of base dn + SearchControls ctrl = new SearchControls(); + ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); - // do the search for all ldap users - NamingEnumeration results = ctx.search(baseDN, filter, ctrl); - while (results.hasMore()) { - SearchResult result = results.next(); - Attributes attrs = result.getAttributes(); + // do the search for all ldap users + NamingEnumeration results = ctx.search(baseDN, filter, ctrl); + while (results.hasMore()) { + SearchResult result = results.next(); + Attributes attrs = result.getAttributes(); - // add or update this user to LAMS - boolean disabled = getDisabledBoolean(attrs); - String login = getSingleAttributeString(attrs.get(Configuration.get(ConfigurationKeys.LDAP_LOGIN_ATTR))); - if (login != null && login.trim().length() > 0) { - User user = getService().getUserByLogin(login); - if (!disabled) { - if (user == null) { - log.info("Creating new user for LDAP username: " + login); - if (createLDAPUser(attrs)) { - user = getService().getUserByLogin(login); - } else { - log.error("Couldn't create new user for LDAP username: "+login); - } - } else { - updateLDAPUser(user, attrs); - } - if (!addLDAPUser(attrs, user.getUserId())) { - log.error("Couldn't add LDAP user: "+login+" to organisation."); - } - } else { - // remove user from groups and set disabled flag - if (user != null) { - getService().disableUser(user.getUserId()); - } - } - } else { - log.error("Couldn't find login attribute for user using attribute name: " - + Configuration.get(ConfigurationKeys.LDAP_LOGIN_ATTR) + ". Dumping attributes..."); - NamingEnumeration enumAttrs = attrs.getAll(); - while (enumAttrs.hasMoreElements()) { - log.error(enumAttrs.next()); - } - } + // add or update this user to LAMS + boolean disabled = getDisabledBoolean(attrs); + String login = getSingleAttributeString(attrs.get(Configuration.get(ConfigurationKeys.LDAP_LOGIN_ATTR))); + if (login != null && login.trim().length() > 0) { + int code = bulkUpdateLDAPUser(login, attrs, disabled); + switch (code) { + case BULK_UPDATE_CREATED: createdUsers++; break; + case BULK_UPDATE_UPDATED: updatedUsers++; break; + case BULK_UPDATE_DISABLED: disabledUsers++; break; + } + } else { + log.error("Couldn't find login attribute for user using attribute name: " + + Configuration.get(ConfigurationKeys.LDAP_LOGIN_ATTR) + ". Dumping attributes..."); + NamingEnumeration enumAttrs = attrs.getAll(); + while (enumAttrs.hasMoreElements()) { + log.error(enumAttrs.next()); + } + } - numResults++; - } - log.info("Ldap returned " + numResults + " users."); - } catch (Exception e) { - log.error(e, e); + contextResults++; + } + + cookie = getPagedResponseCookie(ctx.getResponseControls()); + + // set response cookie to continue paged result + ctx.setRequestControls(new Control[] { + new PagedResultsControl(pageSize, cookie, Control.NONCRITICAL) } + ); + } while (cookie != null); + log.info("Ldap context " + baseDN + " returned " + contextResults + " users."); + ctx.close(); + } catch (Exception e) { + messages.add("Error while processing " + baseDN + ": " + e.getMessage()); + log.error(e, e); + } + totalResults += contextResults; } - return numResults; + BulkUpdateResultDTO dto = new BulkUpdateResultDTO(totalResults, createdUsers, updatedUsers, disabledUsers, messages); + + log.info("Ldap returned " + totalResults + " users."); + log.info(createdUsers + " were created, " + updatedUsers + " were updated/existed, and " + disabledUsers + " were disabled."); + + return dto; } + + // create, update, or disable this user + private int bulkUpdateLDAPUser(String login, Attributes attrs, boolean disabled) { + int returnCode = -1; + User user = getService().getUserByLogin(login); + if (!disabled) { + if (user == null) { + log.info("Creating new user for LDAP username: " + login); + if (createLDAPUser(attrs)) { + user = getService().getUserByLogin(login); + returnCode = BULK_UPDATE_CREATED; + } else { + log.error("Couldn't create new user for LDAP username: "+login); + } + } else { + updateLDAPUser(user, attrs); + returnCode = BULK_UPDATE_UPDATED; + } + if (!addLDAPUser(attrs, user.getUserId())) { + log.error("Couldn't add LDAP user: "+login+" to organisation."); + } + } else { + // remove user from groups and set disabled flag + if (user != null) { + getService().disableUser(user.getUserId()); + returnCode = BULK_UPDATE_DISABLED; + } + } + return returnCode; + } + + // get paged result response cookie + private byte[] getPagedResponseCookie(Control[] controls) { + if (controls != null) { + for (Control control : controls) { + if (control instanceof PagedResultsResponseControl) { + PagedResultsResponseControl prrc = (PagedResultsResponseControl)control; + return prrc.getCookie(); + } + } + } + return null; + } + } Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/UserManagementService.java =================================================================== diff -u -r9b9a8c320a611027445fe94db5cd458c2968700d -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/UserManagementService.java (.../UserManagementService.java) (revision 9b9a8c320a611027445fe94db5cd458c2968700d) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/UserManagementService.java (.../UserManagementService.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -39,6 +39,7 @@ import org.lamsfoundation.lams.dao.IBaseDAO; import org.lamsfoundation.lams.learningdesign.dao.IGroupDAO; import org.lamsfoundation.lams.themes.CSSThemeVisualElement; +import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.OrganisationType; import org.lamsfoundation.lams.usermanagement.Role; @@ -529,7 +530,7 @@ } return organisation; - } + } @SuppressWarnings("unchecked") public List getUserManageBeans(Integer orgId) { @@ -909,5 +910,25 @@ String message = messageService.getMessage("audit.user.create", args); getAuditService().log(moduleName, message); } + + public Integer getCountUsers() { + String query = "select count(u) from User u"; + return getFindIntegerResult(query); + } + + public Integer getCountUsers(Integer authenticationMethodId) { + String query = "select count(u) from User u " + + "where u.authenticationMethod.authenticationMethodId=" + + authenticationMethodId; + return getFindIntegerResult(query); + } + + private Integer getFindIntegerResult(String query) { + List list = baseDAO.find(query); + if (list != null && list.size() > 0) { + return (Integer)list.get(0); + } + return null; + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/util/ConfigurationKeys.java =================================================================== diff -u -r109725d30c92dd25ac9cec693a233d6592cfe0e6 -r053b3694d8631a983eb8e95e3f0374a22540b43a --- lams_common/src/java/org/lamsfoundation/lams/util/ConfigurationKeys.java (.../ConfigurationKeys.java) (revision 109725d30c92dd25ac9cec693a233d6592cfe0e6) +++ lams_common/src/java/org/lamsfoundation/lams/util/ConfigurationKeys.java (.../ConfigurationKeys.java) (revision 053b3694d8631a983eb8e95e3f0374a22540b43a) @@ -206,4 +206,6 @@ public static String LDAP_ONLY_ONE_ORG = "LDAPOnlyOneOrg"; public static String LDAP_ENCRYPT_PASSWORD_FROM_BROWSER = "LDAPEncryptPasswordFromBrowser"; + + public static String LDAP_SEARCH_RESULTS_PAGE_SIZE = "LDAPSearchResultsPageSize"; } \ No newline at end of file