From 9cedcb5005ea458ce62e1cce5469edb3603f1d6a Mon Sep 17 00:00:00 2001 From: uhensler <none@none> Date: Tue, 14 Nov 2017 15:37:01 +0100 Subject: [PATCH] OO-2981: Remove invalid authentications after the change if an email address --- .../user/ChangeEMailExecuteController.java | 2 + .../bulkChange/UserBulkChangeManager.java | 2 + .../admin/user/imp/UserImportController.java | 12 +++++ .../org/olat/basesecurity/BaseSecurity.java | 12 +++++ .../basesecurity/BaseSecurityManager.java | 29 ++++++++++++ .../org/olat/user/ProfileFormController.java | 5 ++- .../org/olat/user/restapi/UserWebService.java | 4 +- .../basesecurity/BaseSecurityManagerTest.java | 45 +++++++++++++++++++ 8 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/bps/olat/user/ChangeEMailExecuteController.java b/src/main/java/de/bps/olat/user/ChangeEMailExecuteController.java index f6ae3d44dc2..2922f57455a 100644 --- a/src/main/java/de/bps/olat/user/ChangeEMailExecuteController.java +++ b/src/main/java/de/bps/olat/user/ChangeEMailExecuteController.java @@ -107,6 +107,7 @@ public class ChangeEMailExecuteController extends ChangeEMailController implemen Identity identity = BaseSecurityManager.getInstance().loadIdentityByKey(tempKey.getIdentityKey()); if (identity != null) { + String oldEmail = identity.getUser().getEmail(); identity.getUser().setProperty("email", mails.get("changedEMail")); // if old mail address closed then set the new mail address // unclosed @@ -122,6 +123,7 @@ public class ChangeEMailExecuteController extends ChangeEMailController implemen // remove keys identity.getUser().setProperty("emchangeKey", null); userRequest.getUserSession().removeEntryFromNonClearedStore(ChangeEMailController.CHANGE_EMAIL_ENTRY); + BaseSecurityManager.getInstance().deleteInvalidAuthenticationsByEmail(oldEmail); } else { // error message wControl.setWarning(pT.translate("error.change.email.unexpected", new String[] { mails.get("currentEMail"), mails.get("changedEMail") })); diff --git a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java index a8c4c1a2ef1..5eef9ece5ec 100644 --- a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java +++ b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java @@ -133,6 +133,7 @@ public class UserBulkChangeManager implements InitializingBean { //reload identity from cache, to prevent stale object identity = securityManager.loadIdentityByKey(identity.getKey()); User user = identity.getUser(); + String oldEmail = user.getEmail(); String errorDesc = ""; boolean updateError = false; // change pwd @@ -245,6 +246,7 @@ public class UserBulkChangeManager implements InitializingBean { notUpdatedIdentities.add(errorOutput); } else { userManager.updateUserFromIdentity(identity); + securityManager.deleteInvalidAuthenticationsByEmail(oldEmail); changedIdentities.add(identity); log.audit("User::" + addingIdentity.getName() + " successfully changed account data for user::" + identity.getName() + " in bulk change", null); } diff --git a/src/main/java/org/olat/admin/user/imp/UserImportController.java b/src/main/java/org/olat/admin/user/imp/UserImportController.java index 4ca3d728a20..a763f9c8db3 100644 --- a/src/main/java/org/olat/admin/user/imp/UserImportController.java +++ b/src/main/java/org/olat/admin/user/imp/UserImportController.java @@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.velocity.VelocityContext; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; +import org.olat.basesecurity.BaseSecurityManager; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.gui.UserRequest; @@ -204,8 +205,10 @@ public class UserImportController extends BasicController { Identity identity; if(updateUsers != null && updateUsers.booleanValue()) { identity = userToUpdate.getIdentity(true); + String oldEmail = loadEmail(identity); if(um.updateUserFromIdentity(identity)) { report.incrementUpdatedUser(); + securityManager.deleteInvalidAuthenticationsByEmail(oldEmail); } } else { identity = userToUpdate.getIdentity(); @@ -238,6 +241,15 @@ public class UserImportController extends BasicController { return userToUpdate.getIdentity(); } + private String loadEmail(Identity updatedIdentity) { + String email = null; + Identity oldIdentity = BaseSecurityManager.getInstance().loadIdentityByKey(updatedIdentity.getKey()); + if (oldIdentity != null) { + email = oldIdentity.getUser().getEmail(); + } + return email; + } + @Override protected void doDispose() { // child controllers disposed by basic controller diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java index 6445099485a..4f62739aef7 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurity.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java @@ -402,6 +402,18 @@ public interface BaseSecurity { */ public void deleteAuthentication(Authentication authentication); + /** + * Deletes invalid authentications with the specified email address in the field + * username. An authentication is invalid if no unique user with that email + * exists. Use this method to clean old authentications after the change of the + * email address of a user to avoid duplicate authentications. Because a user + * can have an email address as his username, the authentication of the OLAT + * authentication provider is never deleted. + * + * @param email + */ + public void deleteInvalidAuthenticationsByEmail(String email); + /** * * @param authentication diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 20ce0a75a79..1802c4acb63 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -27,6 +27,7 @@ package org.olat.basesecurity; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -53,6 +54,7 @@ import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.persistence.DBQuery; import org.olat.core.commons.persistence.PersistenceHelper; +import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.ModifiedInfo; @@ -65,6 +67,7 @@ import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Encoder; import org.olat.core.util.Encoder.Algorithm; +import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.coordinate.SyncerCallback; @@ -1512,6 +1515,32 @@ public class BaseSecurityManager implements BaseSecurity { log.error("", e); } } + + @Override + public void deleteInvalidAuthenticationsByEmail(String email) { + if (!StringHelper.containsNonWhitespace(email)) return; + + // If a user with this email exists the email is valid. + Identity identity = UserManager.getInstance().findUniqueIdentityByEmail(email); + if (identity != null) return; + + StringBuilder sb = new StringBuilder(); + sb.append("delete from ").append(AuthenticationImpl.class.getName()).append(" as auth"); + sb.append(" where auth.authusername=:authusername"); + sb.append(" and auth.provider in (:providers)"); + + List<String> providers = Arrays.asList( + WebDAVAuthManager.PROVIDER_HA1_EMAIL, + WebDAVAuthManager.PROVIDER_HA1_INSTITUTIONAL_EMAIL, + WebDAVAuthManager.PROVIDER_WEBDAV_EMAIL, + WebDAVAuthManager.PROVIDER_WEBDAV_INSTITUTIONAL_EMAIL); + + dbInstance.getCurrentEntityManager() + .createQuery(sb.toString()) + .setParameter("authusername", email) + .setParameter("providers", providers) + .executeUpdate(); + } /** * @see org.olat.basesecurity.Manager#findAuthenticationByAuthusername(java.lang.String, java.lang.String) diff --git a/src/main/java/org/olat/user/ProfileFormController.java b/src/main/java/org/olat/user/ProfileFormController.java index 1bc8bec9bb6..53e47229898 100644 --- a/src/main/java/org/olat/user/ProfileFormController.java +++ b/src/main/java/org/olat/user/ProfileFormController.java @@ -107,6 +107,8 @@ public class ProfileFormController extends FormBasicController { @Autowired private UserManager userManager; @Autowired + private BaseSecurityManager securityManager; + @Autowired private RegistrationManager rm; @Autowired private MailManager mailManager; @@ -486,7 +488,8 @@ public class ProfileFormController extends FormBasicController { TemporaryKey tempKey = rm.loadTemporaryKeyByRegistrationKey(key); if (tempKey != null) { rm.deleteTemporaryKey(tempKey); - } + } + securityManager.deleteInvalidAuthenticationsByEmail(currentEmail); } else { emailChanged = true; // change email address to old address until it is verified diff --git a/src/main/java/org/olat/user/restapi/UserWebService.java b/src/main/java/org/olat/user/restapi/UserWebService.java index 5ca79548c26..8c8b61cd337 100644 --- a/src/main/java/org/olat/user/restapi/UserWebService.java +++ b/src/main/java/org/olat/user/restapi/UserWebService.java @@ -780,8 +780,10 @@ public class UserWebService { retrievedIdentity = baseSecurity.setExternalId(retrievedIdentity, user.getExternalId()); retrievedUser = retrievedIdentity.getUser(); } + String oldEmail = retrievedUser.getEmail(); post(retrievedUser, user, getLocale(request)); UserManager.getInstance().updateUser(retrievedUser); + BaseSecurityManager.getInstance().deleteInvalidAuthenticationsByEmail(oldEmail); return Response.ok(get(retrievedIdentity, true, true)).build(); } @@ -794,7 +796,7 @@ public class UserWebService { return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } - + private List<ErrorVO> validateUser(User user, UserVO userVo, HttpServletRequest request) { UserManager um = UserManager.getInstance(); diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java index 030c1185589..fce34a0899c 100644 --- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java +++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java @@ -36,6 +36,7 @@ import java.util.UUID; import org.junit.Assert; import org.junit.Test; import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager; import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.id.User; @@ -971,4 +972,48 @@ public class BaseSecurityManagerTest extends OlatTestCase { Assert.assertEquals(identity, reloadedId); dbInstance.commitAndCloseSession(); } + + @Test + public void deleteInvalidAuthenticationsByEmail() { + Identity identity = JunitTestHelper.createAndPersistIdentityAsUser("auth-del-email-" + UUID.randomUUID().toString()); + User user = identity.getUser(); + String email = user.getEmail(); + securityManager.createAndPersistAuthentication(identity, "OLAT", email, "secret", Encoder.Algorithm.sha512); + securityManager.createAndPersistAuthentication(identity, "del-mail", email, "secret", Encoder.Algorithm.sha512); + securityManager.createAndPersistAuthentication(identity, WebDAVAuthManager.PROVIDER_HA1_EMAIL, email, "secret", Encoder.Algorithm.sha512); + securityManager.createAndPersistAuthentication(identity, WebDAVAuthManager.PROVIDER_HA1_INSTITUTIONAL_EMAIL, email, "secret", Encoder.Algorithm.sha512); + securityManager.createAndPersistAuthentication(identity, WebDAVAuthManager.PROVIDER_WEBDAV_EMAIL, email, "secret", Encoder.Algorithm.sha512); + securityManager.createAndPersistAuthentication(identity, WebDAVAuthManager.PROVIDER_WEBDAV_INSTITUTIONAL_EMAIL, email, "secret", Encoder.Algorithm.sha512); + dbInstance.commitAndCloseSession(); + + // User with email address exists: The authentications are valid. + securityManager.deleteInvalidAuthenticationsByEmail(email); + dbInstance.commitAndCloseSession(); + + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_HA1_EMAIL)); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_HA1_INSTITUTIONAL_EMAIL)); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_WEBDAV_EMAIL)); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_WEBDAV_INSTITUTIONAL_EMAIL)); + Assert.assertNull(securityManager.findAuthenticationByAuthusername(email, "OLAT")); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(identity.getName(), "OLAT")); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(email, "del-mail")); + + // Email of the user changed: The authentications are not valid any longer. + user.setProperty(UserConstants.EMAIL, "new@trashcmail.com"); + user.setProperty(UserConstants.INSTITUTIONALEMAIL, "new@trashcmail.com"); + userManager.updateUser(user); + dbInstance.commitAndCloseSession(); + + securityManager.deleteInvalidAuthenticationsByEmail(email); + dbInstance.commitAndCloseSession(); + + Assert.assertNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_HA1_EMAIL)); + Assert.assertNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_HA1_INSTITUTIONAL_EMAIL)); + Assert.assertNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_WEBDAV_EMAIL)); + Assert.assertNull(securityManager.findAuthenticationByAuthusername(email, WebDAVAuthManager.PROVIDER_WEBDAV_INSTITUTIONAL_EMAIL)); + Assert.assertNull(securityManager.findAuthenticationByAuthusername(email, "OLAT")); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(identity.getName(), "OLAT")); + Assert.assertNotNull(securityManager.findAuthenticationByAuthusername(email, "del-mail")); + } + } -- GitLab