Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserOrgRoleSaveController.java =================================================================== diff -u -rf2ad75cef0c507a64877942631fee13efbc6ed50 -r94a1023cbda3eab32ccba7c3f94c729bb0a32b28 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserOrgRoleSaveController.java (.../UserOrgRoleSaveController.java) (revision f2ad75cef0c507a64877942631fee13efbc6ed50) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserOrgRoleSaveController.java (.../UserOrgRoleSaveController.java) (revision 94a1023cbda3eab32ccba7c3f94c729bb0a32b28) @@ -25,15 +25,20 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.lamsfoundation.lams.admin.web.dto.UserBean; import org.lamsfoundation.lams.admin.web.form.UserOrgRoleForm; +import org.lamsfoundation.lams.usermanagement.Role; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.MessageService; +import org.lamsfoundation.lams.web.filter.AuditLogFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @@ -47,15 +52,15 @@ /** * @author jliew * - * Saves roles for users that were just added. - * Uses session scope because using request scope doesn't copy the form data - * into UserOrgRoleForm's userBeans ArrayList (the list becomes empty). + * Saves roles for users that were just added. + * Uses session scope because using request scope doesn't copy the form data + * into UserOrgRoleForm's userBeans ArrayList (the list becomes empty). */ @Controller @SessionAttributes("userOrgRoleForm") public class UserOrgRoleSaveController { private static Logger log = Logger.getLogger(UserOrgRoleSaveController.class); - + @Autowired private IUserManagementService userManagementService; @Autowired @@ -89,6 +94,9 @@ return "forward:/userorg.do"; } userManagementService.setRolesForUserOrganisation(user, orgId, Arrays.asList(roleIds)); + + auditLog(orgId, user.getUserId(), roleIds); + // FMALIKOFF 5/7/7 Commented out the following code that set the roles in the course if the current org is a class, as the logic // is done in service.setRolesForUserOrganisation() //if (organisation.getOrganisationType().getOrganisationTypeId().equals(OrganisationType.CLASS_TYPE)) { @@ -100,4 +108,11 @@ return "redirect:/usermanage.do?org=" + orgId; } -} + private void auditLog(Integer organisationId, Integer userId, String[] roleIds) { + List roles = Stream.of(roleIds).collect(Collectors + .mapping(roleId -> Role.ROLE_MAP.get(Integer.valueOf(roleId)), Collectors.toUnmodifiableList())); + StringBuilder auditLogMessage = new StringBuilder("assigned to user ").append(userId).append(" roles ") + .append(roles).append(" in organisation ").append(organisationId); + AuditLogFilter.log(auditLogMessage); + } +} \ No newline at end of file Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserOrgSaveController.java =================================================================== diff -u -r8d4e389c1f1f6c95b91080d82960db4478640ced -r94a1023cbda3eab32ccba7c3f94c729bb0a32b28 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserOrgSaveController.java (.../UserOrgSaveController.java) (revision 8d4e389c1f1f6c95b91080d82960db4478640ced) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserOrgSaveController.java (.../UserOrgSaveController.java) (revision 94a1023cbda3eab32ccba7c3f94c729bb0a32b28) @@ -29,6 +29,8 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,6 +44,7 @@ import org.lamsfoundation.lams.usermanagement.UserOrganisation; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.web.filter.AuditLogFilter; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; import org.springframework.beans.factory.annotation.Autowired; @@ -112,6 +115,7 @@ String[] userIds = userOrgForm.getUserIds(); List userIdList = userIds == null ? Collections.emptyList() : Arrays.asList(userIds); log.debug("new user membership of orgId=" + orgId + " will be: " + userIdList); + Set removedUserIds = new TreeSet<>(); // remove UserOrganisations that aren't in form data Iterator iter = uos.iterator(); @@ -127,14 +131,16 @@ log.debug("removed userId=" + userId + " from orgId=" + orgId); // remove from subgroups userManagementService.deleteChildUserOrganisations(uo.getUser(), uo.getOrganisation()); + + removedUserIds.add(userId); } } // add UserOrganisations that are in form data List newUserOrganisations = new ArrayList<>(); for (int i = 0; i < userIdList.size(); i++) { Integer userId = new Integer(userIdList.get(i)); Iterator iter2 = uos.iterator(); - Boolean alreadyInOrg = false; + boolean alreadyInOrg = false; while (iter2.hasNext()) { UserOrganisation uo = (UserOrganisation) iter2.next(); if (uo.getUser().getUserId().equals(userId)) { @@ -152,6 +158,11 @@ organisation.setUserOrganisations(uos); userManagementService.save(organisation); + auditLog(orgId, + newUserOrganisations.stream().collect( + Collectors.mapping(uo -> uo.getUser().getUserId(), Collectors.toCollection(TreeSet::new))), + removedUserIds, canEditRole); + // if no new users, then finish; otherwise forward to where roles can be assigned for new users. if (newUserOrganisations.isEmpty()) { log.debug("no new users to add to orgId=" + orgId); @@ -173,4 +184,27 @@ } } -} + private void auditLog(Integer organisationId, Set addedUserIds, Set removedUserIds, + boolean canEditRole) { + if (addedUserIds.isEmpty() && removedUserIds.isEmpty()) { + return; + } + + StringBuilder auditLogMessage = new StringBuilder(); + if (!addedUserIds.isEmpty()) { + auditLogMessage.append("added users ").append(addedUserIds).append(" "); + if (!canEditRole) { + auditLogMessage.append("and assigned them LEARNER role "); + } + if (!removedUserIds.isEmpty()) { + auditLogMessage.append("and "); + } + } + if (!removedUserIds.isEmpty()) { + auditLogMessage.append("removed users ").append(removedUserIds); + } + auditLogMessage.append(" to/from organisation ").append(organisationId); + + AuditLogFilter.log(auditLogMessage); + } +} \ No newline at end of file Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserRolesSaveController.java =================================================================== diff -u -r3920cb7cce6c4cf2c2f8aa7eec77b0b30b3629ed -r94a1023cbda3eab32ccba7c3f94c729bb0a32b28 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserRolesSaveController.java (.../UserRolesSaveController.java) (revision 3920cb7cce6c4cf2c2f8aa7eec77b0b30b3629ed) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/UserRolesSaveController.java (.../UserRolesSaveController.java) (revision 94a1023cbda3eab32ccba7c3f94c729bb0a32b28) @@ -26,6 +26,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -37,6 +39,7 @@ import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.MessageService; +import org.lamsfoundation.lams.web.filter.AuditLogFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @@ -52,13 +55,13 @@ @Controller public class UserRolesSaveController { private static Logger log = Logger.getLogger(UserRolesSaveController.class); - + @Autowired private IUserManagementService userManagementService; @Autowired @Qualifier("adminMessageService") private MessageService messageService; - + private static List rolelist; @RequestMapping(path = "/userrolessave", method = RequestMethod.POST) @@ -74,7 +77,7 @@ String[] roles = userRolesForm.getRoles(); request.setAttribute("org", orgId); - + if (log.isDebugEnabled()) { String numRoles = roles != null ? Integer.toString(roles.length) : "0"; log.debug(new StringBuilder("userId: ").append(userId).append(", orgId: ").append(orgId) @@ -89,16 +92,25 @@ if (roles == null || roles.length < 1) { errorMap.add("roles", messageService.getMessage("error.roles.empty")); request.setAttribute("errorMap", errorMap); - request.setAttribute("rolelist", - userManagementService.filterRoles(rolelist, request.isUserInRole(Role.SYSADMIN), org.getOrganisationType())); + request.setAttribute("rolelist", userManagementService.filterRoles(rolelist, + request.isUserInRole(Role.SYSADMIN), org.getOrganisationType())); request.setAttribute("login", user.getLogin()); request.setAttribute("fullName", user.getFullName()); return "forward:/userroles.do"; } userManagementService.setRolesForUserOrganisation(user, orgId, Arrays.asList(roles)); + auditLog(orgId, userId, roles); + return "redirect:/usermanage.do?org=" + orgId; } + private void auditLog(Integer organisationId, Integer userId, String[] roleIds) { + List roles = Stream.of(roleIds).collect(Collectors + .mapping(roleId -> Role.ROLE_MAP.get(Integer.valueOf(roleId)), Collectors.toUnmodifiableList())); + StringBuilder auditLogMessage = new StringBuilder("assigned to user ").append(userId).append(" roles ") + .append(roles).append(" in organisation ").append(organisationId); + AuditLogFilter.log(auditLogMessage); + } } Index: lams_common/src/java/org/lamsfoundation/lams/integration/security/SsoHandler.java =================================================================== diff -u -r38bc3bc3c5a2220dd9f5800ad430eba86b237227 -r94a1023cbda3eab32ccba7c3f94c729bb0a32b28 --- lams_common/src/java/org/lamsfoundation/lams/integration/security/SsoHandler.java (.../SsoHandler.java) (revision 38bc3bc3c5a2220dd9f5800ad430eba86b237227) +++ lams_common/src/java/org/lamsfoundation/lams/integration/security/SsoHandler.java (.../SsoHandler.java) (revision 94a1023cbda3eab32ccba7c3f94c729bb0a32b28) @@ -72,7 +72,6 @@ private static IUserManagementService userManagementService = null; private static Logger log = Logger.getLogger(SsoHandler.class); - private static Logger auditLogger = Logger.getLogger(AuditLogFilter.class); private static final String REDIRECT_KEY = "io.undertow.servlet.form.auth.redirect.location"; static final String KEEP_SESSION_ID_KEY = "lams.keepSessionId"; @@ -265,21 +264,19 @@ Date date = new Date(currentTimeMillis + lockOutTimeMillis); user.setLockOutTime(date); - String messageSuffix = new StringBuilder().append(" (").append(user.getUserId()) - .append(") is locked out for ") + String messagePayload = new StringBuilder().append("is locked out for ") .append(Configuration.getAsInt(ConfigurationKeys.LOCK_OUT_TIME)).append(" mins after ") .append(failedAttempts).append(" failed attempts.").toString(); - String message = new StringBuilder("User ").append(user.getLogin()).append(messageSuffix) - .toString(); + String eventMessage = new StringBuilder("User ").append(user.getLogin()).append(" (") + .append(user.getUserId()).append(") ").append(messagePayload).toString(); SsoHandler.getLogEventService(session.getServletContext()).logEvent( - LogEvent.TYPE_ACCOUNT_LOCKED, user.getUserId(), user.getUserId(), null, null, message); + LogEvent.TYPE_ACCOUNT_LOCKED, user.getUserId(), user.getUserId(), null, null, + eventMessage); - message = new StringBuilder().append("\"").append(user.getLogin()).append("\"") - .append(user.getUserId()).append(messageSuffix).toString(); - auditLogger.info(message); + AuditLogFilter.log(user.getUserId(), user.getLogin(), messagePayload); if (log.isDebugEnabled()) { - log.debug(message); + log.debug(eventMessage); } } Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/Role.java =================================================================== diff -u -r29a37489a63e5a95f42a5ef5fd8a7daeb65c53c5 -r94a1023cbda3eab32ccba7c3f94c729bb0a32b28 --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/Role.java (.../Role.java) (revision 29a37489a63e5a95f42a5ef5fd8a7daeb65c53c5) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/Role.java (.../Role.java) (revision 94a1023cbda3eab32ccba7c3f94c729bb0a32b28) @@ -24,6 +24,7 @@ package org.lamsfoundation.lams.usermanagement; import java.io.Serializable; +import java.util.Map; import javax.persistence.Column; import javax.persistence.Entity; @@ -67,6 +68,9 @@ // public static final Integer ROLE_GROUP_ADMIN = 6; /***********************************************************/ + public static final Map ROLE_MAP = Map.of(ROLE_SYSADMIN, SYSADMIN, ROLE_GROUP_MANAGER, + GROUP_MANAGER, ROLE_AUTHOR, AUTHOR, ROLE_MONITOR, MONITOR, ROLE_LEARNER, LEARNER); + @Id @Column(name = "role_id") @GeneratedValue(strategy = GenerationType.IDENTITY) Index: lams_common/src/java/org/lamsfoundation/lams/web/filter/AuditLogFilter.java =================================================================== diff -u -rb6abe020e1a153e53ff398f05f3e3b235bf3660b -r94a1023cbda3eab32ccba7c3f94c729bb0a32b28 --- lams_common/src/java/org/lamsfoundation/lams/web/filter/AuditLogFilter.java (.../AuditLogFilter.java) (revision b6abe020e1a153e53ff398f05f3e3b235bf3660b) +++ lams_common/src/java/org/lamsfoundation/lams/web/filter/AuditLogFilter.java (.../AuditLogFilter.java) (revision 94a1023cbda3eab32ccba7c3f94c729bb0a32b28) @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.Map.Entry; +import java.util.Set; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -22,58 +23,80 @@ */ public class AuditLogFilter extends OncePerRequestFilter { - private static final Logger log = Logger.getLogger(AuditLogFilter.class); + private static final Logger logger = Logger.getLogger(AuditLogFilter.class); + // paths that have more accurate logs built in business logic + // or just need to be ignored + private static final Set IGNORED_PATHS = Set.of("/lams/admin/userorgsave.do", + "/lams/admin/userorgrolesave.do", "/lams/admin/userrolessave.do"); + @Override - public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + public final void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { try { - // look for user information - UserDTO user = AuditLogFilter.getUserDto(); - StringBuilder logMessageBuilder = new StringBuilder(); - if (user == null) { - logMessageBuilder.append("Unauthenticated user "); - } else { - logMessageBuilder.append("\"").append(user.getLogin()).append("\" (").append(user.getUserID()) - .append(") "); - } - // what path was called - logMessageBuilder.append("called ").append(request.getRequestURI()); + String calledPath = request.getRequestURI(); + if (!IGNORED_PATHS.contains(calledPath)) { + StringBuilder logMessageBuilder = new StringBuilder(); - // optional parameters - if (!request.getParameterMap().isEmpty()) { - logMessageBuilder.append(" with parameters: "); - for (Entry entry : request.getParameterMap().entrySet()) { - String key = entry.getKey(); - // skip CSRF parameter - if ("OWASP-CSRFGUARD".equals(key)) { - continue; - } + // what path was called + logMessageBuilder.append("called ").append(request.getRequestURI()); - // print out all parameters with their values - logMessageBuilder.append(key).append("=["); - String[] values = entry.getValue(); - if (values != null && values.length > 0) { - for (String value : entry.getValue()) { - logMessageBuilder.append(value).append(", "); + // optional parameters + if (!request.getParameterMap().isEmpty()) { + logMessageBuilder.append(" with parameters: "); + for (Entry entry : request.getParameterMap().entrySet()) { + String key = entry.getKey(); + // skip CSRF parameter + if ("OWASP-CSRFGUARD".equals(key)) { + continue; } - logMessageBuilder.delete(logMessageBuilder.length() - 2, logMessageBuilder.length()); + + // print out all parameters with their values + logMessageBuilder.append(key).append("=["); + String[] values = entry.getValue(); + if (values != null && values.length > 0) { + for (String value : entry.getValue()) { + logMessageBuilder.append(value).append(", "); + } + logMessageBuilder.delete(logMessageBuilder.length() - 2, logMessageBuilder.length()); + } + logMessageBuilder.append("], "); } - logMessageBuilder.append("], "); + logMessageBuilder.delete(logMessageBuilder.length() - 2, logMessageBuilder.length()); } - logMessageBuilder.delete(logMessageBuilder.length() - 2, logMessageBuilder.length()); - } - log.info(logMessageBuilder); + AuditLogFilter.log(logMessageBuilder); + } } catch (Exception e) { - log.error("Exception while logging to audit log", e); + logger.error("Exception while logging to audit log", e); } chain.doFilter(request, response); } - private static UserDTO getUserDto() { + // utility methods to call from controllers + + public static final void log(CharSequence message) { + // look for user information + UserDTO user = AuditLogFilter.getUserDto(); + AuditLogFilter.log(user.getUserID(), user.getLogin(), message); + } + + public static final void log(Integer userId, String userName, CharSequence message) { + StringBuilder logMessageBuilder = new StringBuilder(); + + if (userId == null) { + logMessageBuilder.append("Unauthenticated user "); + } else { + logMessageBuilder.append("\"").append(userName).append("\" (").append(userId).append(") "); + } + logMessageBuilder.append(message); + + logger.info(logMessageBuilder); + } + + private static final UserDTO getUserDto() { HttpSession ss = SessionManager.getSession(); return (UserDTO) ss.getAttribute(AttributeNames.USER); }