diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/controller/AuditController.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/controller/AuditController.java index 987d843c..120aacbf 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/controller/AuditController.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/controller/AuditController.java @@ -42,9 +42,9 @@ @RequestMapping("/audit") @RestController public class AuditController extends BaseController { - + private static final String AUDIT_REPORT_PREFIX = "auditreport_"; - + private static final String AUDIT_REPORT_EXTENSION = ".csv"; private static final Logger logger = LoggerFactory.getLogger(AuditController.class); @@ -63,14 +63,15 @@ public class AuditController extends BaseController { public ResponseEntity getAuditReport(@Valid @RequestBody AuditReportRequest auditReportRequest, HttpServletRequest request) { - Page pageable = auditService.getAffectedParties( - auditReportRequest.getTransactionTypes(), auditReportRequest.getOrganizations(), - auditReportRequest.getUserId(), auditReportRequest.getStartDate(), auditReportRequest.getEndDate(), - auditReportRequest.getPage(), auditReportRequest.getRows(), auditReportRequest.getSortField(), auditReportRequest.getSortDirection()); + Page pageable = auditService.getAffectedParties(auditReportRequest.getTransactionTypes(), + auditReportRequest.getOrganizations(), auditReportRequest.getSpgRoles(), auditReportRequest.getUserId(), + auditReportRequest.getStartDate(), auditReportRequest.getEndDate(), auditReportRequest.getPage(), + auditReportRequest.getRows(), auditReportRequest.getSortField(), auditReportRequest.getSortDirection()); List auditReport = convertReport(pageable.getContent()); int first = auditReportRequest.getPage() * auditReportRequest.getRows(); - logger.info("Returning {}-{} of {} audit records", first, first + pageable.getNumberOfElements(), pageable.getTotalElements()); + logger.info("Returning {}-{} of {} audit records", first, first + pageable.getNumberOfElements(), + pageable.getTotalElements()); AuditReportResponse auditReportingResponse = new AuditReportResponse(); auditReportingResponse.setRecords(auditReport); @@ -93,9 +94,10 @@ public ResponseEntity> getOrganizations() { ResponseEntity> responseEntity = ResponseEntity.ok(organizations); return responseEntity; } - + /** * Retrieves audit records for download + * * @param auditReportRequest * @param request * @return @@ -106,7 +108,9 @@ public ResponseEntity downloadAuditReport(@Valid @RequestBody AuditRep List affectedPartiesForDownload = auditService.getAffectedPartiesForDownload( auditReportRequest.getTransactionTypes(), auditReportRequest.getOrganizations(), - auditReportRequest.getUserId(), auditReportRequest.getStartDate(), auditReportRequest.getEndDate(), auditReportRequest.getSortField(), auditReportRequest.getSortDirection()); + auditReportRequest.getSpgRoles(), auditReportRequest.getUserId(), auditReportRequest.getStartDate(), + auditReportRequest.getEndDate(), auditReportRequest.getSortField(), + auditReportRequest.getSortDirection()); List auditReport = convertReport(affectedPartiesForDownload); logger.info("Number of records returned for download : {}", auditReport.size()); @@ -126,6 +130,7 @@ private List convertReport(List affectedParties) { affectedParties.forEach(affectedParty -> { AuditRecord model = new AuditRecord(); model.setOrganization(affectedParty.getTransaction().getOrganization()); + model.setSpgRole(affectedParty.getTransaction().getSpgRole()); model.setTransactionId(affectedParty.getTransaction().getTransactionId().toString()); model.setType(affectedParty.getTransaction().getType()); model.setUserId(affectedParty.getTransaction().getUserId()); @@ -140,7 +145,8 @@ private List convertReport(List affectedParties) { } private List convertOrganization(List organizations) { - return organizations.stream().filter(org -> StringUtils.isNotBlank(org.getOrganization())).map(org -> org.getOrganization()).collect(Collectors.toList()); + return organizations.stream().filter(org -> StringUtils.isNotBlank(org.getOrganization())) + .map(org -> org.getOrganization()).collect(Collectors.toList()); } private LocalDateTime convertDate(Date date) { diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditRecord.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditRecord.java index 0eb60ae2..07d5de2e 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditRecord.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditRecord.java @@ -18,6 +18,16 @@ public class AuditRecord { private String transactionId; + private String spgRole; + + public String getSpgRole() { + return spgRole; + } + + public void setSpgRole(String spgRole) { + this.spgRole = spgRole; + } + public String getType() { return type; } diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditReportRequest.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditReportRequest.java index 4eeea223..464b2b4d 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditReportRequest.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/model/rest/auditreport/AuditReportRequest.java @@ -10,6 +10,8 @@ public class AuditReportRequest { private List organizations; private List transactionTypes; + + private List spgRoles; private LocalDate startDate; @@ -17,6 +19,14 @@ public class AuditReportRequest { private Integer page = 0; + public List getSpgRoles() { + return spgRoles; + } + + public void setSpgRoles(List spgRoles) { + this.spgRoles = spgRoles; + } + private Integer rows = 10; private String sortField; diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/entity/Transaction.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/entity/Transaction.java index d6c08eff..4da13eab 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/entity/Transaction.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/entity/Transaction.java @@ -62,6 +62,21 @@ public class Transaction { @Column(name = "organization") private String organization; + /** + * SPG of the user performing the transaction + */ + @Basic + @Column(name = "spg_role") + private String spgRole; + + public String getSpgRole() { + return spgRole; + } + + public void setSpgRole(String spgRole) { + this.spgRole = spgRole; + } + /** * ID of the user that initiated the transaction */ @@ -139,7 +154,7 @@ public Date getStartTime() { public void setStartTime(Date startTime) { this.startTime = startTime; } - + @PrePersist public void prePersist() { if (startTime == null) { @@ -216,8 +231,9 @@ public boolean equals(Object obj) { @Override public String toString() { - return "Transaction [transactionId=" + transactionId + ", type=" + type + ", sessionId=" + sessionId + ", server=" + server - + ", sourceIp=" + sourceIp + ", organization=" + organization + ", userId=" + userId + ", startTime=" + startTime + "]"; + return "Transaction [transactionId=" + transactionId + ", type=" + type + ", sessionId=" + sessionId + + ", server=" + server + ", sourceIp=" + sourceIp + ", organization=" + organization + ", userId=" + + userId + ", startTime=" + startTime + "]"; } } \ No newline at end of file diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/repository/AffectedPartyPageableRepository.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/repository/AffectedPartyPageableRepository.java index 9a6d2692..efdac9a3 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/repository/AffectedPartyPageableRepository.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/persistence/repository/AffectedPartyPageableRepository.java @@ -14,22 +14,28 @@ public interface AffectedPartyPageableRepository extends PagingAndSortingRepository { - @Query("SELECT af from AffectedParty af where " - +"(COALESCE(:organizations, null) is null or af.transaction.organization IN (:organizations)) and " - +"(COALESCE(:type, null) is null or af.transaction.type IN (:type)) and " - +"(COALESCE(:userId, null) is null or :userId = '' or upper(af.transaction.userId)= upper(:userId)) and " - +"(af.direction=:direction) and " - +"(date_trunc('day', af.transaction.startTime) between :startDate and :endDate) ") + @Query("SELECT af from AffectedParty af where " + + "(COALESCE(:organizations, null) is null or af.transaction.organization IN (:organizations)) and " + + "(COALESCE(:type, null) is null or af.transaction.type IN (:type)) and " + + "(COALESCE(:spgRoles, null) is null or af.transaction.spgRole IN (:spgRoles)) and " + + "(COALESCE(:userId, null) is null or :userId = '' or upper(af.transaction.userId)= upper(:userId)) and " + + "(af.direction=:direction) and " + + "(date_trunc('day', af.transaction.startTime) between :startDate and :endDate) ") Page findByTransactionAndDirection(@Param("type") List type, - @Param("organizations") List organizations, @Param("userId") String userId, @Param("direction") String direction, @Param("startDate") Date startDate, @Param("endDate") Date endDate, Pageable pageable); - - @Query("SELECT af from AffectedParty af where " - +"(COALESCE(:organizations, null) is null or af.transaction.organization IN (:organizations)) and " - +"(COALESCE(:type, null) is null or af.transaction.type IN (:type)) and " - +"(COALESCE(:userId, null) is null or :userId = '' or upper(af.transaction.userId)= upper(:userId)) and " - +"(af.direction=:direction) and " - +"(date_trunc('day', af.transaction.startTime) between :startDate and :endDate) ") + @Param("organizations") List organizations, @Param("spgRoles") List spgRoles, + @Param("userId") String userId, @Param("direction") String direction, @Param("startDate") Date startDate, + @Param("endDate") Date endDate, Pageable pageable); + + @Query("SELECT af from AffectedParty af where " + + "(COALESCE(:organizations, null) is null or af.transaction.organization IN (:organizations)) and " + + "(COALESCE(:type, null) is null or af.transaction.type IN (:type)) and " + + "(COALESCE(:spgRoles, null) is null or af.transaction.spgRole IN (:spgRoles)) and " + + "(COALESCE(:userId, null) is null or :userId = '' or upper(af.transaction.userId)= upper(:userId)) and " + + "(af.direction=:direction) and " + + "(date_trunc('day', af.transaction.startTime) between :startDate and :endDate) ") List findByTransactionAndDirection(@Param("type") List type, - @Param("organizations") List organizations, @Param("userId") String userId, @Param("direction") String direction, @Param("startDate") Date startDate, @Param("endDate") Date endDate, Sort sort); + @Param("organizations") List organizations, @Param("spgRoles") List spgRoles, + @Param("userId") String userId, @Param("direction") String direction, @Param("startDate") Date startDate, + @Param("endDate") Date endDate, Sort sort); } diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/SecurityUtil.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/SecurityUtil.java index 74d73f27..11b920c2 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/SecurityUtil.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/SecurityUtil.java @@ -4,9 +4,9 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -26,80 +26,135 @@ public class SecurityUtil { private static final String CLAIM_RESOURCE_ACCESS = "resource_access"; private static final String CLAIM_SESSION_STATE = "session_state"; public static final String CLAIM_USERNAME = "preferred_username"; - private static final String CLAIM_SUB = "sub"; //the Subject claim identifies the principal that is the subject of the JWT + private static final String CLAIM_SUB = "sub"; // the Subject claim identifies the principal that is the subject of the JWT public static final String CLAIM_ORGANIZATION = "org_details"; - + private static final String ORGANIZATION_ID = "id"; - + private static final String USER_ROLES = "roles"; + + private static final String UNKNOWN_ROLE = "UNKNOWN"; + + private static String KEYCLOAK_CLIENT; - private static String KEYCLOAK_CLIENT; - - @Value("${spring.security.oauth2.resourceserver.jwt.audience}") - private void setKeycloakClientStatic(String keycloakClient){ - SecurityUtil.KEYCLOAK_CLIENT = keycloakClient; - } + private static SecurityProperties securityProperties; + + @Autowired + public void setSecurityProperties(SecurityProperties properties) { + SecurityUtil.securityProperties = properties; + } - public static UserInfo loadUserInfo() { + @Value("${spring.security.oauth2.resourceserver.jwt.audience}") + private void setKeycloakClientStatic(String keycloakClient) { + SecurityUtil.KEYCLOAK_CLIENT = keycloakClient; + } + + public static UserInfo loadUserInfo() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - Jwt jwt = (Jwt)auth.getPrincipal(); - + Jwt jwt = (Jwt) auth.getPrincipal(); + UserInfo userInfo = new UserInfo(); userInfo.setOrganization(extractOrganization(jwt)); - + List roles = loadRoles(jwt); - userInfo.setRole(StringUtils.join(roles, " ")); + userInfo.setRoles(roles); userInfo.setSessionState(jwt.getClaim(CLAIM_SESSION_STATE)); userInfo.setUsername(jwt.getClaim(CLAIM_USERNAME)); userInfo.setUserId(jwt.getClaim(CLAIM_SUB)); - + return userInfo; } - + + public static String loadSPGBasedOnTransactionType(UserInfo userInfo, TransactionType transactionType) { + Map> rolePermissions = securityProperties.getRolePermissions(); + List roles = userInfo.getRoles(); + if (roles.size() == 1) { + return roles.get(0); + } + for (String role : roles) { + List permissions = rolePermissions.get(role.toLowerCase()); + if (permissions == null) { + continue; + } + switch (transactionType) { + case ENROLL_SUBSCRIBER: + if (permissions.contains("AddPermitHolderWOPHN") || permissions.contains("AddPermitHolderWithPHN")) { + return role; + } + break; + case GET_PERSON_DETAILS: + if (permissions.contains("AddPermitHolderWithPHN")) { + return role; + } + break; + case NAME_SEARCH: + if (permissions.contains("AddPermitHolderWOPHN")) { + return role; + } + break; + case CONTRACT_INQUIRY: + if (permissions.contains("ContractInquiry") || permissions.contains("GetContractAddress")) { + return role; + } + break; + case GET_PATIENT_REGISTRATION: + if (permissions.contains("PatientRegistration")) { + return role; + } + break; + default: + if (permissions.contains(transactionType.getValue())) { + return role; + } + } + } + return UNKNOWN_ROLE; + } + private static String extractOrganization(Jwt jwt) { try { ObjectMapper mapper = new ObjectMapper(); - JsonNode node = mapper.readTree((String)jwt.getClaim(CLAIM_ORGANIZATION)); + JsonNode node = mapper.readTree((String) jwt.getClaim(CLAIM_ORGANIZATION)); return node.get(ORGANIZATION_ID).asText(); } catch (Exception e) { logger.warn("User {} does not have claim {} set", jwt.getClaim(CLAIM_USERNAME), CLAIM_ORGANIZATION); return null; } } - + @SuppressWarnings("unchecked") private static List loadRoles(Jwt jwt) { List permissions = new ArrayList<>(); - Map resourceAccesses = (Map) jwt.getClaims().get(CLAIM_RESOURCE_ACCESS); + Map resourceAccesses = (Map) jwt.getClaims().get(CLAIM_RESOURCE_ACCESS); - if (resourceAccesses == null) { - return permissions; - } + if (resourceAccesses == null) { + return permissions; + } - Map resource = (Map) resourceAccesses.get(KEYCLOAK_CLIENT); - if (resource == null) { - return permissions; - } + Map resource = (Map) resourceAccesses.get(KEYCLOAK_CLIENT); + if (resource == null) { + return permissions; + } - return (List)resource.get(USER_ROLES); + return (List) resource.get(USER_ROLES); } - + public static List loadPermissions(Jwt jwt, Map> rolePermissions) { - List roles = loadRoles(jwt); - List permissions = new ArrayList<>(); - roles.forEach(role -> { - List currentPermissions = rolePermissions.get(role.toLowerCase()); - if (currentPermissions != null) { - permissions.addAll(currentPermissions); - } else { - logger.warn("Role {} has no permissions defined.", role); - } - - }); - - return permissions; + List roles = loadRoles(jwt); + List permissions = new ArrayList<>(); + roles.forEach(role -> { + List currentPermissions = rolePermissions.get(role.toLowerCase()); + if (currentPermissions != null) { + permissions.addAll(currentPermissions); + } else { + logger.warn("Role {} has no permissions defined.", role); + } + + }); + + return permissions; } } \ No newline at end of file diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/UserInfo.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/UserInfo.java index 1b135aa0..c2740eee 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/UserInfo.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/security/UserInfo.java @@ -1,5 +1,9 @@ package ca.bc.gov.hlth.hnweb.security; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + /** * Information on the current user obtained from the JWT. */ @@ -9,6 +13,7 @@ public class UserInfo { private String organization; private String role; private String sessionState; + private List roles; public UserInfo() { super(); @@ -20,7 +25,7 @@ public UserInfo(String username, String organization, String role) { this.organization = organization; this.role = role; } - + public UserInfo(String username, String userId, String organization, String role, String sessionState) { super(); this.username = username; @@ -55,11 +60,15 @@ public void setOrganization(String organization) { } public String getRole() { - return role; + return StringUtils.join(this.roles, " "); } - public void setRole(String role) { - this.role = role; + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; } public String getSessionState() { diff --git a/backend/src/main/java/ca/bc/gov/hlth/hnweb/service/AuditService.java b/backend/src/main/java/ca/bc/gov/hlth/hnweb/service/AuditService.java index 3651e5f4..f8e0840c 100644 --- a/backend/src/main/java/ca/bc/gov/hlth/hnweb/service/AuditService.java +++ b/backend/src/main/java/ca/bc/gov/hlth/hnweb/service/AuditService.java @@ -62,39 +62,40 @@ public class AuditService { private static final String DEFAULT_SORT = "transaction.startTime"; private static final String LOCAL_DATE_FORMAT = "yyyy-MM-dd"; - - public static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; - - private static final String[] HEADERS = { "Type", "Organization", "User ID", "Transaction Start Time", + + public static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + + private static final String[] HEADERS = { "Type", "Organization", "SPG", "User ID", "Transaction Start Time", "Affected Party ID", "Affected Party ID Type", "Transaction ID" }; private static final CSVFormat FORMAT = CSVFormat.DEFAULT.withHeader(HEADERS); @Autowired private AffectedPartyRepository affectedPartyRepository; - + @Autowired private AffectedPartyPageableRepository affectedPartyPageableRepository; - + @Autowired private EventMessageRepository eventMessageRepository; @Autowired private TransactionEventRepository transactionEventRepository; - + @Autowired private TransactionRepository transactionRepository; - + @Autowired private OrganizationRepository organizationRepository; - + /** Maps simple sort names to their JPA equivalent */ private static Map sortMap = new HashMap<>(); - + static { sortMap.put("affectedPartyId", "identifier"); sortMap.put("affectedPartyType", "identifierType"); sortMap.put("organization", "transaction.organization"); + sortMap.put("spgRole", "transaction.spgRole"); sortMap.put("transactionStartTime", "transaction.startTime"); sortMap.put("type", "transaction.type"); sortMap.put("userId", "transaction.userId"); @@ -104,7 +105,7 @@ public class AuditService { * Creates a new {@link Transaction}. * * @param sourceIP Source IP address - * @param type The type of transaction + * @param type The type of transaction * @return The persisted Transaction */ public Transaction createTransaction(String sourceIP, TransactionType type) { @@ -113,27 +114,28 @@ public Transaction createTransaction(String sourceIP, TransactionType type) { try { // This can throw an exception under certain auth failures // E.g. if an empty or invalid token is provided - userInfo = SecurityUtil.loadUserInfo(); + userInfo = SecurityUtil.loadUserInfo(); } catch (Exception e) { // Ignore } - transaction.setOrganization(userInfo != null ? userInfo.getOrganization(): null); + transaction.setOrganization(userInfo != null ? userInfo.getOrganization() : null); transaction.setServer(getServer()); - transaction.setSessionId(userInfo != null ? userInfo.getSessionState(): null); + transaction.setSessionId(userInfo != null ? userInfo.getSessionState() : null); transaction.setSourceIp(sourceIP); transaction.setStartTime(new Date()); transaction.setTransactionId(UUID.randomUUID()); transaction.setType(type.getValue()); transaction.setUserId(userInfo != null ? userInfo.getUsername() : null); + transaction.setSpgRole(SecurityUtil.loadSPGBasedOnTransactionType(userInfo, type)); return transactionRepository.save(transaction); } - private String getServer() { + private String getServer() { String hostname = ""; try { hostname = InetAddress.getLocalHost().getHostName(); logger.debug("The hostname is {}", hostname); - } catch (UnknownHostException e) { + } catch (UnknownHostException e) { logger.warn("Could not get server name"); } return hostname; @@ -143,7 +145,7 @@ private String getServer() { * Creates a new {@link TransactionEvent}. * * @param transaction The associated Transaction - * @param eventType The type of event + * @param eventType The type of event * @return The persisted TransactionEvent. */ public TransactionEvent createTransactionEvent(Transaction transaction, TransactionEventType eventType) { @@ -154,11 +156,12 @@ public TransactionEvent createTransactionEvent(Transaction transaction, Transact * Creates a new {@link TransactionEvent}. * * @param transaction The associated Transaction - * @param eventType The type of event - * @param messageId The associated message ID + * @param eventType The type of event + * @param messageId The associated message ID * @return The persisted TransactionEvent. */ - public TransactionEvent createTransactionEvent(Transaction transaction, TransactionEventType eventType, String messageId) { + public TransactionEvent createTransactionEvent(Transaction transaction, TransactionEventType eventType, + String messageId) { TransactionEvent transactionEvent = new TransactionEvent(); transactionEvent.setEventTime(new Date()); transactionEvent.setMessageId(messageId); @@ -166,20 +169,20 @@ public TransactionEvent createTransactionEvent(Transaction transaction, Transact transactionEvent.setType(eventType.getValue()); return transactionEventRepository.save(transactionEvent); } - + /** * Creates a new {@link EventMessage}. * * @param transactionEvent The associated TransactionEvent - * @param level The level of the event (e.g.ERROR) - * @param status The HTTP Status code related to the message + * @param level The level of the event (e.g.ERROR) + * @param status The HTTP Status code related to the message */ public EventMessage createEventMessage(TransactionEvent transactionEvent, ErrorLevel level, HttpStatus status) { - EventMessage eventMessage = new EventMessage(); - eventMessage.setErrorCode(Integer.toString(status.value())); - eventMessage.setErrorLevel(level); - eventMessage.setMessageText(status.getReasonPhrase()); - eventMessage.setTransactionEvent(transactionEvent); + EventMessage eventMessage = new EventMessage(); + eventMessage.setErrorCode(Integer.toString(status.value())); + eventMessage.setErrorLevel(level); + eventMessage.setMessageText(status.getReasonPhrase()); + eventMessage.setTransactionEvent(transactionEvent); return eventMessageRepository.save(eventMessage); } @@ -187,16 +190,17 @@ public EventMessage createEventMessage(TransactionEvent transactionEvent, ErrorL * Creates a new {@link EventMessage}. * * @param transactionEvent The associated TransactionEvent - * @param level The level of the event (e.g.ERROR) - * @param exception The associated exception - * @param status The HTTP Status code related to the message + * @param level The level of the event (e.g.ERROR) + * @param exception The associated exception + * @param status The HTTP Status code related to the message */ - public EventMessage createEventMessage(TransactionEvent transactionEvent, ErrorLevel level, HttpStatus status, Exception exception) { - EventMessage eventMessage = new EventMessage(); - eventMessage.setErrorCode(Integer.toString(status.value())); - eventMessage.setErrorLevel(level); - eventMessage.setMessageText(exception.getMessage()); - eventMessage.setTransactionEvent(transactionEvent); + public EventMessage createEventMessage(TransactionEvent transactionEvent, ErrorLevel level, HttpStatus status, + Exception exception) { + EventMessage eventMessage = new EventMessage(); + eventMessage.setErrorCode(Integer.toString(status.value())); + eventMessage.setErrorLevel(level); + eventMessage.setMessageText(exception.getMessage()); + eventMessage.setTransactionEvent(transactionEvent); return eventMessageRepository.save(eventMessage); } @@ -204,21 +208,23 @@ public EventMessage createEventMessage(TransactionEvent transactionEvent, ErrorL * Creates a new {@link AffectedParty}. * * @param transaction The associated Transaction - * @param phn The phn of the affected party - * @param direction The value to indicate if the party is affected when the transaction is being sent or being received. + * @param phn The phn of the affected party + * @param direction The value to indicate if the party is affected when the + * transaction is being sent or being received. */ public AffectedParty createAffectedParty(Transaction transaction, String phn, AffectedPartyDirection direction) { return createAffectedParty(transaction, IdentifierType.PHN, phn, direction); } - + /** * Creates a new {@link AffectedParty}. * - * @param transaction The associated Transaction + * @param transaction The associated Transaction * @param identifierType The type of identifier - * @param identifier The value of the identifier + * @param identifier The value of the identifier */ - public AffectedParty createAffectedParty(Transaction transaction, IdentifierType identifierType, String identifier, AffectedPartyDirection direction) { + public AffectedParty createAffectedParty(Transaction transaction, IdentifierType identifierType, String identifier, + AffectedPartyDirection direction) { AffectedParty affectedParty = new AffectedParty(); affectedParty.setIdentifier(identifier); affectedParty.setIdentifierType(identifierType.getValue()); @@ -226,9 +232,10 @@ public AffectedParty createAffectedParty(Transaction transaction, IdentifierType affectedParty.setTransaction(transaction); return affectedPartyRepository.save(affectedParty); } - + /** * Retrieves distinct organization for audit report. + * * @return list of organization. */ public List getOrganizations() { @@ -237,7 +244,8 @@ public List getOrganizations() { /** * Retrieves audit records for the given search parameters - * @param types Transaction types + * + * @param types Transaction types * @param organizations * @param userId * @param direction @@ -245,33 +253,36 @@ public List getOrganizations() { * @param endDate * @return */ - public Page getAffectedParties(List types, List organizations, String userId, LocalDate startDate, - LocalDate endDate, int page, int rows, String sortField, String sortDirection) { + public Page getAffectedParties(List types, List organizations, List spgRoles, + String userId, LocalDate startDate, LocalDate endDate, int page, int rows, String sortField, + String sortDirection) { logger.info("Querying page {} with {} rows", page, rows); try { Date formattedStartDate = convertLocalDateToDate(startDate); - Date formattedEndDate = convertLocalDateToDate(endDate); + Date formattedEndDate = convertLocalDateToDate(endDate); String property = sortMap.get(sortField); if (StringUtils.isBlank(property)) { property = DEFAULT_SORT; } - Direction direction = StringUtils.isNotEmpty(sortDirection) ? Direction.valueOf(sortDirection) : Direction.DESC; + Direction direction = StringUtils.isNotEmpty(sortDirection) ? Direction.valueOf(sortDirection) + : Direction.DESC; Sort sort = Sort.by(direction, property); - + Pageable pageable = PageRequest.of(page, rows, sort); - - return affectedPartyPageableRepository.findByTransactionAndDirection(types, organizations, userId, AffectedPartyDirection.INBOUND.getValue(), formattedStartDate, - formattedEndDate, pageable); + + return affectedPartyPageableRepository.findByTransactionAndDirection(types, organizations, spgRoles, + userId, AffectedPartyDirection.INBOUND.getValue(), formattedStartDate, formattedEndDate, pageable); } catch (ParseException e) { logger.error(e.getLocalizedMessage()); return null; - } + } } - + /** * This method loads data into csv format + * * @param auditReport * @return */ @@ -281,17 +292,18 @@ public ByteArrayInputStream load(final List auditReport) { /** * This method writes audit report data into csv + * * @param auditReport * @return */ private ByteArrayInputStream writeDataToCsv(final List auditReports) { logger.info("Writing data to the csv format"); - + try (ByteArrayOutputStream stream = new ByteArrayOutputStream(); CSVPrinter printer = new CSVPrinter(new PrintWriter(stream), FORMAT)) { for (AuditRecord auditRecord : auditReports) { - List auditData = Arrays.asList(String.valueOf(auditRecord.getType()), - auditRecord.getOrganization(), auditRecord.getUserId(), + List auditData = Arrays.asList(String.valueOf(auditRecord.getType()), + auditRecord.getOrganization(), auditRecord.getSpgRole(), auditRecord.getUserId(), convertLocalDateTime(auditRecord.getTransactionStartTime()), auditRecord.getAffectedPartyId(), auditRecord.getAffectedPartyType(), auditRecord.getTransactionId()); @@ -315,19 +327,21 @@ private ByteArrayInputStream writeDataToCsv(final List auditReports * @return */ public List getAffectedPartiesForDownload(List types, List organizations, - String userId, LocalDate startDate, LocalDate endDate, String sortField, String sortDirection) { + List spgRoles, String userId, LocalDate startDate, LocalDate endDate, String sortField, + String sortDirection) { try { Date formattedStartDate = convertLocalDateToDate(startDate); Date formattedEndDate = convertLocalDateToDate(endDate); - + String property = sortMap.get(sortField); if (StringUtils.isBlank(property)) { property = DEFAULT_SORT; } - Direction direction = StringUtils.isNotEmpty(sortDirection) ? Direction.valueOf(sortDirection) : Direction.DESC; + Direction direction = StringUtils.isNotEmpty(sortDirection) ? Direction.valueOf(sortDirection) + : Direction.DESC; Sort sort = Sort.by(direction, property); - - return affectedPartyPageableRepository.findByTransactionAndDirection(types, organizations, + + return affectedPartyPageableRepository.findByTransactionAndDirection(types, organizations, spgRoles, userId, AffectedPartyDirection.INBOUND.getValue(), formattedStartDate, formattedEndDate, sort); } catch (ParseException e) { logger.error(e.getLocalizedMessage()); @@ -345,5 +359,5 @@ private String convertLocalDateTime(LocalDateTime dateTime) { private Date convertLocalDateToDate(LocalDate date) throws ParseException { return new SimpleDateFormat(LOCAL_DATE_FORMAT).parse(date.toString()); } - + } diff --git a/backend/src/test/java/ca/bc/gov/hlth/hnweb/controller/AuditControllerTest.java b/backend/src/test/java/ca/bc/gov/hlth/hnweb/controller/AuditControllerTest.java index e99b1d24..0250b4ca 100644 --- a/backend/src/test/java/ca/bc/gov/hlth/hnweb/controller/AuditControllerTest.java +++ b/backend/src/test/java/ca/bc/gov/hlth/hnweb/controller/AuditControllerTest.java @@ -42,7 +42,7 @@ public class AuditControllerTest extends BaseControllerTest { @Autowired private AffectedPartyRepository affectedPartyRepository; - + @Autowired private TransactionRepository transactionRepository; @@ -61,7 +61,7 @@ public void testGetOrganization() throws Exception { } @Test - public void testGetAuditReport_withoutOptionalParam() {; + public void testGetAuditReport_withoutOptionalParam() { createAuditReports(2, TransactionType.CHECK_ELIGIBILITY); AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); @@ -80,7 +80,7 @@ public void testGetOrganization() throws Exception { @Test public void testGetAuditReport_withOptionalParam() { createAuditReports(1, TransactionType.CHECK_ELIGIBILITY); - + List types = new ArrayList<>(); types.add(TransactionType.CHECK_ELIGIBILITY.name()); types.add(TransactionType.PHN_INQUIRY.name()); @@ -89,15 +89,19 @@ public void testGetAuditReport_withOptionalParam() { orgs.add("00000010"); orgs.add("00000020"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setTransactionTypes(types); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setPage(0); auditReportRequest.setRows(10); - + ResponseEntity auditReport = auditReportController.getAuditReport(auditReportRequest, createHttpServletRequest()); @@ -107,31 +111,66 @@ public void testGetAuditReport_withOptionalParam() { } @Test - public void testGetAuditReports_firstPage() { + public void testGetAuditReports_spgNoResults() { createAuditReports(15, TransactionType.CHECK_ELIGIBILITY); + + List types = new ArrayList<>(); + types.add(TransactionType.CHECK_ELIGIBILITY.name()); + + List orgs = new ArrayList<>(); + orgs.add("00000010"); + + List spgRoles = new ArrayList<>(); + spgRoles.add("E45"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); + auditReportRequest.setUserId("hnweb1"); + auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); + auditReportRequest.setTransactionTypes(types); + auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); + auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); + auditReportRequest.setPage(0); + auditReportRequest.setRows(10); + + ResponseEntity auditReport = auditReportController.getAuditReport(auditReportRequest, + createHttpServletRequest()); + + assertEquals(HttpStatus.OK, auditReport.getStatusCode()); + assertEquals(0, auditReport.getBody().getRecords().size()); + + } + + @Test + public void testGetAuditReports_firstPage() { + createAuditReports(15, TransactionType.CHECK_ELIGIBILITY); + List types = new ArrayList<>(); types.add(TransactionType.CHECK_ELIGIBILITY.name()); List orgs = new ArrayList<>(); orgs.add("00000010"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setTransactionTypes(types); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setPage(0); auditReportRequest.setRows(10); - + ResponseEntity auditReport = auditReportController.getAuditReport(auditReportRequest, createHttpServletRequest()); assertEquals(HttpStatus.OK, auditReport.getStatusCode()); assertEquals(10, auditReport.getBody().getRecords().size()); } - + @Test public void testGetAuditReports_secondPage() { createAuditReports(15, TransactionType.CHECK_ELIGIBILITY); @@ -141,22 +180,26 @@ public void testGetAuditReports_secondPage() { List orgs = new ArrayList<>(); orgs.add("00000010"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setTransactionTypes(types); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setPage(1); auditReportRequest.setRows(10); - + ResponseEntity auditReport = auditReportController.getAuditReport(auditReportRequest, createHttpServletRequest()); assertEquals(HttpStatus.OK, auditReport.getStatusCode()); assertEquals(5, auditReport.getBody().getRecords().size()); } - + @Test public void testGetAuditReports_sortAsc() { createAuditReports(5, TransactionType.CHECK_ELIGIBILITY); @@ -165,26 +208,30 @@ public void testGetAuditReports_sortAsc() { List orgs = new ArrayList<>(); orgs.add("00000010"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setPage(0); auditReportRequest.setRows(10); auditReportRequest.setSortDirection("ASC"); auditReportRequest.setSortField("type"); - + ResponseEntity auditReport = auditReportController.getAuditReport(auditReportRequest, createHttpServletRequest()); assertEquals(HttpStatus.OK, auditReport.getStatusCode()); - + List records = auditReport.getBody().getRecords(); assertEquals(10, records.size()); assertEquals(TransactionType.CHECK_ELIGIBILITY.name(), records.get(0).getType()); } - + @Test public void testGetAuditReports_sortDesc() { createAuditReports(5, TransactionType.CHECK_ELIGIBILITY); @@ -193,118 +240,134 @@ public void testGetAuditReports_sortDesc() { List orgs = new ArrayList<>(); orgs.add("00000010"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setPage(0); auditReportRequest.setRows(10); auditReportRequest.setSortDirection("DESC"); auditReportRequest.setSortField("type"); - + ResponseEntity auditReport = auditReportController.getAuditReport(auditReportRequest, createHttpServletRequest()); assertEquals(HttpStatus.OK, auditReport.getStatusCode()); - + List records = auditReport.getBody().getRecords(); assertEquals(10, records.size()); assertEquals(TransactionType.PHN_INQUIRY.name(), records.get(0).getType()); } - + @Test public void testGetAuditReport_downloadCSV() throws IOException { createAuditReports(20, TransactionType.CHECK_ELIGIBILITY); createAuditReports(20, TransactionType.PHN_INQUIRY); - + List orgs = new ArrayList<>(); orgs.add("00000010"); orgs.add("00000020"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); - auditReportRequest.setOrganizations(orgs); + auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setSortDirection("ASC"); auditReportRequest.setSortField("type"); - - ResponseEntity downloadReport = auditReportController.downloadAuditReport(auditReportRequest, + + ResponseEntity downloadReport = auditReportController.downloadAuditReport(auditReportRequest, createHttpServletRequest()); - - List reportData = new ArrayList(); - InputStream downloadData = downloadReport.getBody().getInputStream(); - read(downloadData, reportData); - - assertEquals(HttpStatus.OK, downloadReport.getStatusCode()); - - //Check csv headers and data - assertEquals("Type", reportData.get(0)); - assertEquals("Organization", reportData.get(1)); - assertEquals("User ID", reportData.get(2)); - assertEquals("Transaction Start Time", reportData.get(3)); - assertEquals("Affected Party ID", reportData.get(4)); - assertEquals("Affected Party ID Type", reportData.get(5)); - assertEquals("Transaction ID\r\n" + - "CHECK_ELIGIBILITY", reportData.get(6)); - assertEquals("00000010", reportData.get(7)); - assertEquals("hnweb1", reportData.get(8)); - assertEquals("2022-08-05T00:00:00", reportData.get(9)); - assertEquals("PHN", reportData.get(10)); - - //Check the last (40th) record - assertEquals("00000010", reportData.get(241)); - assertEquals("hnweb1", reportData.get(242)); - assertEquals("2022-08-05T00:00:00", reportData.get(243)); - assertEquals("PHN", reportData.get(244)); + + List reportData = new ArrayList(); + InputStream downloadData = downloadReport.getBody().getInputStream(); + read(downloadData, reportData); + + assertEquals(HttpStatus.OK, downloadReport.getStatusCode()); + + // Check csv headers and data + assertEquals("Type", reportData.get(0)); + assertEquals("Organization", reportData.get(1)); + assertEquals("SPG", reportData.get(2)); + assertEquals("User ID", reportData.get(3)); + assertEquals("Transaction Start Time", reportData.get(4)); + assertEquals("Affected Party ID", reportData.get(5)); + assertEquals("Affected Party ID Type", reportData.get(6)); + assertEquals("Transaction ID\r\n" + "CHECK_ELIGIBILITY", reportData.get(7)); + assertEquals("00000010", reportData.get(8)); + assertEquals("TRAININGHEALTHAUTH", reportData.get(9)); + assertEquals("hnweb1", reportData.get(10)); + assertEquals("2022-08-05T00:00:00", reportData.get(11)); + assertEquals("PHN", reportData.get(12)); + + // Check the last (40th) record + assertEquals("00000010", reportData.get(239)); + assertEquals("TRAININGHEALTHAUTH", reportData.get(240)); + assertEquals("hnweb1", reportData.get(241)); + assertEquals("2022-08-05T00:00:00", reportData.get(242)); + assertEquals("PHN", reportData.get(243)); } - + @Test public void testGetAuditReport_downloadCSV_sortDesc() throws IOException { createAuditReports(20, TransactionType.CHECK_ELIGIBILITY); createAuditReports(20, TransactionType.PHN_INQUIRY); - + List orgs = new ArrayList<>(); orgs.add("00000010"); orgs.add("00000020"); + List spgRoles = new ArrayList<>(); + spgRoles.add("TRAININGHEALTHAUTH"); + AuditReportRequest auditReportRequest = new AuditReportRequest(); auditReportRequest.setUserId("hnweb1"); - auditReportRequest.setOrganizations(orgs); + auditReportRequest.setOrganizations(orgs); + auditReportRequest.setSpgRoles(spgRoles); auditReportRequest.setStartDate(LocalDate.of(2022, 7, 1)); auditReportRequest.setEndDate(LocalDate.of(2022, 12, 8)); auditReportRequest.setSortDirection("DESC"); auditReportRequest.setSortField("type"); - - ResponseEntity downloadReport = auditReportController.downloadAuditReport(auditReportRequest, + + ResponseEntity downloadReport = auditReportController.downloadAuditReport(auditReportRequest, createHttpServletRequest()); - - List reportData = new ArrayList(); - InputStream downloadData = downloadReport.getBody().getInputStream(); - read(downloadData, reportData); - - assertEquals(HttpStatus.OK, downloadReport.getStatusCode()); - - //Check csv headers and data - assertEquals("Type", reportData.get(0)); - assertEquals("Organization", reportData.get(1)); - assertEquals("User ID", reportData.get(2)); - assertEquals("Transaction Start Time", reportData.get(3)); - assertEquals("Affected Party ID", reportData.get(4)); - assertEquals("Affected Party ID Type", reportData.get(5)); - assertEquals("Transaction ID\r\n" + - "PHN_INQUIRY", reportData.get(6)); - assertEquals("00000010", reportData.get(7)); - assertEquals("hnweb1", reportData.get(8)); - assertEquals("2022-08-05T00:00:00", reportData.get(9)); - assertEquals("PHN", reportData.get(10)); - - //Check the last (40th) record - assertEquals("00000010", reportData.get(241)); - assertEquals("hnweb1", reportData.get(242)); - assertEquals("2022-08-05T00:00:00", reportData.get(243)); - assertEquals("PHN", reportData.get(244)); + + List reportData = new ArrayList(); + InputStream downloadData = downloadReport.getBody().getInputStream(); + read(downloadData, reportData); + + assertEquals(HttpStatus.OK, downloadReport.getStatusCode()); + + // Check csv headers and data + assertEquals("Type", reportData.get(0)); + assertEquals("Organization", reportData.get(1)); + assertEquals("SPG", reportData.get(2)); + assertEquals("User ID", reportData.get(3)); + assertEquals("Transaction Start Time", reportData.get(4)); + assertEquals("Affected Party ID", reportData.get(5)); + assertEquals("Affected Party ID Type", reportData.get(6)); + assertEquals("Transaction ID\r\n" + "PHN_INQUIRY", reportData.get(7)); + assertEquals("00000010", reportData.get(8)); + assertEquals("TRAININGHEALTHAUTH", reportData.get(9)); + assertEquals("hnweb1", reportData.get(10)); + assertEquals("2022-08-05T00:00:00", reportData.get(11)); + assertEquals("PHN", reportData.get(12)); + + // Check the last (40th) record + assertEquals("00000010", reportData.get(239)); + assertEquals("TRAININGHEALTHAUTH", reportData.get(240)); + assertEquals("hnweb1", reportData.get(241)); + assertEquals("2022-08-05T00:00:00", reportData.get(242)); + assertEquals("PHN", reportData.get(243)); } private void read(InputStream input, List reportData) throws IOException { @@ -330,12 +393,13 @@ private void createOrganization() { organizationRepository.save(org); } - + private void createAuditReports(int count, TransactionType transactionType) { for (int i = 0; i < count; i++) { Transaction transaction = new Transaction(); transaction.setOrganization("00000010"); + transaction.setSpgRole("TRAININGHEALTHAUTH"); transaction.setServer("server1"); transaction.setSessionId("123456"); transaction.setSourceIp("0:0:0:0:0:0:0:1"); @@ -351,7 +415,7 @@ private void createAuditReports(int count, TransactionType transactionType) { affectedParty.setIdentifierType(IdentifierType.PHN.getValue()); affectedParty.setDirection(AffectedPartyDirection.INBOUND.getValue()); affectedParty.setTransaction(transaction); - + affectedPartyRepository.save(affectedParty); } } diff --git a/frontend/src/views/reports/AuditReporting.vue b/frontend/src/views/reports/AuditReporting.vue index b8dc6b79..c8cd5afd 100644 --- a/frontend/src/views/reports/AuditReporting.vue +++ b/frontend/src/views/reports/AuditReporting.vue @@ -7,8 +7,8 @@ - - Organization + + Organization