diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java index 62b56d1311d..2637e9690e1 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java @@ -2644,6 +2644,7 @@ public interface Captions { String userrole_main = "userrole.main"; String userrole_notifications = "userrole.notifications"; String UserRole_portHealthUser = "UserRole.portHealthUser"; + String UserRole_restrictAccessToAssignedEntities = "UserRole.restrictAccessToAssignedEntities"; String UserRole_smsNotificationTypes = "UserRole.smsNotificationTypes"; String UserRole_templateUserRole = "UserRole.templateUserRole"; String UserRole_userRights = "UserRole.userRights"; @@ -2652,6 +2653,7 @@ public interface Captions { String userRoleNotifications = "userRoleNotifications"; String userRoleNotificationTypeEmail = "userRoleNotificationTypeEmail"; String userRoleNotificationTypeSms = "userRoleNotificationTypeSms"; + String userRoleShowOnlyRestrictedAccessToAssignCases = "userRoleShowOnlyRestrictedAccessToAssignCases"; String userRoleUserrolesView = "userRoleUserrolesView"; String userUpdatePasswordConfirmation = "userUpdatePasswordConfirmation"; String Vaccination = "Vaccination"; diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserCriteria.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserCriteria.java index a081c98a87b..31358e7aa30 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserCriteria.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserCriteria.java @@ -16,6 +16,7 @@ public class UserCriteria extends BaseCriteria implements Serializable { private RegionReferenceDto region; private DistrictReferenceDto district; private String freeText; + private Boolean showOnlyRestrictedAccessToAssignedEntities; public UserCriteria active(Boolean active) { this.active = active; @@ -62,4 +63,12 @@ public UserCriteria freeText(String freeText) { public String getFreeText() { return freeText; } + + public Boolean getShowOnlyRestrictedAccessToAssignedEntities() { + return showOnlyRestrictedAccessToAssignedEntities; + } + + public void setShowOnlyRestrictedAccessToAssignedEntities(Boolean showOnlyRestrictedAccessToAssignedEntities) { + this.showOnlyRestrictedAccessToAssignedEntities = showOnlyRestrictedAccessToAssignedEntities; + } } diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleCriteria.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleCriteria.java index c7268a95df2..bd08d4e413d 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleCriteria.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleCriteria.java @@ -9,6 +9,7 @@ public class UserRoleCriteria extends BaseCriteria implements Serializable { private Boolean enabled; private UserRight userRight; private JurisdictionLevel jurisdictionLevel; + private Boolean showOnlyRestrictedAccessToAssignedEntities; public UserRoleCriteria enabled(Boolean enabled) { this.enabled = enabled; @@ -40,4 +41,12 @@ public JurisdictionLevel getJurisdictionLevel() { public void setUserRight(UserRight userRight) { this.userRight = userRight; } + + public Boolean getShowOnlyRestrictedAccessToAssignedEntities() { + return showOnlyRestrictedAccessToAssignedEntities; + } + + public void setShowOnlyRestrictedAccessToAssignedEntities(Boolean showOnlyRestrictedAccessToAssignedEntities) { + this.showOnlyRestrictedAccessToAssignedEntities = showOnlyRestrictedAccessToAssignedEntities; + } } diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleDto.java index 6b0be932df1..8016b1f17db 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRoleDto.java @@ -53,6 +53,7 @@ public class UserRoleDto extends EntityDto { public static final String PORT_HEALTH_USER = "portHealthUser"; public static final String NOTIFICATION_TYPES = "notificationTypes"; public static final String LINKED_DEFAULT_USER_ROLE = "linkedDefaultUserRole"; + public static final String RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES = "restrictAccessToAssignedEntities"; private Set userRights; private boolean enabled = true; @@ -68,6 +69,7 @@ public class UserRoleDto extends EntityDto { private JurisdictionLevel jurisdictionLevel; private Set emailNotificationTypes = Collections.emptySet(); private Set smsNotificationTypes = Collections.emptySet(); + private boolean restrictAccessToAssignedEntities = false; public static UserRoleDto build(UserRight... userRights) { @@ -207,6 +209,14 @@ public void setNotificationTypes(NotificationTypes notificationTypes) { this.emailNotificationTypes = notificationTypes.email; } + public boolean isRestrictAccessToAssignedEntities() { + return restrictAccessToAssignedEntities; + } + + public void setRestrictAccessToAssignedEntities(boolean restrictAccessToAssignedEntities) { + this.restrictAccessToAssignedEntities = restrictAccessToAssignedEntities; + } + @Override public String buildCaption() { return caption; diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index 163ae323625..0ef7dc0c300 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -2855,6 +2855,8 @@ UserRole.linkedDefaultUserRole=Linked default user role UserRole.portHealthUser=Port health user UserRole.smsNotificationTypes=SMS notification types UserRole.templateUserRole=User role template +UserRole.restrictAccessToAssignedEntities=Restrict access to assigned entities +userRoleShowOnlyRestrictedAccessToAssignCases=Only show user roles with access restricted to assigned entities userRoleUserrolesView=User role list userRoleNotifications=Notifications userRoleNotificationTypeSms = SMS diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java index 2864f49effe..e0dca473768 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java @@ -203,6 +203,10 @@ private CriteriaQuery buildIndexCriteria( if (caseCriteria != null) { caseUserFilterCriteria.setIncludeCasesFromOtherJurisdictions(caseCriteria.getIncludeCasesFromOtherJurisdictions()); } + + if (currentUserService.hasRestrictedAccessToAssignedEntities()) { + caseUserFilterCriteria.setRestrictAccessToAssignedEntities(true); + } Predicate filter = caseService.createUserFilter(caseQueryContext, caseUserFilterCriteria); if (!prefetchIds) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java index e97ef29a18b..684a4d73873 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java @@ -1394,6 +1394,11 @@ public Predicate createUserFilter(CaseQueryContext caseQueryContext, CaseUserFil Predicate filter = null; final JurisdictionLevel jurisdictionLevel = currentUser.getJurisdictionLevel(); + + if (currentUserHasRestrictedAccessToAssignedEntities()) { + filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(casePath.get(Case.SURVEILLANCE_OFFICER).get(User.ID), currentUser.getId())); + } + if (jurisdictionLevel != JurisdictionLevel.NATION) { // whoever created the case or is assigned to it is allowed to access it if (userFilterCriteria == null || (userFilterCriteria.getIncludeCasesFromOtherJurisdictions())) { @@ -1405,61 +1410,63 @@ public Predicate createUserFilter(CaseQueryContext caseQueryContext, CaseUserFil filterResponsible = cb.disjunction(); } - switch (jurisdictionLevel) { - case REGION: - final Region region = currentUser.getRegion(); - if (region != null) { - filter = CriteriaBuilderHelper.or( - cb, - filter, - cb.equal(casePath.get(Case.REGION).get(Region.ID), region.getId()), - cb.equal(casePath.get(Case.RESPONSIBLE_REGION).get(Region.ID), region.getId())); - } - break; - case DISTRICT: - final District district = currentUser.getDistrict(); - if (district != null) { - filter = CriteriaBuilderHelper.or( - cb, - filter, - cb.equal(casePath.get(Case.DISTRICT).get(District.ID), district.getId()), - cb.equal(casePath.get(Case.RESPONSIBLE_DISTRICT).get(District.ID), district.getId())); - } - break; - case HEALTH_FACILITY: - final Facility healthFacility = currentUser.getHealthFacility(); - if (healthFacility != null) { - filter = - CriteriaBuilderHelper.or(cb, filter, cb.equal(casePath.get(Case.HEALTH_FACILITY).get(Facility.ID), healthFacility.getId())); - } - break; - case COMMUNITY: - final Community community = currentUser.getCommunity(); - if (community != null) { - filter = CriteriaBuilderHelper.or( - cb, - filter, - cb.equal(casePath.get(Case.COMMUNITY).get(Community.ID), community.getId()), - cb.equal(casePath.get(Case.RESPONSIBLE_COMMUNITY).get(Community.ID), community.getId())); - } - break; - case POINT_OF_ENTRY: - final PointOfEntry pointOfEntry = currentUser.getPointOfEntry(); - if (pointOfEntry != null) { - filter = - CriteriaBuilderHelper.or(cb, filter, cb.equal(casePath.get(Case.POINT_OF_ENTRY).get(PointOfEntry.ID), pointOfEntry.getId())); + if (!currentUserHasRestrictedAccessToAssignedEntities()) { + switch (jurisdictionLevel) { + case REGION: + final Region region = currentUser.getRegion(); + if (region != null) { + filter = CriteriaBuilderHelper.or( + cb, + filter, + cb.equal(casePath.get(Case.REGION).get(Region.ID), region.getId()), + cb.equal(casePath.get(Case.RESPONSIBLE_REGION).get(Region.ID), region.getId())); + } + break; + case DISTRICT: + final District district = currentUser.getDistrict(); + if (district != null) { + filter = CriteriaBuilderHelper.or( + cb, + filter, + cb.equal(casePath.get(Case.DISTRICT).get(District.ID), district.getId()), + cb.equal(casePath.get(Case.RESPONSIBLE_DISTRICT).get(District.ID), district.getId())); + } + break; + case HEALTH_FACILITY: + final Facility healthFacility = currentUser.getHealthFacility(); + if (healthFacility != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(casePath.get(Case.HEALTH_FACILITY).get(Facility.ID), healthFacility.getId())); + } + break; + case COMMUNITY: + final Community community = currentUser.getCommunity(); + if (community != null) { + filter = CriteriaBuilderHelper.or( + cb, + filter, + cb.equal(casePath.get(Case.COMMUNITY).get(Community.ID), community.getId()), + cb.equal(casePath.get(Case.RESPONSIBLE_COMMUNITY).get(Community.ID), community.getId())); + } + break; + case POINT_OF_ENTRY: + final PointOfEntry pointOfEntry = currentUser.getPointOfEntry(); + if (pointOfEntry != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(casePath.get(Case.POINT_OF_ENTRY).get(PointOfEntry.ID), pointOfEntry.getId())); + } + break; + case LABORATORY: + final Subquery sampleSubQuery = cq.subquery(Long.class); + final Root sampleRoot = sampleSubQuery.from(Sample.class); + final SampleJoins joins = new SampleJoins(sampleRoot); + final Join cazeJoin = joins.getCaze(); + sampleSubQuery.where(cb.and(cb.equal(cazeJoin, casePath), sampleService.createUserFilterWithoutAssociations(cb, joins))); + sampleSubQuery.select(sampleRoot.get(Sample.ID)); + filter = CriteriaBuilderHelper.or(cb, filter, cb.exists(sampleSubQuery)); + break; + default: } - break; - case LABORATORY: - final Subquery sampleSubQuery = cq.subquery(Long.class); - final Root sampleRoot = sampleSubQuery.from(Sample.class); - final SampleJoins joins = new SampleJoins(sampleRoot); - final Join cazeJoin = joins.getCaze(); - sampleSubQuery.where(cb.and(cb.equal(cazeJoin, casePath), sampleService.createUserFilterWithoutAssociations(cb, joins))); - sampleSubQuery.select(sampleRoot.get(Sample.ID)); - filter = CriteriaBuilderHelper.or(cb, filter, cb.exists(sampleSubQuery)); - break; - default: } // get all cases based on the user's contact association @@ -1680,12 +1687,20 @@ public EditPermissionType isAddContactAllowed(Case caze) { return EditPermissionType.REFUSED; } + if (currentUserHasRestrictedAccessToAssignedEntities() && !DataHelper.equal(caze.getSurveillanceOfficer(), getCurrentUser())) { + return EditPermissionType.REFUSED; + } + return super.getEditPermissionType(caze); } @Override public EditPermissionType getEditPermissionType(Case caze) { + if (currentUserHasRestrictedAccessToAssignedEntities() && !DataHelper.equal(caze.getSurveillanceOfficer(), getCurrentUser())) { + return EditPermissionType.REFUSED; + } + if (!inJurisdictionOrOwned(caze)) { return EditPermissionType.OUTSIDE_JURISDICTION; } @@ -2337,11 +2352,11 @@ public String getCaseUuidForAutomaticSampleAssignment(Set uuids, Disease cb.lessThanOrEqualTo( CriteriaBuilderHelper.dateDiff( cb, - cb.function( - ExtendedPostgreSQL94Dialect.DATE, - Date.class, - CriteriaBuilderHelper.coalesce(cb, Date.class, earliestSampleSq, caseRoot.get(Case.REPORT_DATE))), - cb.function(ExtendedPostgreSQL94Dialect.DATE, Date.class, cb.literal(new Date()))), + cb.function( + ExtendedPostgreSQL94Dialect.DATE, + Date.class, + CriteriaBuilderHelper.coalesce(cb, Date.class, earliestSampleSq, caseRoot.get(Case.REPORT_DATE))), + cb.function(ExtendedPostgreSQL94Dialect.DATE, Date.class, cb.literal(new Date()))), Long.valueOf(TimeUnit.DAYS.toSeconds(automaticSampleAssignmentThreshold)).doubleValue())); List caseUuids = em.createQuery(cq).getResultList(); diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseUserFilterCriteria.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseUserFilterCriteria.java index 0fa3eb01809..58a9cbe5687 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseUserFilterCriteria.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseUserFilterCriteria.java @@ -8,6 +8,7 @@ public class CaseUserFilterCriteria { private boolean excludeCasesFromContacts; private Boolean includeCasesFromOtherJurisdictions = Boolean.FALSE; private boolean excludeLimitedSyncRestrictions; + private boolean restrictAccessToAssignedEntities; public boolean isExcludeCasesFromContacts() { return excludeCasesFromContacts; @@ -39,4 +40,12 @@ public CaseUserFilterCriteria excludeLimitedSyncRestrictions(boolean excludeLimi this.excludeLimitedSyncRestrictions = excludeLimitedSyncRestrictions; return this; } + + public boolean isRestrictAccessToAssignedEntities() { + return restrictAccessToAssignedEntities; + } + + public void setRestrictAccessToAssignedEntities(boolean restrictAccessToAssignedEntities) { + this.restrictAccessToAssignedEntities = restrictAccessToAssignedEntities; + } } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java index bdf6e4ea2a9..1c8d252c7ae 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java @@ -94,6 +94,10 @@ public User getCurrentUser() { return currentUserService.getCurrentUser(); } + public boolean currentUserHasRestrictedAccessToAssignedEntities() { + return currentUserService.hasRestrictedAccessToAssignedEntities(); + } + public boolean hasRight(UserRight right) { return currentUserService.hasUserRight(right); } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java index 57868211d23..eca0c747ad0 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java @@ -1078,49 +1078,56 @@ public Predicate createUserFilterWithoutCase(ContactQueryContext qc, ContactCrit CriteriaQuery cq = qc.getQuery(); From contactRoot = qc.getRoot(); + Predicate filter = null; + + if (currentUserHasRestrictedAccessToAssignedEntities()) { + filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(contactRoot.get(Contact.CONTACT_OFFICER).get(User.ID), getCurrentUser().getId())); + } + // National users can access all contacts in the system final JurisdictionLevel jurisdictionLevel = currentUser.getJurisdictionLevel(); if ((jurisdictionLevel == JurisdictionLevel.NATION && !UserRole.isPortHealthUser(currentUser.getUserRoles()))) { return CriteriaBuilderHelper.limitedDiseasePredicate(cb, currentUser, contactRoot.get(Contact.DISEASE)); } - Predicate filter = null; // whoever created it or is assigned to it is allowed to access it if (contactCriteria == null || contactCriteria.getIncludeContactsFromOtherJurisdictions()) { filter = cb.equal(contactRoot.get(Contact.REPORTING_USER), currentUser); filter = cb.or(filter, cb.equal(contactRoot.get(Contact.CONTACT_OFFICER), currentUser)); } - switch (jurisdictionLevel) { - case REGION: - final Region region = currentUser.getRegion(); - if (region != null) { - filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(contactRoot.get(Contact.REGION), currentUser.getRegion())); - } - break; - case DISTRICT: - final District district = currentUser.getDistrict(); - if (district != null) { - filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(contactRoot.get(Contact.DISTRICT), currentUser.getDistrict())); - } - break; - case COMMUNITY: - final Community community = currentUser.getCommunity(); - if (community != null) { - filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(contactRoot.get(Contact.COMMUNITY), currentUser.getCommunity())); + if (!currentUserHasRestrictedAccessToAssignedEntities()) { + switch (jurisdictionLevel) { + case REGION: + final Region region = currentUser.getRegion(); + if (region != null) { + filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(contactRoot.get(Contact.REGION), currentUser.getRegion())); + } + break; + case DISTRICT: + final District district = currentUser.getDistrict(); + if (district != null) { + filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(contactRoot.get(Contact.DISTRICT), currentUser.getDistrict())); + } + break; + case COMMUNITY: + final Community community = currentUser.getCommunity(); + if (community != null) { + filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(contactRoot.get(Contact.COMMUNITY), currentUser.getCommunity())); + } + break; + case LABORATORY: + final Subquery sampleSubQuery = cq.subquery(Long.class); + final Root sampleRoot = sampleSubQuery.from(Sample.class); + final SampleJoins joins = new SampleJoins(sampleRoot); + final Join contactJoin = joins.getContact(); + + sampleSubQuery.where(cb.and(cb.equal(contactJoin, contactRoot), sampleService.createUserFilterWithoutAssociations(cb, joins))); + sampleSubQuery.select(sampleRoot.get(Sample.ID)); + filter = CriteriaBuilderHelper.or(cb, filter, cb.exists(sampleSubQuery)); + break; + default: } - break; - case LABORATORY: - final Subquery sampleSubQuery = cq.subquery(Long.class); - final Root sampleRoot = sampleSubQuery.from(Sample.class); - final SampleJoins joins = new SampleJoins(sampleRoot); - final Join contactJoin = joins.getContact(); - - sampleSubQuery.where(cb.and(cb.equal(contactJoin, contactRoot), sampleService.createUserFilterWithoutAssociations(cb, joins))); - sampleSubQuery.select(sampleRoot.get(Sample.ID)); - filter = CriteriaBuilderHelper.or(cb, filter, cb.exists(sampleSubQuery)); - break; - default: } filter = CriteriaBuilderHelper.and( @@ -1837,6 +1844,10 @@ public EditPermissionType getEditPermissionType(Contact contact) { return EditPermissionType.OUTSIDE_JURISDICTION; } + if (currentUserHasRestrictedAccessToAssignedEntities() && !DataHelper.equal(contact.getContactOfficer(), (getCurrentUser()))) { + return EditPermissionType.REFUSED; + } + if (sormasToSormasShareInfoService.isContactOwnershipHandedOver(contact) || (contact.getSormasToSormasOriginInfo() != null && !contact.getSormasToSormasOriginInfo().isOwnershipHandedOver())) { return EditPermissionType.WITHOUT_OWNERSHIP; diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/EnvironmentService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/EnvironmentService.java index 9f193bfc6e2..199498a56bf 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/EnvironmentService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/EnvironmentService.java @@ -90,36 +90,41 @@ public Predicate createUserFilter(EnvironmentQueryContext queryContext) { final EnvironmentJoins environmentJoins = queryContext.getJoins(); final From environmentJoin = queryContext.getRoot(); - if (jurisdictionLevel != JurisdictionLevel.NATION) { - switch (jurisdictionLevel) { - case REGION: - if (currentUser.getRegion() != null) { - filter = - CriteriaBuilderHelper.or(cb, filter, cb.equal(environmentJoins.getLocation().get(Location.REGION), currentUser.getRegion())); - } - break; - case DISTRICT: - if (currentUser.getDistrict() != null) { - filter = CriteriaBuilderHelper - .or(cb, filter, cb.equal(environmentJoins.getLocation().get(Location.DISTRICT), currentUser.getDistrict())); - } - break; - case COMMUNITY: - if (currentUser.getCommunity() != null) { - filter = CriteriaBuilderHelper - .or(cb, filter, cb.equal(environmentJoins.getLocation().get(Location.COMMUNITY), currentUser.getCommunity())); + if (currentUserHasRestrictedAccessToAssignedEntities()) { + filter = + CriteriaBuilderHelper.and(cb, filter, cb.equal(environmentJoin.get(Environment.RESPONSIBLE_USER).get(User.ID), currentUser.getId())); + } else { + if (jurisdictionLevel != JurisdictionLevel.NATION) { + switch (jurisdictionLevel) { + case REGION: + if (currentUser.getRegion() != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(environmentJoins.getLocation().get(Location.REGION), currentUser.getRegion())); + } + break; + case DISTRICT: + if (currentUser.getDistrict() != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(environmentJoins.getLocation().get(Location.DISTRICT), currentUser.getDistrict())); + } + break; + case COMMUNITY: + if (currentUser.getCommunity() != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(environmentJoins.getLocation().get(Location.COMMUNITY), currentUser.getCommunity())); + } + break; + default: } - break; - default: - } - Predicate filterResponsible = cb.equal(environmentJoins.getRoot().get(Environment.REPORTING_USER), currentUser); - filterResponsible = cb.or(filterResponsible, cb.equal(environmentJoins.getRoot().get(Environment.RESPONSIBLE_USER), currentUser)); + Predicate filterResponsible = cb.equal(environmentJoins.getRoot().get(Environment.REPORTING_USER), currentUser); + filterResponsible = cb.or(filterResponsible, cb.equal(environmentJoins.getRoot().get(Environment.RESPONSIBLE_USER), currentUser)); - if (filter != null) { - filter = CriteriaBuilderHelper.or(cb, filter, filterResponsible, createEnvironmentSampleFilter(queryContext)); - } else { - filter = filterResponsible; + if (filter != null) { + filter = CriteriaBuilderHelper.or(cb, filter, filterResponsible, createEnvironmentSampleFilter(queryContext)); + } else { + filter = filterResponsible; + } } } @@ -398,6 +403,10 @@ public EditPermissionType getEditPermissionType(Environment environment) { return EditPermissionType.OUTSIDE_JURISDICTION; } + if (currentUserHasRestrictedAccessToAssignedEntities() && !DataHelper.equal(environment.getResponsibleUser(), getCurrentUser())) { + return EditPermissionType.REFUSED; + } + return super.getEditPermissionType(environment); } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleJurisdictionValidator.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleJurisdictionValidator.java index 695870eba73..5ca583b4a31 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleJurisdictionValidator.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleJurisdictionValidator.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.NotImplementedException; +import de.symeda.sormas.backend.environment.Environment; import de.symeda.sormas.backend.location.Location; import de.symeda.sormas.backend.user.User; import de.symeda.sormas.backend.util.PredicateJurisdictionValidator; @@ -45,13 +46,28 @@ public static EnvironmentSampleJurisdictionValidator of(EnvironmentSampleQueryCo return new EnvironmentSampleJurisdictionValidator(qc.getCriteriaBuilder(), user, qc.getJoins(), null); } - @Override - public Predicate isRootInJurisdictionOrOwned() { - final Predicate reportedByCurrentUser = cb.and( + public Predicate isRootInJurisdictionOrOwned(boolean currentHasUserRestrictedAccess) { + if (currentHasUserRestrictedAccess) { + final Predicate reportedByCurrentUser = getReportedByCurrentUserPredicate(); + final Predicate restrictedAccess = + cb.equal(joins.getEnvironment().get(Environment.RESPONSIBLE_USER).get(User.ID), user.getChangeUser().getId()); + return cb.or(reportedByCurrentUser, restrictedAccess); + } else { + return isRootInJurisdictionOrOwned(); + } + } + + private Predicate getReportedByCurrentUserPredicate() { + return cb.and( cb.isNotNull(joins.getRoot().get(EnvironmentSample.REPORTING_USER)), user != null ? cb.equal(joins.getRoot().get(EnvironmentSample.REPORTING_USER).get(User.ID), user.getId()) : cb.equal(joins.getRoot().get(EnvironmentSample.REPORTING_USER).get(User.ID), userPath.get(User.ID))); + } + + @Override + public Predicate isRootInJurisdictionOrOwned() { + final Predicate reportedByCurrentUser = getReportedByCurrentUserPredicate(); return cb.or(reportedByCurrentUser, isRootInJurisdiction()); } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleService.java index e7974284352..d1793c97732 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleService.java @@ -194,6 +194,6 @@ public Predicate buildCriteriaFilter(EnvironmentSampleCriteria criteria, Environ private Predicate inJurisdictionOrOwned(CriteriaBuilder cb, CriteriaQuery cq, From from) { return EnvironmentSampleJurisdictionValidator .of(new EnvironmentSampleQueryContext(cb, cq, from, new EnvironmentSampleJoins(from)), userService.getCurrentUser()) - .isRootInJurisdictionOrOwned(); + .isRootInJurisdictionOrOwned(currentUserHasRestrictedAccessToAssignedEntities()); } } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java index c387ede83e8..a6173b103d0 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java @@ -466,62 +466,67 @@ public Predicate createUserFilter(final EventQueryContext queryContext, final Ev final EventJoins eventJoins = queryContext.getJoins(); final From eventJoin = queryContext.getRoot(); - if (jurisdictionLevel != JurisdictionLevel.NATION) { - switch (jurisdictionLevel) { - case REGION: - if (currentUser.getRegion() != null) { - filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.REGION), currentUser.getRegion())); - } - break; - case DISTRICT: - if (currentUser.getDistrict() != null) { - filter = - CriteriaBuilderHelper.or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.DISTRICT), currentUser.getDistrict())); - } - break; - case COMMUNITY: - if (currentUser.getCommunity() != null) { - filter = - CriteriaBuilderHelper.or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.COMMUNITY), currentUser.getCommunity())); - } - break; - case HEALTH_FACILITY: - if (currentUser.getHealthFacility() != null && currentUser.getHealthFacility().getDistrict() != null) { - filter = CriteriaBuilderHelper - .or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.DISTRICT), currentUser.getHealthFacility().getDistrict())); + if (currentUserHasRestrictedAccessToAssignedEntities()) { + filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(eventJoin.get(Event.RESPONSIBLE_USER).get(User.ID), currentUser.getId())); + } else { + if (jurisdictionLevel != JurisdictionLevel.NATION) { + switch (jurisdictionLevel) { + case REGION: + if (currentUser.getRegion() != null) { + filter = + CriteriaBuilderHelper.or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.REGION), currentUser.getRegion())); + } + break; + case DISTRICT: + if (currentUser.getDistrict() != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.DISTRICT), currentUser.getDistrict())); + } + break; + case COMMUNITY: + if (currentUser.getCommunity() != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.COMMUNITY), currentUser.getCommunity())); + } + break; + case HEALTH_FACILITY: + if (currentUser.getHealthFacility() != null && currentUser.getHealthFacility().getDistrict() != null) { + filter = CriteriaBuilderHelper + .or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.DISTRICT), currentUser.getHealthFacility().getDistrict())); + } + break; + case LABORATORY: + final Subquery sampleSubQuery = cq.subquery(Long.class); + final Root sampleRoot = sampleSubQuery.from(Sample.class); + final SampleJoins sampleJoins = new SampleJoins(sampleRoot); + final Join eventParticipant = sampleJoins.getEventParticipant(); + final From eventParticipantJoin = eventJoins.getEventParticipants(); + SampleJurisdictionPredicateValidator sampleJurisdictionPredicateValidator = + SampleJurisdictionPredicateValidator.withoutAssociations(cb, sampleJoins, currentUser); + sampleSubQuery.where( + cb.and(cb.equal(eventParticipant, eventParticipantJoin), sampleJurisdictionPredicateValidator.inJurisdictionOrOwned())); + sampleSubQuery.select(sampleRoot.get(Sample.ID)); + filter = CriteriaBuilderHelper.or(cb, cb.exists(sampleSubQuery)); + break; + default: } - break; - case LABORATORY: - final Subquery sampleSubQuery = cq.subquery(Long.class); - final Root sampleRoot = sampleSubQuery.from(Sample.class); - final SampleJoins sampleJoins = new SampleJoins(sampleRoot); - final Join eventParticipant = sampleJoins.getEventParticipant(); - final From eventParticipantJoin = eventJoins.getEventParticipants(); - SampleJurisdictionPredicateValidator sampleJurisdictionPredicateValidator = - SampleJurisdictionPredicateValidator.withoutAssociations(cb, sampleJoins, currentUser); - sampleSubQuery - .where(cb.and(cb.equal(eventParticipant, eventParticipantJoin), sampleJurisdictionPredicateValidator.inJurisdictionOrOwned())); - sampleSubQuery.select(sampleRoot.get(Sample.ID)); - filter = CriteriaBuilderHelper.or(cb, cb.exists(sampleSubQuery)); - break; - default: - } - Predicate filterResponsible = cb.equal(eventJoins.getRoot().get(Event.REPORTING_USER), currentUser); - filterResponsible = cb.or(filterResponsible, cb.equal(eventJoins.getRoot().get(Event.RESPONSIBLE_USER), currentUser)); + Predicate filterResponsible = cb.equal(eventJoins.getRoot().get(Event.REPORTING_USER), currentUser); + filterResponsible = cb.or(filterResponsible, cb.equal(eventJoins.getRoot().get(Event.RESPONSIBLE_USER), currentUser)); - if (eventUserFilterCriteria != null && eventUserFilterCriteria.isIncludeUserCaseAndEventParticipantFilter()) { - filter = CriteriaBuilderHelper.or(cb, filter, createCaseAndEventParticipantFilter(queryContext)); - } + if (eventUserFilterCriteria != null && eventUserFilterCriteria.isIncludeUserCaseAndEventParticipantFilter()) { + filter = CriteriaBuilderHelper.or(cb, filter, createCaseAndEventParticipantFilter(queryContext)); + } - if (eventUserFilterCriteria != null && eventUserFilterCriteria.isForceRegionJurisdiction()) { - filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.REGION), currentUser.getRegion())); - } + if (eventUserFilterCriteria != null && eventUserFilterCriteria.isForceRegionJurisdiction()) { + filter = CriteriaBuilderHelper.or(cb, filter, cb.equal(eventJoins.getLocation().get(Location.REGION), currentUser.getRegion())); + } - if (filter != null) { - filter = CriteriaBuilderHelper.or(cb, filter, filterResponsible); - } else { - filter = filterResponsible; + if (filter != null) { + filter = CriteriaBuilderHelper.or(cb, filter, filterResponsible); + } else { + filter = filterResponsible; + } } } @@ -1062,6 +1067,10 @@ public EditPermissionType getEditPermissionType(Event event) { return EditPermissionType.OUTSIDE_JURISDICTION; } + if (currentUserHasRestrictedAccessToAssignedEntities() && !DataHelper.equal(event.getResponsibleUser(), getCurrentUser())) { + return EditPermissionType.REFUSED; + } + if (sormasToSormasShareInfoService.isEventOwnershipHandedOver(event) || event.getSormasToSormasOriginInfo() != null && !event.getSormasToSormasOriginInfo().isOwnershipHandedOver()) { return EditPermissionType.WITHOUT_OWNERSHIP; diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java index 99ad9da19fd..d4c09b19fe2 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java @@ -90,7 +90,6 @@ import de.symeda.sormas.backend.common.CoreAdo; import de.symeda.sormas.backend.common.CriteriaBuilderHelper; import de.symeda.sormas.backend.common.FilterProvider; -import de.symeda.sormas.backend.manualmessagelog.ManualMessageLogService; import de.symeda.sormas.backend.contact.Contact; import de.symeda.sormas.backend.contact.ContactJoins; import de.symeda.sormas.backend.contact.ContactQueryContext; @@ -108,6 +107,7 @@ import de.symeda.sormas.backend.immunization.entity.Immunization; import de.symeda.sormas.backend.infrastructure.district.District; import de.symeda.sormas.backend.location.Location; +import de.symeda.sormas.backend.manualmessagelog.ManualMessageLogService; import de.symeda.sormas.backend.travelentry.TravelEntry; import de.symeda.sormas.backend.travelentry.TravelEntryJoins; import de.symeda.sormas.backend.travelentry.TravelEntryQueryContext; @@ -1095,7 +1095,12 @@ public boolean isEditAllowed(String personUuid) { cb.equal(from.get(Person.UUID), personUuid), cb.or( cb.and( - cb.and(cb.isNotNull(joins.getCaze()), cb.isFalse(joins.getCaze().get(Case.DELETED))), + cb.and( + cb.isNotNull(joins.getCaze()), + cb.isFalse(joins.getCaze().get(Case.DELETED)), + currentUserHasRestrictedAccessToAssignedEntities() + ? cb.equal(joins.getCaze().get(Case.SURVEILLANCE_OFFICER).get(User.ID), getCurrentUser().getId()) + : cb.conjunction()), caseService.createOwnershipPredicate(true, joins.getCaze(), cb, cq)), cb.and( cb.and(cb.isNotNull(joins.getContact()), cb.isFalse(joins.getContact().get(Contact.DELETED))), diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/task/TaskService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/task/TaskService.java index 0daf00b0793..cdf58fa3342 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/task/TaskService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/task/TaskService.java @@ -219,7 +219,12 @@ public Predicate createUserFilter(TaskQueryContext taskQueryContext, TaskCriteri TaskJoins joins = taskQueryContext.getJoins(); - Predicate assigneeFilter = createAssigneeFilter(cb, joins.getAssignee()); + Predicate assigneeFilter; + if (currentUserHasRestrictedAccessToAssignedEntities()) { + assigneeFilter = cb.disjunction(); + } else { + assigneeFilter = createAssigneeFilter(cb, joins.getAssignee()); + } Predicate relatedEntityNotDeletedFilter = cb.or( cb.equal(taskPath.get(Task.TASK_CONTEXT), TaskContext.GENERAL), diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/CurrentUserService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/CurrentUserService.java index f067a49249a..16c3a2b3800 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/CurrentUserService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/CurrentUserService.java @@ -1,6 +1,7 @@ package de.symeda.sormas.backend.user; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Resource; import javax.ejb.LocalBean; @@ -98,6 +99,15 @@ public boolean hasAnyUserRight(Set userRights) { return getCurrentUser().hasAnyUserRight(userRights); } + public boolean hasRestrictedAccessToAssignedEntities() { + if (getCurrentUser() != null && !getCurrentUser().getUserRoles().isEmpty()) { + return getCurrentUser().getUserRoles() + .stream() + .allMatch(UserRole::isRestrictAccessToAssignedEntities); + } + return false; + } + // We need a clean transaction as we do not want call potential entity listeners which would lead to recursion @Transactional(Transactional.TxType.REQUIRES_NEW) User fetchUser(String userName) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserFacadeEjb.java index 43864621244..bf98568c25c 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserFacadeEjb.java @@ -692,6 +692,11 @@ public List getIndexList(UserCriteria userCriteria, Integer first, Inte filter = userService.buildCriteriaFilter(userCriteria, cb, user); } + if (userCriteria != null && Boolean.TRUE.equals(userCriteria.getShowOnlyRestrictedAccessToAssignedEntities())) { + Join rolesJoin = user.join(User.USER_ROLES, JoinType.LEFT); + filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(rolesJoin.get(UserRole.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES), true)); + } + if (filter != null) { /* * No preemptive distinct because this does collide with @@ -737,7 +742,11 @@ public List getIndexList(UserCriteria userCriteria, Integer first, Inte cq.select(user); - return QueryHelper.getResultList(em, cq, first, max, UserFacadeEjb::toDto); + final List resultList = QueryHelper.getResultList(em, cq, first, max, UserFacadeEjb::toDto); + // because the selection is based on User entity and we need userRole join (we cannot avoid it) which pulls duplicate rows, + // and distinct on the query does not work because of sorting (e.g. Address) + // we need to deduplicate the list using java code + return resultList.stream().distinct().collect(Collectors.toList()); } @Override @@ -754,11 +763,16 @@ public long count(UserCriteria userCriteria) { filter = userService.buildCriteriaFilter(userCriteria, cb, root); } + if (userCriteria != null && Boolean.TRUE.equals(userCriteria.getShowOnlyRestrictedAccessToAssignedEntities())) { + Join rolesJoin = root.join(User.USER_ROLES, JoinType.LEFT); + filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(rolesJoin.get(UserRole.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES), true)); + } + if (filter != null) { cq.where(filter); } - cq.select(cb.count(root)); + cq.select(cb.countDistinct(root)); return em.createQuery(cq).getSingleResult(); } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRole.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRole.java index dbaa82a7841..55a2c10305c 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRole.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRole.java @@ -68,6 +68,7 @@ public class UserRole extends AbstractDomainObject { public static final String LINKED_DEFAULT_USER_ROLE = "linkedDefaultUserRole"; public static final String EMAIL_NOTIFICATIONS = "emailNotificationTypes"; public static final String SMS_NOTIFICATIONS = "smsNotificationTypes"; + public static final String RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES = "restrictAccessToAssignedEntities"; private Set userRights; private boolean enabled = true; @@ -80,6 +81,7 @@ public class UserRole extends AbstractDomainObject { private JurisdictionLevel jurisdictionLevel; private Set emailNotificationTypes = Collections.emptySet(); private Set smsNotificationTypes = Collections.emptySet(); + private boolean restrictAccessToAssignedEntities; @ElementCollection(fetch = FetchType.EAGER) @Enumerated(EnumType.STRING) @@ -151,6 +153,15 @@ public void setPortHealthUser(boolean portHealthUser) { this.portHealthUser = portHealthUser; } + @Column + public boolean isRestrictAccessToAssignedEntities() { + return restrictAccessToAssignedEntities; + } + + public void setRestrictAccessToAssignedEntities(boolean restrictAccessToAssignedEntities) { + this.restrictAccessToAssignedEntities = restrictAccessToAssignedEntities; + } + @Enumerated(EnumType.STRING) public JurisdictionLevel getJurisdictionLevel() { return jurisdictionLevel; diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleFacadeEjb.java index 9090087f759..9aebc4057a3 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleFacadeEjb.java @@ -282,6 +282,7 @@ public UserRole fillOrBuildEntity(UserRoleDto source, UserRole target, boolean c target.setSmsNotificationTypes(source.getSmsNotificationTypes()); target.setJurisdictionLevel(source.getJurisdictionLevel()); target.setLinkedDefaultUserRole(source.getLinkedDefaultUserRole()); + target.setRestrictAccessToAssignedEntities(source.isRestrictAccessToAssignedEntities()); return target; } @@ -306,6 +307,7 @@ public static UserRoleDto toDto(UserRole source) { target.setSmsNotificationTypes(new HashSet<>(source.getSmsNotificationTypes())); target.setJurisdictionLevel(source.getJurisdictionLevel()); target.setLinkedDefaultUserRole(source.getLinkedDefaultUserRole()); + target.setRestrictAccessToAssignedEntities(source.isRestrictAccessToAssignedEntities()); return target; } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java index 90af64c2f5d..6fc1d89e702 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java @@ -165,6 +165,11 @@ public Predicate buildCriteriaFilter( filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(from.get(UserRole.JURISDICTION_LEVEL), userRoleCriteria.getJurisdictionLevel())); } + if (userRoleCriteria.getShowOnlyRestrictedAccessToAssignedEntities() != null + && Boolean.TRUE.equals(userRoleCriteria.getShowOnlyRestrictedAccessToAssignedEntities())) { + filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(from.get(UserRole.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES), true)); + } + return filter; } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserService.java index 9379a52a417..0961f9fa9c4 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserService.java @@ -291,9 +291,9 @@ public List getUserReferences( // eliminate users that are limited to others diseases if (limitedDisease != null) { - Predicate restrictOtherLimitedDiseaseUsers = cb.or( - cb.isNull(userRoot.get(User.LIMITED_DISEASES)), - cb.like(userRoot.get(User.LIMITED_DISEASES).as(String.class), "%" + limitedDisease.name() + '%')); + Predicate restrictOtherLimitedDiseaseUsers = cb.or( + cb.isNull(userRoot.get(User.LIMITED_DISEASES)), + cb.like(userRoot.get(User.LIMITED_DISEASES).as(String.class), "%" + limitedDisease.name() + '%')); filter = CriteriaBuilderHelper.and(cb, filter, restrictOtherLimitedDiseaseUsers); } @@ -388,9 +388,9 @@ public List getUserRefsByInfrastructure( filter = CriteriaBuilderHelper.and(cb, filter, jurisdictionFilter); if (limitedDisease != null) { - Predicate restrictOtherLimitedDiseaseUsers = cb.or( - cb.isNull(userRoot.get(User.LIMITED_DISEASES)), - cb.like(userRoot.get(User.LIMITED_DISEASES).as(String.class), "%" + limitedDisease.name() + "%")); + Predicate restrictOtherLimitedDiseaseUsers = cb.or( + cb.isNull(userRoot.get(User.LIMITED_DISEASES)), + cb.like(userRoot.get(User.LIMITED_DISEASES).as(String.class), "%" + limitedDisease.name() + "%")); filter = CriteriaBuilderHelper.and(cb, filter, restrictOtherLimitedDiseaseUsers); } @@ -921,7 +921,7 @@ public boolean isPortHealthUser() { return userRoleFacade.isPortHealthUser(userRoleDtos); } - public Predicate inJurisdictionOrOwned(CriteriaBuilder cb, UserJoins joins) { - return new UserJurisdictionPredicateValidator(cb, getCurrentUser(), null, joins).inJurisdictionOrOwned(); - } + public Predicate inJurisdictionOrOwned(CriteriaBuilder cb, UserJoins joins) { + return new UserJurisdictionPredicateValidator(cb, getCurrentUser(), null, joins).inJurisdictionOrOwned(); + } } diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index 1002fe5d7ba..206da133468 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -12827,4 +12827,11 @@ ALTER TABLE manualmessagelog_history INSERT INTO schema_version (version_number, comment) VALUES (535, 'Display a history of sent external emails #12465'); +-- 2023-12-05 Assign case(s) to a User and allow them to see the data of only the assigned case(s) in the system #12697 +ALTER TABLE userroles ADD COLUMN restrictAccessToAssignedEntities boolean NOT NULL DEFAULT false; +ALTER TABLE userroles_history ADD COLUMN restrictAccessToAssignedEntities boolean; + +INSERT INTO schema_version (version_number, comment) VALUES (536, 'Assign case(s) to a User and allow them to see the data of only the assigned case(s) in the system #12697'); + + -- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** \ No newline at end of file diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/TestDataCreator.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/TestDataCreator.java index fe0c36ee9eb..6224a457d22 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/TestDataCreator.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/TestDataCreator.java @@ -198,6 +198,15 @@ public UserRoleReferenceDto getUserRoleReference(DefaultUserRole userRole) { return userRoleDtoMap.get(userRole); } + public UserRoleReferenceDto createUserRoleWithRestrictedAccessToAssignedEntitiesUsingTemplate(DefaultUserRole templateUserRole) { + UserRoleDto userRole = new UserRoleDto(); + userRole.setCaption(templateUserRole.toString() + "Restricted"); + userRole.setJurisdictionLevel(templateUserRole.getJurisdictionLevel()); + userRole.setUserRights(Arrays.stream(templateUserRole.getDefaultUserRights().toArray(new UserRight[0])).collect(Collectors.toSet())); + userRole.setRestrictAccessToAssignedEntities(true); + return beanTest.getUserRoleFacade().saveUserRole(userRole).toReference(); + } + public UserRole getUserRole(DefaultUserRole userRole) { if (userRoleMap.isEmpty()) { createUserRoles(); @@ -231,6 +240,22 @@ public UserDto createSurveillanceOfficer(RDCF rdcf) { getUserRoleReference(DefaultUserRole.SURVEILLANCE_OFFICER)); } + public UserDto createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(RDCF rdcf) { + if (rdcf == null) { + rdcf = createRDCF("Region", "District", "Community", "Facility"); + } + + final UserDto user = createUser( + rdcf.region.getUuid(), + rdcf.district.getUuid(), + rdcf.facility.getUuid(), + "SurvOff", + "RestrictedAccess", + createUserRoleWithRestrictedAccessToAssignedEntitiesUsingTemplate(DefaultUserRole.SURVEILLANCE_OFFICER)); + + return user; + } + public UserDto createContactOfficer(RDCF rdcf) { if (rdcf == null) { rdcf = createRDCF("Region", "District", "Community", "Facility"); @@ -275,12 +300,12 @@ public UserDto createUser(RDCF rdcf, UserRoleReferenceDto... roles) { } public UserDto createUser(RDCF rdcf, DefaultUserRole defaultUserRole) { - return createUser(rdcf, "User", defaultUserRole); - } + return createUser(rdcf, "User", defaultUserRole); + } - public UserDto createUser(RDCF rdcf, String lastName, DefaultUserRole defaultUserRole) { + public UserDto createUser(RDCF rdcf, String lastName, DefaultUserRole defaultUserRole) { UserRoleReferenceDto userRole = getUserRoleReference(defaultUserRole); - return createUser(rdcf.region.getUuid(), rdcf.district.getUuid(), rdcf.facility.getUuid(), userRole.getCaption(), lastName, userRole); + return createUser(rdcf.region.getUuid(), rdcf.district.getUuid(), rdcf.facility.getUuid(), userRole.getCaption(), lastName, userRole); } public UserDto createUser(RDCF rdcf, String firstName, String lastName, UserRoleReferenceDto... roles) { @@ -940,7 +965,7 @@ public ContactDto createContact( } public TaskDto createTask(UserReferenceDto assigneeUser) { - return createTask(TaskContext.GENERAL, TaskType.OTHER, TaskStatus.PENDING, null, null, null, new Date(), assigneeUser); + return createTask(TaskContext.GENERAL, TaskType.OTHER, TaskStatus.PENDING, null, null, null, null, new Date(), assigneeUser); } public TaskDto createTask(TaskContext context, ReferenceDto entityRef, Consumer customConfig) { @@ -963,6 +988,7 @@ public TaskDto createTask( CaseReferenceDto caze, ContactReferenceDto contact, EventReferenceDto event, + EnvironmentReferenceDto environment, Date dueDate, UserReferenceDto assigneeUser) { @@ -977,6 +1003,9 @@ public TaskDto createTask( case EVENT: entityRef = event; break; + case ENVIRONMENT: + entityRef = environment; + break; case GENERAL: entityRef = null; break; diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java index 9ff32cc896d..9f1f9e1c852 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java @@ -192,6 +192,7 @@ public class CaseFacadeEjbTest extends AbstractBeanTest { private UserDto nationalUser; private UserDto surveillanceSupervisor; private UserDto surveillanceOfficer; + private UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities; @Override public void init() { @@ -200,6 +201,7 @@ public void init() { rdcf = creator.createRDCF("Region", "District", "Community", "Facility"); surveillanceSupervisor = creator.createSurveillanceSupervisor(rdcf); surveillanceOfficer = creator.createSurveillanceOfficer(rdcf); + surveillanceOfficerWithRestrictedAccessToAssignedEntities = creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf); nationalUser = creator.createNationalUser(); } @@ -573,6 +575,7 @@ public void testMovingCaseUpdatesTaskAssigneeAndCreatesPreviousHospitalization() caze.toReference(), null, null, + null, new Date(), surveillanceOfficer.toReference()); TaskDto doneTask = creator.createTask( @@ -582,6 +585,7 @@ public void testMovingCaseUpdatesTaskAssigneeAndCreatesPreviousHospitalization() caze.toReference(), null, null, + null, new Date(), surveillanceOfficer.toReference()); @@ -813,6 +817,71 @@ public void testGetIndexListByEventFreeText() { assertEquals(1, getCaseFacade().getIndexList(new CaseCriteria().eventLike("signal description"), 0, 100, null).size()); } + @Test + public void testGetIndexListByARestrictedAccessToAssignedEntities() { + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + + String lastName = "Person"; + PersonDto cazePerson = creator.createPerson("Case", lastName); + final CaseDataDto firstCase = creator.createCase( + surveillanceSupervisor.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf); + creator.createCase( + surveillanceSupervisor.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf, + c -> c.setHealthFacilityDetails("abc")); + creator.createCase( + surveillanceSupervisor.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf, + c -> c.setHealthFacilityDetails("xyz")); + + List results = getCaseFacade().getIndexList( + null, + 0, + 100, + Arrays.asList( + new SortProperty(CaseIndexDto.DISEASE), + new SortProperty(CaseIndexDto.PERSON_FIRST_NAME), + new SortProperty(CaseIndexDto.RESPONSIBLE_DISTRICT_NAME), + new SortProperty(CaseIndexDto.HEALTH_FACILITY_NAME, false), + new SortProperty(CaseIndexDto.SURVEILLANCE_OFFICER_UUID))); + + assertEquals(0, results.size()); + + loginWith(nationalUser); + firstCase.setSurveillanceOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + CaseDataDto caze = getCaseFacade().save(firstCase); + + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + List results2 = getCaseFacade().getIndexList( + null, + 0, + 100, + Arrays.asList( + new SortProperty(CaseIndexDto.DISEASE), + new SortProperty(CaseIndexDto.PERSON_FIRST_NAME), + new SortProperty(CaseIndexDto.RESPONSIBLE_DISTRICT_NAME), + new SortProperty(CaseIndexDto.HEALTH_FACILITY_NAME, false), + new SortProperty(CaseIndexDto.SURVEILLANCE_OFFICER_UUID))); + assertEquals(1, results2.size()); + } + @Test public void testCaseExportWithPrescriptionsTreatmentsVisits() { @@ -1297,6 +1366,7 @@ public void testCaseDeletionAndRestoration() throws ExternalSurveillanceToolRunt caze.toReference(), null, null, + null, new Date(), surveillanceSupervisor.toReference()); SampleDto sample = creator @@ -1607,6 +1677,7 @@ public void testMergeCase() throws IOException { otherCaseReference, new ContactReferenceDto(), new EventReferenceDto(), + null, new Date(), otherUserReference); TreatmentDto treatment = creator.createTreatment(otherCase); @@ -2091,7 +2162,9 @@ public void testCreateInvestigationTask() { CaseDataDto caze = creator.createCase(informant, person, rdcf); List caseTasks = getTaskFacade().getAllPendingByCase(caze.toReference()); - assertEquals(surveillanceOfficer.toReference(), caseTasks.get(0).getAssigneeUser()); + final UserReferenceDto assigneeUser = caseTasks.get(0).getAssigneeUser(); + final UserDto assigneeUserDetail = getUserFacade().getByUuid(assigneeUser.getUuid()); + assertTrue(assigneeUserDetail.getUserRoles().containsAll(surveillanceOfficer.getUserRoles())); } @Test diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/contact/ContactFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/contact/ContactFacadeEjbTest.java index 19bc75a1a97..afdfcc9b9b9 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/contact/ContactFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/contact/ContactFacadeEjbTest.java @@ -548,6 +548,7 @@ public void testContactDeletionAndRestoration() { null, contact.toReference(), null, + null, new Date(), user.toReference()); SampleDto sample = @@ -991,6 +992,50 @@ public void testGetIndexDetailedList() { } } + @Test + public void testGetIndexListByARestrictedAccessToAssignedEntities() { + RDCF rdcf = creator.createRDCF(); + UserDto user = creator.createNationalUser(); + + PersonDto cazePerson = creator.createPerson("Case", "Person"); + CaseDataDto caze = creator.createCase( + user.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf); + PersonDto contactPerson = creator.createPerson("Contact", "Person"); + final ContactDto contact = + creator.createContact(user.toReference(), user.toReference(), contactPerson.toReference(), caze, new Date(), new Date(), null); + + PersonDto contactPerson2 = creator.createPerson("Contact2", "Person2"); + final ContactDto contact2 = + creator.createContact(user.toReference(), user.toReference(), contactPerson2.toReference(), null, new Date(), new Date(), Disease.EVD); + + assertEquals(2, getContactFacade().getIndexList(null, 0, 100, null).size()); + + UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities = + creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + final List indexList = getContactFacade().getIndexList(null, 0, 100, null); + assertEquals(0, indexList.size()); + + loginWith(user); + contact2.setContactOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getContactFacade().save(contact2); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(1, getContactFacade().getIndexList(null, 0, 100, null).size()); + + loginWith(user); + caze.setSurveillanceOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getCaseFacade().save(caze); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(2, getContactFacade().getIndexList(null, 0, 100, null).size()); + } + @Test public void testGetContactCountsByCasesForDashboard() { @@ -1891,6 +1936,7 @@ public void testMergeContact() throws IOException { null, otherContactReference, new EventReferenceDto(), + null, new Date(), otherUserReference); getContactFacade().save(otherContact); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/deletionconfiguration/CoreEntityDeletionServiceTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/deletionconfiguration/CoreEntityDeletionServiceTest.java index 7e11dec3e39..69fed078cb2 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/deletionconfiguration/CoreEntityDeletionServiceTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/deletionconfiguration/CoreEntityDeletionServiceTest.java @@ -627,8 +627,16 @@ public void testContactPermanentDeletion() { contactDto.setReportDateTime(beyondRelevanceDate); getContactFacade().save(contactDto); - TaskDto taskDto = creator - .createTask(TaskContext.CONTACT, TaskType.CONTACT_FOLLOW_UP, TaskStatus.PENDING, null, contactDto.toReference(), null, new Date(), null); + TaskDto taskDto = creator.createTask( + TaskContext.CONTACT, + TaskType.CONTACT_FOLLOW_UP, + TaskStatus.PENDING, + null, + contactDto.toReference(), + null, + null, + new Date(), + null); SampleDto sample = creator.createSample( contactDto.toReference(), @@ -640,8 +648,16 @@ public void testContactPermanentDeletion() { ContactDto contactDto2 = creator.createContact(user.toReference(), person.toReference(), Disease.CORONAVIRUS); - TaskDto taskDto2 = creator - .createTask(TaskContext.CONTACT, TaskType.CONTACT_FOLLOW_UP, TaskStatus.PENDING, null, contactDto2.toReference(), null, new Date(), null); + TaskDto taskDto2 = creator.createTask( + TaskContext.CONTACT, + TaskType.CONTACT_FOLLOW_UP, + TaskStatus.PENDING, + null, + contactDto2.toReference(), + null, + null, + new Date(), + null); SampleDto sample2 = creator.createSample( contactDto2.toReference(), @@ -678,8 +694,16 @@ public void testContactPermanentDeletion() { ContactDto contactDto3 = creator.createContact(user.toReference(), person.toReference(), Disease.CORONAVIRUS); - TaskDto taskDto3 = creator - .createTask(TaskContext.CONTACT, TaskType.CONTACT_FOLLOW_UP, TaskStatus.PENDING, null, contactDto3.toReference(), null, new Date(), null); + TaskDto taskDto3 = creator.createTask( + TaskContext.CONTACT, + TaskType.CONTACT_FOLLOW_UP, + TaskStatus.PENDING, + null, + contactDto3.toReference(), + null, + null, + new Date(), + null); SampleDto sample3 = creator.createSample( contactDto3.toReference(), @@ -741,8 +765,16 @@ public void testPermanentDeletionOfVisitLinkedToMultipleContacts() throws IOExce ContactDto contactDto = creator.createContact(user.toReference(), person.toReference(), Disease.CORONAVIRUS); - TaskDto taskDto = creator - .createTask(TaskContext.CONTACT, TaskType.CONTACT_FOLLOW_UP, TaskStatus.PENDING, null, contactDto.toReference(), null, new Date(), null); + TaskDto taskDto = creator.createTask( + TaskContext.CONTACT, + TaskType.CONTACT_FOLLOW_UP, + TaskStatus.PENDING, + null, + contactDto.toReference(), + null, + null, + new Date(), + null); SampleDto sample = creator.createSample( contactDto.toReference(), @@ -761,8 +793,16 @@ public void testPermanentDeletionOfVisitLinkedToMultipleContacts() throws IOExce //create second contact with the same person ContactDto contactDto2 = creator.createContact(user.toReference(), person.toReference(), Disease.CORONAVIRUS); - TaskDto taskDto2 = creator - .createTask(TaskContext.CONTACT, TaskType.CONTACT_FOLLOW_UP, TaskStatus.PENDING, null, contactDto2.toReference(), null, new Date(), null); + TaskDto taskDto2 = creator.createTask( + TaskContext.CONTACT, + TaskType.CONTACT_FOLLOW_UP, + TaskStatus.PENDING, + null, + contactDto2.toReference(), + null, + null, + new Date(), + null); SampleDto sample2 = creator.createSample( contactDto2.toReference(), user.toReference(), diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/EnvironmentFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/EnvironmentFacadeEjbTest.java index 5f9b2c6df13..71d7acd86af 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/EnvironmentFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/EnvironmentFacadeEjbTest.java @@ -1,6 +1,7 @@ package de.symeda.sormas.backend.environment; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -111,4 +112,31 @@ public void testGetIndexList() { assertEquals(1, byUserRdcf.size()); assertEquals(environment2.getUuid(), byUserRdcf.get(0).getUuid()); } + + @Test + public void testGetIndexListByARestrictedAccessToAssignedEntities() { + + UserDto user = creator.createNationalUser(); + TestDataCreator.RDCF rdcf1 = creator.createRDCF(); + EnvironmentDto environment1 = creator.createEnvironment("Test Environment", EnvironmentMedia.WATER, user.toReference(), rdcf1); + TestDataCreator.RDCF rdcf2 = creator.createRDCF(); + EnvironmentDto environment2 = creator.createEnvironment("Test Environment 2", EnvironmentMedia.AIR, user.toReference(), rdcf2); + + List allResults = getEnvironmentFacade().getIndexList(new EnvironmentCriteria(), 0, 100, null); + assertEquals(2, allResults.size()); + + UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities = + creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf1); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + assertEquals(0, getEnvironmentFacade().getIndexList(new EnvironmentCriteria(), 0, 100, null).size()); + + loginWith(user); + environment1.setResponsibleUser(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getEnvironmentFacade().save(environment1); + + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(1, getEnvironmentFacade().getIndexList(new EnvironmentCriteria(), 0, 100, null).size()); + } + } diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleFacadeEjbTest.java index 64ab62bf3b9..659ed85488c 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/environment/environmentsample/EnvironmentSampleFacadeEjbTest.java @@ -26,6 +26,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDate; import java.time.ZoneId; @@ -98,19 +99,19 @@ public void init() { public void testSave() { EnvironmentSampleDto dto = creator.createEnvironmentSample(environment.toReference(), reportingUser.toReference(), rdcf, lab.toReference(), s -> { - s.setSampleMaterial(EnvironmentSampleMaterial.WATER); - s.setLaboratory(lab.toReference()); - s.setFieldSampleId("123"); - s.setChlorineResiduals(1.0f); - s.setPhValue(8); - s.setHeavyRain(YesNoUnknown.YES); - s.setDispatched(true); - s.setReceived(false); - s.setGeneralComment("General comment"); - s.getLocation().setAddressType(PersonAddressType.OTHER_ADDRESS); - s.getLocation().setCity("City"); - s.getLocation().setStreet("Street"); - }); + s.setSampleMaterial(EnvironmentSampleMaterial.WATER); + s.setLaboratory(lab.toReference()); + s.setFieldSampleId("123"); + s.setChlorineResiduals(1.0f); + s.setPhValue(8); + s.setHeavyRain(YesNoUnknown.YES); + s.setDispatched(true); + s.setReceived(false); + s.setGeneralComment("General comment"); + s.getLocation().setAddressType(PersonAddressType.OTHER_ADDRESS); + s.getLocation().setCity("City"); + s.getLocation().setStreet("Street"); + }); EnvironmentSampleDto createdDto = getEnvironmentSampleFacade().save(dto); @@ -210,8 +211,8 @@ public void testGetByUuid() { public void testGetReferenceByUuid() { EnvironmentSampleDto dto = creator.createEnvironmentSample(environment.toReference(), reportingUser.toReference(), rdcf, lab.toReference(), s -> { - s.setSampleMaterial(EnvironmentSampleMaterial.AIR); - }); + s.setSampleMaterial(EnvironmentSampleMaterial.AIR); + }); EnvironmentSampleReferenceDto referenceDto = getEnvironmentSampleFacade().getReferenceByUuid(dto.getUuid()); @@ -525,6 +526,42 @@ public void testGetIndexList() { assertThat(getEnvironmentSampleFacade().getIndexList(criteria3, null, null, null), hasSize(2)); } + @Test + public void testGetIndexListWithRestrictedAccessToAssignedEntities() { + EnvironmentSampleDto environmentSample = + creator.createEnvironmentSample(environment.toReference(), reportingUser.toReference(), rdcf, lab.toReference(), s -> { + s.setFieldSampleId("field_sample-1"); + s.getLocation().setRegion(rdcf.region); + s.getLocation().setDistrict(rdcf.district); + s.getLocation().setStreet("street"); + s.getLocation().setHouseNumber("1"); + s.getLocation().setPostalCode("12345"); + s.getLocation().setCity("city"); + s.setDispatched(true); + s.setDispatchDate(new Date()); + s.setReceived(false); + s.setSampleMaterial(EnvironmentSampleMaterial.OTHER); + s.setOtherSampleMaterial("Other sample material"); + }); + Pathogen positivePathogen = creator.createPathogen("TEST_PATHOGEN", "Test pathogen"); + PathogenTestDto positiveTest = creator.createPathogenTest( + environmentSample.toReference(), + PathogenTestType.ISOLATION, + positivePathogen, + lab.toReference(), + reportingUser.toReference(), + PathogenTestResultType.POSITIVE, + null); + assertThat(getEnvironmentSampleFacade().getIndexList(null, null, null, null), hasSize(1)); + + UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities = + creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + assertThat(getEnvironmentSampleFacade().getIndexList(null, null, null, null), hasSize(0)); + + } + @Test public void testRelevanceStatusFilter() { EnvironmentDto environment1 = creator.createEnvironment("Env1", EnvironmentMedia.WATER, reportingUser.toReference(), rdcf); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/event/EventFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/event/EventFacadeEjbTest.java index 3164ff5e684..d21d495c443 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/event/EventFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/event/EventFacadeEjbTest.java @@ -228,6 +228,61 @@ public void testGetIndexList() { assertEquals("TitleEv2", results.get(0).getEventTitle()); } + @Test + public void testGetIndexListByARestrictedAccessToAssignedEntities() { + RDCF rdcf = creator.createRDCF(); + UserDto user = creator.createSurveillanceSupervisor(rdcf); + final EventDto event = creator.createEvent( + EventStatus.SIGNAL, + EventInvestigationStatus.PENDING, + "TitleEv1", + "DescriptionEv1", + "First", + "Name", + "12345", + TypeOfPlace.PUBLIC_PLACE, + DateHelper.subtractDays(new Date(), 1), + new Date(), + user.toReference(), + user.toReference(), + Disease.EVD, + rdcf); + + final EventDto event1 = creator.createEvent( + EventStatus.EVENT, + EventInvestigationStatus.PENDING, + "TitleEv2", + "DescriptionEv2", + "First", + "Name", + "12345", + TypeOfPlace.FACILITY, + DateHelper.subtractDays(new Date(), 1), + new Date(), + user.toReference(), + user.toReference(), + Disease.EVD, + rdcf); + + EventCriteria eventCriteria = new EventCriteria(); + List results = getEventFacade().getIndexList(eventCriteria, 0, 100, null); + assertEquals(2, results.size()); + + UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities = + creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + List results2 = getEventFacade().getIndexList(eventCriteria, 0, 100, null); + assertEquals(0, results2.size()); + + loginWith(user); + event.setResponsibleUser(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getEventFacade().save(event); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + List results3 = getEventFacade().getIndexList(eventCriteria, 0, 100, null); + assertEquals(1, results3.size()); + } + @Test public void testGetExportList() { diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalsurveillancetool/ExternalSurveillanceToolGatewayFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalsurveillancetool/ExternalSurveillanceToolGatewayFacadeEjbTest.java index 2d6c2b0a909..44f8a7e6d75 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalsurveillancetool/ExternalSurveillanceToolGatewayFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalsurveillancetool/ExternalSurveillanceToolGatewayFacadeEjbTest.java @@ -488,6 +488,7 @@ public void testCaseDeletionAndRestoration_WithoutCaseAllowedToBeSharedWithRepor caze.toReference(), null, null, + null, new Date(), user.toReference()); SampleDto sample = creator.createSample(caze.toReference(), new Date(), new Date(), user.toReference(), SampleMaterial.BLOOD, rdcf.facility); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java index 490d765c1dc..8811ec8aa87 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java @@ -275,6 +275,51 @@ public void testGetIndexListBySampleAssociationType() { getSampleFacade().getIndexList(new SampleCriteria().sampleAssociationType(SampleAssociationType.EVENT_PARTICIPANT), 0, 100, null).size()); } + @Test + public void testGetIndexListBySampleAssociationTypeAndRestrictedAccessToAssignedEntities() { + + TestDataCreator.RDCF rdcf = creator.createRDCF("Region", "District", "Community", "Facility"); + UserDto user = useNationalAdminLogin(); + PersonDto cazePerson = creator.createPerson("Case", "Person1"); + CaseDataDto caze = creator.createCase( + user.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf); + + SampleDto cazeSample = creator.createSample(caze.toReference(), user.toReference(), rdcf.facility); + + PersonDto contactPerson = creator.createPerson("Contact", "Person2"); + ContactDto contact = creator.createContact(user.toReference(), contactPerson.toReference(), caze); + cazeSample.setSampleDateTime(DateHelper.subtractDays(new Date(), 5)); + getSampleFacade().saveSample(cazeSample); + SampleDto sample = creator.createSample( + contact.toReference(), + DateHelper.subtractDays(new Date(), 4), + new Date(), + user.toReference(), + SampleMaterial.BLOOD, + rdcf.facility); + assertEquals(2, getSampleFacade().getIndexList(new SampleCriteria(), 0, 100, null).size()); + + UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities = + creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + assertEquals(0, getSampleFacade().getIndexList(new SampleCriteria(), 0, 100, null).size()); + + loginWith(user); + caze.setSurveillanceOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getCaseFacade().save(caze); + contact.setContactOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getContactFacade().save(contact); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(2, getSampleFacade().getIndexList(new SampleCriteria(), 0, 100, null).size()); + } + @Test public void testGetIndexListForCaseConvertedFromContact() { diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbPseudonymizationTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbPseudonymizationTest.java index 51ec2bf406a..a2336fdc8ad 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbPseudonymizationTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbPseudonymizationTest.java @@ -389,12 +389,20 @@ public void testPseudonymizeGetByContact() { private TaskDto createCaseTask(CaseDataDto caze) { return creator - .createTask(TaskContext.CASE, TaskType.CASE_INVESTIGATION, TaskStatus.PENDING, caze.toReference(), null, null, new Date(), null); + .createTask(TaskContext.CASE, TaskType.CASE_INVESTIGATION, TaskStatus.PENDING, caze.toReference(), null, null, null, new Date(), null); } private TaskDto createContactTask(ContactDto contact) { - return creator - .createTask(TaskContext.CONTACT, TaskType.CONTACT_FOLLOW_UP, TaskStatus.PENDING, null, contact.toReference(), null, new Date(), null); + return creator.createTask( + TaskContext.CONTACT, + TaskType.CONTACT_FOLLOW_UP, + TaskStatus.PENDING, + null, + contact.toReference(), + null, + null, + new Date(), + null); } private void assertNotPseudonymized(TaskDto task) { diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbTest.java index 050e7004264..3336cdd7bde 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskFacadeEjbTest.java @@ -50,6 +50,8 @@ import de.symeda.sormas.api.caze.InvestigationStatus; import de.symeda.sormas.api.common.DeletionDetails; import de.symeda.sormas.api.contact.ContactDto; +import de.symeda.sormas.api.environment.EnvironmentDto; +import de.symeda.sormas.api.environment.EnvironmentMedia; import de.symeda.sormas.api.event.EventDto; import de.symeda.sormas.api.event.EventInvestigationStatus; import de.symeda.sormas.api.event.EventStatus; @@ -105,6 +107,7 @@ public void testTaskDirectoryForDeletedLinkedCase() { caze.toReference(), null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); @@ -129,6 +132,7 @@ public void testSampleDeletion() { null, null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); // Database should contain the created task @@ -149,6 +153,126 @@ public void testGetIndexList() { assertNotNull(getTaskFacade().getIndexList(null, 0, 100, null)); } + @Test + public void testGetIndexListByARestrictedAccessToAssignedEntitiesUser() { + RDCF rdcf = creator.createRDCF(); + + UserDto user = creator.createNationalUser(); + UserDto surveillanceOfficer = creator.createSurveillanceOfficer(rdcf); + assertNotNull(getTaskFacade().getIndexList(null, 0, 100, null)); + + //test tasks related to cases + loginWith(user); + String lastName = "Person"; + PersonDto cazePerson = creator.createPerson("Case", lastName); + final CaseDataDto firstCase = creator.createCase( + surveillanceOfficer.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf); + final CaseDataDto secondCase = creator.createCase( + surveillanceOfficer.toReference(), + cazePerson.toReference(), + Disease.EVD, + CaseClassification.PROBABLE, + InvestigationStatus.PENDING, + new Date(), + rdcf, + c -> c.setHealthFacilityDetails("abc")); + assertEquals(2, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + useNationalAdminLogin(); + UserDto surveillanceOfficerWithRestrictedAccessToAssignedEntities = + creator.createSurveillanceOfficerWithRestrictedAccessToAssignedEntities(rdcf); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertTrue(getCurrentUserService().hasRestrictedAccessToAssignedEntities()); + assertEquals(0, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + loginWith(user); + firstCase.setSurveillanceOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getCaseFacade().save(firstCase); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(1, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + //test tasks related to contacts + loginWith(user); + PersonDto contactPerson = creator.createPerson("Contact2", "Person2"); + final ContactDto contact = + creator.createContact(user.toReference(), user.toReference(), contactPerson.toReference(), null, new Date(), new Date(), Disease.EVD); + assertEquals(3, getTaskFacade().getIndexList(null, 0, 100, null).size()); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(1, getTaskFacade().getIndexList(null, 0, 100, null).size()); + loginWith(user); + contact.setContactOfficer(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getContactFacade().save(contact); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(2, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + //test tasks related to events + loginWith(user); + final EventDto event = creator.createEvent( + EventStatus.SIGNAL, + EventInvestigationStatus.PENDING, + "TitleEv1", + "DescriptionEv1", + "First", + "Name", + "12345", + TypeOfPlace.PUBLIC_PLACE, + DateHelper.subtractDays(new Date(), 1), + new Date(), + user.toReference(), + user.toReference(), + Disease.EVD, + rdcf); + creator.createTask( + TaskContext.EVENT, + TaskType.OTHER, + TaskStatus.PENDING, + null, + null, + event.toReference(), + null, + DateHelper.addDays(new Date(), 1), + user.toReference()); + assertEquals(4, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(2, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + loginWith(user); + event.setResponsibleUser(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getEventFacade().save(event); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(3, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + //test tasks related to environments + loginWith(user); + EnvironmentDto environment = creator.createEnvironment("Test Environment", EnvironmentMedia.WATER, user.toReference(), rdcf); + creator.createTask( + TaskContext.ENVIRONMENT, + TaskType.OTHER, + TaskStatus.PENDING, + null, + null, + null, + environment.toReference(), + DateHelper.addDays(new Date(), 1), + user.toReference()); + assertEquals(5, getTaskFacade().getIndexList(null, 0, 100, null).size()); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(3, getTaskFacade().getIndexList(null, 0, 100, null).size()); + + loginWith(user); + environment.setResponsibleUser(surveillanceOfficerWithRestrictedAccessToAssignedEntities.toReference()); + getEnvironmentFacade().save(environment); + loginWith(surveillanceOfficerWithRestrictedAccessToAssignedEntities); + assertEquals(4, getTaskFacade().getIndexList(null, 0, 100, null).size()); + } + @Test public void testArchivedTaskNotGettingTransfered() { @@ -189,6 +313,7 @@ public void testArchivedTaskNotGettingTransfered() { null, null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); creator.createTask( @@ -198,6 +323,7 @@ public void testArchivedTaskNotGettingTransfered() { caze.toReference(), null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); creator.createTask( @@ -207,6 +333,7 @@ public void testArchivedTaskNotGettingTransfered() { null, contact.toReference(), null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); creator.createTask( @@ -216,6 +343,7 @@ public void testArchivedTaskNotGettingTransfered() { null, null, event.toReference(), + null, DateHelper.addDays(new Date(), 1), user.toReference()); // getAllActiveTasks and getAllUuids should return length 4+1+1 (case investigation & contact investigation) @@ -283,6 +411,7 @@ public void testGetAllActiveTasksBatched() { null, null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); creator.createTask( @@ -292,6 +421,7 @@ public void testGetAllActiveTasksBatched() { caze.toReference(), null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); creator.createTask( @@ -301,6 +431,7 @@ public void testGetAllActiveTasksBatched() { null, contact.toReference(), null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); creator.createTask( @@ -310,6 +441,7 @@ public void testGetAllActiveTasksBatched() { null, null, event.toReference(), + null, DateHelper.addDays(new Date(), 1), user.toReference()); @@ -409,6 +541,7 @@ public void testFilterTasksByUserJurisdiction() { caze.toReference(), null, null, + null, new Date(), survOff.toReference()); assertThat(getTaskFacade().getIndexList(null, 0, 100, null), is(not(empty()))); @@ -541,6 +674,7 @@ public void testGetTaskListForUserWithoutEventViewRight() { null, null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); @@ -551,6 +685,7 @@ public void testGetTaskListForUserWithoutEventViewRight() { caze.toReference(), null, null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); @@ -562,6 +697,7 @@ public void testGetTaskListForUserWithoutEventViewRight() { null, contactDto.toReference(), null, + null, DateHelper.addDays(new Date(), 1), user.toReference()); @@ -573,6 +709,7 @@ public void testGetTaskListForUserWithoutEventViewRight() { null, null, eventDto.toReference(), + null, DateHelper.addDays(new Date(), 1), user.toReference()); @@ -643,6 +780,7 @@ public void testUserWithoutEventViewRightSeeHisAssignTask() { null, null, eventDto.toReference(), + null, DateHelper.addDays(new Date(), 1), user.toReference()); TaskDto taskEvent2 = creator.createTask( @@ -652,6 +790,7 @@ public void testUserWithoutEventViewRightSeeHisAssignTask() { null, null, eventDto.toReference(), + null, DateHelper.addDays(new Date(), 1), noEventNoCaseViewUser.toReference()); @@ -727,8 +866,8 @@ public void testGetIndexListArchived() { RDCF rdcf = creator.createRDCF(); UserDto user = creator.createSurveillanceOfficer(rdcf); - TaskDto task = - creator.createTask(TaskContext.GENERAL, TaskType.ANIMAL_TESTING, TaskStatus.PENDING, null, null, null, new Date(), user.toReference()); + TaskDto task = creator + .createTask(TaskContext.GENERAL, TaskType.ANIMAL_TESTING, TaskStatus.PENDING, null, null, null, null, new Date(), user.toReference()); getTaskFacade().archive(Collections.singletonList(task.getUuid())); List archivedTasks = @@ -771,8 +910,8 @@ public void testGetEditPermissionTypeOnGenericTask() { RDCF rdcf = creator.createRDCF(); UserDto user = creator.createSurveillanceOfficer(rdcf); - TaskDto task = - creator.createTask(TaskContext.GENERAL, TaskType.ANIMAL_TESTING, TaskStatus.PENDING, null, null, null, new Date(), user.toReference()); + TaskDto task = creator + .createTask(TaskContext.GENERAL, TaskType.ANIMAL_TESTING, TaskStatus.PENDING, null, null, null, null, new Date(), user.toReference()); assertThat(getTaskFacade().getEditPermissionType(task.getUuid()), is(EditPermissionType.ALLOWED)); getTaskFacade().archive(Collections.singletonList(task.getUuid())); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskServiceTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskServiceTest.java index 7f02c8a5967..e8c6802da18 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskServiceTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/task/TaskServiceTest.java @@ -154,6 +154,7 @@ public void testFindBy() { contact.toReference(), null, null, + null, otherUser.toReference()); result = getTaskService().findByAssigneeContactTypeAndStatuses(null, contact.toReference(), null, null); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/user/UserFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/user/UserFacadeEjbTest.java index 6a5cc090302..59ed2aa4e71 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/user/UserFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/user/UserFacadeEjbTest.java @@ -17,6 +17,7 @@ import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -171,10 +172,13 @@ public void testGetIndexList() { */ @Test public void testGetIndexListFilteredAndOrderedByAddress() { - List result = - getUserFacade().getIndexList(new UserCriteria().freeText("min"), 0, 100, Collections.singletonList(new SortProperty(UserDto.ADDRESS))); + final UserCriteria userCriteria = new UserCriteria().freeText("min"); + List result = getUserFacade().getIndexList(userCriteria, 0, 100, Collections.singletonList(new SortProperty(UserDto.ADDRESS))); + + final long count = getUserFacade().count(userCriteria); assertThat(result, hasSize(1)); + assertThat(count, is(1L)); assertThat(result.get(0).getUserName(), equalTo("admin")); } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseController.java index f08e6fcac64..8e8996c8b62 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseController.java @@ -957,7 +957,10 @@ public CommitDiscardWrapperComponent getCaseDataEditComponent(fina editView.getWrappedComponent().addButtonListener(CaseDataForm.CASE_REFER_POINT_OF_ENTRY_BTN_LOC, clickListener); } - appendSpecialCommands(caze, editView); + if (UserProvider.getCurrent().getUserRoles().stream().anyMatch(userRoleDto -> !userRoleDto.isRestrictAccessToAssignedEntities()) + || caze.getSurveillanceOfficer().equals(UserProvider.getCurrent().getUserReference())) { + appendSpecialCommands(caze, editView); + } editView.restrictEditableComponentsOnEditView( UserRight.CASE_EDIT, diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactController.java index 5ba60858f85..f1e2b454b8f 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactController.java @@ -687,23 +687,26 @@ public CommitDiscardWrapperComponent getContactDataEditComponen } }); - if (UserProvider.getCurrent().hasUserRight(UserRight.CONTACT_DELETE)) { - editComponent.addDeleteWithReasonOrRestoreListener( - ContactsView.VIEW_NAME, - getDeleteConfirmationDetails(Collections.singletonList(contact.getUuid())), - I18nProperties.getString(Strings.entityContact), - contactUuid, - FacadeProvider.getContactFacade()); - } + if (UserProvider.getCurrent().getUserRoles().stream().anyMatch(userRoleDto -> !userRoleDto.isRestrictAccessToAssignedEntities()) + || DataHelper.equal(contact.getContactOfficer(), UserProvider.getCurrent().getUserReference())) { + if (UserProvider.getCurrent().hasUserRight(UserRight.CONTACT_DELETE)) { + editComponent.addDeleteWithReasonOrRestoreListener( + ContactsView.VIEW_NAME, + getDeleteConfirmationDetails(Collections.singletonList(contact.getUuid())), + I18nProperties.getString(Strings.entityContact), + contactUuid, + FacadeProvider.getContactFacade()); + } - // Initialize 'Archive' button - if (UserProvider.getCurrent().hasUserRight(UserRight.CONTACT_ARCHIVE)) { - ControllerProvider.getArchiveController() - .addArchivingButton( - contact, - ArchiveHandlers.forContact(), - editComponent, - () -> navigateToView(ContactDataView.VIEW_NAME, contact.getUuid(), false)); + // Initialize 'Archive' button + if (UserProvider.getCurrent().hasUserRight(UserRight.CONTACT_ARCHIVE)) { + ControllerProvider.getArchiveController() + .addArchivingButton( + contact, + ArchiveHandlers.forContact(), + editComponent, + () -> navigateToView(ContactDataView.VIEW_NAME, contact.getUuid(), false)); + } } editComponent.restrictEditableComponentsOnEditView( diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/environment/EnvironmentController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/environment/EnvironmentController.java index f98dfe9eda7..cf59568719d 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/environment/EnvironmentController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/environment/EnvironmentController.java @@ -15,6 +15,8 @@ package de.symeda.sormas.ui.environment; +import java.util.Objects; + import com.vaadin.navigator.Navigator; import com.vaadin.ui.Label; import com.vaadin.ui.Notification; @@ -160,24 +162,30 @@ public CommitDiscardWrapperComponent getEnvironmentDataEdit } } - // Initialize 'Delete' button - if (UserProvider.getCurrent().hasUserRight(UserRight.ENVIRONMENT_DELETE)) { - editComponent.addDeleteWithReasonOrRestoreListener( - EnvironmentsView.VIEW_NAME, - null, - I18nProperties.getString(Strings.entityEnvironment), - environmentDto.getUuid(), - FacadeProvider.getEnvironmentFacade()); - } + if (Objects.requireNonNull(UserProvider.getCurrent()) + .getUserRoles() + .stream() + .anyMatch(userRoleDto -> !userRoleDto.isRestrictAccessToAssignedEntities()) + || DataHelper.equal(environmentDto.getResponsibleUser(), UserProvider.getCurrent().getUserReference())) { + // Initialize 'Delete' button + if (UserProvider.getCurrent().hasUserRight(UserRight.ENVIRONMENT_DELETE)) { + editComponent.addDeleteWithReasonOrRestoreListener( + EnvironmentsView.VIEW_NAME, + null, + I18nProperties.getString(Strings.entityEnvironment), + environmentDto.getUuid(), + FacadeProvider.getEnvironmentFacade()); + } - // Initialize 'Archive' button - if (UserProvider.getCurrent().hasUserRight(UserRight.ENVIRONMENT_ARCHIVE)) { - ControllerProvider.getArchiveController() - .addArchivingButton( - environmentDto, - ArchiveHandlers.forEnvironment(), - editComponent, - () -> navigateToEnvironment(environmentDto.getUuid())); + // Initialize 'Archive' button + if (UserProvider.getCurrent().hasUserRight(UserRight.ENVIRONMENT_ARCHIVE)) { + ControllerProvider.getArchiveController() + .addArchivingButton( + environmentDto, + ArchiveHandlers.forEnvironment(), + editComponent, + () -> navigateToEnvironment(environmentDto.getUuid())); + } } editComponent.restrictEditableComponentsOnEditView( diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventController.java index 3b3fe20f0c2..53194d37cc4 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventController.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; @@ -946,38 +947,46 @@ public CommitDiscardWrapperComponent getEventDataEditComponent(fi } }); - final String uuid = event.getUuid(); - if (UserProvider.getCurrent().hasUserRight(UserRight.EVENT_DELETE)) { - editView.addDeleteWithReasonOrRestoreListener((deleteDetails) -> { - if (!existEventParticipantsLinkedToEvent(event)) { - try { - FacadeProvider.getEventFacade().delete(uuid, deleteDetails); - } catch (ExternalSurveillanceToolRuntimeException e) { - Notification.show( - String.format( - I18nProperties.getString(Strings.ExternalSurveillanceToolGateway_notificationEntryNotDeleted), - DataHelper.getShortUuid(uuid)), - "", - Type.ERROR_MESSAGE); + if (Objects.requireNonNull(UserProvider.getCurrent()) + .getUserRoles() + .stream() + .anyMatch(userRoleDto -> !userRoleDto.isRestrictAccessToAssignedEntities()) + || DataHelper.equal(event.getResponsibleUser(), UserProvider.getCurrent().getUserReference())) { + final String uuid = event.getUuid(); + if (UserProvider.getCurrent().hasUserRight(UserRight.EVENT_DELETE)) { + editView.addDeleteWithReasonOrRestoreListener((deleteDetails) -> { + if (!existEventParticipantsLinkedToEvent(event)) { + try { + FacadeProvider.getEventFacade().delete(uuid, deleteDetails); + } catch (ExternalSurveillanceToolRuntimeException e) { + Notification.show( + String.format( + I18nProperties.getString(Strings.ExternalSurveillanceToolGateway_notificationEntryNotDeleted), + DataHelper.getShortUuid(uuid)), + "", + Type.ERROR_MESSAGE); + } + } else { + VaadinUiUtil.showSimplePopupWindow( + I18nProperties.getString(Strings.headingEventNotDeleted), + I18nProperties.getString(Strings.messageEventsNotDeletedLinkedEntitiesReason)); } - } else { - VaadinUiUtil.showSimplePopupWindow( - I18nProperties.getString(Strings.headingEventNotDeleted), - I18nProperties.getString(Strings.messageEventsNotDeletedLinkedEntitiesReason)); - } - UI.getCurrent().getNavigator().navigateTo(EventsView.VIEW_NAME); - }, getDeleteConfirmationDetails(Collections.singletonList(eventUuid)), (deleteDetails) -> { - FacadeProvider.getEventFacade().restore(uuid); - UI.getCurrent().getNavigator().navigateTo(EventsView.VIEW_NAME); - }, I18nProperties.getString(Strings.entityEvent), uuid, FacadeProvider.getEventFacade()); - } + UI.getCurrent().getNavigator().navigateTo(EventsView.VIEW_NAME); + }, getDeleteConfirmationDetails(Collections.singletonList(eventUuid)), (deleteDetails) -> { + FacadeProvider.getEventFacade().restore(uuid); + UI.getCurrent().getNavigator().navigateTo(EventsView.VIEW_NAME); + }, I18nProperties.getString(Strings.entityEvent), uuid, FacadeProvider.getEventFacade()); + } - // Initialize 'Archive' button - if (UserProvider.getCurrent().hasUserRight(UserRight.EVENT_ARCHIVE)) { - ControllerProvider.getArchiveController().addArchivingButton(event, ArchiveHandlers.forEvent(), editView, () -> { - ViewModelProviders.of(EventParticipantsView.class).get(EventParticipantsViewConfiguration.class).setRelevanceStatusChangedEvent(null); - navigateToData(uuid); - }); + // Initialize 'Archive' button + if (UserProvider.getCurrent().hasUserRight(UserRight.EVENT_ARCHIVE)) { + ControllerProvider.getArchiveController().addArchivingButton(event, ArchiveHandlers.forEvent(), editView, () -> { + ViewModelProviders.of(EventParticipantsView.class) + .get(EventParticipantsViewConfiguration.class) + .setRelevanceStatusChangedEvent(null); + navigateToData(uuid); + }); + } } editView.restrictEditableComponentsOnEditView( diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleCreateForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleCreateForm.java index c5782ce46bd..3a932216f7d 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleCreateForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleCreateForm.java @@ -47,7 +47,8 @@ public class UserRoleCreateForm extends AbstractEditForm { + fluidRowLocs(UserRoleDto.DESCRIPTION) + fluidRowLocs(UserRoleDto.HAS_OPTIONAL_HEALTH_FACILITY) + fluidRowLocs(UserRoleDto.HAS_ASSOCIATED_DISTRICT_USER) - + fluidRowLocs(UserRoleDto.PORT_HEALTH_USER); + + fluidRowLocs(UserRoleDto.PORT_HEALTH_USER) + + fluidRowLocs(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES); protected UserRoleCreateForm() { super(UserRoleDto.class, UserRoleDto.I18N_PREFIX); @@ -78,7 +79,8 @@ protected void addFields() { addField(UserRoleDto.HAS_OPTIONAL_HEALTH_FACILITY).addStyleName(CssStyles.VSPACE_TOP_3); addField(UserRoleDto.HAS_ASSOCIATED_DISTRICT_USER).addStyleName(CssStyles.VSPACE_TOP_3); - addField(UserRoleDto.PORT_HEALTH_USER).addStyleNames(CssStyles.VSPACE_TOP_3, CssStyles.VSPACE_3); + addField(UserRoleDto.PORT_HEALTH_USER).addStyleNames(CssStyles.VSPACE_TOP_3); + addField(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES).addStyleNames(CssStyles.VSPACE_TOP_3, CssStyles.VSPACE_3); UserRoleFormHelper.createFieldDependencies(this); } @@ -100,6 +102,8 @@ private void applyTemplateData(UserRoleDto templateRole) { this.> getField(UserRoleDto.HAS_OPTIONAL_HEALTH_FACILITY).setValue(templateRole.getHasOptionalHealthFacility()); this.> getField(UserRoleDto.HAS_ASSOCIATED_DISTRICT_USER).setValue(templateRole.getHasAssociatedDistrictUser()); this.> getField(UserRoleDto.PORT_HEALTH_USER).setValue(templateRole.isPortHealthUser()); + this.> getField(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES) + .setValue(templateRole.isRestrictAccessToAssignedEntities()); } } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleEditForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleEditForm.java index 8d795a01233..f2c5af9cb46 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleEditForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRoleEditForm.java @@ -59,6 +59,7 @@ public class UserRoleEditForm extends AbstractUserRoleForm { + fluidRowLocs(UserRoleDto.HAS_OPTIONAL_HEALTH_FACILITY) + fluidRowLocs(UserRoleDto.HAS_ASSOCIATED_DISTRICT_USER) + fluidRowLocs(UserRoleDto.PORT_HEALTH_USER) + + fluidRowLocs(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES) + fluidRowLocs(USER_RIGHTS_LABEL_LOC) + fluidRowLocs(UserRoleDto.USER_RIGHTS); @@ -84,7 +85,8 @@ protected void addFields() { addField(UserRoleDto.HAS_OPTIONAL_HEALTH_FACILITY).addStyleName(CssStyles.VSPACE_TOP_3); addField(UserRoleDto.HAS_ASSOCIATED_DISTRICT_USER).addStyleName(CssStyles.VSPACE_TOP_3); - addField(UserRoleDto.PORT_HEALTH_USER).addStyleNames(CssStyles.VSPACE_TOP_3, CssStyles.VSPACE_3); + addField(UserRoleDto.PORT_HEALTH_USER).addStyleNames(CssStyles.VSPACE_TOP_3); + addField(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES).addStyleNames(CssStyles.VSPACE_TOP_3, CssStyles.VSPACE_3); Label userRightsLabel = new Label(I18nProperties.getCaption(Captions.UserRole_userRights), ContentMode.HTML); userRightsLabel.addStyleNames(CssStyles.H2); @@ -145,6 +147,8 @@ void applyTemplateData(UserRoleDto templateRole) { this.> getField(UserRoleDto.HAS_OPTIONAL_HEALTH_FACILITY).setValue(templateRole.getHasOptionalHealthFacility()); this.> getField(UserRoleDto.HAS_ASSOCIATED_DISTRICT_USER).setValue(templateRole.getHasAssociatedDistrictUser()); this.> getField(UserRoleDto.PORT_HEALTH_USER).setValue(templateRole.isPortHealthUser()); + this.> getField(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES) + .setValue(templateRole.isRestrictAccessToAssignedEntities()); } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRolesView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRolesView.java index 9f57534744f..de239534fd9 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRolesView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UserRolesView.java @@ -14,6 +14,7 @@ import com.vaadin.server.StreamResource; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; +import com.vaadin.ui.CheckBox; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Notification; import com.vaadin.ui.VerticalLayout; @@ -55,6 +56,7 @@ public class UserRolesView extends AbstractUserView { private ComboBox userRightsFilter; private ComboBox jurisdictionFilter; private ComboBox enabledFilter; + private CheckBox showOnlyRestrictedAccessToAssignedEntities; private VerticalLayout gridLayout; @@ -165,6 +167,16 @@ public HorizontalLayout createFilterBar() { }); filterLayout.addComponent(jurisdictionFilter); + showOnlyRestrictedAccessToAssignedEntities = new CheckBox(); + showOnlyRestrictedAccessToAssignedEntities.setId(UserRoleDto.RESTRICT_ACCESS_TO_ASSIGNED_ENTITIES); + showOnlyRestrictedAccessToAssignedEntities.setCaption(I18nProperties.getCaption(Captions.userRoleShowOnlyRestrictedAccessToAssignCases)); + showOnlyRestrictedAccessToAssignedEntities.addStyleName(CssStyles.CHECKBOX_FILTER_INLINE); + showOnlyRestrictedAccessToAssignedEntities.addValueChangeListener(e -> { + criteria.setShowOnlyRestrictedAccessToAssignedEntities(e.getValue()); + navigateTo(criteria); + }); + filterLayout.addComponent(showOnlyRestrictedAccessToAssignedEntities); + return filterLayout; } @@ -190,6 +202,10 @@ public void updateFilterComponents() { jurisdictionFilter.setValue(criteria.getJurisdictionLevel() == null ? null : criteria.getJurisdictionLevel()); enabledFilter.setValue(criteria.getEnabled() == null ? ALL_FILTER : criteria.getEnabled() ? ENABLED_FILTER : DISABLED_FILTER); userRightsFilter.setValue(criteria.getUserRight() == null ? null : criteria.getUserRight()); + showOnlyRestrictedAccessToAssignedEntities.setValue( + criteria.getShowOnlyRestrictedAccessToAssignedEntities() == null + ? Boolean.FALSE + : criteria.getShowOnlyRestrictedAccessToAssignedEntities()); applyingCriteria = false; } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UsersView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UsersView.java index c46b59c99e5..bbeb0fa7e5e 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UsersView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/user/UsersView.java @@ -21,6 +21,7 @@ import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; +import com.vaadin.ui.CheckBox; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.MenuBar; import com.vaadin.ui.VerticalLayout; @@ -81,6 +82,7 @@ public class UsersView extends AbstractUserView { private ComboBox regionFilter; private ComboBox districtFilter; private TextField searchField; + private CheckBox showOnlyRestrictedAccessToAssignedEntities; private RowCount rowsCount; @@ -235,6 +237,16 @@ public HorizontalLayout createFilterBar() { }); filterLayout.addComponent(searchField); + showOnlyRestrictedAccessToAssignedEntities = new CheckBox(); + showOnlyRestrictedAccessToAssignedEntities.setId("showOnly"); + showOnlyRestrictedAccessToAssignedEntities.setCaption(I18nProperties.getCaption(Captions.userRoleShowOnlyRestrictedAccessToAssignCases)); + showOnlyRestrictedAccessToAssignedEntities.addStyleName(CssStyles.CHECKBOX_FILTER_INLINE); + showOnlyRestrictedAccessToAssignedEntities.addValueChangeListener(e -> { + criteria.setShowOnlyRestrictedAccessToAssignedEntities(e.getValue()); + navigateTo(criteria); + }); + filterLayout.addComponent(showOnlyRestrictedAccessToAssignedEntities); + return filterLayout; } @@ -252,14 +264,12 @@ public HorizontalLayout createActionsBar() { actionButtonsLayout.setSpacing(true); bulkOperationsDropdown = MenuBarHelper.createDropDown( - Captions.bulkActions, - new MenuBarHelper.MenuBarItem(I18nProperties.getCaption(Captions.actionEnable), VaadinIcons.CHECK_SQUARE_O, selectedItem -> { - ControllerProvider.getUserController() - .enableAllSelectedItems(grid.asMultiSelect().getSelectedItems(), grid); + Captions.bulkActions, + new MenuBarHelper.MenuBarItem(I18nProperties.getCaption(Captions.actionEnable), VaadinIcons.CHECK_SQUARE_O, selectedItem -> { + ControllerProvider.getUserController().enableAllSelectedItems(grid.asMultiSelect().getSelectedItems(), grid); }, UserProvider.getCurrent().hasUserRight(UserRight.USER_EDIT)), - new MenuBarHelper.MenuBarItem(I18nProperties.getCaption(Captions.actionDisable), VaadinIcons.THIN_SQUARE, selectedItem -> { - ControllerProvider.getUserController() - .disableAllSelectedItems(grid.asMultiSelect().getSelectedItems(), grid); + new MenuBarHelper.MenuBarItem(I18nProperties.getCaption(Captions.actionDisable), VaadinIcons.THIN_SQUARE, selectedItem -> { + ControllerProvider.getUserController().disableAllSelectedItems(grid.asMultiSelect().getSelectedItems(), grid); }, UserProvider.getCurrent().hasUserRight(UserRight.USER_EDIT))); bulkOperationsDropdown.setVisible(ViewModelProviders.of(UsersView.class).get(ViewConfiguration.class).isInEagerMode()); @@ -296,6 +306,8 @@ public void updateFilterComponents() { activeFilter.setValue(criteria.getActive() == null ? null : criteria.getActive() ? ACTIVE_FILTER : INACTIVE_FILTER); userRolesFilter.setValue(criteria.getUserRole()); regionFilter.setValue(criteria.getRegion()); + showOnlyRestrictedAccessToAssignedEntities.setValue( + criteria.getShowOnlyRestrictedAccessToAssignedEntities() == null ? false : criteria.getShowOnlyRestrictedAccessToAssignedEntities()); if (user.getRegion() != null && user.getDistrict() == null) { districtFilter.addItems(FacadeProvider.getDistrictFacade().getAllActiveByRegion(user.getRegion().getUuid()));