From 8af5a4fc93a8d9b075eca4d2e32762ee08ce0a03 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 26 Feb 2019 16:33:20 +0100 Subject: [PATCH] SEBSERV-21 & SEBSERV-27 fixed role based User-Account access --- .../gbl/model/user/PasswordChange.java | 18 +-- .../sebserver/gbl/model/user/UserAccount.java | 3 + .../sebserver/gbl/model/user/UserInfo.java | 11 ++ .../seb/sebserver/gbl/model/user/UserMod.java | 33 +++-- .../sebserver/gbl/model/user/UserRole.java | 20 +++ .../gui/content/InstitutionForm.java | 2 +- .../gui/content/InstitutionList.java | 6 +- .../UserAccountChangePasswordForm.java | 8 +- .../gui/content/UserAccountForm.java | 21 ++- .../gui/content/UserAccountList.java | 6 +- .../gui/content/action/ActionDefinition.java | 2 +- .../gui/content/activity/ActivitiesPane.java | 4 +- .../gui/form/SelectionFieldBuilder.java | 37 +++-- .../gui/service/i18n/I18nSupport.java | 13 +- .../service/i18n/impl/I18nSupportImpl.java | 14 ++ .../remote/webservice/auth/CurrentUser.java | 22 ++- .../OAuth2AuthorizationContextHolder.java | 14 ++ .../auth/SEBServerAuthorizationContext.java | 12 +- .../sebserver/gui/widget/MultiSelection.java | 1 + .../authorization/AuthorizationService.java | 30 +++- .../servicelayer/dao/FilterMap.java | 5 + .../{UserDaoImpl.java => UserDAOImpl.java} | 6 +- .../api/ActivatableEntityController.java | 16 +- .../weblayer/api/EntityController.java | 2 +- .../weblayer/api/UserAccountController.java | 49 +++++- src/main/resources/logback-spring.xml | 29 +++- src/main/resources/messages-de.properties | 0 src/main/resources/messages.properties | 12 +- src/main/resources/messages_de.properties | 140 ++++++++++++++++++ .../api/admin/InstitutionAPITest.java | 8 +- .../integration/api/admin/UserAPITest.java | 8 +- 31 files changed, 450 insertions(+), 102 deletions(-) rename src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/{UserDaoImpl.java => UserDAOImpl.java} (97%) delete mode 100644 src/main/resources/messages-de.properties create mode 100644 src/main/resources/messages_de.properties diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/PasswordChange.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/PasswordChange.java index 8b9f6dc1..c8384831 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/PasswordChange.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/PasswordChange.java @@ -22,7 +22,7 @@ public class PasswordChange implements Entity { public static final String ATTR_NAME_OLD_PASSWORD = "oldPassword"; public static final String ATTR_NAME_NEW_PASSWORD = "newPassword"; - public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword"; + public static final String ATTR_NAME_CONFIRM_NEW_PASSWORD = "confirmNewPassword"; @NotNull @JsonProperty(USER.ATTR_UUID) @@ -37,21 +37,21 @@ public class PasswordChange implements Entity { @JsonProperty(ATTR_NAME_NEW_PASSWORD) private final String newPassword; - @NotNull(message = "user:retypedNewPassword:notNull") - @JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) - private final String retypedNewPassword; + @NotNull(message = "user:confirmNewPassword:notNull") + @JsonProperty(ATTR_NAME_CONFIRM_NEW_PASSWORD) + private final String confirmNewPassword; @JsonCreator public PasswordChange( @JsonProperty(USER.ATTR_UUID) final String userId, @JsonProperty(ATTR_NAME_OLD_PASSWORD) final String oldPassword, @JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword, - @JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword) { + @JsonProperty(ATTR_NAME_CONFIRM_NEW_PASSWORD) final String confirmNewPassword) { this.userId = userId; this.oldPassword = oldPassword; this.newPassword = newPassword; - this.retypedNewPassword = retypedNewPassword; + this.confirmNewPassword = confirmNewPassword; } public String getOldPassword() { @@ -62,12 +62,12 @@ public class PasswordChange implements Entity { return this.newPassword; } - public String getRetypedNewPassword() { - return this.retypedNewPassword; + public String getConfirmNewPassword() { + return this.confirmNewPassword; } public boolean newPasswordMatch() { - return this.newPassword.equals(this.retypedNewPassword); + return this.newPassword.equals(this.confirmNewPassword); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java index a247bf77..3b7a7cee 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.gbl.model.user; +import java.util.EnumSet; import java.util.Locale; import java.util.Set; @@ -41,6 +42,8 @@ public interface UserAccount extends GrantEntity { Set getRoles(); + EnumSet getUserRoles(); + String getNewPassword(); String getRetypedNewPassword(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java index ebe4b516..77f27776 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java @@ -9,8 +9,10 @@ package ch.ethz.seb.sebserver.gbl.model.user; import java.io.Serializable; +import java.util.EnumSet; import java.util.Locale; import java.util.Set; +import java.util.stream.Collectors; import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; @@ -182,6 +184,15 @@ public final class UserInfo implements UserAccount, Activatable, Serializable { return this.roles; } + @Override + @JsonIgnore + public EnumSet getUserRoles() { + return EnumSet.copyOf( + getRoles().stream() + .map(r -> UserRole.valueOf(r)) + .collect(Collectors.toList())); + } + public boolean hasRole(final UserRole userRole) { if (userRole == null) { return false; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java index 600c580d..0c682e01 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java @@ -9,8 +9,10 @@ package ch.ethz.seb.sebserver.gbl.model.user; import java.util.Collections; +import java.util.EnumSet; import java.util.Locale; import java.util.Set; +import java.util.stream.Collectors; import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; @@ -79,8 +81,8 @@ public final class UserMod implements UserAccount { private final String newPassword; @NotNull(message = "user:retypedNewPassword:notNull") - @JsonProperty(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD) - private final String retypedNewPassword; + @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) + private final String confirmNewPassword; @JsonCreator @JsonIgnoreProperties(ignoreUnknown = true) @@ -90,7 +92,7 @@ public final class UserMod implements UserAccount { @JsonProperty(USER.ATTR_NAME) final String name, @JsonProperty(USER.ATTR_USERNAME) final String username, @JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD) final String newPassword, - @JsonProperty(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword, + @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) final String confirmNewPassword, @JsonProperty(USER.ATTR_EMAIL) final String email, @JsonProperty(USER.ATTR_LANGUAGE) final Locale language, @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, @@ -99,7 +101,7 @@ public final class UserMod implements UserAccount { this.uuid = uuid; this.institutionId = institutionId; this.newPassword = newPassword; - this.retypedNewPassword = retypedNewPassword; + this.confirmNewPassword = confirmNewPassword; this.name = name; this.username = username; this.email = email; @@ -110,11 +112,11 @@ public final class UserMod implements UserAccount { : Collections.emptySet(); } - public UserMod(final UserInfo userInfo, final String newPassword, final String retypedNewPassword) { + public UserMod(final UserInfo userInfo, final String newPassword, final String confirmNewPassword) { this.uuid = userInfo.uuid; this.institutionId = userInfo.institutionId; this.newPassword = newPassword; - this.retypedNewPassword = retypedNewPassword; + this.confirmNewPassword = confirmNewPassword; this.name = userInfo.name; this.username = userInfo.username; this.email = userInfo.email; @@ -127,7 +129,7 @@ public final class UserMod implements UserAccount { this.uuid = modelId; this.institutionId = postAttrMapper.getLong(USER.ATTR_INSTITUTION_ID); this.newPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_NEW_PASSWORD); - this.retypedNewPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD); + this.confirmNewPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD); this.name = postAttrMapper.getString(USER.ATTR_NAME); this.username = postAttrMapper.getString(USER.ATTR_USERNAME); this.email = postAttrMapper.getString(USER.ATTR_EMAIL); @@ -140,7 +142,7 @@ public final class UserMod implements UserAccount { this.uuid = modelId; this.institutionId = institutionId; this.newPassword = null; - this.retypedNewPassword = null; + this.confirmNewPassword = null; this.name = null; this.username = null; this.email = null; @@ -204,9 +206,18 @@ public final class UserMod implements UserAccount { return this.roles; } + @Override + @JsonIgnore + public EnumSet getUserRoles() { + return EnumSet.copyOf( + getRoles().stream() + .map(r -> UserRole.valueOf(r)) + .collect(Collectors.toList())); + } + @Override public String getRetypedNewPassword() { - return this.retypedNewPassword; + return this.confirmNewPassword; } public boolean passwordChangeRequest() { @@ -214,7 +225,7 @@ public final class UserMod implements UserAccount { } public boolean newPasswordMatch() { - return passwordChangeRequest() && this.newPassword.equals(this.retypedNewPassword); + return passwordChangeRequest() && this.newPassword.equals(this.confirmNewPassword); } @Override @@ -243,7 +254,7 @@ public final class UserMod implements UserAccount { + this.username + ", email=" + this.email + ", language=" + this.language + ", timeZone=" + this.timeZone + ", roles=" + this.roles - + ", newPassword=" + this.newPassword + ", retypedNewPassword=" + this.retypedNewPassword + "]"; + + ", newPassword=" + this.newPassword + ", retypedNewPassword=" + this.confirmNewPassword + "]"; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java index e92c49ff..9e649592 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java @@ -8,6 +8,11 @@ package ch.ethz.seb.sebserver.gbl.model.user; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Entity; @@ -32,4 +37,19 @@ public enum UserRole implements Entity { public String getName() { return name(); } + + public static List publicRolesForUser(final UserInfo user) { + final EnumSet roles = user.getUserRoles(); + if (roles.contains(SEB_SERVER_ADMIN)) { + return Arrays.asList(UserRole.values()); + } else if (roles.contains(INSTITUTIONAL_ADMIN)) { + return Arrays.asList(INSTITUTIONAL_ADMIN, EXAM_ADMIN, EXAM_SUPPORTER); + } else if (roles.contains(EXAM_ADMIN)) { + return Arrays.asList(EXAM_ADMIN, EXAM_SUPPORTER); + } else if (roles.contains(EXAM_SUPPORTER)) { + return Arrays.asList(EXAM_SUPPORTER); + } else { + return Collections.emptyList(); + } + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java index 389c01e8..5ac9b200 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java @@ -83,7 +83,7 @@ public class InstitutionForm implements TemplateComposer { final boolean writeGrant = this.currentUser.hasPrivilege(PrivilegeType.WRITE, institution); final boolean modifyGrant = this.currentUser.hasPrivilege(PrivilegeType.MODIFY, institution); - final boolean userWriteGrant = this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.USER); + final boolean userWriteGrant = this.currentUser.hasBasePrivilege(PrivilegeType.WRITE, EntityType.USER); final boolean isReadonly = pageContext.isReadonly(); // new PageContext with actual EntityKey diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java index 83de280a..9abdb90e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java @@ -79,10 +79,10 @@ public class InstitutionList implements TemplateComposer { // propagate content actions to action-pane pageContext.createAction(ActionDefinition.INSTITUTION_NEW) .readonly(false) - .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.INSTITUTION)) + .publishIf(() -> this.currentUser.hasBasePrivilege(PrivilegeType.WRITE, EntityType.INSTITUTION)) .createAction(ActionDefinition.USER_ACCOUNT_NEW) .withExec(UserAccountActions::newUserAccount) - .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.USER)) + .publishIf(() -> this.currentUser.hasBasePrivilege(PrivilegeType.WRITE, EntityType.USER)) .createAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) .withSelectionSupplier(table::getSelection) .withExec(InstitutionActions::viewInstitutionFromList) @@ -91,7 +91,7 @@ public class InstitutionList implements TemplateComposer { .withSelectionSupplier(table::getSelection) .withExec(InstitutionActions::editInstitutionFromList) .readonly(false) - .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.INSTITUTION)); + .publishIf(() -> this.currentUser.hasBasePrivilege(PrivilegeType.MODIFY, EntityType.INSTITUTION)); ; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java index 2aad76c4..9689f67d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java @@ -83,15 +83,15 @@ public class UserAccountChangePasswordForm implements TemplateComposer { entityKey.getModelId()) .addField(FormBuilder.text( PasswordChange.ATTR_NAME_OLD_PASSWORD, - "sebserver.useraccount.form.institution.password.old") + "sebserver.useraccount.form.password.old") .asPasswordField()) .addField(FormBuilder.text( PasswordChange.ATTR_NAME_NEW_PASSWORD, - "sebserver.useraccount.form.institution.password.new") + "sebserver.useraccount.form.password.new") .asPasswordField()) .addField(FormBuilder.text( - PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD, - "sebserver.useraccount.form.institution.password.retyped") + PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, + "sebserver.useraccount.form.password.new.confirm") .asPasswordField() .withCondition(() -> entityKey != null)) .buildFor(this.restService.getRestCall(ChangePassword.class)); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java index ead2c6de..b0d441ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java @@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageUtils; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; @@ -71,12 +72,13 @@ public class UserAccountForm implements TemplateComposer { @Override public void compose(final PageContext pageContext) { + final UserInfo currentUser = this.currentUser.get(); final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory(); final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final BooleanSupplier isNew = () -> entityKey == null; final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); - final BooleanSupplier isSEBAdmin = () -> this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final BooleanSupplier isSEBAdmin = () -> currentUser.hasRole(UserRole.SEB_SERVER_ADMIN); final boolean readonly = pageContext.isReadonly(); // get data or create new. handle error if happen final UserAccount userAccount = isNew.getAsBoolean() @@ -84,7 +86,7 @@ public class UserAccountForm implements TemplateComposer { UUID.randomUUID().toString(), (parentEntityKey != null) ? Long.valueOf(parentEntityKey.modelId) - : this.currentUser.get().institutionId) + : currentUser.institutionId) : this.restService .getBuilder(GetUserAccount.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) @@ -99,6 +101,7 @@ public class UserAccountForm implements TemplateComposer { return; } + final boolean ownAccount = currentUser.uuid.equals(userAccount.getModelId()); final boolean writeGrant = this.currentUser.hasPrivilege(PrivilegeType.WRITE, userAccount); final boolean modifyGrant = this.currentUser.hasPrivilege(PrivilegeType.MODIFY, userAccount); // modifying an UserAccount is not possible if the root institution is inactive @@ -178,8 +181,8 @@ public class UserAccountForm implements TemplateComposer { .asPasswordField() .withCondition(isNew)) .addField(FormBuilder.text( - PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD, - "sebserver.useraccount.form.password.retyped") + PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, + "sebserver.useraccount.form.password.confirm") .asPasswordField() .withCondition(isNew)) .buildFor((entityKey == null) @@ -189,6 +192,7 @@ public class UserAccountForm implements TemplateComposer { // propagate content actions to action-pane formContext.createAction(ActionDefinition.USER_ACCOUNT_NEW) + .resetEntity() .withExec(UserAccountActions::newUserAccount) .publishIf(() -> writeGrant && readonly && istitutionActive) @@ -210,7 +214,14 @@ public class UserAccountForm implements TemplateComposer { .publishIf(() -> writeGrant && readonly && istitutionActive && !userAccount.isActive()) .createAction(ActionDefinition.USER_ACCOUNT_SAVE) - .withExec(formHandle::postChanges) + .withExec(action -> { + final Action postChanges = formHandle.postChanges(action); + if (ownAccount) { + this.currentUser.refresh(); + pageContext.forwardToMainPage(pageContext); + } + return postChanges; + }) .publishIf(() -> !readonly) .createAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java index 6aa9d6f6..d0befb64 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java @@ -99,15 +99,15 @@ public class UserAccountList implements TemplateComposer { // propagate content actions to action-pane pageContext.createAction(ActionDefinition.USER_ACCOUNT_NEW) .withExec(UserAccountActions::newUserAccount) - .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.USER)) + .publishIf(() -> this.currentUser.hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.USER)) .createAction(ActionDefinition.USER_ACCOUNT_VIEW) .withSelectionSupplier(table::getSelection) .withExec(UserAccountActions::viewUserAccountFromList) .publish() - .createAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM__LIST) + .createAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST) .withSelectionSupplier(table::getSelection) .withExec(UserAccountActions::editUserAccountFromList) - .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.USER)); + .publishIf(() -> this.currentUser.hasInstitutionalPrivilege(PrivilegeType.MODIFY, EntityType.USER)); } private String getLocaleDisplayText(final UserInfo userInfo) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index b07f03ff..efae0b22 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -98,7 +98,7 @@ public enum ActionDefinition { UserAccountForm.class, USER_ACCOUNT_VIEW_LIST), - USER_ACCOUNT_MODIFY_FROM__LIST( + USER_ACCOUNT_MODIFY_FROM_LIST( new LocTextKey("sebserver.useraccount.action.list.modify"), ImageIcon.EDIT, UserAccountForm.class, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java index 5753ae31..797b0201 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java @@ -97,8 +97,8 @@ public class ActivitiesPane implements TemplateComposer { } // User Account - // if current user has base read privilege for User Account, show list - if (this.currentUser.hasPrivilege(PrivilegeType.READ_ONLY, EntityType.USER)) { + // if current user has base or institutional read privilege for User Account, show list + if (this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ_ONLY, EntityType.USER)) { final TreeItem userAccounts = this.widgetFactory.treeItemLocalized( navigation, ActionDefinition.USER_ACCOUNT_VIEW_LIST.title); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java index b2c1dba1..34dcf66a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java @@ -25,6 +25,7 @@ import org.eclipse.swt.widgets.Label; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; import ch.ethz.seb.sebserver.gui.widget.MultiSelection; import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.SingleSelection; @@ -104,24 +105,38 @@ public final class SelectionFieldBuilder extends FieldBuilder { this.itemsSupplier.get() .stream() .filter(tuple -> keys.contains(tuple._1)) - .map(tuple -> tuple._2) - .forEach(v -> createMuliSelectionReadonlyLabel(composite, v)); + .map(tuple -> tuple._1) + .forEach(v -> buildReadonlyLabel(composite, v, 0)); } } else { builder.form.putField( - this.name, lab, - builder.valueLabel( - builder.formParent, - this.itemsSupplier.get().stream() - .filter(tuple -> this.value.equals(tuple._1)) - .findFirst() - .map(tuple -> tuple._2) - .orElse(null), - this.spanInput)); + this.name, + lab, + buildReadonlyLabel(builder.formParent, this.value, this.spanInput)); builder.setFieldVisible(this.visible, this.name); } } + private Label buildReadonlyLabel(final Composite composite, final String valueKey, final int hspan) { + final Label label = new Label(composite, SWT.NONE); + final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, hspan, 1); + gridData.verticalIndent = 0; + gridData.horizontalIndent = 0; + label.setLayoutData(gridData); + label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION_READONLY.key); + + final Supplier valueSupplier = () -> this.itemsSupplier.get().stream() + .filter(tuple -> valueKey.equals(tuple._1)) + .findFirst() + .map(tuple -> tuple._2) + .orElse(Constants.EMPTY_NOTE); + final Consumer