Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit f8f07de

Browse files
lgallgal
authored andcommitted
SORMAS-Foundation#3136 add user right for sormas 2 sormas share
1 parent f94c13d commit f8f07de

10 files changed

Lines changed: 135 additions & 42 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ public interface Strings {
172172
String errorSormasToSormasDecrypt = "errorSormasToSormasDecrypt";
173173
String errorSormasToSormasEncrypt = "errorSormasToSormasEncrypt";
174174
String errorSormasToSormasInfrastructure = "errorSormasToSormasInfrastructure";
175+
String errorSormasToSormasNotEditable = "errorSormasToSormasNotEditable";
175176
String errorSormasToSormasResult = "errorSormasToSormasResult";
176177
String errorSormasToSormasSend = "errorSormasToSormasSend";
177178
String errorSormasToSormasServerAccess = "errorSormasToSormasServerAccess";

sormas-api/src/main/java/de/symeda/sormas/api/user/UserRight.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,23 @@ public enum UserRight {
10841084
CONTACT_SUPERVISOR,
10851085
POE_SUPERVISOR,
10861086
LAB_USER
1087+
),
1088+
SORMAS_TO_SORMAS_SHARE(
1089+
ADMIN,
1090+
NATIONAL_USER,
1091+
SURVEILLANCE_SUPERVISOR,
1092+
SURVEILLANCE_OFFICER,
1093+
CASE_SUPERVISOR,
1094+
CASE_OFFICER,
1095+
CONTACT_SUPERVISOR,
1096+
CONTACT_OFFICER,
1097+
EVENT_OFFICER,
1098+
NATIONAL_OBSERVER,
1099+
STATE_OBSERVER,
1100+
DISTRICT_OBSERVER,
1101+
NATIONAL_CLINICIAN,
1102+
POE_SUPERVISOR,
1103+
POE_NATIONAL_USER
10871104
);
10881105
//@formatter:on
10891106

sormas-api/src/main/resources/strings.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ errorWasReported = The error was automatically reported to the support.
221221
errorInvalidValue = Invalid value
222222
errorCaseDuplicateDeletion = The duplicate case could not be deleted due to an internal error.
223223
errorCaseMerging = The cases could not be merged due to an internal error.
224-
errorSormasToSormasShare = The shared data could not be saved due to some errors.
224+
errorSormasToSormasShare = The data could not be shared due to some errors.
225225
errorSormasToSormasServerAccess = The selected health department is not configured. Please contact an admin and tell them about this issue.
226226
errorSormasToSormasSend = Unexpected error occurred while sharing. Please contact an admin and tell them about this issue.
227227
errorSormasToSormasConnection = Connection refused by the target health department.
@@ -230,6 +230,7 @@ errorSormasToSormasInfrastructure = Infrastructure data does not match. Unmatche
230230
errorSormasToSormasCertNotGenerated = Sormas to sormas certificate is not yet generated. Please contact an admin and tell them about this issue.
231231
errorSormasToSormasEncrypt = Could not encrypt the data. Please contact an admin and tell them about this issue.
232232
errorSormasToSormasDecrypt = Could not decrypt the shared data. Please contact an admin and tell them about this issue.
233+
errorSormasToSormasNotEditable = Readonly data can not be shared.
233234

234235
# headings
235236
headingAccessDenied = Access denied

sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseFacadeEjb.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@
230230
import de.symeda.sormas.backend.sormastosormas.SormasToSormasFacadeEjb;
231231
import de.symeda.sormas.backend.sormastosormas.SormasToSormasFacadeEjb.SormasToSormasFacadeEjbLocal;
232232
import de.symeda.sormas.backend.sormastosormas.SormasToSormasShareInfo;
233-
import de.symeda.sormas.backend.sormastosormas.SormasToSormasShareInfoService;
234233
import de.symeda.sormas.backend.symptoms.Symptoms;
235234
import de.symeda.sormas.backend.symptoms.SymptomsFacadeEjb;
236235
import de.symeda.sormas.backend.symptoms.SymptomsFacadeEjb.SymptomsFacadeEjbLocal;
@@ -363,8 +362,6 @@ public class CaseFacadeEjb implements CaseFacade {
363362
private CaseJurisdictionChecker caseJurisdictionChecker;
364363
@EJB
365364
private SormasToSormasFacadeEjbLocal sormasToSormasFacade;
366-
@EJB
367-
private SormasToSormasShareInfoService sormasToSormasShareInfoService;
368365

369366
@Override
370367
public List<CaseDataDto> getAllActiveCasesAfter(Date date) {
@@ -3093,10 +3090,6 @@ public static class CaseFacadeEjbLocal extends CaseFacadeEjb {
30933090
public Boolean isCaseEditAllowed(String caseUuid) {
30943091
Case caze = caseService.getByUuid(caseUuid);
30953092

3096-
if (caze.getSormasToSormasOriginInfo() != null) {
3097-
return caze.getSormasToSormasOriginInfo().isOwnershipHandedOver();
3098-
}
3099-
3100-
return caseJurisdictionChecker.isInJurisdictionOrOwned(caze) && !sormasToSormasShareInfoService.isCaseOwnershipHandedOver(caze);
3093+
return caseService.isCaseEditAllowed(caze);
31013094
}
31023095
}

sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
import de.symeda.sormas.backend.sample.Sample;
101101
import de.symeda.sormas.backend.sample.SampleJoins;
102102
import de.symeda.sormas.backend.sample.SampleService;
103+
import de.symeda.sormas.backend.sormastosormas.SormasToSormasShareInfoService;
103104
import de.symeda.sormas.backend.symptoms.Symptoms;
104105
import de.symeda.sormas.backend.task.Task;
105106
import de.symeda.sormas.backend.task.TaskService;
@@ -146,6 +147,11 @@ public class CaseService extends AbstractCoreAdoService<Case> {
146147
@EJB
147148
private DiseaseConfigurationFacadeEjb.DiseaseConfigurationFacadeEjbLocal diseaseConfigurationFacade;
148149

150+
@EJB
151+
private CaseJurisdictionChecker caseJurisdictionChecker;
152+
@EJB
153+
private SormasToSormasShareInfoService sormasToSormasShareInfoService;
154+
149155
public CaseService() {
150156
super(Case.class);
151157
}
@@ -1135,4 +1141,12 @@ public void updateArchived(List<String> caseUuids, boolean archived) {
11351141

11361142
em.createQuery(cu).executeUpdate();
11371143
}
1144+
1145+
public boolean isCaseEditAllowed(Case caze) {
1146+
if (caze.getSormasToSormasOriginInfo() != null) {
1147+
return caze.getSormasToSormasOriginInfo().isOwnershipHandedOver();
1148+
}
1149+
1150+
return caseJurisdictionChecker.isInJurisdictionOrOwned(caze) && !sormasToSormasShareInfoService.isCaseOwnershipHandedOver(caze);
1151+
}
11381152
}

sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactFacadeEjb.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@
141141
import de.symeda.sormas.backend.sormastosormas.SormasToSormasFacadeEjb;
142142
import de.symeda.sormas.backend.sormastosormas.SormasToSormasFacadeEjb.SormasToSormasFacadeEjbLocal;
143143
import de.symeda.sormas.backend.sormastosormas.SormasToSormasShareInfo;
144-
import de.symeda.sormas.backend.sormastosormas.SormasToSormasShareInfoService;
145144
import de.symeda.sormas.backend.symptoms.Symptoms;
146145
import de.symeda.sormas.backend.symptoms.SymptomsFacadeEjb;
147146
import de.symeda.sormas.backend.task.Task;
@@ -205,8 +204,6 @@ public class ContactFacadeEjb implements ContactFacade {
205204
@EJB
206205
private SormasToSormasFacadeEjbLocal sormasToSormasFacade;
207206
@EJB
208-
private SormasToSormasShareInfoService sormasToSormasShareInfoService;
209-
@EJB
210207
private FeatureConfigurationFacadeEjbLocal featureConfigurationFacade;
211208

212209
@Override
@@ -1444,10 +1441,6 @@ public static class ContactFacadeEjbLocal extends ContactFacadeEjb {
14441441
public boolean isContactEditAllowed(String contactUuid) {
14451442
Contact contact = contactService.getByUuid(contactUuid);
14461443

1447-
if (contact.getSormasToSormasOriginInfo() != null) {
1448-
return contact.getSormasToSormasOriginInfo().isOwnershipHandedOver();
1449-
}
1450-
1451-
return contactJurisdictionChecker.isInJurisdictionOrOwned(contact) && !sormasToSormasShareInfoService.isContactOwnershipHandedOver(contact);
1444+
return contactService.isContactEditAllowed(contact);
14521445
}
14531446
}

sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import de.symeda.sormas.backend.region.District;
8282
import de.symeda.sormas.backend.region.Region;
8383
import de.symeda.sormas.backend.sample.SampleService;
84+
import de.symeda.sormas.backend.sormastosormas.SormasToSormasShareInfoService;
8485
import de.symeda.sormas.backend.symptoms.Symptoms;
8586
import de.symeda.sormas.backend.task.Task;
8687
import de.symeda.sormas.backend.task.TaskService;
@@ -109,6 +110,11 @@ public class ContactService extends AbstractCoreAdoService<Contact> {
109110
@EJB
110111
private HealthConditionsService healthConditionsService;
111112

113+
@EJB
114+
private SormasToSormasShareInfoService sormasToSormasShareInfoService;
115+
@EJB
116+
private ContactJurisdictionChecker contactJurisdictionChecker;
117+
112118
public ContactService() {
113119
super(Contact.class);
114120
}
@@ -1204,4 +1210,12 @@ public Predicate isInJurisdictionOrOwned(CriteriaBuilder cb, CriteriaQuery<Long>
12041210
}
12051211
return cb.or(reportedByCurrentUser, contactCaseInJurisdiction, jurisdictionPredicate);
12061212
}
1213+
1214+
public boolean isContactEditAllowed(Contact contact) {
1215+
if (contact.getSormasToSormasOriginInfo() != null) {
1216+
return contact.getSormasToSormasOriginInfo().isOwnershipHandedOver();
1217+
}
1218+
1219+
return contactJurisdictionChecker.isInJurisdictionOrOwned(contact) && !sormasToSormasShareInfoService.isContactOwnershipHandedOver(contact);
1220+
}
12071221
}

sormas-backend/src/main/java/de/symeda/sormas/backend/sormastosormas/SormasToSormasFacadeEjb.java

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import com.fasterxml.jackson.core.JsonProcessingException;
6363
import com.fasterxml.jackson.databind.ObjectMapper;
6464

65+
import de.symeda.sormas.api.HasUuid;
6566
import de.symeda.sormas.api.caze.CaseDataDto;
6667
import de.symeda.sormas.api.caze.maternalhistory.MaternalHistoryDto;
6768
import de.symeda.sormas.api.clinicalcourse.HealthConditionsDto;
@@ -93,6 +94,7 @@
9394
import de.symeda.sormas.api.sormastosormas.SormasToSormasShareInfoDto;
9495
import de.symeda.sormas.api.sormastosormas.SormasToSormasValidationException;
9596
import de.symeda.sormas.api.user.UserReferenceDto;
97+
import de.symeda.sormas.api.user.UserRight;
9698
import de.symeda.sormas.api.utils.DataHelper;
9799
import de.symeda.sormas.api.utils.ValidationRuntimeException;
98100
import de.symeda.sormas.api.utils.fieldaccess.checkers.PersonalDataFieldAccessChecker;
@@ -290,17 +292,18 @@ public void saveSharedContacts(SormasToSormasEncryptedDataDto sharedData) throws
290292
public void shareCases(List<String> caseUuids, SormasToSormasOptionsDto options) throws SormasToSormasException {
291293
User currentUser = userService.getCurrentUser();
292294

293-
List<Case> casesToSend = new ArrayList<>(caseUuids.size());
295+
List<Case> casesToSend = caseService.getByUuids(caseUuids);
296+
validateCasesBeforeSend(casesToSend);
297+
294298
List<Contact> contactsToSend = new ArrayList<>();
295299
List<SormasToSormasCaseDto> entitiesToSend = new ArrayList<>();
296300

297-
for (String uuid : caseUuids) {
298-
Case caze = caseService.getByUuid(uuid);
299-
301+
for (Case caze : casesToSend) {
300302
Pseudonymizer pseudonymizer = createPseudonymizer(options);
301303

302304
PersonDto personDto = getPersonDto(caze.getPerson(), pseudonymizer, options);
303305
CaseDataDto cazeDto = getCazeDto(caze, pseudonymizer);
306+
304307
SormasToSormasOriginInfoDto originInfo = createSormasToSormasOriginInfo(currentUser, options);
305308

306309
SormasToSormasCaseDto entity = new SormasToSormasCaseDto(personDto, cazeDto, originInfo);
@@ -324,13 +327,13 @@ public void shareCases(List<String> caseUuids, SormasToSormasOptionsDto options)
324327
@Override
325328
public void shareContacts(List<String> contactUuids, SormasToSormasOptionsDto options) throws SormasToSormasException {
326329
User currentUser = userService.getCurrentUser();
330+
List<Contact> contactsToSend = contactService.getByUuids(contactUuids);
327331

328-
List<Contact> contactsToSend = new ArrayList<>();
329-
List<SormasToSormasContactDto> entitiesToSend = new ArrayList<>();
332+
validateContactsBeforeSend(contactsToSend);
330333

331-
for (String uuid : contactUuids) {
332-
Contact contact = contactService.getByUuid(uuid);
334+
List<SormasToSormasContactDto> entitiesToSend = new ArrayList<>();
333335

336+
for (Contact contact : contactsToSend) {
334337
Pseudonymizer pseudonymizer = createPseudonymizer(options);
335338

336339
PersonDto personDto = getPersonDto(contact.getPerson(), pseudonymizer, options);
@@ -374,14 +377,50 @@ public List<SormasToSormasShareInfoDto> getShareInfoIndexList(SormasToSormasShar
374377

375378
@Override
376379
public boolean isFeatureEnabled() {
377-
return !serverAccessDataService.getOrganizationList().isEmpty();
380+
return userService.hasRight(UserRight.SORMAS_TO_SORMAS_SHARE) && !serverAccessDataService.getOrganizationList().isEmpty();
378381
}
379382

380383
@Override
381384
public ServerAccessDataReferenceDto getOrganizationRef(String id) {
382385
return getOrganizationServerAccessData(id).map(OrganizationServerAccessData::toReference).orElseGet(null);
383386
}
384387

388+
private void validateCasesBeforeSend(List<Case> cases) throws SormasToSormasException {
389+
Map<String, Map<String, List<String>>> validationErrors = new HashMap<>();
390+
for (Case caze : cases) {
391+
if (!caseService.isCaseEditAllowed(caze)) {
392+
Map<String, List<String>> error = new HashMap<>(1);
393+
error.put(
394+
I18nProperties.getCaption(Captions.CaseData),
395+
Collections.singletonList(I18nProperties.getString(Strings.errorSormasToSormasNotEditable)));
396+
397+
validationErrors.put(buildCaseValidationGroupName(caze), error);
398+
}
399+
}
400+
401+
if (validationErrors.size() > 0) {
402+
throw new SormasToSormasException(I18nProperties.getString(Strings.errorSormasToSormasShare), validationErrors);
403+
}
404+
}
405+
406+
private void validateContactsBeforeSend(List<Contact> contacts) throws SormasToSormasException {
407+
Map<String, Map<String, List<String>>> validationErrors = new HashMap<>();
408+
for (Contact contact : contacts) {
409+
if (!contactService.isContactEditAllowed(contact)) {
410+
Map<String, List<String>> error = new HashMap<>(1);
411+
error.put(
412+
I18nProperties.getCaption(Captions.Contact),
413+
Collections.singletonList(I18nProperties.getString(Strings.errorSormasToSormasNotEditable)));
414+
415+
validationErrors.put(buildCaseValidationGroupName(contact), error);
416+
}
417+
}
418+
419+
if (validationErrors.size() > 0) {
420+
throw new SormasToSormasException(I18nProperties.getString(Strings.errorSormasToSormasShare), validationErrors);
421+
}
422+
}
423+
385424
private PersonDto getPersonDto(Person person, Pseudonymizer pseudonymizer, SormasToSormasOptionsDto options) {
386425
PersonDto personDto = personFacade.convertToDto(person, pseudonymizer, true);
387426

@@ -727,8 +766,8 @@ private <T> T handleValidationError(Supplier<T> saveOperation, String validation
727766
}
728767
}
729768

730-
private String buildCaseValidationGroupName(CaseDataDto caze) {
731-
return String.format("%s %s", I18nProperties.getCaption(Captions.CaseData), DataHelper.getShortUuid(caze));
769+
private String buildCaseValidationGroupName(HasUuid caze) {
770+
return String.format("%s %s", I18nProperties.getCaption(Captions.CaseData), DataHelper.getShortUuid(caze.getUuid()));
732771
}
733772

734773
private String buildContactValidationGroupName(ContactDto contact) {

sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CasesView.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ private void addCommonCasesOverviewToolbar() {
372372
}
373373
}
374374

375-
if (UserProvider.getCurrent().hasUserRight(UserRight.PERFORM_BULK_OPERATIONS)) {
375+
if (isBulkEditAllowed()) {
376376
btnEnterBulkEditMode = ButtonHelper.createIconButton(Captions.actionEnterBulkEditMode, VaadinIcons.CHECK_SQUARE_O, e -> {
377377
if (grid.getItemCount() > BULK_EDIT_MODE_WARNING_THRESHOLD) {
378378
VaadinUiUtil.showConfirmationPopup(
@@ -678,36 +678,40 @@ public HorizontalLayout createStatusFilterBar() {
678678
if (viewConfiguration.getViewType().isCaseOverview()) {
679679
AbstractCaseGrid<?> caseGrid = (AbstractCaseGrid<?>) this.grid;
680680
// Bulk operation dropdown
681-
if (UserProvider.getCurrent().hasUserRight(UserRight.PERFORM_BULK_OPERATIONS)) {
681+
if (isBulkEditAllowed()) {
682+
boolean hasBulkOperationsRight = UserProvider.getCurrent().hasUserRight(UserRight.PERFORM_BULK_OPERATIONS);
683+
682684
bulkOperationsDropdown = MenuBarHelper.createDropDown(
683685
Captions.bulkActions,
684686
new MenuBarHelper.MenuBarItem(
685687
I18nProperties.getCaption(Captions.bulkEdit),
686688
VaadinIcons.ELLIPSIS_H,
687-
mi -> ControllerProvider.getCaseController().showBulkCaseDataEditComponent(caseGrid.asMultiSelect().getSelectedItems())),
689+
mi -> ControllerProvider.getCaseController().showBulkCaseDataEditComponent(caseGrid.asMultiSelect().getSelectedItems()),
690+
hasBulkOperationsRight),
688691
new MenuBarHelper.MenuBarItem(
689692
I18nProperties.getCaption(Captions.bulkDelete),
690693
VaadinIcons.TRASH,
691694
selectedItem -> ControllerProvider.getCaseController()
692-
.deleteAllSelectedItems(caseGrid.asMultiSelect().getSelectedItems(), () -> navigateTo(criteria))),
695+
.deleteAllSelectedItems(caseGrid.asMultiSelect().getSelectedItems(), () -> navigateTo(criteria)),
696+
hasBulkOperationsRight),
693697
new MenuBarHelper.MenuBarItem(
694698
I18nProperties.getCaption(Captions.actionArchive),
695699
VaadinIcons.ARCHIVE,
696700
mi -> ControllerProvider.getCaseController()
697701
.archiveAllSelectedItems(caseGrid.asMultiSelect().getSelectedItems(), () -> navigateTo(criteria)),
698-
EntityRelevanceStatus.ACTIVE.equals(criteria.getRelevanceStatus())),
702+
hasBulkOperationsRight && EntityRelevanceStatus.ACTIVE.equals(criteria.getRelevanceStatus())),
699703
new MenuBarHelper.MenuBarItem(
700704
I18nProperties.getCaption(Captions.actionDearchive),
701705
VaadinIcons.ARCHIVE,
702706
mi -> ControllerProvider.getCaseController()
703707
.dearchiveAllSelectedItems(caseGrid.asMultiSelect().getSelectedItems(), () -> navigateTo(criteria)),
704-
EntityRelevanceStatus.ARCHIVED.equals(criteria.getRelevanceStatus())),
708+
hasBulkOperationsRight && EntityRelevanceStatus.ARCHIVED.equals(criteria.getRelevanceStatus())),
705709
new MenuBarHelper.MenuBarItem(
706710
I18nProperties.getCaption(Captions.sormasToSormasShare),
707711
VaadinIcons.SHARE,
708712
mi -> ControllerProvider.getSormasToSormasController()
709713
.shareSelectedCases(caseGrid.asMultiSelect().getSelectedItems(), () -> navigateTo(criteria)),
710-
true));
714+
FacadeProvider.getSormasToSormasFacade().isFeatureEnabled()));
711715

712716
bulkOperationsDropdown.setVisible(viewConfiguration.isInEagerMode());
713717
actionButtonsLayout.addComponent(bulkOperationsDropdown);
@@ -766,4 +770,9 @@ private void updateStatusButtons() {
766770
.setCaption(statusButtons.get(activeStatusButton) + LayoutUtil.spanCss(CssStyles.BADGE, String.valueOf(grid.getItemCount())));
767771
}
768772
}
773+
774+
private boolean isBulkEditAllowed() {
775+
return UserProvider.getCurrent().hasUserRight(UserRight.PERFORM_BULK_OPERATIONS)
776+
|| FacadeProvider.getSormasToSormasFacade().isFeatureEnabled();
777+
}
769778
}

0 commit comments

Comments
 (0)