From 6ad08f98a51a19235d1c84d5a4d623c0093f851f Mon Sep 17 00:00:00 2001 From: Sam Ottenhoff Date: Tue, 19 Dec 2017 13:51:01 -0500 Subject: [PATCH] KNL-1577 new method to remap username and clear relevant caches (#5123) --- .../user/api/UserDirectoryService.java | 22 ++++++-- .../user/cover/UserDirectoryService.java | 8 +++ .../user/impl/AuthnCacheWatcher.java | 18 +++--- .../user/impl/BaseUserDirectoryService.java | 56 +++++++++++++++++-- .../sakaiproject/user/impl/DbUserService.java | 4 +- .../sakaiproject/webservices/SakaiScript.java | 30 ++++++++++ 6 files changed, 114 insertions(+), 24 deletions(-) diff --git a/kernel/api/src/main/java/org/sakaiproject/user/api/UserDirectoryService.java b/kernel/api/src/main/java/org/sakaiproject/user/api/UserDirectoryService.java index 6ffc0e48980f..0ed3fe75c5f4 100644 --- a/kernel/api/src/main/java/org/sakaiproject/user/api/UserDirectoryService.java +++ b/kernel/api/src/main/java/org/sakaiproject/user/api/UserDirectoryService.java @@ -71,6 +71,10 @@ public interface UserDirectoryService extends EntityProducer /** User eid for the admin user. */ static final String ADMIN_EID = "admin"; + /** Cache keys for the id/eid mapping **/ + static final String EIDCACHE = "eid:"; + static final String IDCACHE = "id:"; + /** * This function returns a boolean value of true/false, * depending on if the given password meets the validation criteria. @@ -178,14 +182,24 @@ User addUser(String id, String eid, String firstName, String lastName, String em * @return true if the user is allowed to update their own first and last names, false if not. */ public boolean allowUpdateUserName(String id); + + /** + * Gets the UserEdit object from storage inorder to update the user eid and email + * + * @param id The user id. + * @param newEmail the new email that will set the eid + email fields + * @return UserEdit object + */ + public boolean updateUserId(String id, String newEmail); + /** - * Gets the UserEdit object from storage inorder to update the user Eid() + * Gets the UserEdit object from storage to remap the username (eid) * - * @param eId The user id. - * @param newEmail the Id with which userId will be updated + * @param id The internal user id. + * @param newEid the new username * @return UserEdit object */ - public boolean updateUserId(String eId,String newEmail); + public boolean updateUserEid(String id, String newEid); /** * check permissions for editUser() diff --git a/kernel/api/src/main/java/org/sakaiproject/user/cover/UserDirectoryService.java b/kernel/api/src/main/java/org/sakaiproject/user/cover/UserDirectoryService.java index 5bcce65dfd7a..78e5d0638a52 100644 --- a/kernel/api/src/main/java/org/sakaiproject/user/cover/UserDirectoryService.java +++ b/kernel/api/src/main/java/org/sakaiproject/user/cover/UserDirectoryService.java @@ -162,6 +162,14 @@ public static boolean updateUserId(java.lang.String param0,java.lang.String para return service.updateUserId(param0,param1); } + public static boolean updateUserEid(java.lang.String param0,java.lang.String param1) + { + org.sakaiproject.user.api.UserDirectoryService service = getInstance(); + if (service == null) return false; + + return service.updateUserEid(param0,param1); + } + public static org.sakaiproject.user.api.UserEdit editUser(java.lang.String param0) throws org.sakaiproject.user.api.UserNotDefinedException, org.sakaiproject.user.api.UserPermissionException, org.sakaiproject.user.api.UserLockedException diff --git a/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/AuthnCacheWatcher.java b/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/AuthnCacheWatcher.java index f3e3b9982f70..6c5fbeda6734 100644 --- a/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/AuthnCacheWatcher.java +++ b/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/AuthnCacheWatcher.java @@ -45,9 +45,6 @@ */ @Slf4j public class AuthnCacheWatcher implements Observer { - //Copied from DbUserService as they are private - private static final String EIDCACHE = "eid:"; - private static final String IDCACHE = "id:"; private AuthenticationCache authenticationCache; private UserDirectoryService userDirectoryService; private EventTrackingService eventTrackingService; @@ -86,9 +83,9 @@ public void setUserDirectoryService(UserDirectoryService userDirectoryService) { public void init() { log.info("init()"); - if (userCache == null) { // this is the user id->eid mapping cache - userCache = memoryService.getCache("org.sakaiproject.user.api.UserDirectoryService"); - } + if (userCache == null) { // this is the user id->eid mapping cache + userCache = memoryService.getCache("org.sakaiproject.user.api.UserDirectoryService"); + } eventTrackingService.addObserver(this); } @@ -103,11 +100,10 @@ public void update(Observable arg0, Object arg) { if (!(arg instanceof Event)) return; Event event = (Event) arg; - - + // check the event function against the functions we have notifications watching for String function = event.getEvent(); - + //we err on the side of caution here in checking all events that might invalidate the data in the cache -DH if (UserDirectoryService.SECURE_ADD_USER.equals(function) || UserDirectoryService.SECURE_UPDATE_USER_OWN_PASSWORD.equals(function) || UserDirectoryService.SECURE_UPDATE_USER_ANY.equals(function) || UserDirectoryService.SECURE_UPDATE_USER_OWN.equals(function)) { @@ -121,8 +117,8 @@ public void update(Observable arg0, Object arg) { String eid = userDirectoryService.getUserEid(refId); log.debug("removing " + eid + " from cache"); authenticationCache.removeAuthentification(eid); - userCache.remove(EIDCACHE + eid); - userCache.remove(IDCACHE + refId); + userCache.remove(UserDirectoryService.IDCACHE + eid); + userCache.remove(UserDirectoryService.EIDCACHE + refId); } catch (UserNotDefinedException e) { //not sure how we'd end up here log.warn(e.getMessage(), e); diff --git a/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/BaseUserDirectoryService.java b/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/BaseUserDirectoryService.java index 962523c9b863..2c1df985a2a2 100644 --- a/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/BaseUserDirectoryService.java +++ b/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/BaseUserDirectoryService.java @@ -82,7 +82,10 @@ public abstract class BaseUserDirectoryService implements UserDirectoryService, /** A cache of users */ protected Cache m_callCache = null; - + + /** A cache of users' id/eid map */ + protected Cache m_userCache = null; + /** Optional service to provide site-specific aliases for a user's display ID and display name. */ protected ContextualUserDisplayService m_contextualUserDisplayService = null; @@ -540,6 +543,7 @@ public void init() } // caching for users + m_userCache = memoryService().getCache("org.sakaiproject.user.api.UserDirectoryService"); m_callCache = memoryService().getCache("org.sakaiproject.user.api.UserDirectoryService.callCache"); if (!m_callCache.isDistributed()) { // KNL_1229 use an Observer for cache cleanup when the cache is not distributed @@ -621,7 +625,9 @@ public void update(Observable observable, Object o) { ) ) { String userRef = event.getResource(); - removeCachedUser(userRef); + UserEdit u = getCachedUser(userRef); + String oldEid = u != null ? u.getEid() : null; + removeCachedUser(userRef, oldEid); } } @@ -638,8 +644,8 @@ public void destroy() m_provider = null; m_anon = null; m_passwordPolicyProvider = null; - m_callCache.close(); - m_userCacheObserver = null; + m_callCache.close(); + m_userCacheObserver = null; log.info("destroy()"); } @@ -1592,7 +1598,7 @@ public void removeUser(UserEdit user) throws UserPermissionException } // Remove from cache. - removeCachedUser(ref); + removeCachedUser(ref, user.getEid()); } /** @@ -1785,12 +1791,17 @@ protected void putCachedUser(String ref, UserEdit user) } } - protected void removeCachedUser(String ref) + protected void removeCachedUser(String ref, String eid) { if (m_callCache != null) { m_callCache.remove(ref); } + + if (m_userCache != null && StringUtils.isNotBlank(eid)) + { + m_userCache.remove(IDCACHE + eid); + } } /********************************************************************************************************************************************************************************************************************************************************** @@ -1980,6 +1991,7 @@ public boolean updateUserId(String id,String newEmail) } user.setEid(newEmail); user.setEmail(newEmail); + ((BaseUserEdit) user).setEvent(SECURE_UPDATE_USER_ANY); commitEdit(user); return true; } @@ -1996,6 +2008,38 @@ public boolean updateUserId(String id,String newEmail) } } + public boolean updateUserEid(String id, String newEid) + { + try { + List locksSucceeded = new ArrayList(); + + List locks = new ArrayList(); + locks.add(SECURE_UPDATE_USER_ANY); + locksSucceeded = unlock(locks, userReference(id)); + + if(!locksSucceeded.isEmpty()) { + UserEdit user = m_storage.edit(id); + if (user == null) { + log.warn("Can't find user " + id + " when trying to update user eid"); + return false; + } + user.setEid(newEid); + ((BaseUserEdit) user).setEvent(SECURE_UPDATE_USER_ANY); + commitEdit(user); + return true; + } + else { + log.warn("User with id: "+id+" failed permission checks" ); + return false; + } + } catch (UserPermissionException e) { + log.warn("You do not have sufficient permission to edit the user eid with Id: "+id, e); + return false; + } catch (UserAlreadyDefinedException e) { + log.error("A user already exists with EID of: "+id +"having eid :"+ newEid, e); + return false; + } + } /********************************************************************************************************************************************************************************************************************************************************** * UserEdit implementation diff --git a/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/DbUserService.java b/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/DbUserService.java index dbaeb30c9a66..9f2044106076 100644 --- a/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/DbUserService.java +++ b/kernel/kernel-impl/src/main/java/org/sakaiproject/user/impl/DbUserService.java @@ -209,8 +209,6 @@ public void setIdEidCache(Cache cache) */ protected class DbStorage extends BaseDbFlatStorage implements Storage, SqlReader { - private static final String EIDCACHE = "eid:"; - private static final String IDCACHE = "id:"; /** * Construct. @@ -549,7 +547,7 @@ protected void unMap(String id) if (!m_separateIdEid) return; // clear both sides of the cache - String eid = (String) cache.get(EIDCACHE+id); + String eid = (String) cache.get(EIDCACHE+id); if ( eid != null ) { cache.remove(IDCACHE+eid); } diff --git a/webservices/cxf/src/java/org/sakaiproject/webservices/SakaiScript.java b/webservices/cxf/src/java/org/sakaiproject/webservices/SakaiScript.java index 0d0a96e52701..9a8e061fd596 100644 --- a/webservices/cxf/src/java/org/sakaiproject/webservices/SakaiScript.java +++ b/webservices/cxf/src/java/org/sakaiproject/webservices/SakaiScript.java @@ -288,6 +288,36 @@ public String changeUserInfo( return "success"; } + /** + * Edit a user's eid + * Commonly needed when a user changes legal name and username changes at institution + * + * @param sessionid the id of a valid session + * @param eid the current username (ie jsmith26) of the user you want to edit + * @param mewEid the new username + * @return success or exception message + * @throws RuntimeException + */ + @WebMethod + @Path("/changeUserEid") + @Produces("text/plain") + @GET + public String changeUserEid( + @WebParam(name = "sessionid", partName = "sessionid") @QueryParam("sessionid") String sessionid, + @WebParam(name = "eid", partName = "eid") @QueryParam("eid") String eid, + @WebParam(name = "newEid", partName = "newEid") @QueryParam("newEid") String newEid) { + Session session = establishSession(sessionid); + + try { + String userid = userDirectoryService.getUserByEid(eid).getId(); + boolean success = userDirectoryService.updateUserEid(userid, newEid); + return success ? "success" : "failure"; + } catch (Exception e) { + log.error("WS changeUserEid(): " + e.getClass().getName() + " : " + e.getMessage()); + return e.getClass().getName() + " : " + e.getMessage(); + } + } + /** * Edit a user's firstname/lastname *