From ac78a6a8b86a29e8a5bb6dfb2183b3a9c1289672 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Fri, 11 Nov 2016 16:12:38 +0100 Subject: [PATCH] OO-2354: decouple attribute sued to search a user on the LDAP server from the user identifier and make it configurable, add embbeded LDAP server for unit tests --- pom.xml | 6 + .../UserAuthenticationsEditorController.java | 33 ++- .../webdav/manager/WebDAVAuthManager.java | 56 ++-- .../java/org/olat/ldap/LDAPLoginManager.java | 4 +- .../java/org/olat/ldap/LDAPLoginModule.java | 2 + .../org/olat/ldap/LDAPSyncConfiguration.java | 9 + .../org/olat/ldap/_spring/ldapContext.xml | 7 +- .../java/org/olat/ldap/manager/LDAPDAO.java | 30 +- .../ldap/manager/LDAPLoginManagerImpl.java | 108 +++++-- .../org/olat/ldap/ui/LDAPAdminController.java | 2 + .../ldap/ui/LDAPAuthenticationController.java | 66 +---- .../olat/login/auth/AuthenticationSPI.java | 8 + .../org/olat/login/auth/OLATAuthManager.java | 43 ++- .../olat/user/ChangePasswordController.java | 10 +- .../olat/user/WebDAVPasswordController.java | 12 +- .../resources/serviceconfig/olat.properties | 4 +- .../java/org/olat/ldap/LDAPLoginTest.java | 265 +++++++++--------- .../org/olat/ldap/junittestdata/olattest.ldif | 8 +- src/test/profile/mysql/olat.local.properties | 9 + .../profile/postgresql/olat.local.properties | 9 + 20 files changed, 404 insertions(+), 287 deletions(-) diff --git a/pom.xml b/pom.xml index 5bbe03d236c..c043830fe24 100644 --- a/pom.xml +++ b/pom.xml @@ -2415,6 +2415,12 @@ <version>1.2.9.Final</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.zapodot</groupId> + <artifactId>embedded-ldap-junit</artifactId> + <version>0.5.2</version> + <scope>test</scope> + </dependency> <!-- Start test dependencies for Arquillian and Selenium --> <dependency> diff --git a/src/main/java/org/olat/admin/user/UserAuthenticationsEditorController.java b/src/main/java/org/olat/admin/user/UserAuthenticationsEditorController.java index 7142bd76ff1..d6a9b46cc70 100644 --- a/src/main/java/org/olat/admin/user/UserAuthenticationsEditorController.java +++ b/src/main/java/org/olat/admin/user/UserAuthenticationsEditorController.java @@ -29,7 +29,7 @@ import java.util.ArrayList; import java.util.List; import org.olat.basesecurity.Authentication; -import org.olat.basesecurity.BaseSecurityManager; +import org.olat.basesecurity.BaseSecurity; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.table.DefaultColumnDescriptor; @@ -47,6 +47,7 @@ import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.Identity; import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; /** * Initial Date: Aug 27, 2004 @@ -58,6 +59,11 @@ public class UserAuthenticationsEditorController extends BasicController{ private AuthenticationsTableDataModel authTableModel; private DialogBoxController confirmationDialog; private Identity changeableIdentity; + + @Autowired + private UserManager userManager; + @Autowired + private BaseSecurity securityManager; /** * @param ureq @@ -76,7 +82,7 @@ public class UserAuthenticationsEditorController extends BasicController{ tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.auth.login", 1, null, ureq.getLocale())); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.auth.credential", 2, null, ureq.getLocale())); tableCtr.addColumnDescriptor(new StaticColumnDescriptor("delete", "table.header.action", translate("delete"))); - authTableModel = new AuthenticationsTableDataModel(BaseSecurityManager.getInstance().getAuthentications(changeableIdentity)); + authTableModel = new AuthenticationsTableDataModel(securityManager.getAuthentications(changeableIdentity)); tableCtr.setTableDataModel(authTableModel); listenTo(tableCtr); @@ -86,6 +92,7 @@ public class UserAuthenticationsEditorController extends BasicController{ /** * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.components.Component, org.olat.core.gui.control.Event) */ + @Override public void event(UserRequest ureq, Component source, Event event) { // no events to catch } @@ -94,21 +101,22 @@ public class UserAuthenticationsEditorController extends BasicController{ * Rebuild the authentications table data model */ public void rebuildAuthenticationsTableDataModel() { - authTableModel = new AuthenticationsTableDataModel(BaseSecurityManager.getInstance().getAuthentications(changeableIdentity)); + authTableModel = new AuthenticationsTableDataModel(securityManager.getAuthentications(changeableIdentity)); tableCtr.setTableDataModel(authTableModel); } /** * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event) */ + @Override public void event(UserRequest ureq, Controller source, Event event) { if (source == confirmationDialog) { if (DialogBoxUIFactory.isYesEvent(event)) { Authentication auth = (Authentication)confirmationDialog.getUserObject(); - BaseSecurityManager.getInstance().deleteAuthentication(auth); + securityManager.deleteAuthentication(auth); getWindowControl().setInfo(getTranslator().translate("authedit.delete.success", new String[] { auth.getProvider(), changeableIdentity.getName() })); - authTableModel.setObjects(BaseSecurityManager.getInstance().getAuthentications(changeableIdentity)); + authTableModel.setObjects(securityManager.getAuthentications(changeableIdentity)); tableCtr.modelChanged(); } } @@ -119,11 +127,10 @@ public class UserAuthenticationsEditorController extends BasicController{ if (actionid.equals("delete")) { int rowid = te.getRowId(); Authentication auth = authTableModel.getObject(rowid); - String fullname = UserManager.getInstance().getUserDisplayName(auth.getIdentity()); + String fullname = userManager.getUserDisplayName(changeableIdentity); String msg = translate("authedit.delete.confirm", new String[] { auth.getProvider(), fullname }); confirmationDialog = activateYesNoDialog(ureq, null, msg, confirmationDialog); confirmationDialog.setUserObject(auth); - return; } } } @@ -134,6 +141,7 @@ public class UserAuthenticationsEditorController extends BasicController{ * * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) */ + @Override protected void doDispose() { // DialogBoxController and TableController get disposed by BasicController } @@ -153,20 +161,21 @@ public class UserAuthenticationsEditorController extends BasicController{ /** * @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int) */ + @Override public final Object getValueAt(int row, int col) { Authentication auth = getObject(row); switch (col) { - case 0 : return auth.getProvider(); - case 1 : return auth.getAuthusername(); - case 2 : return auth.getCredential(); - default : - return "error"; + case 0: return auth.getProvider(); + case 1: return auth.getAuthusername(); + case 2: return auth.getCredential(); + default: return "error"; } } /** * @see org.olat.core.gui.components.table.TableDataModel#getColumnCount() */ + @Override public int getColumnCount() { return 3; } diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java index a96bba351fe..ec28f37f574 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java @@ -81,7 +81,7 @@ public class WebDAVAuthManager implements AuthenticationSPI { if(verity.equals(response)) { Identity identity = olatAuth.getIdentity(); return identity; - } else { + } else if(log.isDebug()) { // don't log as error, happens all the time with certain clients, e.g. Microsoft-WebDAV-MiniRedir log.debug("Verity::" + verity + " doesn't equals response::" + response); } @@ -125,9 +125,9 @@ public class WebDAVAuthManager implements AuthenticationSPI { if(webDAVModule.isEnabled() && webDAVModule.isDigestAuthenticationEnabled()) { Authentication digestAuth = securityManager.findAuthentication(identity, PROVIDER_HA1); if(digestAuth == null) { - String digestToken = identity.getName() + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + password; + String digestToken = login + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + password; Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); - securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_HA1, identity.getName(), digestToken, Encoder.Algorithm.md5_noSalt); + securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_HA1, login, digestToken, Encoder.Algorithm.md5_noSalt); } } } @@ -144,41 +144,45 @@ public class WebDAVAuthManager implements AuthenticationSPI { if (identity == null || identity.getKey() == null) throw new AssertException("cannot change password on a nonpersisted identity"); - - {//For Basic - Authentication auth = securityManager.findAuthentication(identity, PROVIDER_WEBDAV); - if (auth == null) { // create new authentication for provider OLAT - Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); - auth = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_WEBDAV, identity.getName(), newPwd, loginModule.getDefaultHashAlgorithm()); - log.audit(doer.getName() + " created new WebDAV authentication for identity: " + identity.getName()); - } else { - auth = securityManager.updateCredentials(auth, newPwd, loginModule.getDefaultHashAlgorithm()); - log.audit(doer.getName() + " set new WebDAV password for identity: " +identity.getName()); - } + //For Basic + Authentication auth = securityManager.findAuthentication(identity, PROVIDER_WEBDAV); + if (auth == null) { // create new authentication for provider OLAT + Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); + auth = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_WEBDAV, identity.getName(), newPwd, loginModule.getDefaultHashAlgorithm()); + log.audit(doer.getName() + " created new WebDAV authentication for identity: " + identity.getName()); + } else { + auth = securityManager.updateCredentials(auth, newPwd, loginModule.getDefaultHashAlgorithm()); + log.audit(doer.getName() + " set new WebDAV password for identity: " +identity.getName()); } - + //For Digest - changeDigestPassword(doer, identity, newPwd); + changeDigestPassword(doer, identity, identity.getName(), newPwd); return true; } - public boolean changeDigestPassword(Identity doer, Identity identity, String newPwd) { - if (doer==null) throw new AssertException("password changing identity cannot be undefined!"); - if (identity == null || identity.getKey() == null) + public boolean changeDigestPassword(Identity doer, Identity identity, String username, String newPwd) { + if (doer == null) { + throw new AssertException("password changing identity cannot be undefined!"); + } + if (identity == null || identity.getKey() == null) { throw new AssertException("cannot change password on a nonpersisted identity"); + } - //For Digest if(webDAVModule.isDigestAuthenticationEnabled()) { Authentication authHa1 = securityManager.findAuthentication(identity, PROVIDER_HA1); - String digestToken = identity.getName() + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + newPwd; + String digestToken = username + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + newPwd; + String md5DigestToken = Encoder.encrypt(digestToken, null, Encoder.Algorithm.md5_noSalt); + if (authHa1 == null) { // create new authentication for provider OLAT Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); - authHa1 = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_HA1, identity.getName(), digestToken, Encoder.Algorithm.md5_noSalt); - log.audit(doer.getName() + " created new WebDAV authenticatin for identity: " + identity.getName()); - } else { - authHa1 = securityManager.updateCredentials(authHa1, digestToken, Encoder.Algorithm.md5_noSalt); - log.audit(doer.getName() + " set new WebDAV password for identity: " +identity.getName()); + authHa1 = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_HA1, username, digestToken, Encoder.Algorithm.md5_noSalt); + log.audit(doer.getName() + " created new WebDAV (HA1) authenticatin for identity: " + identity.getName()); + } else if (username != null && (!username.equals(authHa1.getAuthusername()) || !md5DigestToken.equals(authHa1.getCredential()))) { + authHa1.setCredential(md5DigestToken); + authHa1.setAuthusername(username); + authHa1 = securityManager.updateAuthentication(authHa1); + log.audit(doer.getName() + " set new WebDAV (HA1) password for identity: " +identity.getName()); } } diff --git a/src/main/java/org/olat/ldap/LDAPLoginManager.java b/src/main/java/org/olat/ldap/LDAPLoginManager.java index fe378800d29..7ffac3daf83 100644 --- a/src/main/java/org/olat/ldap/LDAPLoginManager.java +++ b/src/main/java/org/olat/ldap/LDAPLoginManager.java @@ -38,6 +38,8 @@ public interface LDAPLoginManager { public LdapContext bindSystem(); public Attributes bindUser(String uid, String pwd, LDAPError errors); + + public Identity authenticate(String username, String pwd, LDAPError ldapError); public boolean changePassword(Identity identity, String pwd, LDAPError errors); @@ -47,7 +49,7 @@ public interface LDAPLoginManager { public List<Identity> getIdentitysDeletedInLdap(LdapContext ctx); - public Identity findIdentyByLdapAuthentication(String uid, LDAPError errors); + public Identity findIdentityByLdapAuthentication(Attributes attrs, LDAPError errors); public void syncUser(Map<String,String> olatPropertyMap, Identity identity); diff --git a/src/main/java/org/olat/ldap/LDAPLoginModule.java b/src/main/java/org/olat/ldap/LDAPLoginModule.java index ae19352d641..de36fb5a973 100644 --- a/src/main/java/org/olat/ldap/LDAPLoginModule.java +++ b/src/main/java/org/olat/ldap/LDAPLoginModule.java @@ -57,6 +57,8 @@ import org.springframework.stereotype.Service; public class LDAPLoginModule extends AbstractSpringModule { // Connection configuration + public static final long WARNING_LIMIT = 15 *1000 * 1000 * 1000; + @Value("${ldap.ldapUrl}") private String ldapUrl; @Value("${ldap.enable:false}") diff --git a/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java b/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java index ecddb9724cf..2b3d3bdce56 100644 --- a/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java +++ b/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java @@ -58,6 +58,7 @@ public class LDAPSyncConfiguration { private String ldapUserCreatedTimestampAttribute; private String ldapUserLastModifiedTimestampAttribute; private String ldapUserPasswordAttribute; + private String ldapUserLoginAttribute; private String[] userAttributes; @@ -372,6 +373,14 @@ public class LDAPSyncConfiguration { this.ldapUserPasswordAttribute = attribute; } + public String getLdapUserLoginAttribute() { + return ldapUserLoginAttribute; + } + + public void setLdapUserLoginAttribute(String ldapUserLoginAttribute) { + this.ldapUserLoginAttribute = ldapUserLoginAttribute; + } + public Map<String, String> getRequestAttributes() { return requestAttributes; } diff --git a/src/main/java/org/olat/ldap/_spring/ldapContext.xml b/src/main/java/org/olat/ldap/_spring/ldapContext.xml index 5b1ce8b8cc0..bac68561dc7 100644 --- a/src/main/java/org/olat/ldap/_spring/ldapContext.xml +++ b/src/main/java/org/olat/ldap/_spring/ldapContext.xml @@ -16,8 +16,9 @@ <property name="ldapUserCreatedTimestampAttribute" value="${ldap.ldapUserCreatedTimestampAttribute}"/> <property name="ldapUserLastModifiedTimestampAttribute" value="${ldap.ldapUserLastModifiedTimestampAttribute}"/> - <property name="ldapUserPasswordAttribute" value="${ldap.ldapUserPassordAttribute}"/> - + <property name="ldapUserPasswordAttribute" value="${ldap.ldapUserPassordAttribute}"/> + <property name="ldapUserLoginAttribute" value="${ldap.login.attribute}" /> + <property name="ldapUserFilter" value="${ldap.ldapUserFilter}"/> <property name="ldapGroupFilter" value="${ldap.ldapGroupFilter}"/> @@ -83,7 +84,7 @@ </property> <property name="learningResourceManagerRoleAttribute" value="${ldap.learningResourceManagerRoleAttribute}"/> <property name="learningResourceManagerRoleValue" value="${ldap.learningResourceManagerRoleValue}"/> - + <!-- Define which user attributes are mandatory and how they are mapped to OLAT user properties. Note that OLAT requires at least the user properties userID and email. Those must be listed diff --git a/src/main/java/org/olat/ldap/manager/LDAPDAO.java b/src/main/java/org/olat/ldap/manager/LDAPDAO.java index 999900a32b8..07429db9d25 100644 --- a/src/main/java/org/olat/ldap/manager/LDAPDAO.java +++ b/src/main/java/org/olat/ldap/manager/LDAPDAO.java @@ -173,14 +173,27 @@ public class LDAPDAO { } } - public String searchUserDN(String uid, DirContext ctx) { - if(ctx == null) - return null; - + public String searchUserForLogin(String login, DirContext ctx) { + if(ctx == null) return null; + + String ldapUserIDAttribute = syncConfiguration.getLdapUserLoginAttribute(); + String filter = buildSearchUserFilter(ldapUserIDAttribute, login); + return searchUserDN(login, filter, ctx); + } + + public String searchUserDNByUid(String uid, DirContext ctx) { + if(ctx == null) return null; + + String ldapUserIDAttribute = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); + String filter = buildSearchUserFilter(ldapUserIDAttribute, uid); + return searchUserDN(uid, filter, ctx); + } + + private String searchUserDN(String username, String filter, DirContext ctx) { + List<String> ldapBases = syncConfiguration.getLdapBases(); String[] serachAttr = { "dn" }; - String filter = buildSearchUserFilter(uid); SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes(serachAttr); @@ -197,22 +210,21 @@ public class LDAPDAO { break; } } catch (NamingException e) { - log.error("NamingException when trying to bind user with username::" + uid + " on ldapBase::" + ldapBase, e); + log.error("NamingException when trying to bind user with username::" + username + " on ldapBase::" + ldapBase, e); } } return userDN; } - public String buildSearchUserFilter(String uid) { - String ldapUserIDAttribute = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); + private String buildSearchUserFilter(String attribute, String uid) { String ldapUserFilter = syncConfiguration.getLdapUserFilter(); StringBuilder filter = new StringBuilder(); if (ldapUserFilter != null) { // merge preconfigured filter (e.g. object class, group filters) with username using AND rule filter.append("(&").append(ldapUserFilter); } - filter.append("(").append(ldapUserIDAttribute).append("=").append(uid).append(")"); + filter.append("(").append(attribute).append("=").append(uid).append(")"); if (ldapUserFilter != null) { filter.append(")"); } diff --git a/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java index 5d67410e46f..bcf84195b82 100644 --- a/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java +++ b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java @@ -52,6 +52,7 @@ import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.Constants; import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.SecurityGroup; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.taskexecutor.TaskExecutorManager; import org.olat.core.gui.control.Event; @@ -83,6 +84,7 @@ import org.olat.ldap.LDAPSyncConfiguration; import org.olat.ldap.model.LDAPGroup; import org.olat.ldap.model.LDAPUser; import org.olat.ldap.ui.LDAPAuthenticationController; +import org.olat.login.auth.OLATAuthManager; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -255,13 +257,13 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe * @throws NamingException */ @Override - public Attributes bindUser(String uid, String pwd, LDAPError errors) { + public Attributes bindUser(String login, String pwd, LDAPError errors) { // get user name, password and attributes String ldapUrl = ldapLoginModule.getLdapUrl(); String[] userAttr = syncConfiguration.getUserAttributes(); - if (uid == null || pwd == null) { - if (log.isDebug()) log.debug("Error when trying to bind user, missing username or password. Username::" + uid + " pwd::" + pwd); + if (login == null || pwd == null) { + if (log.isDebug()) log.debug("Error when trying to bind user, missing username or password. Username::" + login + " pwd::" + pwd); errors.insert("Username and password must be selected"); return null; } @@ -271,9 +273,9 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe errors.insert("LDAP connection error"); return null; } - String userDN = ldapDao.searchUserDN(uid, ctx); + String userDN = ldapDao.searchUserForLogin(login, ctx); if (userDN == null) { - log.info("Error when trying to bind user with username::" + uid + " - user not found on LDAP server" + log.info("Error when trying to bind user with username::" + login + " - user not found on LDAP server" + (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin() ? ", trying with OLAT login provider" : "")); errors.insert("Username or password incorrect"); return null; @@ -301,16 +303,57 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe userBind.close(); return attributes; } catch (AuthenticationException e) { - log.info("Error when trying to bind user with username::" + uid + " - invalid LDAP password"); + log.info("Error when trying to bind user with username::" + login + " - invalid LDAP password"); errors.insert("Username or password incorrect"); return null; } catch (NamingException e) { - log.error("NamingException when trying to get attributes after binding user with username::" + uid, e); + log.error("NamingException when trying to get attributes after binding user with username::" + login, e); errors.insert("Username or password incorrect"); return null; } } + @Override + public Identity authenticate(String username, String pwd, LDAPError ldapError) { + long start = System.nanoTime(); + //authenticate against LDAP server + Attributes attrs = bindUser(username, pwd, ldapError); + long takes = System.nanoTime() - start; + if(takes > LDAPLoginModule.WARNING_LIMIT) { + log.warn("LDAP Authentication takes (ms): " + (takes / 1000000)); + } + + if (ldapError.isEmpty() && attrs != null) { + Identity identity = findIdentityByLdapAuthentication(attrs, ldapError); + if (!ldapError.isEmpty()) { + return null; + } + if (identity == null) { + if(ldapLoginModule.isCreateUsersOnLogin()) { + // User authenticated but not yet existing - create as new OLAT user + createAndPersistUser(attrs); + identity = findIdentityByLdapAuthentication(attrs, ldapError); + } else { + ldapError.insert("login.notauthenticated"); + } + } else { + // User does already exist - just sync attributes + Map<String, String> olatProToSync = prepareUserPropertyForSync(attrs, identity); + if (olatProToSync != null) { + syncUser(olatProToSync, identity); + } + } + // Add or update an OLAT authentication token for this user if configured in the module + if (identity != null && ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { + // there is no WEBDAV token but an HA1, the HA1 is linked to the OLAT one. + CoreSpringFactory.getImpl(OLATAuthManager.class) + .synchronizeOlatPasswordAndUsername(identity, identity, username, pwd); + } + return identity; + } + return null; + } + /** * Change the password on the LDAP server. * @see org.olat.ldap.LDAPLoginManager#changePassword(org.olat.core.id.Identity, java.lang.String, org.olat.ldap.LDAPError) @@ -321,7 +364,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe String ldapUserPasswordAttribute = syncConfiguration.getLdapUserPasswordAttribute(); try { DirContext ctx = bindSystem(); - String dn = ldapDao.searchUserDN(uid, ctx); + String dn = ldapDao.searchUserDNByUid(uid, ctx); ModificationItem [] modificationItems = new ModificationItem [ 1 ]; @@ -588,7 +631,16 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe * modified) */ @Override - public Identity findIdentyByLdapAuthentication(String uid, LDAPError errors) { + public Identity findIdentityByLdapAuthentication(Attributes attrs, LDAPError errors) { + if(attrs == null) { + errors.insert("findIdentyByLdapAuthentication: attrs::null"); + return null; + } + + String uid = getAttributeValue(attrs.get(syncConfiguration + .getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER))); + String token = getAttributeValue(attrs.get(syncConfiguration.getLdapUserLoginAttribute())); + Identity identity = securityManager.findIdentityByNameCaseInsensitive(uid); if (identity == null) { return null; @@ -602,22 +654,22 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe Authentication ldapAuth = securityManager.findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP); if(ldapAuth == null) { //BUG Fixe: update the user and test if it has a ldap provider - securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, identity.getName(), null, null); + securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, token, null, null); + } else if(StringHelper.containsNonWhitespace(token) && !token.equals(ldapAuth.getAuthusername())) { + ldapAuth.setAuthusername(token); + ldapAuth = securityManager.updateAuthentication(ldapAuth); } return identity; - } - else { - if (ldapLoginModule.isConvertExistingLocalUsersToLDAPUsers()) { - // Add user to LDAP security group and add the ldap provider - securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, identity.getName(), null, null); - securityManager.addIdentityToSecurityGroup(identity, ldapGroup); - log.info("Found identity by LDAP username that was not yet in LDAP security group. Converted user::" + uid - + " to be an LDAP managed user"); - return identity; - } else { - errors.insert("findIdentyByLdapAuthentication: User with username::" + uid + " exist but not Managed by LDAP"); - return null; - } + } else if (ldapLoginModule.isConvertExistingLocalUsersToLDAPUsers()) { + // Add user to LDAP security group and add the ldap provider + securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, token, null, null); + securityManager.addIdentityToSecurityGroup(identity, ldapGroup); + log.info("Found identity by LDAP username that was not yet in LDAP security group. Converted user::" + uid + + " to be an LDAP managed user"); + return identity; + } else { + errors.insert("findIdentyByLdapAuthentication: User with username::" + uid + " exist but not Managed by LDAP"); + return null; } } } @@ -972,7 +1024,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe Attributes userAttrs = ldapUser.getAttributes(); String uidProp = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); user = getAttributeValue(userAttrs.get(uidProp)); - Identity identity = findIdentyByLdapAuthentication(user, errors); + Identity identity = findIdentityByLdapAuthentication(userAttrs, errors); if (identity != null) { Map<String, String> changedAttrMap = prepareUserPropertyForSync(userAttrs, identity); if (changedAttrMap != null) { @@ -1209,8 +1261,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe Identity identity = ldapUser == null ? null : ldapUser.getCachedIdentity(); if(identity == null) { String userFilter = syncConfiguration.getLdapUserFilter(); - String uidProp = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); - + String userDN = member; LDAPUserVisitor visitor = new LDAPUserVisitor(syncConfiguration); ldapDao.search(visitor, userDN, userFilter, syncConfiguration.getUserAttributes(), ctx); @@ -1219,8 +1270,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe if(ldapUserList.size() == 1) { ldapUser = ldapUserList.get(0); Attributes userAttrs = ldapUser.getAttributes(); - String user = getAttributeValue(userAttrs.get(uidProp)); - identity = findIdentyByLdapAuthentication(user, errors); + identity = findIdentityByLdapAuthentication(userAttrs, errors); if(identity != null) { dnToIdentityKeyMap.put(userDN, ldapUser); } @@ -1235,7 +1285,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe if (ctx == null) { log.error("could not bind to ldap", null); } - String userDN = ldapDao.searchUserDN(ident.getName(), ctx); + String userDN = ldapDao.searchUserDNByUid(ident.getName(), ctx); final List<Attributes> ldapUserList = new ArrayList<Attributes>(); // TODO: use userDN instead of filter to get users attribs diff --git a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java index 1237a8f1182..f7cd3f3986d 100644 --- a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java +++ b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java @@ -200,6 +200,8 @@ public class LDAPAdminController extends BasicController implements GenericEvent public Step execute(UserRequest uureq, WindowControl control, StepsRunContext runContext) { hasIdentitiesToDeleteAfterRun = ((Boolean) runContext.get("hasIdentitiesToDelete")).booleanValue(); if (hasIdentitiesToDeleteAfterRun) { + + @SuppressWarnings("unchecked") List<Identity> idToDelete = (List<Identity>) runContext.get("identitiesToDelete"); amountUsersToDelete = idToDelete.size(); // Delete all identities now and tell everybody that diff --git a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java index 2feb1f3b5ce..dccad37ca9f 100644 --- a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java +++ b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java @@ -21,15 +21,11 @@ package org.olat.ldap.ui; import java.util.List; import java.util.Locale; -import java.util.Map; - -import javax.naming.directory.Attributes; import org.olat.admin.user.delete.service.UserDeletionManager; import org.olat.basesecurity.AuthHelper; import org.olat.basesecurity.BaseSecurityModule; -import org.olat.core.CoreSpringFactory; -import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.commons.persistence.DB; import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -71,7 +67,6 @@ public class LDAPAuthenticationController extends AuthenticationController imple private static final OLog log = Tracing.createLoggerFor(LDAPAuthenticationController.class); public static final String PROVIDER_LDAP = "LDAP"; - public static final long WARNING_LIMIT = 15 *1000 * 1000 * 1000; private VelocityContainer loginComp; private Component pwLink; @@ -83,8 +78,8 @@ public class LDAPAuthenticationController extends AuthenticationController imple private CloseableModalController cmc; - private final OLATAuthManager olatAuthenticationSpi; - + @Autowired + private DB dbInstance; @Autowired private UserModule userModule; @Autowired @@ -92,14 +87,16 @@ public class LDAPAuthenticationController extends AuthenticationController imple @Autowired private LDAPLoginModule ldapLoginModule; @Autowired + private LDAPLoginManager ldapLoginManager; + @Autowired + private OLATAuthManager olatAuthenticationSpi; + @Autowired private RegistrationManager registrationManager; public LDAPAuthenticationController(UserRequest ureq, WindowControl control) { // use fallback translator to login and registration package super(ureq, control, Util.createPackageTranslator(LoginModule.class, ureq.getLocale(), Util.createPackageTranslator(RegistrationManager.class, ureq.getLocale()))); - olatAuthenticationSpi = CoreSpringFactory.getImpl(OLATAuthManager.class); - loginComp = createVelocityContainer("ldaplogin"); if(userModule.isAnyPasswordChangeAllowed() && ldapLoginModule.isPropagatePasswordChangedOnLdapServer()) { @@ -154,6 +151,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple } } + @Override protected void event(UserRequest ureq, Controller source, Event event) { LDAPError ldapError = new LDAPError(); @@ -169,7 +167,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple getLogger().audit("Login attempt on already blocked login for " + login + ". IP::" + ureq.getHttpReq().getRemoteAddr(), null); return; } - authenticatedIdentity = authenticate(login, pass, ldapError); + authenticatedIdentity = ldapLoginManager.authenticate(login, pass, ldapError); if(!ldapError.isEmpty()) { final String errStr = ldapError.get(); @@ -189,7 +187,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple try { //prevents database timeout - DBFactory.getInstance().commitAndCloseSession(); + dbInstance.commitAndCloseSession(); } catch (Exception e) { log.error("", e); } @@ -207,11 +205,10 @@ public class LDAPAuthenticationController extends AuthenticationController imple if (loginModule.registerFailedLoginAttempt(login)) { logAudit("Too many failed login attempts for " + login + ". Login blocked. IP::" + ureq.getHttpReq().getRemoteAddr(), null); showError("login.blocked", loginModule.getAttackPreventionTimeoutMin().toString()); - return; } else { showError("login.error", ldapError.get()); - return; } + return; } else { try { String language = authenticatedIdentity.getUser().getPreferences().getLanguage(); @@ -280,47 +277,6 @@ public class LDAPAuthenticationController extends AuthenticationController imple } } - public static Identity authenticate(String username, String pwd, LDAPError ldapError) { - final LDAPLoginModule ldapModule = CoreSpringFactory.getImpl(LDAPLoginModule.class); - final LDAPLoginManager ldapManager = CoreSpringFactory.getImpl(LDAPLoginManager.class); - - long start = System.nanoTime(); - //authenticate against LDAP server - Attributes attrs = ldapManager.bindUser(username, pwd, ldapError); - long takes = System.nanoTime() - start; - if(takes > WARNING_LIMIT) { - log.warn("LDAP Authentication takes (ms): " + (takes / 1000000)); - } - - if (ldapError.isEmpty() && attrs != null) { - Identity identity = ldapManager.findIdentyByLdapAuthentication(username, ldapError); - if (!ldapError.isEmpty()) { - return null; - } - if (identity == null) { - if(ldapModule.isCreateUsersOnLogin()) { - // User authenticated but not yet existing - create as new OLAT user - ldapManager.createAndPersistUser(attrs); - identity = ldapManager.findIdentyByLdapAuthentication(username, ldapError); - } else { - ldapError.insert("login.notauthenticated"); - } - } else { - // User does already exist - just sync attributes - Map<String, String> olatProToSync = ldapManager.prepareUserPropertyForSync(attrs, identity); - if (olatProToSync != null) { - ldapManager.syncUser(olatProToSync, identity); - } - } - // Add or update an OLAT authentication token for this user if configured in the module - if (identity != null && ldapModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { - CoreSpringFactory.getImpl(OLATAuthManager.class).changeOlatPassword(identity, identity, pwd); - } - return identity; - } - return null; - } - /** * Internal helper to perform the real login code and do all necessary steps to * register the user session diff --git a/src/main/java/org/olat/login/auth/AuthenticationSPI.java b/src/main/java/org/olat/login/auth/AuthenticationSPI.java index a1f8b8a69c8..6e44daf039e 100644 --- a/src/main/java/org/olat/login/auth/AuthenticationSPI.java +++ b/src/main/java/org/olat/login/auth/AuthenticationSPI.java @@ -31,6 +31,14 @@ public interface AuthenticationSPI { public Identity authenticate(Identity identity, String login, String password); + /** + * Update the password + * + * @param identity + * @param login + * @param password + */ public void upgradePassword(Identity identity, String login, String password); + } diff --git a/src/main/java/org/olat/login/auth/OLATAuthManager.java b/src/main/java/org/olat/login/auth/OLATAuthManager.java index a89c40df8c7..0be2fda87cc 100644 --- a/src/main/java/org/olat/login/auth/OLATAuthManager.java +++ b/src/main/java/org/olat/login/auth/OLATAuthManager.java @@ -43,6 +43,7 @@ import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.manager.BasicManager; import org.olat.core.util.Encoder.Algorithm; +import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.WebappHelper; import org.olat.core.util.i18n.I18nManager; @@ -206,11 +207,11 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { allOk = ldapError.isEmpty(); if(allOk && ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { - allOk &= changeOlatPassword(doer, identity, newPwd); + allOk &= changeOlatPassword(doer, identity, identity.getName(), newPwd); } } } else { - allOk = changeOlatPassword(doer, identity, newPwd); + allOk = changeOlatPassword(doer, identity, identity.getName(), newPwd); } if(allOk) { sendConfirmationEmail(doer, identity); @@ -243,7 +244,14 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { mailManager.sendMessage(bundle); } - public boolean changeOlatPassword(Identity doer, Identity identity, String newPwd) { + /** + * This update the OLAT and the HA1 passwords + * @param doer + * @param identity + * @param newPwd + * @return + */ + public boolean changeOlatPassword(Identity doer, Identity identity, String username, String newPwd) { Authentication auth = securityManager.findAuthentication(identity, "OLAT"); if (auth == null) { // create new authentication for provider OLAT auth = securityManager.createAndPersistAuthentication(identity, "OLAT", identity.getName(), newPwd, loginModule.getDefaultHashAlgorithm()); @@ -253,8 +261,33 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { log.audit(doer.getName() + " set new password for identity: " + identity.getName()); } - if(identity != null && webDAVAuthManager != null) { - webDAVAuthManager.changeDigestPassword(doer, identity, newPwd); + if(identity != null && StringHelper.containsNonWhitespace(username) && webDAVAuthManager != null) { + webDAVAuthManager.changeDigestPassword(doer, identity, username, newPwd); + } + return true; + } + + public boolean synchronizeOlatPasswordAndUsername(Identity doer, Identity identity, String username, String newPwd) { + Authentication auth = securityManager.findAuthentication(identity, "OLAT"); + if (auth == null) { // create new authentication for provider OLAT + auth = securityManager.createAndPersistAuthentication(identity, "OLAT", username, newPwd, loginModule.getDefaultHashAlgorithm()); + log.audit(doer.getName() + " created new authenticatin for identity: " + identity.getName()); + } else { + //update credentials + if(!securityManager.checkCredentials(auth, newPwd)) { + auth = securityManager.updateCredentials(auth, newPwd, loginModule.getDefaultHashAlgorithm()); + } + + if(!username.equals(auth.getAuthusername())) { + auth.setAuthusername(username); + auth = securityManager.updateAuthentication(auth); + } + + log.audit(doer.getName() + " set new password for identity: " + identity.getName()); + } + + if(identity != null && StringHelper.containsNonWhitespace(username) && webDAVAuthManager != null) { + webDAVAuthManager.changeDigestPassword(doer, identity, username, newPwd); } return true; } diff --git a/src/main/java/org/olat/user/ChangePasswordController.java b/src/main/java/org/olat/user/ChangePasswordController.java index a4848b6f6e0..bb8689d47bc 100644 --- a/src/main/java/org/olat/user/ChangePasswordController.java +++ b/src/main/java/org/olat/user/ChangePasswordController.java @@ -44,6 +44,7 @@ import org.olat.core.id.Identity; import org.olat.core.util.WebappHelper; import org.olat.core.util.resource.OresHelper; import org.olat.ldap.LDAPError; +import org.olat.ldap.LDAPLoginManager; import org.olat.ldap.LDAPLoginModule; import org.olat.ldap.ui.LDAPAuthenticationController; import org.olat.login.SupportsAfterLoginInterceptor; @@ -75,11 +76,13 @@ public class ChangePasswordController extends BasicController implements Support @Autowired private UserModule userModule; @Autowired + private BaseSecurity securityManager; + @Autowired private LDAPLoginModule ldapLoginModule; @Autowired - private OLATAuthManager olatAuthenticationSpi; + private LDAPLoginManager ldapLoginManager; @Autowired - private BaseSecurity securityManager; + private OLATAuthManager olatAuthenticationSpi; /** * @param ureq @@ -140,7 +143,7 @@ public class ChangePasswordController extends BasicController implements Support if (securityManager.findAuthentication(ureq.getIdentity(), LDAPAuthenticationController.PROVIDER_LDAP) != null) { LDAPError ldapError = new LDAPError(); //fallback to OLAT if enabled happen automatically in LDAPAuthenticationController - provenIdent = LDAPAuthenticationController.authenticate(ureq.getIdentity().getName(), oldPwd, ldapError); + provenIdent = ldapLoginManager.authenticate(ureq.getIdentity().getName(), oldPwd, ldapError); } else if(securityManager.findAuthentication(ureq.getIdentity(), BaseSecurityModule.getDefaultAuthProviderIdentifier()) != null) { provenIdent = olatAuthenticationSpi.authenticate(ureq.getIdentity(), ureq.getIdentity().getName(), oldPwd); } @@ -151,7 +154,6 @@ public class ChangePasswordController extends BasicController implements Support String newPwd = chPwdForm.getNewPasswordValue(); if(olatAuthenticationSpi.changePassword(ureq.getIdentity(), provenIdent, newPwd)) { //TODO: verify that we are NOT in a transaction (changepwd should be commited immediately) - // fxdiff: we need this event for the afterlogin-controller fireEvent(ureq, Event.DONE_EVENT); getLogger().audit("Changed password for identity."+provenIdent.getName()); showInfo("password.successful"); diff --git a/src/main/java/org/olat/user/WebDAVPasswordController.java b/src/main/java/org/olat/user/WebDAVPasswordController.java index c8c29ca543c..517e67822ee 100644 --- a/src/main/java/org/olat/user/WebDAVPasswordController.java +++ b/src/main/java/org/olat/user/WebDAVPasswordController.java @@ -25,7 +25,6 @@ import java.util.List; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityModule; -import org.olat.core.CoreSpringFactory; import org.olat.core.commons.modules.bc.FolderManager; import org.olat.core.commons.modules.bc.FolderRunController; import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager; @@ -45,6 +44,7 @@ import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; +import org.springframework.beans.factory.annotation.Autowired; /** * @@ -66,15 +66,13 @@ public class WebDAVPasswordController extends FormBasicController { private FormLayoutContainer accessDataFlc; private FormLayoutContainer buttonGroupLayout; - private final WebDAVAuthManager webDAVAuthManager; - private final BaseSecurity securityManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private WebDAVAuthManager webDAVAuthManager; public WebDAVPasswordController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl, "pwdav", Util.createPackageTranslator(FolderRunController.class, ureq.getLocale())); - - webDAVAuthManager = CoreSpringFactory.getImpl(WebDAVAuthManager.class); - securityManager = CoreSpringFactory.getImpl(BaseSecurity.class); - initForm(ureq); } diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index 62cd0658742..0e5d2a577f0 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -905,6 +905,7 @@ ldap.ldapUserObjectClass=person ldap.ldapUserFilter=(objectClass=${ldap.ldapUserObjectClass}) # Example for more complex filter: # ldap.ldapUserFilter=(&(objectClass=${ldap.ldapUserObjectClass})(memberOf=CN=OpenOLATAccess,OU=Students,DC=openolat,DC=org)) +# Attribute to resolve the DN of the suer during login (value is the internal attribute) ldap.ldapUserCreatedTimestampAttribute=whenCreated ldap.ldapUserLastModifiedTimestampAttribute=whenChanged # OpenLDAP is userPassword, ActiveDirectory is unicodePwd @@ -915,7 +916,8 @@ ldap.attributename.useridentifyer=sAMAccountName ldap.attributename.email=mail ldap.attributename.firstName=givenName ldap.attributename.lastName=sn -# fxdiff: allow to config xml in here: +# attribute used as username to log in +ldap.login.attribute=${ldap.attributename.useridentifyer} #mappings from ldap-attrib to olat-userproperty ldap.attrib.gen.map.ldapkey1= ldap.attrib.gen.map.olatkey1= diff --git a/src/test/java/org/olat/ldap/LDAPLoginTest.java b/src/test/java/org/olat/ldap/LDAPLoginTest.java index 9212fa42e4f..67811727283 100644 --- a/src/test/java/org/olat/ldap/LDAPLoginTest.java +++ b/src/test/java/org/olat/ldap/LDAPLoginTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -35,13 +36,14 @@ import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapContext; +import org.junit.Assert; import org.junit.Assume; +import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.olat.admin.user.delete.service.UserDeletionManager; import org.olat.basesecurity.BaseSecurity; -import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.SecurityGroup; -import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; import org.olat.core.id.User; @@ -50,6 +52,8 @@ import org.olat.ldap.model.LDAPUser; import org.olat.test.OlatTestCase; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; +import org.zapodot.junit.ldap.EmbeddedLdapRule; +import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder; /** @@ -67,167 +71,89 @@ public class LDAPLoginTest extends OlatTestCase { @Autowired private LDAPDAO ldapDao; @Autowired + private LDAPLoginManager ldapManager; + @Autowired + private BaseSecurity securityManager; + @Autowired private LDAPLoginModule ldapLoginModule; @Autowired private LDAPSyncConfiguration syncConfiguration; - @Test - public void testSystemBind() { - Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - - //edit olatextconfig.xml for testing - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); - LdapContext ctx = ldapManager.bindSystem(); - assertEquals(true, (ctx != null)); - } + @Rule + public EmbeddedLdapRule embeddedLdapRule = EmbeddedLdapRuleBuilder + .newInstance() + .usingDomainDsn("dc=olattest,dc=org") + .importingLdifs("org/olat/ldap/junittestdata/olattest.ldif") + .bindingToAddress("localhost") + .bindingToPort(1389) + .build(); @Test - public void testCreateUser() { + public void testSystemBind() { Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); - BaseSecurity securityManager = BaseSecurityManager.getInstance(); - String uid = "mrohrer"; - String userPW = "olat"; - LDAPError errors = new LDAPError(); - boolean usersSyncedAtStartup = ldapLoginModule.isLdapSyncOnStartup(); - //user should not exits in OLAT when not synced during startup - assertEquals(usersSyncedAtStartup, (securityManager.findIdentityByName(uid) != null)); - // bind user - Attributes attrs = ldapManager.bindUser(uid, userPW, errors); - assertEquals(usersSyncedAtStartup, (securityManager.findIdentityByName(uid) != null)); - //user should be created - ldapManager.createAndPersistUser(attrs); - assertEquals(true, (securityManager.findIdentityByName(uid) != null)); - - //should fail, user is existing - ldapManager.createAndPersistUser(attrs); - assertEquals(true, (securityManager.findIdentityByName(uid) != null)); + LdapContext ctx = ldapManager.bindSystem(); + Assert.assertNotNull(ctx); } @Test public void testUserBind() throws NamingException { Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); LDAPError errors = new LDAPError(); String uid = "mrohrer"; String userPW = "olat"; //normal bind, should work Attributes attrs = ldapManager.bindUser(uid, userPW, errors); - assertEquals("Rohrer", attrs.get("sn").get()); + Assert.assertNotNull(attrs); + Assert.assertEquals("Rohrer", attrs.get("sn").get()); //wrong password, should fail userPW = "haha"; attrs = ldapManager.bindUser(uid, userPW, errors); - assertEquals("Username or passwort incorrect", errors.get()); + Assert.assertNull(attrs); + Assert.assertEquals("Username or password incorrect", errors.get()); //wrong username, should fail uid = "ruedisueli"; userPW = "olat"; attrs = ldapManager.bindUser(uid, userPW, errors); - assertEquals("Username or passwort incorrect", errors.get()); + Assert.assertNull(attrs); + Assert.assertEquals("Username or password incorrect", errors.get()); //no password, should fail uid = "mrohrer"; userPW = null; attrs = ldapManager.bindUser(uid, userPW, errors); - assertEquals("Username and passwort must be selected", errors.get()); + Assert.assertNull(attrs); + Assert.assertEquals("Username and password must be selected", errors.get()); } - - @Test + @Test @Ignore //need to sync the user public void testCheckUser() { Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); LDAPError errors = new LDAPError(); - //should create error entry, since Administrator is existing in OLAT but not Managed by LDAP + //should create error entry String uid = "Administrator"; - Identity identity = ldapManager.findIdentyByLdapAuthentication(uid, errors); - assertEquals("findIdentyByLdapAuthentication: User with username::Administrator exist but not Managed by LDAP", errors.get()); - - //should return null, since user duda is not existing - uid = "duda"; - identity = ldapManager.findIdentyByLdapAuthentication(uid, errors); - assertEquals(true, (identity==null)); - assertEquals(true, errors.isEmpty()); + Attributes attrs = ldapManager.bindUser(uid, "olat", errors); + Identity identity = ldapManager.findIdentityByLdapAuthentication(attrs, errors); + Assert.assertEquals("findIdentyByLdapAuthentication: attrs::null", errors.get()); //should return identity, since is existing in OLAT and Managed by LDAP uid = "mrohrer"; - identity = ldapManager.findIdentyByLdapAuthentication(uid, errors); - assertEquals(uid, identity.getName()); - assertEquals(true, errors.isEmpty()); + attrs = ldapManager.bindUser(uid, "olat", errors); + identity = ldapManager.findIdentityByLdapAuthentication(attrs, errors); + Assert.assertEquals(uid, identity.getName()); + Assert.assertTrue(errors.isEmpty()); } - @Test - public void testCreateChangedAttrMap() { - Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - - // simulate closed session (user adding from startup job) - DBFactory.getInstance().intermediateCommit(); - - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); - BaseSecurity securityManager = BaseSecurityManager.getInstance(); - String uid = "kmeier"; - String pwd = "olat"; - LDAPError errors = new LDAPError(); - - boolean usersSyncedAtStartup = ldapLoginModule.isLdapSyncOnStartup(); - if (usersSyncedAtStartup) { - try { - //create user but with different attributes - must fail since user already exists - User user = UserManager.getInstance().createUser("klaus", "Meier", "klaus@meier.ch"); - Identity identity = securityManager.createAndPersistIdentityAndUser("kmeier", null, user, "LDAP", "kmeier"); - SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); - securityManager.addIdentityToSecurityGroup(identity, secGroup); - - // simulate closed session (user adding from startup job) - DBFactory.getInstance().intermediateCommit(); - fail("Expected constrant violation becaus of doupliate entry"); - } catch (Exception e) { - // success, this is what we expected - } - //changedAttrMap empty since already synchronized - Attributes attrs = ldapManager.bindUser(uid, pwd, errors); - Identity identitys = securityManager.findIdentityByName(uid); - Map<String, String> changedAttrMap = ldapManager.prepareUserPropertyForSync(attrs, identitys); - // map is empty - no attributes to sync - assertNull(changedAttrMap); - } else { - //create user but with different attributes - must fail since user already exists - User user = UserManager.getInstance().createUser("klaus", "Meier", "klaus@meier.ch"); - Identity identity = securityManager.createAndPersistIdentityAndUser("kmeier", null, user, "LDAP", "kmeier"); - SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); - securityManager.addIdentityToSecurityGroup(identity, secGroup); - // simulate closed session (user adding from startup job) - DBFactory.getInstance().intermediateCommit(); - - //changedAttrMap has 2 changes and uid as entrys (Klaus!=klaus, klaus@olat.org!=klaus@meier.ch) - Attributes attrs = ldapManager.bindUser(uid, pwd, errors); - Identity identitys = securityManager.findIdentityByName(uid); - Map<String, String> changedAttrMap = ldapManager.prepareUserPropertyForSync(attrs, identitys); - // result must be 3: 2 changed plus the user ID which is always in the map - assertEquals(3, changedAttrMap.keySet().size()); - } - - //nothing to change for this user - uid= "mrohrer"; - Attributes attrs = ldapManager.bindUser(uid, pwd, errors); - Identity identitys = securityManager.findIdentityByName(uid); - Map<String, String> changedAttrMap = ldapManager.prepareUserPropertyForSync(attrs, identitys); - assertEquals(true, (changedAttrMap==null)); - } - - @Test + @Test @Ignore //need to sync the user public void testSyncUser(){ Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); - BaseSecurity securityManager = BaseSecurityManager.getInstance(); Map<String,String> changedMap = new HashMap<String,String>(); LDAPError errors = new LDAPError(); @@ -246,12 +172,10 @@ public class LDAPLoginTest extends OlatTestCase { assertEquals(true, (changedMap==null)); } - @Test + @Test @Ignore public void testIdentityDeletedInLDAP(){ Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); - LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); - BaseSecurity securityManager = BaseSecurityManager.getInstance(); List<Identity> deletList; //should be empty @@ -293,7 +217,87 @@ public class LDAPLoginTest extends OlatTestCase { assertEquals(2, (deletList.size())); } - @Test + @Test @Ignore + public void testCreateUser() { + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); + + String uid = "mrohrer"; + String userPW = "olat"; + LDAPError errors = new LDAPError(); + + boolean usersSyncedAtStartup = ldapLoginModule.isLdapSyncOnStartup(); + //user should not exits in OLAT when not synced during startup + assertEquals(usersSyncedAtStartup, (securityManager.findIdentityByName(uid) != null)); + // bind user + Attributes attrs = ldapManager.bindUser(uid, userPW, errors); + assertEquals(usersSyncedAtStartup, (securityManager.findIdentityByName(uid) != null)); + //user should be created + ldapManager.createAndPersistUser(attrs); + assertEquals(true, (securityManager.findIdentityByName(uid) != null)); + + //should fail, user is existing + ldapManager.createAndPersistUser(attrs); + assertEquals(true, (securityManager.findIdentityByName(uid) != null)); + } + + @Test @Ignore + public void testCreateChangedAttrMap() { + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); + + // simulate closed session (user adding from startup job) + DBFactory.getInstance().intermediateCommit(); + + String uid = "kmeier"; + String pwd = "olat"; + LDAPError errors = new LDAPError(); + + boolean usersSyncedAtStartup = ldapLoginModule.isLdapSyncOnStartup(); + if (usersSyncedAtStartup) { + try { + //create user but with different attributes - must fail since user already exists + User user = UserManager.getInstance().createUser("klaus", "Meier", "klaus@meier.ch"); + Identity identity = securityManager.createAndPersistIdentityAndUser("kmeier", null, user, "LDAP", "kmeier"); + SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); + securityManager.addIdentityToSecurityGroup(identity, secGroup); + + // simulate closed session (user adding from startup job) + DBFactory.getInstance().intermediateCommit(); + fail("Expected constrant violation becaus of doupliate entry"); + } catch (Exception e) { + // success, this is what we expected + } + //changedAttrMap empty since already synchronized + Attributes attrs = ldapManager.bindUser(uid, pwd, errors); + Identity identitys = securityManager.findIdentityByName(uid); + Map<String, String> changedAttrMap = ldapManager.prepareUserPropertyForSync(attrs, identitys); + // map is empty - no attributes to sync + assertNull(changedAttrMap); + } else { + //create user but with different attributes - must fail since user already exists + User user = UserManager.getInstance().createUser("klaus", "Meier", "klaus@meier.ch"); + Identity identity = securityManager.createAndPersistIdentityAndUser("kmeier", null, user, "LDAP", "kmeier"); + SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); + securityManager.addIdentityToSecurityGroup(identity, secGroup); + // simulate closed session (user adding from startup job) + DBFactory.getInstance().intermediateCommit(); + + //changedAttrMap has 2 changes and uid as entrys (Klaus!=klaus, klaus@olat.org!=klaus@meier.ch) + Attributes attrs = ldapManager.bindUser(uid, pwd, errors); + Identity identitys = securityManager.findIdentityByName(uid); + Map<String, String> changedAttrMap = ldapManager.prepareUserPropertyForSync(attrs, identitys); + // result must be 3: 2 changed plus the user ID which is always in the map + assertEquals(3, changedAttrMap.keySet().size()); + } + + //nothing to change for this user + uid= "mrohrer"; + Attributes attrs = ldapManager.bindUser(uid, pwd, errors); + Identity identitys = securityManager.findIdentityByName(uid); + Map<String, String> changedAttrMap = ldapManager.prepareUserPropertyForSync(attrs, identitys); + assertEquals(true, (changedAttrMap==null)); + } + + @Test @Ignore public void testCronSync() throws Exception { Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); @@ -302,14 +306,14 @@ public class LDAPLoginTest extends OlatTestCase { List<Attributes> newLdapUserList; Map<Identity, Map<String, String>> changedMapIdenityMap; List<Identity> deletedUserList; - String user; + LDAPError errors = new LDAPError(); - BaseSecurity securityManager = BaseSecurityManager.getInstance(); - LDAPLoginManager ldapMan = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); //find user changed after 2010,01,09,00,00 - ctx = ldapMan.bindSystem(); - Date syncDate = new Date(110,00,10,00,00); + ctx = ldapManager.bindSystem(); + Calendar cal = Calendar.getInstance(); + cal.set(2010, 0, 10, 0, 0, 0); + Date syncDate = cal.getTime(); ldapUserList = ldapDao.getUserAttributesModifiedSince(syncDate, ctx); assertEquals(1, ldapUserList.size()); @@ -318,7 +322,6 @@ public class LDAPLoginTest extends OlatTestCase { ldapUserList = ldapDao.getUserAttributesModifiedSince(syncDate, ctx); assertEquals(6, ldapUserList.size()); - //prepare create- and sync-Lists for each user from defined syncTime Identity idenity; Map<String,String> changedAttrMap; @@ -326,10 +329,10 @@ public class LDAPLoginTest extends OlatTestCase { changedMapIdenityMap = new HashMap<Identity, Map<String, String>>(); for (int i = 0; i < ldapUserList.size(); i++) { Attributes userAttrs = ldapUserList.get(i).getAttributes(); - user = getAttributeValue(userAttrs.get(syncConfiguration.getOlatPropertyToLdapAttribute("userID"))); - idenity = ldapMan.findIdentyByLdapAuthentication(user, errors); + String user = getAttributeValue(userAttrs.get(syncConfiguration.getOlatPropertyToLdapAttribute("userID"))); + idenity = ldapManager.findIdentityByLdapAuthentication(userAttrs, errors); if (idenity != null) { - changedAttrMap = ldapMan.prepareUserPropertyForSync(userAttrs, idenity); + changedAttrMap = ldapManager.prepareUserPropertyForSync(userAttrs, idenity); if(changedAttrMap!= null) changedMapIdenityMap.put(idenity, changedAttrMap); } else { if(errors.isEmpty()) { @@ -352,7 +355,7 @@ public class LDAPLoginTest extends OlatTestCase { securityManager.addIdentityToSecurityGroup(identity1, secGroup1); //create User to Delete List - deletedUserList = ldapMan.getIdentitysDeletedInLdap(ctx); + deletedUserList = ldapManager.getIdentitysDeletedInLdap(ctx); assertEquals(4, (deletedUserList.size())); @@ -360,20 +363,20 @@ public class LDAPLoginTest extends OlatTestCase { Iterator<Identity> itrIdent = changedMapIdenityMap.keySet().iterator(); while(itrIdent.hasNext()){ Identity ident = itrIdent.next(); - ldapMan.syncUser(changedMapIdenityMap.get(ident), ident); + ldapManager.syncUser(changedMapIdenityMap.get(ident), ident); } //create all users for (int i = 0; i < newLdapUserList.size(); i++) { - ldapMan.createAndPersistUser(newLdapUserList.get(i)); + ldapManager.createAndPersistUser(newLdapUserList.get(i)); } //delete all users - ldapMan.deletIdentities(deletedUserList); + ldapManager.deletIdentities(deletedUserList); //check if users are deleted - deletedUserList = ldapMan.getIdentitysDeletedInLdap(ctx); + deletedUserList = ldapManager.getIdentitysDeletedInLdap(ctx); assertEquals(0, (deletedUserList.size())); } diff --git a/src/test/java/org/olat/ldap/junittestdata/olattest.ldif b/src/test/java/org/olat/ldap/junittestdata/olattest.ldif index bbbbc28cbed..6b831e0b225 100644 --- a/src/test/java/org/olat/ldap/junittestdata/olattest.ldif +++ b/src/test/java/org/olat/ldap/junittestdata/olattest.ldif @@ -29,7 +29,7 @@ mail: mrohrer@olat.org o: Informatik sn: Rohrer uid: mrohrer -userpassword:: e1NIQX1SWEhpU0lWZGN1SU1EdVNiVCtaaFpOcWFwYWs9 +userPassword: olat modifyTimestamp: 20100630180000Z dn: uid=test,ou=person,dc=olattest,dc=org @@ -43,8 +43,8 @@ labeleduri: http://www.olat.org mail: test@test.com o: Informatik sn: Test -uid: ldaptest -userpassword:: e1NIQX1SWEhpU0lWZGN1SU1EdVNiVCtaaFpOcWFwYWs9 +uid: test +userPassword: olattest dn: uid=halpen,ou=person,dc=olattest,dc=org objectClass: organizationalPerson @@ -72,7 +72,7 @@ mail: kmeier@olat.org o: Informatik sn: Meier uid: kmeier -userpassword:: e1NIQX1SWEhpU0lWZGN1SU1EdVNiVCtaaFpOcWFwYWs9 +userPassword: olat dn: uid=hhuerlimann,ou=person,dc=olattest,dc=org objectClass: organizationalPerson diff --git a/src/test/profile/mysql/olat.local.properties b/src/test/profile/mysql/olat.local.properties index bdb197bb898..77e3df44fc2 100644 --- a/src/test/profile/mysql/olat.local.properties +++ b/src/test/profile/mysql/olat.local.properties @@ -50,6 +50,15 @@ auto.upgrade.database=false #site we need site.portal.enable=true +# ldap +ldap.enable=true +ldap.ldapSyncOnStartup=false +ldap.ldapUrl=ldap://localhost:1389 +ldap.ldapSystemDN=uid=test,ou=person,dc=olattest,dc=org +ldap.ldapSystemPW=olattest +ldap.ldapBases=ou=person,dc=olattest,dc=org +ldap.attributename.useridentifyer=uid + #make sure it works it mimic the pre 8.3 behavior group.mandatory.enrolment.email.users=false group.mandatory.enrolment.email.authors=false diff --git a/src/test/profile/postgresql/olat.local.properties b/src/test/profile/postgresql/olat.local.properties index ec0468d5e2e..ff368deacf0 100644 --- a/src/test/profile/postgresql/olat.local.properties +++ b/src/test/profile/postgresql/olat.local.properties @@ -51,6 +51,15 @@ db.hibernate.hikari.leakDetectionThreshold=120000 #site we need site.portal.enable=true +# ldap +ldap.enable=true +ldap.ldapSyncOnStartup=false +ldap.ldapUrl=ldap://localhost:1389 +ldap.ldapSystemDN=uid=test,ou=person,dc=olattest,dc=org +ldap.ldapSystemPW=olattest +ldap.ldapBases=ou=person,dc=olattest,dc=org +ldap.attributename.useridentifyer=uid + #make sure it works it mimic the pre 8.3 behavior group.mandatory.enrolment.email.users=false group.mandatory.enrolment.email.authors=false -- GitLab