From 6683ad734f95bfa279f382d0aa2d050efa78e752 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Tue, 28 Jul 2026 01:30:00 +0530 Subject: [PATCH 01/14] AUDIT-61:Added REST APIs for security audit --- .../api/dto/SecurityAuditLogDTO.java | 40 +++++ .../api/dto/SecurityLogResponseDTO.java | 31 ++++ .../rest/SecurityAuditRestController.java | 138 ++++++++++++++++++ .../rest/SecurityAuditRestControllerTest.java | 131 +++++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityAuditLogDTO.java create mode 100644 api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityLogResponseDTO.java create mode 100644 omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java create mode 100644 omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityAuditLogDTO.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityAuditLogDTO.java new file mode 100644 index 0000000..4750e31 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityAuditLogDTO.java @@ -0,0 +1,40 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.auditlogweb.api.dto; + +import lombok.Builder; +import lombok.Data; +import org.openmrs.module.auditlogweb.api.utils.AuditSecurityEventType; + +import java.util.Date; + +@Data +@Builder +public class SecurityAuditLogDTO { + + private Integer id; + + private AuditSecurityEventType eventType; + + private String username; + + private String userUuid; + + private Date eventTime; + + private String ipAddress; + + private String userAgent; + + private String sessionId; + + private String details; + +} diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityLogResponseDTO.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityLogResponseDTO.java new file mode 100644 index 0000000..27a3a2c --- /dev/null +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/dto/SecurityLogResponseDTO.java @@ -0,0 +1,31 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.auditlogweb.api.dto; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class SecurityLogResponseDTO { + + private long totalLogs; + + private int currentLogs; + + private int totalPages; + + private int currentPage; + + private List securityAuditLogs; + +} diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java new file mode 100644 index 0000000..a3aed1f --- /dev/null +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -0,0 +1,138 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.auditlogweb.rest; + +import lombok.RequiredArgsConstructor; +import org.openmrs.module.auditlogweb.AuditSecurityEvent; +import org.openmrs.module.auditlogweb.api.AuditService; +import org.openmrs.module.auditlogweb.api.dto.SecurityAuditLogDTO; +import org.openmrs.module.auditlogweb.api.dto.SecurityLogResponseDTO; +import org.openmrs.module.auditlogweb.api.utils.UtilClass; +import org.openmrs.module.webservices.rest.web.RestConstants; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +@RestController +@RequestMapping("/rest/" + RestConstants.VERSION_1 + "/securityauditlogs") +@RequiredArgsConstructor +public class SecurityAuditRestController { + + private final AuditService auditService; + + @GetMapping + public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", required = false) Integer logId, + @RequestParam(value = "eventType", required = false) String eventType, + @RequestParam(value = "username", required = false) String username, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "size", defaultValue = "15") int size) { + + if (logId != null && logId <= 0) { + throw new IllegalArgumentException("Please provide a valid log ID"); + } + + if (logId != null) { + AuditSecurityEvent securityEvent = auditService.getSecurityEventById(logId); + if (securityEvent == null) { + return SecurityLogResponseDTO.builder().totalLogs(0).currentLogs(0) + .securityAuditLogs(Collections.emptyList()).build(); + } + List securityAuditLogsDTO = mapToDTOs(Collections.singletonList(securityEvent)); + return SecurityLogResponseDTO.builder().totalLogs(1).currentLogs(1).securityAuditLogs(securityAuditLogsDTO) + .build(); + } + + if (page < 0) { + page = 0; + } + if (size <= 0) { + size = 15; + } + + Date start = UtilClass.parseDate(startDate, false); + Date end = UtilClass.parseDate(endDate, true); + + List securityEvents = auditService.getSecurityEvents(eventType, username, start, end, page, + size); + long totalCount = auditService.countSecurityEvents(eventType, username, start, end); + int totalPages = UtilClass.computeTotalPages(totalCount, size); + + List securityAuditLogsDTO = mapToDTOs(securityEvents); + + return SecurityLogResponseDTO.builder().totalLogs(totalCount).currentLogs(securityAuditLogsDTO.size()) + .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); + } + + @GetMapping("/releatedAudits") + public SecurityLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionId") String sessionId, + @RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "size", defaultValue = "15") int size) { + + if (sessionId == null || sessionId.isEmpty()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid session id"); + } + + if (page < 0) { + page = 0; + } + if (size <= 0) { + size = 15; + } + + List allRelated = auditService.getRelatedSecurityEvents(sessionId, 1000); + long totalCount = allRelated.size(); + int totalPages = UtilClass.computeTotalPages(totalCount, size); + + int fromIndex = page * size; + List pagedList; + if (fromIndex >= allRelated.size()) { + pagedList = Collections.emptyList(); + } else { + int toIndex = Math.min(fromIndex + size, allRelated.size()); + pagedList = allRelated.subList(fromIndex, toIndex); + } + + List securityAuditLogsDTO = mapToDTOs(pagedList); + + return SecurityLogResponseDTO.builder().totalLogs(totalCount) + .currentLogs(securityAuditLogsDTO != null ? securityAuditLogsDTO.size() : 0).totalPages(totalPages) + .currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); + } + + private SecurityAuditLogDTO mapToDTO(AuditSecurityEvent event) { + if (event == null) { + return null; + } + return SecurityAuditLogDTO.builder().id(event.getId()).eventType(event.getEventType()).username(event.getUsername()) + .userUuid(event.getUserUuid()).eventTime(event.getEventTime()).ipAddress(event.getIpAddress()) + .userAgent(event.getUserAgent()).sessionId(event.getSessionId()).details(event.getDetails()).build(); + } + + private List mapToDTOs(List events) { + if (events == null) { + return Collections.emptyList(); + } + List dtos = new ArrayList<>(events.size()); + for (AuditSecurityEvent event : events) { + dtos.add(mapToDTO(event)); + } + return dtos; + } +} diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java new file mode 100644 index 0000000..61bfd80 --- /dev/null +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -0,0 +1,131 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.auditlogweb.rest; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openmrs.module.auditlogweb.AuditSecurityEvent; +import org.openmrs.module.auditlogweb.api.AuditService; +import org.openmrs.module.auditlogweb.api.utils.AuditSecurityEventType; +import org.openmrs.module.auditlogweb.rest.exceptions.RestExceptionHandler; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class SecurityAuditRestControllerTest { + + private MockMvc mockMvc; + + @Mock + private AuditService auditService; + + @InjectMocks + private SecurityAuditRestController securityAuditRestController; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(securityAuditRestController) + .setControllerAdvice(new RestExceptionHandler()).build(); + } + + @Test + public void shouldFetchSecurityAuditsSuccessfullyWithoutFilter() throws Exception { + when(auditService.getSecurityEvents(null, null, null, null, 0, 15)).thenReturn(Collections.emptyList()); + when(auditService.countSecurityEvents(null, null, null, null)).thenReturn(0L); + + mockMvc.perform(get("/rest/v1/securityauditlogs")).andExpect(status().isOk()); + + verify(auditService).getSecurityEvents(null, null, null, null, 0, 15); + verify(auditService).countSecurityEvents(null, null, null, null); + } + + @Test + public void shouldFetchSecurityAuditsSuccessfullyWithIdFilter() throws Exception { + AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); + when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); + when(auditService.getSecurityEventById(1)).thenReturn(mockEvent); + + mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "1")).andExpect(status().isOk()); + + verify(auditService).getSecurityEventById(1); + } + + @Test + public void shouldThrowErrorIfInvalidLogIdPassed() throws Exception { + mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "-1")).andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error", is("Bad Request"))) + .andExpect(jsonPath("$.message", is("Please provide a valid log ID"))); + } + + @Test + public void shouldFetchSecurityAuditsSuccessfullyWithEventTypeFilter() throws Exception { + AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); + when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); + List eventList = Collections.singletonList(mockEvent); + + when(auditService.getSecurityEvents("LOGIN_SUCCESS", null, null, null, 0, 15)).thenReturn(eventList); + when(auditService.countSecurityEvents("LOGIN_SUCCESS", null, null, null)).thenReturn(1L); + + mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "LOGIN_SUCCESS")).andExpect(status().isOk()); + + verify(auditService).getSecurityEvents("LOGIN_SUCCESS", null, null, null, 0, 15); + verify(auditService).countSecurityEvents("LOGIN_SUCCESS", null, null, null); + } + + @Test + public void shouldFetchSecurityAuditsSuccessfullyWithUserNameFilter() throws Exception { + AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); + when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); + List eventList = Collections.singletonList(mockEvent); + + when(auditService.getSecurityEvents(null, "admin", null, null, 0, 15)).thenReturn(eventList); + when(auditService.countSecurityEvents(null, "admin", null, null)).thenReturn(1L); + + mockMvc.perform(get("/rest/v1/securityauditlogs").param("username", "admin")).andExpect(status().isOk()); + + verify(auditService).getSecurityEvents(null, "admin", null, null, 0, 15); + verify(auditService).countSecurityEvents(null, "admin", null, null); + } + + @Test + public void shouldThrowErrorIfInvalidDatePassed() throws Exception { + mockMvc.perform(get("/rest/v1/securityauditlogs").param("startDate", "31/02/2025")) + .andExpect(status().isBadRequest()).andExpect(jsonPath("$.error", is("Bad Request"))).andExpect(jsonPath( + "$.message", is("Invalid month date or date format: '31/02/2025'. Expected format: DD/MM/YYYY"))); + } + + @Test + public void shouldFetchRelatedAuditsSuccessfully() throws Exception { + AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); + when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); + List relatedList = Collections.singletonList(mockEvent); + + when(auditService.getRelatedSecurityEvents("session-123", 1000)).thenReturn(relatedList); + + mockMvc.perform(get("/rest/v1/securityauditlogs/releatedAudits").param("sessionId", "session-123").param("page", "0") + .param("size", "10")).andExpect(status().isOk()); + + verify(auditService).getRelatedSecurityEvents("session-123", 1000); + } +} From 86e653b88034556251810682749f347165449008 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Tue, 28 Jul 2026 15:02:28 +0530 Subject: [PATCH 02/14] AUDIT-61:Fixed related security logs bug and added it's count method --- .../module/auditlogweb/api/AuditService.java | 8 ++++++-- .../module/auditlogweb/api/dao/AuditDao.java | 16 ++++++++++++--- .../api/impl/AuditServiceImpl.java | 9 +++++++-- .../auditlogweb/api/dao/AuditDaoTest.java | 18 +++++++++++++++-- .../api/impl/AuditServiceImplTest.java | 12 +++++++++-- .../rest/SecurityAuditRestController.java | 20 +++++-------------- .../SecurityAuditDetailController.java | 4 ++-- .../rest/SecurityAuditRestControllerTest.java | 4 ++-- .../SecurityAuditDetailControllerTest.java | 4 ++-- 9 files changed, 63 insertions(+), 32 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java index 30ea5a8..73d1ea2 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java @@ -334,10 +334,14 @@ void logSecurityEvent(AuditSecurityEventType eventType, String username, String * Retrieves the most recent N security events from the same session (for related activity). * * @param sessionId the session ID to filter by - * @param limit the maximum number of events to return + * @param page defines the particular page in a paginated view + * @param size defines how many logs want in one go * @return a list of {@link AuditSecurityEvent} ordered by eventTime descending */ @Authorized(AuditLogConstants.VIEW_SECURITY_AUDIT_LOGS) - List getRelatedSecurityEvents(String sessionId, int limit); + List getRelatedSecurityEvents(String sessionId, int size, int page); + + @Authorized(AuditLogConstants.VIEW_READ_AUDIT_LOGS) + long countRelatedSecurityEvents(String sessionId); } diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java index 993d10e..1b34acb 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java @@ -714,14 +714,24 @@ public AuditSecurityEvent getSecurityEventById(Integer eventId) { * Retrieves the most recent N security events from the same session (for related activity). * * @param sessionId the session ID to filter by - * @param limit the maximum number of events to return + * @param page defines the particular page in a paginated view + * @param size defines how many logs want in one go * @return a list of {@link AuditSecurityEvent} ordered by eventTime descending */ - public List getRelatedSecurityEvents(String sessionId, int limit) { + public List getRelatedSecurityEvents(String sessionId, int size, int page) { Query query = sessionFactory.getCurrentSession().createQuery( "from AuditSecurityEvent e where e.sessionId = :sessionId order by e.eventTime desc", AuditSecurityEvent.class); query.setParameter("sessionId", sessionId); - query.setMaxResults(limit); + query.setFirstResult(page * size); + query.setMaxResults(size); return query.getResultList(); } + + public long countRelatedSecurityEvent(String sessionId) { + Query query = sessionFactory.getCurrentSession() + .createQuery("select count(e) from AuditSecurityEvent e where e.sessionId = :sessionId", Long.class); + query.setParameter("sessionId", sessionId); + Long result = query.uniqueResult(); + return result != null ? result : 0L; + } } diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java index 599d7a9..2d7aa9e 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java @@ -387,8 +387,13 @@ public AuditSecurityEvent getSecurityEventById(Integer eventId) { } @Override - public List getRelatedSecurityEvents(String sessionId, int limit) { - return auditDao.getRelatedSecurityEvents(sessionId, limit); + public List getRelatedSecurityEvents(String sessionId, int size, int page) { + return auditDao.getRelatedSecurityEvents(sessionId, size, page); + } + + @Override + public long countRelatedSecurityEvents(String sessionId) { + return auditDao.countRelatedSecurityEvent(sessionId); } private Object fetchPreviousRevision(AuditEntity entity, Object currentEntity) { diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java index 7967f20..d87a2a7 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java @@ -580,10 +580,11 @@ void shouldReturnRelatedSecurityEvents_WhenSessionIdProvided() { when(session.createQuery(anyString(), eq(AuditSecurityEvent.class))).thenReturn(securityEventQuery); when(securityEventQuery.setParameter("sessionId", sessionId)).thenReturn(securityEventQuery); + when(securityEventQuery.setFirstResult(0)).thenReturn(securityEventQuery); when(securityEventQuery.setMaxResults(5)).thenReturn(securityEventQuery); when(securityEventQuery.getResultList()).thenReturn(Arrays.asList(e1, e2)); - List result = auditDao.getRelatedSecurityEvents(sessionId, 5); + List result = auditDao.getRelatedSecurityEvents(sessionId, 5, 0); assertNotNull(result); assertThat(result, hasSize(2)); @@ -595,15 +596,28 @@ void shouldReturnRelatedSecurityEvents_WhenSessionIdProvided() { void shouldReturnEmptyList_WhenNoRelatedSecurityEventsFound() { when(session.createQuery(anyString(), eq(AuditSecurityEvent.class))).thenReturn(securityEventQuery); when(securityEventQuery.setParameter(anyString(), anyString())).thenReturn(securityEventQuery); + when(securityEventQuery.setFirstResult(0)).thenReturn(securityEventQuery); when(securityEventQuery.setMaxResults(anyInt())).thenReturn(securityEventQuery); when(securityEventQuery.getResultList()).thenReturn(Collections.emptyList()); - List result = auditDao.getRelatedSecurityEvents("sess-ghost", 10); + List result = auditDao.getRelatedSecurityEvents("sess-ghost", 10, 0); assertNotNull(result); assertThat(result, empty()); } + @Test + void shouldReturnRelatedSecurityEventsCount() { + when(session.createQuery(anyString(), eq(Long.class))).thenReturn(countQuery); + when(countQuery.setParameter(eq("sessionId"), eq("session-123"))).thenReturn(countQuery); + when(countQuery.uniqueResult()).thenReturn(10L); + + long count = auditDao.countRelatedSecurityEvent("session-123"); + + assertThat(count, is(10L)); + verify(countQuery).setParameter("sessionId", "session-123"); + } + @Test void shouldBindUnknownEventType_WhenGivenSecurityEventIsInvalid() { when(session.createQuery(anyString(), eq(AuditSecurityEvent.class))).thenReturn(securityEventQuery); diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java index fd6c8b5..68dbc84 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java @@ -397,10 +397,10 @@ void shouldReturnRelatedSecurityEvents() { AuditSecurityEvent relatedSecurityEvent2 = AuditSecurityEvent.builder() .eventType(AuditSecurityEventType.LOGIN_SUCCESS).eventTime(new Date()).sessionId("session-123").build(); - when(auditDao.getRelatedSecurityEvents("session-123", 2)) + when(auditDao.getRelatedSecurityEvents("session-123", 2, 0)) .thenReturn(Arrays.asList(relatedSecurityEvent1, relatedSecurityEvent2)); - List result = auditService.getRelatedSecurityEvents("session-123", 2); + List result = auditService.getRelatedSecurityEvents("session-123", 2, 0); assertNotNull(result); assertEquals(2, result.size()); @@ -408,6 +408,14 @@ void shouldReturnRelatedSecurityEvents() { assertEquals(relatedSecurityEvent2, result.get(1)); } + @Test + void shouldCountRelatedSecurityEvents() { + when(auditDao.countRelatedSecurityEvent("session-123")).thenReturn(10L); + + long count = auditService.countRelatedSecurityEvents("session-123"); + assertEquals(10L, count); + } + public static class TestEntity { private Integer id; diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index a3aed1f..f63e62c 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -96,24 +96,14 @@ public SecurityLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionI size = 15; } - List allRelated = auditService.getRelatedSecurityEvents(sessionId, 1000); - long totalCount = allRelated.size(); + List allRelated = auditService.getRelatedSecurityEvents(sessionId, size, page); + long totalCount = auditService.countRelatedSecurityEvents(sessionId); int totalPages = UtilClass.computeTotalPages(totalCount, size); - int fromIndex = page * size; - List pagedList; - if (fromIndex >= allRelated.size()) { - pagedList = Collections.emptyList(); - } else { - int toIndex = Math.min(fromIndex + size, allRelated.size()); - pagedList = allRelated.subList(fromIndex, toIndex); - } - - List securityAuditLogsDTO = mapToDTOs(pagedList); + List securityAuditLogsDTO = mapToDTOs(allRelated); - return SecurityLogResponseDTO.builder().totalLogs(totalCount) - .currentLogs(securityAuditLogsDTO != null ? securityAuditLogsDTO.size() : 0).totalPages(totalPages) - .currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); + return SecurityLogResponseDTO.builder().totalLogs(totalCount).currentLogs(securityAuditLogsDTO.size()) + .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); } private SecurityAuditLogDTO mapToDTO(AuditSecurityEvent event) { diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java index 2f4662a..5e22821 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java @@ -48,7 +48,7 @@ public class SecurityAuditDetailController { /** * Display security audit event details. - * + * * @param request HTTP request * @param model model map for JSP * @return ModelAndView pointing to viewSecurityAudit.jsp @@ -83,7 +83,7 @@ public ModelAndView showDetails(HttpServletRequest request, ModelMap model) { // Fetch related events from the same session List relatedEvents = null; if (event.getSessionId() != null && !event.getSessionId().isEmpty()) { - relatedEvents = auditService.getRelatedSecurityEvents(event.getSessionId(), RELATED_EVENTS_LIMIT); + relatedEvents = auditService.getRelatedSecurityEvents(event.getSessionId(), RELATED_EVENTS_LIMIT, 0); } model.addAttribute("event", event); diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index 61bfd80..25b88d9 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -121,11 +121,11 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); List relatedList = Collections.singletonList(mockEvent); - when(auditService.getRelatedSecurityEvents("session-123", 1000)).thenReturn(relatedList); + when(auditService.getRelatedSecurityEvents("session-123", 10, 0)).thenReturn(relatedList); mockMvc.perform(get("/rest/v1/securityauditlogs/releatedAudits").param("sessionId", "session-123").param("page", "0") .param("size", "10")).andExpect(status().isOk()); - verify(auditService).getRelatedSecurityEvents("session-123", 1000); + verify(auditService).getRelatedSecurityEvents("session-123", 10, 0); } } diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java index 142ddf3..f1a7a4e 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java @@ -103,7 +103,7 @@ void shouldLoadEventDetailsWithSessionAndRelatedEvents() throws Exception { when(mockEvent.getSessionId()).thenReturn("session-test"); when(auditService.getSecurityEventById(2)).thenReturn(mockEvent); - when(auditService.getRelatedSecurityEvents("session-test", 10)).thenReturn(relatedList); + when(auditService.getRelatedSecurityEvents("session-test", 10, 0)).thenReturn(relatedList); mockMvc.perform(get("/module/auditlogweb/viewSecurityAudit.form").param("eventId", "2")).andExpect(status().isOk()) .andExpect(view().name("/module/auditlogweb/viewSecurityAudit")) @@ -111,7 +111,7 @@ void shouldLoadEventDetailsWithSessionAndRelatedEvents() throws Exception { .andExpect(model().attribute("page", "securityauditlogs")); verify(auditService).getSecurityEventById(2); - verify(auditService).getRelatedSecurityEvents("session-test", 10); + verify(auditService).getRelatedSecurityEvents("session-test", 10, 0); } @Test From 41a382d82d325fd2c737547802bc994d6ff58620 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Tue, 28 Jul 2026 15:57:55 +0530 Subject: [PATCH 03/14] AUDIT-61:Replaced security event mock with real one and add response body validation too --- .../rest/SecurityAuditRestController.java | 4 +- .../rest/SecurityAuditRestControllerTest.java | 124 ++++++++++++------ 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index f63e62c..f3eabff 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -51,12 +51,12 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", if (logId != null) { AuditSecurityEvent securityEvent = auditService.getSecurityEventById(logId); if (securityEvent == null) { - return SecurityLogResponseDTO.builder().totalLogs(0).currentLogs(0) + return SecurityLogResponseDTO.builder().totalLogs(0).currentLogs(0).totalPages(0).currentPage(0) .securityAuditLogs(Collections.emptyList()).build(); } List securityAuditLogsDTO = mapToDTOs(Collections.singletonList(securityEvent)); return SecurityLogResponseDTO.builder().totalLogs(1).currentLogs(1).securityAuditLogs(securityAuditLogsDTO) - .build(); + .totalPages(1).currentPage(0).build(); } if (page < 0) { diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index 25b88d9..c1843d1 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -22,10 +22,10 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.Collections; +import java.util.Date; import java.util.List; import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -33,99 +33,141 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class SecurityAuditRestControllerTest { - + private MockMvc mockMvc; - + @Mock private AuditService auditService; - + @InjectMocks private SecurityAuditRestController securityAuditRestController; - + @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); mockMvc = MockMvcBuilders.standaloneSetup(securityAuditRestController) .setControllerAdvice(new RestExceptionHandler()).build(); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithoutFilter() throws Exception { when(auditService.getSecurityEvents(null, null, null, null, 0, 15)).thenReturn(Collections.emptyList()); when(auditService.countSecurityEvents(null, null, null, null)).thenReturn(0L); - + mockMvc.perform(get("/rest/v1/securityauditlogs")).andExpect(status().isOk()); - + verify(auditService).getSecurityEvents(null, null, null, null, 0, 15); verify(auditService).countSecurityEvents(null, null, null, null); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithIdFilter() throws Exception { - AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); - when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); - when(auditService.getSecurityEventById(1)).thenReturn(mockEvent); - - mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "1")).andExpect(status().isOk()); - + AuditSecurityEvent event = buildAuditSecurityEvent(); + when(auditService.getSecurityEventById(1)).thenReturn(event); + + mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "1")).andExpect(status().isOk()) + .andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) + .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) + .andExpect(jsonPath("$.securityAuditLogs[0].username", is("admin"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userUuid", is("user-uuid-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].ipAddress", is("127.0.0.1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userAgent", is("user-agent-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].sessionId", is("session-123"))) + .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) + .andExpect(jsonPath("$.totalPages", is(1))).andExpect(jsonPath("$.currentPage", is(0))); + verify(auditService).getSecurityEventById(1); } - + @Test public void shouldThrowErrorIfInvalidLogIdPassed() throws Exception { mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "-1")).andExpect(status().isBadRequest()) .andExpect(jsonPath("$.error", is("Bad Request"))) .andExpect(jsonPath("$.message", is("Please provide a valid log ID"))); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithEventTypeFilter() throws Exception { - AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); - when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); - List eventList = Collections.singletonList(mockEvent); - + AuditSecurityEvent event = buildAuditSecurityEvent(); + List eventList = Collections.singletonList(event); + when(auditService.getSecurityEvents("LOGIN_SUCCESS", null, null, null, 0, 15)).thenReturn(eventList); when(auditService.countSecurityEvents("LOGIN_SUCCESS", null, null, null)).thenReturn(1L); - - mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "LOGIN_SUCCESS")).andExpect(status().isOk()); - + + mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "LOGIN_SUCCESS")).andExpect(status().isOk()) + .andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) + .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) + .andExpect(jsonPath("$.securityAuditLogs[0].username", is("admin"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userUuid", is("user-uuid-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].ipAddress", is("127.0.0.1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userAgent", is("user-agent-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].sessionId", is("session-123"))) + .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) + .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) + .andExpect(jsonPath("$.currentPage", is(0))); + verify(auditService).getSecurityEvents("LOGIN_SUCCESS", null, null, null, 0, 15); verify(auditService).countSecurityEvents("LOGIN_SUCCESS", null, null, null); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithUserNameFilter() throws Exception { - AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); - when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); - List eventList = Collections.singletonList(mockEvent); - + AuditSecurityEvent event = buildAuditSecurityEvent(); + List eventList = Collections.singletonList(event); + when(auditService.getSecurityEvents(null, "admin", null, null, 0, 15)).thenReturn(eventList); when(auditService.countSecurityEvents(null, "admin", null, null)).thenReturn(1L); - - mockMvc.perform(get("/rest/v1/securityauditlogs").param("username", "admin")).andExpect(status().isOk()); - + + mockMvc.perform(get("/rest/v1/securityauditlogs").param("username", "admin")).andExpect(status().isOk()) + .andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) + .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) + .andExpect(jsonPath("$.securityAuditLogs[0].username", is("admin"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userUuid", is("user-uuid-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].ipAddress", is("127.0.0.1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userAgent", is("user-agent-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].sessionId", is("session-123"))) + .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) + .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) + .andExpect(jsonPath("$.currentPage", is(0))); + verify(auditService).getSecurityEvents(null, "admin", null, null, 0, 15); verify(auditService).countSecurityEvents(null, "admin", null, null); } - + @Test public void shouldThrowErrorIfInvalidDatePassed() throws Exception { mockMvc.perform(get("/rest/v1/securityauditlogs").param("startDate", "31/02/2025")) .andExpect(status().isBadRequest()).andExpect(jsonPath("$.error", is("Bad Request"))).andExpect(jsonPath( "$.message", is("Invalid month date or date format: '31/02/2025'. Expected format: DD/MM/YYYY"))); } - + @Test public void shouldFetchRelatedAuditsSuccessfully() throws Exception { - AuditSecurityEvent mockEvent = mock(AuditSecurityEvent.class); - when(mockEvent.getEventType()).thenReturn(AuditSecurityEventType.LOGIN_SUCCESS); - List relatedList = Collections.singletonList(mockEvent); - + AuditSecurityEvent event = buildAuditSecurityEvent(); + List relatedList = Collections.singletonList(event); + when(auditService.getRelatedSecurityEvents("session-123", 10, 0)).thenReturn(relatedList); - + when(auditService.countRelatedSecurityEvents("session-123")).thenReturn(1L); + mockMvc.perform(get("/rest/v1/securityauditlogs/releatedAudits").param("sessionId", "session-123").param("page", "0") - .param("size", "10")).andExpect(status().isOk()); - + .param("size", "10")).andExpect(status().isOk()).andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) + .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) + .andExpect(jsonPath("$.securityAuditLogs[0].username", is("admin"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userUuid", is("user-uuid-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].ipAddress", is("127.0.0.1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].userAgent", is("user-agent-1"))) + .andExpect(jsonPath("$.securityAuditLogs[0].sessionId", is("session-123"))) + .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) + .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) + .andExpect(jsonPath("$.currentPage", is(0))); + verify(auditService).getRelatedSecurityEvents("session-123", 10, 0); + verify(auditService).countRelatedSecurityEvents("session-123"); + } + + private AuditSecurityEvent buildAuditSecurityEvent() { + return AuditSecurityEvent.builder().id(1).eventType(AuditSecurityEventType.LOGIN_SUCCESS).username("admin") + .userUuid("user-uuid-1").eventTime(new Date()).ipAddress("127.0.0.1").userAgent("user-agent-1") + .sessionId("session-123").details("{}").build(); } } From 5206f029132e28059944ae43b628ef19b3913080 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Tue, 28 Jul 2026 16:25:16 +0530 Subject: [PATCH 04/14] AUDIT-61:Filtering the invalid event type --- .../rest/SecurityAuditRestController.java | 8 +++ .../rest/SecurityAuditRestControllerTest.java | 57 +++++++++++-------- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index f3eabff..1fb51d4 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -14,6 +14,7 @@ import org.openmrs.module.auditlogweb.api.AuditService; import org.openmrs.module.auditlogweb.api.dto.SecurityAuditLogDTO; import org.openmrs.module.auditlogweb.api.dto.SecurityLogResponseDTO; +import org.openmrs.module.auditlogweb.api.utils.AuditSecurityEventType; import org.openmrs.module.auditlogweb.api.utils.UtilClass; import org.openmrs.module.webservices.rest.web.RestConstants; import org.springframework.http.HttpStatus; @@ -48,6 +49,13 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", throw new IllegalArgumentException("Please provide a valid log ID"); } + if (eventType != null) { + AuditSecurityEventType parsed = AuditSecurityEventType.fromName(eventType); + if (parsed == null || parsed == AuditSecurityEventType.UNKNOWN) { + throw new IllegalArgumentException("Invalid eventType " + eventType); + } + } + if (logId != null) { AuditSecurityEvent securityEvent = auditService.getSecurityEventById(logId); if (securityEvent == null) { diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index c1843d1..67e12b4 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -33,38 +33,38 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class SecurityAuditRestControllerTest { - + private MockMvc mockMvc; - + @Mock private AuditService auditService; - + @InjectMocks private SecurityAuditRestController securityAuditRestController; - + @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); mockMvc = MockMvcBuilders.standaloneSetup(securityAuditRestController) .setControllerAdvice(new RestExceptionHandler()).build(); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithoutFilter() throws Exception { when(auditService.getSecurityEvents(null, null, null, null, 0, 15)).thenReturn(Collections.emptyList()); when(auditService.countSecurityEvents(null, null, null, null)).thenReturn(0L); - + mockMvc.perform(get("/rest/v1/securityauditlogs")).andExpect(status().isOk()); - + verify(auditService).getSecurityEvents(null, null, null, null, 0, 15); verify(auditService).countSecurityEvents(null, null, null, null); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithIdFilter() throws Exception { AuditSecurityEvent event = buildAuditSecurityEvent(); when(auditService.getSecurityEventById(1)).thenReturn(event); - + mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "1")).andExpect(status().isOk()) .andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) @@ -75,25 +75,32 @@ public void shouldFetchSecurityAuditsSuccessfullyWithIdFilter() throws Exception .andExpect(jsonPath("$.securityAuditLogs[0].sessionId", is("session-123"))) .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) .andExpect(jsonPath("$.totalPages", is(1))).andExpect(jsonPath("$.currentPage", is(0))); - + verify(auditService).getSecurityEventById(1); } - + @Test public void shouldThrowErrorIfInvalidLogIdPassed() throws Exception { mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "-1")).andExpect(status().isBadRequest()) .andExpect(jsonPath("$.error", is("Bad Request"))) .andExpect(jsonPath("$.message", is("Please provide a valid log ID"))); } - + + @Test + public void shouldThrowErrorIfInvalidEventTypePassed() throws Exception { + mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "LOGIN_SUCESS")) + .andExpect(status().isBadRequest()).andExpect(jsonPath("$.error", is("Bad Request"))) + .andExpect(jsonPath("$.message", is("Invalid eventType LOGIN_SUCESS"))); + } + @Test public void shouldFetchSecurityAuditsSuccessfullyWithEventTypeFilter() throws Exception { AuditSecurityEvent event = buildAuditSecurityEvent(); List eventList = Collections.singletonList(event); - + when(auditService.getSecurityEvents("LOGIN_SUCCESS", null, null, null, 0, 15)).thenReturn(eventList); when(auditService.countSecurityEvents("LOGIN_SUCCESS", null, null, null)).thenReturn(1L); - + mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "LOGIN_SUCCESS")).andExpect(status().isOk()) .andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) @@ -105,19 +112,19 @@ public void shouldFetchSecurityAuditsSuccessfullyWithEventTypeFilter() throws Ex .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) .andExpect(jsonPath("$.currentPage", is(0))); - + verify(auditService).getSecurityEvents("LOGIN_SUCCESS", null, null, null, 0, 15); verify(auditService).countSecurityEvents("LOGIN_SUCCESS", null, null, null); } - + @Test public void shouldFetchSecurityAuditsSuccessfullyWithUserNameFilter() throws Exception { AuditSecurityEvent event = buildAuditSecurityEvent(); List eventList = Collections.singletonList(event); - + when(auditService.getSecurityEvents(null, "admin", null, null, 0, 15)).thenReturn(eventList); when(auditService.countSecurityEvents(null, "admin", null, null)).thenReturn(1L); - + mockMvc.perform(get("/rest/v1/securityauditlogs").param("username", "admin")).andExpect(status().isOk()) .andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) @@ -129,26 +136,26 @@ public void shouldFetchSecurityAuditsSuccessfullyWithUserNameFilter() throws Exc .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) .andExpect(jsonPath("$.currentPage", is(0))); - + verify(auditService).getSecurityEvents(null, "admin", null, null, 0, 15); verify(auditService).countSecurityEvents(null, "admin", null, null); } - + @Test public void shouldThrowErrorIfInvalidDatePassed() throws Exception { mockMvc.perform(get("/rest/v1/securityauditlogs").param("startDate", "31/02/2025")) .andExpect(status().isBadRequest()).andExpect(jsonPath("$.error", is("Bad Request"))).andExpect(jsonPath( "$.message", is("Invalid month date or date format: '31/02/2025'. Expected format: DD/MM/YYYY"))); } - + @Test public void shouldFetchRelatedAuditsSuccessfully() throws Exception { AuditSecurityEvent event = buildAuditSecurityEvent(); List relatedList = Collections.singletonList(event); - + when(auditService.getRelatedSecurityEvents("session-123", 10, 0)).thenReturn(relatedList); when(auditService.countRelatedSecurityEvents("session-123")).thenReturn(1L); - + mockMvc.perform(get("/rest/v1/securityauditlogs/releatedAudits").param("sessionId", "session-123").param("page", "0") .param("size", "10")).andExpect(status().isOk()).andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) @@ -160,11 +167,11 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { .andExpect(jsonPath("$.securityAuditLogs[0].details", is("{}"))).andExpect(jsonPath("$.totalLogs", is(1))) .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) .andExpect(jsonPath("$.currentPage", is(0))); - + verify(auditService).getRelatedSecurityEvents("session-123", 10, 0); verify(auditService).countRelatedSecurityEvents("session-123"); } - + private AuditSecurityEvent buildAuditSecurityEvent() { return AuditSecurityEvent.builder().id(1).eventType(AuditSecurityEventType.LOGIN_SUCCESS).username("admin") .userUuid("user-uuid-1").eventTime(new Date()).ipAddress("127.0.0.1").userAgent("user-agent-1") From f6ac8f847e92745a14bcc4674040ecde95621213 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Wed, 29 Jul 2026 00:40:06 +0530 Subject: [PATCH 05/14] AUDIT-61:Fixed related audit log fetch API misspelling --- .../module/auditlogweb/rest/ReadAuditRestController.java | 2 +- .../module/auditlogweb/rest/SecurityAuditRestController.java | 2 +- .../module/auditlogweb/rest/ReadAuditRestControllerTest.java | 2 +- .../auditlogweb/rest/SecurityAuditRestControllerTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestController.java index 5b54ba9..8d14289 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestController.java @@ -81,7 +81,7 @@ public ReadAuditLogResponseDTO fetchReadAudits(@RequestParam(value = "logId", re } - @GetMapping("/releatedAudits") + @GetMapping("/relatedAudits") public ReadAuditLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionId") String sessionId, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "15") int size) { diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index 1fb51d4..7d58ef6 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -88,7 +88,7 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); } - @GetMapping("/releatedAudits") + @GetMapping("/relatedAudits") public SecurityLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionId") String sessionId, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "15") int size) { diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestControllerTest.java index 1b4cf6b..a17860e 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/ReadAuditRestControllerTest.java @@ -134,7 +134,7 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { when(readAuditService.countRelatedReadLogs("session-123")).thenReturn(1L); when(readAuditService.mapToReadAuditLogDTO(relatedList)).thenReturn(Collections.emptyList()); - mockMvc.perform(get("/rest/v1/readauditlogs/releatedAudits").param("sessionId", "session-123").param("page", "0") + mockMvc.perform(get("/rest/v1/readauditlogs/relatedAudits").param("sessionId", "session-123").param("page", "0") .param("size", "10")).andExpect(status().isOk()); verify(readAuditService).getRelatedReadLogs("session-123", 0, 10); diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index 67e12b4..2ccf1e1 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -156,7 +156,7 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { when(auditService.getRelatedSecurityEvents("session-123", 10, 0)).thenReturn(relatedList); when(auditService.countRelatedSecurityEvents("session-123")).thenReturn(1L); - mockMvc.perform(get("/rest/v1/securityauditlogs/releatedAudits").param("sessionId", "session-123").param("page", "0") + mockMvc.perform(get("/rest/v1/securityauditlogs/relatedAudits").param("sessionId", "session-123").param("page", "0") .param("size", "10")).andExpect(status().isOk()).andExpect(jsonPath("$.securityAuditLogs[0].id", is(1))) .andExpect(jsonPath("$.securityAuditLogs[0].eventType", is("LOGIN_SUCCESS"))) .andExpect(jsonPath("$.securityAuditLogs[0].username", is("admin"))) From 850b6cfd36171584852a9c6b2907b26eb965378a Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Wed, 29 Jul 2026 01:20:08 +0530 Subject: [PATCH 06/14] AUDIT-61:Added APIAuthException exception handler for REST API --- .../auditlogweb/rest/exceptions/RestExceptionHandler.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java index be6c635..3d7e51d 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java @@ -10,6 +10,7 @@ package org.openmrs.module.auditlogweb.rest.exceptions; import org.hibernate.ObjectNotFoundException; +import org.openmrs.api.APIAuthenticationException; import org.openmrs.module.auditlogweb.api.exception.AuditLogUnavailableException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -84,6 +85,11 @@ public ResponseEntity> handleAuditLogUnavailable(AuditLogUna return buildResponseEntity("Audit Log Unavailable", ex.getMessage(), HttpStatus.SERVICE_UNAVAILABLE); } + @ExceptionHandler(APIAuthenticationException.class) + public ResponseEntity> handleAPIAuthException(APIAuthenticationException ex) { + return buildResponseEntity("Unauthorized access", ex.getMessage(), HttpStatus.FORBIDDEN); + } + @ExceptionHandler(Exception.class) public ResponseEntity> handleGeneralError(Exception ex) { return buildResponseEntity("Internal Server Error", "An unexpected error occurred", From ff7ecce4c1c07a9aab08b432604bb5c55b60bcaa Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Wed, 29 Jul 2026 08:42:26 +0530 Subject: [PATCH 07/14] AUDIT-61:Fixed Javadoc and added for new method and fixed privilege for new method --- .../openmrs/module/auditlogweb/api/AuditService.java | 10 ++++++++-- .../openmrs/module/auditlogweb/api/dao/AuditDao.java | 10 ++++++++-- .../module/auditlogweb/api/impl/AuditServiceImpl.java | 2 +- .../module/auditlogweb/api/dao/AuditDaoTest.java | 2 +- .../auditlogweb/api/impl/AuditServiceImplTest.java | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java index 73d1ea2..c140a6b 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java @@ -334,14 +334,20 @@ void logSecurityEvent(AuditSecurityEventType eventType, String username, String * Retrieves the most recent N security events from the same session (for related activity). * * @param sessionId the session ID to filter by - * @param page defines the particular page in a paginated view * @param size defines how many logs want in one go + * @param page defines the particular page in a paginated view * @return a list of {@link AuditSecurityEvent} ordered by eventTime descending */ @Authorized(AuditLogConstants.VIEW_SECURITY_AUDIT_LOGS) List getRelatedSecurityEvents(String sessionId, int size, int page); - @Authorized(AuditLogConstants.VIEW_READ_AUDIT_LOGS) + /** + * Counts the related security audit events with session ID filter. + * + * @param sessionId the session ID to filter by + * @return the number of logs found + */ + @Authorized(AuditLogConstants.VIEW_SECURITY_AUDIT_LOGS) long countRelatedSecurityEvents(String sessionId); } diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java index 1b34acb..7b542f0 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java @@ -714,8 +714,8 @@ public AuditSecurityEvent getSecurityEventById(Integer eventId) { * Retrieves the most recent N security events from the same session (for related activity). * * @param sessionId the session ID to filter by - * @param page defines the particular page in a paginated view * @param size defines how many logs want in one go + * @param page defines the particular page in a paginated view * @return a list of {@link AuditSecurityEvent} ordered by eventTime descending */ public List getRelatedSecurityEvents(String sessionId, int size, int page) { @@ -727,7 +727,13 @@ public List getRelatedSecurityEvents(String sessionId, int s return query.getResultList(); } - public long countRelatedSecurityEvent(String sessionId) { + /** + * Returns the count for all related security events for the given session ID + * + * @param sessionId the session ID to filter by + * @return the count of logs found + */ + public long countRelatedSecurityEvents(String sessionId) { Query query = sessionFactory.getCurrentSession() .createQuery("select count(e) from AuditSecurityEvent e where e.sessionId = :sessionId", Long.class); query.setParameter("sessionId", sessionId); diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java index 2d7aa9e..3940fbc 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java @@ -393,7 +393,7 @@ public List getRelatedSecurityEvents(String sessionId, int s @Override public long countRelatedSecurityEvents(String sessionId) { - return auditDao.countRelatedSecurityEvent(sessionId); + return auditDao.countRelatedSecurityEvents(sessionId); } private Object fetchPreviousRevision(AuditEntity entity, Object currentEntity) { diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java index d87a2a7..949ea7f 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java @@ -612,7 +612,7 @@ void shouldReturnRelatedSecurityEventsCount() { when(countQuery.setParameter(eq("sessionId"), eq("session-123"))).thenReturn(countQuery); when(countQuery.uniqueResult()).thenReturn(10L); - long count = auditDao.countRelatedSecurityEvent("session-123"); + long count = auditDao.countRelatedSecurityEvents("session-123"); assertThat(count, is(10L)); verify(countQuery).setParameter("sessionId", "session-123"); diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java index 68dbc84..4e2ecf9 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java @@ -410,7 +410,7 @@ void shouldReturnRelatedSecurityEvents() { @Test void shouldCountRelatedSecurityEvents() { - when(auditDao.countRelatedSecurityEvent("session-123")).thenReturn(10L); + when(auditDao.countRelatedSecurityEvents("session-123")).thenReturn(10L); long count = auditService.countRelatedSecurityEvents("session-123"); assertEquals(10L, count); From 09a7ad0503e543bb4b68c74436c6b541f15bc092 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Wed, 29 Jul 2026 08:54:04 +0530 Subject: [PATCH 08/14] AUDIT-61:Fixed authentication and authorization error handling and event type param --- .../auditlogweb/rest/SecurityAuditRestController.java | 2 +- .../auditlogweb/rest/exceptions/RestExceptionHandler.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index 7d58ef6..dd36221 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -49,7 +49,7 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", throw new IllegalArgumentException("Please provide a valid log ID"); } - if (eventType != null) { + if (eventType != null && !eventType.trim().isEmpty()) { AuditSecurityEventType parsed = AuditSecurityEventType.fromName(eventType); if (parsed == null || parsed == AuditSecurityEventType.UNKNOWN) { throw new IllegalArgumentException("Invalid eventType " + eventType); diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java index 3d7e51d..6ca0f20 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/exceptions/RestExceptionHandler.java @@ -11,6 +11,7 @@ import org.hibernate.ObjectNotFoundException; import org.openmrs.api.APIAuthenticationException; +import org.openmrs.api.context.Context; import org.openmrs.module.auditlogweb.api.exception.AuditLogUnavailableException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -87,7 +88,10 @@ public ResponseEntity> handleAuditLogUnavailable(AuditLogUna @ExceptionHandler(APIAuthenticationException.class) public ResponseEntity> handleAPIAuthException(APIAuthenticationException ex) { - return buildResponseEntity("Unauthorized access", ex.getMessage(), HttpStatus.FORBIDDEN); + if (Context.isAuthenticated()) { + return buildResponseEntity("Forbidden", ex.getMessage(), HttpStatus.FORBIDDEN); + } + return buildResponseEntity("Unauthorized", ex.getMessage(), HttpStatus.UNAUTHORIZED); } @ExceptionHandler(Exception.class) From 6a26eb7cb143d3e6474ee1e4f56a6ef2d21c7cfc Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Thu, 30 Jul 2026 00:07:49 +0530 Subject: [PATCH 09/14] AUDIT-61:Fixed params ordering for related security method and tweaked the test cases --- .../openmrs/module/auditlogweb/api/AuditService.java | 4 ++-- .../openmrs/module/auditlogweb/api/dao/AuditDao.java | 4 ++-- .../module/auditlogweb/api/impl/AuditServiceImpl.java | 4 ++-- .../module/auditlogweb/api/dao/AuditDaoTest.java | 10 +++++----- .../auditlogweb/api/impl/AuditServiceImplTest.java | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java index c140a6b..59444ca 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/AuditService.java @@ -334,12 +334,12 @@ void logSecurityEvent(AuditSecurityEventType eventType, String username, String * Retrieves the most recent N security events from the same session (for related activity). * * @param sessionId the session ID to filter by - * @param size defines how many logs want in one go * @param page defines the particular page in a paginated view + * @param size defines how many logs want in one go * @return a list of {@link AuditSecurityEvent} ordered by eventTime descending */ @Authorized(AuditLogConstants.VIEW_SECURITY_AUDIT_LOGS) - List getRelatedSecurityEvents(String sessionId, int size, int page); + List getRelatedSecurityEvents(String sessionId, int page, int size); /** * Counts the related security audit events with session ID filter. diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java index 7b542f0..4e23887 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/dao/AuditDao.java @@ -714,11 +714,11 @@ public AuditSecurityEvent getSecurityEventById(Integer eventId) { * Retrieves the most recent N security events from the same session (for related activity). * * @param sessionId the session ID to filter by - * @param size defines how many logs want in one go * @param page defines the particular page in a paginated view + * @param size defines how many logs want in one go * @return a list of {@link AuditSecurityEvent} ordered by eventTime descending */ - public List getRelatedSecurityEvents(String sessionId, int size, int page) { + public List getRelatedSecurityEvents(String sessionId, int page, int size) { Query query = sessionFactory.getCurrentSession().createQuery( "from AuditSecurityEvent e where e.sessionId = :sessionId order by e.eventTime desc", AuditSecurityEvent.class); query.setParameter("sessionId", sessionId); diff --git a/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java b/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java index 3940fbc..228d77d 100644 --- a/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImpl.java @@ -387,8 +387,8 @@ public AuditSecurityEvent getSecurityEventById(Integer eventId) { } @Override - public List getRelatedSecurityEvents(String sessionId, int size, int page) { - return auditDao.getRelatedSecurityEvents(sessionId, size, page); + public List getRelatedSecurityEvents(String sessionId, int page, int size) { + return auditDao.getRelatedSecurityEvents(sessionId, page, size); } @Override diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java index 949ea7f..6869ac8 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java @@ -580,16 +580,16 @@ void shouldReturnRelatedSecurityEvents_WhenSessionIdProvided() { when(session.createQuery(anyString(), eq(AuditSecurityEvent.class))).thenReturn(securityEventQuery); when(securityEventQuery.setParameter("sessionId", sessionId)).thenReturn(securityEventQuery); - when(securityEventQuery.setFirstResult(0)).thenReturn(securityEventQuery); - when(securityEventQuery.setMaxResults(5)).thenReturn(securityEventQuery); + when(securityEventQuery.setFirstResult(30)).thenReturn(securityEventQuery); + when(securityEventQuery.setMaxResults(2)).thenReturn(securityEventQuery); when(securityEventQuery.getResultList()).thenReturn(Arrays.asList(e1, e2)); - List result = auditDao.getRelatedSecurityEvents(sessionId, 5, 0); + List result = auditDao.getRelatedSecurityEvents(sessionId, 15, 2); assertNotNull(result); assertThat(result, hasSize(2)); verify(securityEventQuery).setParameter("sessionId", sessionId); - verify(securityEventQuery).setMaxResults(5); + verify(securityEventQuery).setMaxResults(2); } @Test @@ -600,7 +600,7 @@ void shouldReturnEmptyList_WhenNoRelatedSecurityEventsFound() { when(securityEventQuery.setMaxResults(anyInt())).thenReturn(securityEventQuery); when(securityEventQuery.getResultList()).thenReturn(Collections.emptyList()); - List result = auditDao.getRelatedSecurityEvents("sess-ghost", 10, 0); + List result = auditDao.getRelatedSecurityEvents("sess-ghost", 0, 10); assertNotNull(result); assertThat(result, empty()); diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java index 4e2ecf9..fad6b1c 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/impl/AuditServiceImplTest.java @@ -397,10 +397,10 @@ void shouldReturnRelatedSecurityEvents() { AuditSecurityEvent relatedSecurityEvent2 = AuditSecurityEvent.builder() .eventType(AuditSecurityEventType.LOGIN_SUCCESS).eventTime(new Date()).sessionId("session-123").build(); - when(auditDao.getRelatedSecurityEvents("session-123", 2, 0)) + when(auditDao.getRelatedSecurityEvents("session-123", 0, 2)) .thenReturn(Arrays.asList(relatedSecurityEvent1, relatedSecurityEvent2)); - List result = auditService.getRelatedSecurityEvents("session-123", 2, 0); + List result = auditService.getRelatedSecurityEvents("session-123", 0, 2); assertNotNull(result); assertEquals(2, result.size()); From f012ec31d401b450aa44b4f6edc1392ae529d879 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Thu, 30 Jul 2026 01:09:40 +0530 Subject: [PATCH 10/14] AUDIT-61:Added more test cases for empty event param and auth error cases --- .../rest/SecurityAuditRestControllerTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index 2ccf1e1..1e12251 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -13,7 +13,10 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; +import org.openmrs.api.APIAuthenticationException; +import org.openmrs.api.context.Context; import org.openmrs.module.auditlogweb.AuditSecurityEvent; import org.openmrs.module.auditlogweb.api.AuditService; import org.openmrs.module.auditlogweb.api.utils.AuditSecurityEventType; @@ -26,8 +29,11 @@ import java.util.List; import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mockStatic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -93,6 +99,11 @@ public void shouldThrowErrorIfInvalidEventTypePassed() throws Exception { .andExpect(jsonPath("$.message", is("Invalid eventType LOGIN_SUCESS"))); } + @Test + public void shouldReturnStatusOkIfEmptyEventTypePassed() throws Exception { + mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "")).andExpect(status().isOk()); + } + @Test public void shouldFetchSecurityAuditsSuccessfullyWithEventTypeFilter() throws Exception { AuditSecurityEvent event = buildAuditSecurityEvent(); @@ -172,6 +183,28 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { verify(auditService).countRelatedSecurityEvents("session-123"); } + @Test + public void shouldThrowUnauthorizedErrorIfNotAuthenticated() throws Exception { + when(auditService.getSecurityEvents(isNull(), isNull(), isNull(), isNull(), anyInt(), anyInt())) + .thenThrow(new APIAuthenticationException("Privileges required: View Security Audit Logs")); + try (MockedStatic ctx = mockStatic(Context.class)) { + ctx.when(Context::isAuthenticated).thenReturn(false); + mockMvc.perform(get("/rest/v1/securityauditlogs")).andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.error", is("Unauthorized"))); + } + } + + @Test + public void shouldThrowForbiddenErrorIfNotHasPrivileged() throws Exception { + when(auditService.getSecurityEvents(isNull(), isNull(), isNull(), isNull(), anyInt(), anyInt())) + .thenThrow(new APIAuthenticationException("Privileges required: View Security Audit Logs")); + try (MockedStatic ctx = mockStatic(Context.class)) { + ctx.when(Context::isAuthenticated).thenReturn(true); + mockMvc.perform(get("/rest/v1/securityauditlogs")).andExpect(status().isForbidden()) + .andExpect(jsonPath("$.error", is("Forbidden"))); + } + } + private AuditSecurityEvent buildAuditSecurityEvent() { return AuditSecurityEvent.builder().id(1).eventType(AuditSecurityEventType.LOGIN_SUCCESS).username("admin") .userUuid("user-uuid-1").eventTime(new Date()).ipAddress("127.0.0.1").userAgent("user-agent-1") From 4444444e5aa53e2835277acbb0d0908c67364809 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Tue, 11 Aug 2026 23:15:14 +0530 Subject: [PATCH 11/14] AUDIT-61:Fixed args order and thier test cases --- .../org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java | 1 + .../module/auditlogweb/rest/SecurityAuditRestController.java | 2 +- .../web/controller/SecurityAuditDetailController.java | 2 +- .../auditlogweb/rest/SecurityAuditRestControllerTest.java | 4 ++-- .../web/controller/SecurityAuditDetailControllerTest.java | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java index 6869ac8..1692280 100644 --- a/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java +++ b/api/src/test/java/org/openmrs/module/auditlogweb/api/dao/AuditDaoTest.java @@ -589,6 +589,7 @@ void shouldReturnRelatedSecurityEvents_WhenSessionIdProvided() { assertNotNull(result); assertThat(result, hasSize(2)); verify(securityEventQuery).setParameter("sessionId", sessionId); + verify(securityEventQuery).setFirstResult(30); verify(securityEventQuery).setMaxResults(2); } diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index dd36221..5700863 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -104,7 +104,7 @@ public SecurityLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionI size = 15; } - List allRelated = auditService.getRelatedSecurityEvents(sessionId, size, page); + List allRelated = auditService.getRelatedSecurityEvents(sessionId, page, size); long totalCount = auditService.countRelatedSecurityEvents(sessionId); int totalPages = UtilClass.computeTotalPages(totalCount, size); diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java index 5e22821..72e3eca 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailController.java @@ -83,7 +83,7 @@ public ModelAndView showDetails(HttpServletRequest request, ModelMap model) { // Fetch related events from the same session List relatedEvents = null; if (event.getSessionId() != null && !event.getSessionId().isEmpty()) { - relatedEvents = auditService.getRelatedSecurityEvents(event.getSessionId(), RELATED_EVENTS_LIMIT, 0); + relatedEvents = auditService.getRelatedSecurityEvents(event.getSessionId(), 0, RELATED_EVENTS_LIMIT); } model.addAttribute("event", event); diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index 1e12251..a9c92f5 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -164,7 +164,7 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { AuditSecurityEvent event = buildAuditSecurityEvent(); List relatedList = Collections.singletonList(event); - when(auditService.getRelatedSecurityEvents("session-123", 10, 0)).thenReturn(relatedList); + when(auditService.getRelatedSecurityEvents("session-123", 0, 10)).thenReturn(relatedList); when(auditService.countRelatedSecurityEvents("session-123")).thenReturn(1L); mockMvc.perform(get("/rest/v1/securityauditlogs/relatedAudits").param("sessionId", "session-123").param("page", "0") @@ -179,7 +179,7 @@ public void shouldFetchRelatedAuditsSuccessfully() throws Exception { .andExpect(jsonPath("$.currentLogs", is(1))).andExpect(jsonPath("$.totalPages", is(1))) .andExpect(jsonPath("$.currentPage", is(0))); - verify(auditService).getRelatedSecurityEvents("session-123", 10, 0); + verify(auditService).getRelatedSecurityEvents("session-123", 0, 10); verify(auditService).countRelatedSecurityEvents("session-123"); } diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java index f1a7a4e..af9c69f 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/web/controller/SecurityAuditDetailControllerTest.java @@ -103,7 +103,7 @@ void shouldLoadEventDetailsWithSessionAndRelatedEvents() throws Exception { when(mockEvent.getSessionId()).thenReturn("session-test"); when(auditService.getSecurityEventById(2)).thenReturn(mockEvent); - when(auditService.getRelatedSecurityEvents("session-test", 10, 0)).thenReturn(relatedList); + when(auditService.getRelatedSecurityEvents("session-test", 0, 10)).thenReturn(relatedList); mockMvc.perform(get("/module/auditlogweb/viewSecurityAudit.form").param("eventId", "2")).andExpect(status().isOk()) .andExpect(view().name("/module/auditlogweb/viewSecurityAudit")) @@ -111,7 +111,7 @@ void shouldLoadEventDetailsWithSessionAndRelatedEvents() throws Exception { .andExpect(model().attribute("page", "securityauditlogs")); verify(auditService).getSecurityEventById(2); - verify(auditService).getRelatedSecurityEvents("session-test", 10, 0); + verify(auditService).getRelatedSecurityEvents("session-test", 0, 10); } @Test From 2d126c58b7a21f14c15a5c06d19d1718daf2648c Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Wed, 12 Aug 2026 23:49:22 +0530 Subject: [PATCH 12/14] AUDIT-61:Added not found error message instead of blank page --- .../auditlogweb/rest/SecurityAuditRestController.java | 4 ++-- .../auditlogweb/rest/SecurityAuditRestControllerTest.java | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index 5700863..6ec0b4f 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -18,6 +18,7 @@ import org.openmrs.module.auditlogweb.api.utils.UtilClass; import org.openmrs.module.webservices.rest.web.RestConstants; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -59,8 +60,7 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", if (logId != null) { AuditSecurityEvent securityEvent = auditService.getSecurityEventById(logId); if (securityEvent == null) { - return SecurityLogResponseDTO.builder().totalLogs(0).currentLogs(0).totalPages(0).currentPage(0) - .securityAuditLogs(Collections.emptyList()).build(); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No log found for this logId"); } List securityAuditLogsDTO = mapToDTOs(Collections.singletonList(securityEvent)); return SecurityLogResponseDTO.builder().totalLogs(1).currentLogs(1).securityAuditLogs(securityAuditLogsDTO) diff --git a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java index a9c92f5..006a95f 100644 --- a/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestControllerTest.java @@ -92,6 +92,14 @@ public void shouldThrowErrorIfInvalidLogIdPassed() throws Exception { .andExpect(jsonPath("$.message", is("Please provide a valid log ID"))); } + @Test + public void shouldThrowNotFoundErrorIfLogNotFoundForId() throws Exception { + when(auditService.getSecurityEventById(anyInt())).thenReturn(null); + mockMvc.perform(get("/rest/v1/securityauditlogs").param("logId", "2121")).andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error", is("Not Found"))) + .andExpect(jsonPath("$.message", is("No log found for this logId"))); + } + @Test public void shouldThrowErrorIfInvalidEventTypePassed() throws Exception { mockMvc.perform(get("/rest/v1/securityauditlogs").param("eventType", "LOGIN_SUCESS")) From fe17e13b55455e13f6f228f03596746fc99c18b7 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Thu, 13 Aug 2026 02:06:36 +0530 Subject: [PATCH 13/14] AUDIT-61:Removed unused imports --- .../rest/SecurityAuditRestController.java | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index 6ec0b4f..87d028b 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -18,7 +18,6 @@ import org.openmrs.module.auditlogweb.api.utils.UtilClass; import org.openmrs.module.webservices.rest.web.RestConstants; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -34,9 +33,9 @@ @RequestMapping("/rest/" + RestConstants.VERSION_1 + "/securityauditlogs") @RequiredArgsConstructor public class SecurityAuditRestController { - + private final AuditService auditService; - + @GetMapping public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", required = false) Integer logId, @RequestParam(value = "eventType", required = false) String eventType, @@ -45,18 +44,18 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", @RequestParam(value = "endDate", required = false) String endDate, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "15") int size) { - + if (logId != null && logId <= 0) { throw new IllegalArgumentException("Please provide a valid log ID"); } - + if (eventType != null && !eventType.trim().isEmpty()) { AuditSecurityEventType parsed = AuditSecurityEventType.fromName(eventType); if (parsed == null || parsed == AuditSecurityEventType.UNKNOWN) { throw new IllegalArgumentException("Invalid eventType " + eventType); } } - + if (logId != null) { AuditSecurityEvent securityEvent = auditService.getSecurityEventById(logId); if (securityEvent == null) { @@ -66,54 +65,54 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", return SecurityLogResponseDTO.builder().totalLogs(1).currentLogs(1).securityAuditLogs(securityAuditLogsDTO) .totalPages(1).currentPage(0).build(); } - + if (page < 0) { page = 0; } if (size <= 0) { size = 15; } - + Date start = UtilClass.parseDate(startDate, false); Date end = UtilClass.parseDate(endDate, true); - + List securityEvents = auditService.getSecurityEvents(eventType, username, start, end, page, size); long totalCount = auditService.countSecurityEvents(eventType, username, start, end); int totalPages = UtilClass.computeTotalPages(totalCount, size); - + List securityAuditLogsDTO = mapToDTOs(securityEvents); - + return SecurityLogResponseDTO.builder().totalLogs(totalCount).currentLogs(securityAuditLogsDTO.size()) .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); } - + @GetMapping("/relatedAudits") public SecurityLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionId") String sessionId, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "15") int size) { - + if (sessionId == null || sessionId.isEmpty()) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid session id"); } - + if (page < 0) { page = 0; } if (size <= 0) { size = 15; } - + List allRelated = auditService.getRelatedSecurityEvents(sessionId, page, size); long totalCount = auditService.countRelatedSecurityEvents(sessionId); int totalPages = UtilClass.computeTotalPages(totalCount, size); - + List securityAuditLogsDTO = mapToDTOs(allRelated); - + return SecurityLogResponseDTO.builder().totalLogs(totalCount).currentLogs(securityAuditLogsDTO.size()) .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); } - + private SecurityAuditLogDTO mapToDTO(AuditSecurityEvent event) { if (event == null) { return null; @@ -122,7 +121,7 @@ private SecurityAuditLogDTO mapToDTO(AuditSecurityEvent event) { .userUuid(event.getUserUuid()).eventTime(event.getEventTime()).ipAddress(event.getIpAddress()) .userAgent(event.getUserAgent()).sessionId(event.getSessionId()).details(event.getDetails()).build(); } - + private List mapToDTOs(List events) { if (events == null) { return Collections.emptyList(); From da812af48859252472f7c74de3cc4e152fb4c291 Mon Sep 17 00:00:00 2001 From: sudhanshu_raj Date: Thu, 13 Aug 2026 10:09:48 +0530 Subject: [PATCH 14/14] AUDIT-61:Fixed formatting changes --- .../rest/SecurityAuditRestController.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java index 87d028b..76c4372 100644 --- a/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java +++ b/omod/src/main/java/org/openmrs/module/auditlogweb/rest/SecurityAuditRestController.java @@ -33,9 +33,9 @@ @RequestMapping("/rest/" + RestConstants.VERSION_1 + "/securityauditlogs") @RequiredArgsConstructor public class SecurityAuditRestController { - + private final AuditService auditService; - + @GetMapping public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", required = false) Integer logId, @RequestParam(value = "eventType", required = false) String eventType, @@ -44,18 +44,18 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", @RequestParam(value = "endDate", required = false) String endDate, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "15") int size) { - + if (logId != null && logId <= 0) { throw new IllegalArgumentException("Please provide a valid log ID"); } - + if (eventType != null && !eventType.trim().isEmpty()) { AuditSecurityEventType parsed = AuditSecurityEventType.fromName(eventType); if (parsed == null || parsed == AuditSecurityEventType.UNKNOWN) { throw new IllegalArgumentException("Invalid eventType " + eventType); } } - + if (logId != null) { AuditSecurityEvent securityEvent = auditService.getSecurityEventById(logId); if (securityEvent == null) { @@ -65,54 +65,54 @@ public SecurityLogResponseDTO fetchSecurityAudits(@RequestParam(value = "logId", return SecurityLogResponseDTO.builder().totalLogs(1).currentLogs(1).securityAuditLogs(securityAuditLogsDTO) .totalPages(1).currentPage(0).build(); } - + if (page < 0) { page = 0; } if (size <= 0) { size = 15; } - + Date start = UtilClass.parseDate(startDate, false); Date end = UtilClass.parseDate(endDate, true); - + List securityEvents = auditService.getSecurityEvents(eventType, username, start, end, page, size); long totalCount = auditService.countSecurityEvents(eventType, username, start, end); int totalPages = UtilClass.computeTotalPages(totalCount, size); - + List securityAuditLogsDTO = mapToDTOs(securityEvents); - + return SecurityLogResponseDTO.builder().totalLogs(totalCount).currentLogs(securityAuditLogsDTO.size()) .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); } - + @GetMapping("/relatedAudits") public SecurityLogResponseDTO fetchRelatedAudits(@RequestParam(value = "sessionId") String sessionId, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "15") int size) { - + if (sessionId == null || sessionId.isEmpty()) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid session id"); } - + if (page < 0) { page = 0; } if (size <= 0) { size = 15; } - + List allRelated = auditService.getRelatedSecurityEvents(sessionId, page, size); long totalCount = auditService.countRelatedSecurityEvents(sessionId); int totalPages = UtilClass.computeTotalPages(totalCount, size); - + List securityAuditLogsDTO = mapToDTOs(allRelated); - + return SecurityLogResponseDTO.builder().totalLogs(totalCount).currentLogs(securityAuditLogsDTO.size()) .totalPages(totalPages).currentPage(page).securityAuditLogs(securityAuditLogsDTO).build(); } - + private SecurityAuditLogDTO mapToDTO(AuditSecurityEvent event) { if (event == null) { return null; @@ -121,7 +121,7 @@ private SecurityAuditLogDTO mapToDTO(AuditSecurityEvent event) { .userUuid(event.getUserUuid()).eventTime(event.getEventTime()).ipAddress(event.getIpAddress()) .userAgent(event.getUserAgent()).sessionId(event.getSessionId()).details(event.getDetails()).build(); } - + private List mapToDTOs(List events) { if (events == null) { return Collections.emptyList();