SEBSERV-65 fixed - exam-admin has now institutional read privilege on

user accounts.
This commit is contained in:
anhefti 2019-06-27 10:42:06 +02:00
parent 3d67b4ed9c
commit 37e75207b1
9 changed files with 95 additions and 46 deletions

View file

@ -8,10 +8,14 @@
package ch.ethz.seb.sebserver.gbl.api.authorization;
import java.util.EnumSet;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
/** Defines a Privilege by combining a PrivilegeType for base (overall) rights,
@ -165,4 +169,31 @@ public final class Privilege {
}
}
/** Checks if the current user has role based edit access to a specified user account.
*
* If user account has UserRole.SEB_SERVER_ADMIN this always gives true
* If user account has UserRole.INSTITUTIONAL_ADMIN this is true if the given user account has
* not the UserRole.SEB_SERVER_ADMIN (institutional administrators should not be able to edit SEB Server
* administrators)
* If the current user is the same as the given user account this is always true no matter if there are any
* user-account based privileges (every user shall see its own account)
*
* @param userAccount the user account the check role based edit access
* @return true if the current user has role based edit access to a specified user account */
public static boolean hasRoleBasedUserAccountEditGrant(final UserAccount userAccount, final UserInfo currentUser) {
final EnumSet<UserRole> userRolesOfUserAccount = userAccount.getUserRoles();
final EnumSet<UserRole> userRolesOfCurrentUser = currentUser.getUserRoles();
if (userRolesOfCurrentUser.contains(UserRole.SEB_SERVER_ADMIN)) {
return true;
}
if (userRolesOfCurrentUser.contains(UserRole.INSTITUTIONAL_ADMIN)) {
return !userRolesOfUserAccount.contains(UserRole.SEB_SERVER_ADMIN);
}
if (currentUser.equals(userAccount)) {
return true;
}
return false;
}
}

View file

@ -215,6 +215,31 @@ public final class UserInfo implements UserAccount, Activatable, Serializable {
return UserInfo.of(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.uuid == null) ? 0 : this.uuid.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final UserInfo other = (UserInfo) obj;
if (this.uuid == null) {
if (other.uuid != null)
return false;
} else if (!this.uuid.equals(other.uuid))
return false;
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();

View file

@ -19,6 +19,9 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
@ -123,8 +126,13 @@ public class UserAccountForm implements TemplateComposer {
final boolean ownAccount = user.uuid.equals(userAccount.getModelId());
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount);
final boolean writeGrant = userGrantCheck.w();
final boolean modifyGrant = userGrantCheck.m();
final boolean roleBasedEditGrant = Privilege.hasRoleBasedUserAccountEditGrant(userAccount, currentUser.get());
final boolean writeGrant = roleBasedEditGrant && userGrantCheck.w();
final boolean modifyGrant = roleBasedEditGrant && userGrantCheck.m();
final boolean institutionalWriteGrant = currentUser.hasInstitutionalPrivilege(
PrivilegeType.WRITE,
EntityType.USER);
final boolean institutionActive = restService.getBuilder(GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(userAccount.getInstitutionId()))
.call()
@ -217,7 +225,7 @@ public class UserAccountForm implements TemplateComposer {
this.pageService.pageActionBuilder(formContext.clearEntityKeys())
.newAction(ActionDefinition.USER_ACCOUNT_NEW)
.publishIf(() -> writeGrant && readonly && institutionActive)
.publishIf(() -> institutionalWriteGrant && readonly && institutionActive)
.newAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.withEntityKey(entityKey)

View file

@ -246,12 +246,13 @@ public class ResourceService {
public List<Tuple<String>> examSupporterResources() {
final UserInfo userInfo = this.currentUser.get();
return this.restService.getBuilder(GetUserAccountNames.class)
final List<EntityName> selection = this.restService.getBuilder(GetUserAccountNames.class)
.withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId))
.withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
.withQueryParam(UserInfo.FILTER_ATTR_ROLE, UserRole.EXAM_SUPPORTER.name())
.call()
.getOr(Collections.emptyList())
.getOr(Collections.emptyList());
return selection
.stream()
.map(entityName -> new Tuple<>(entityName.modelId, entityName.name))
.collect(Collectors.toList());

View file

@ -9,7 +9,6 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
@ -230,31 +229,4 @@ public interface AuthorizationService {
return check(PrivilegeType.WRITE, grantEntity);
}
/** Checks if the current user has role based view access to a specified user account.
*
* If user account has UserRole.SEB_SERVER_ADMIN this always gives true
* If user account has UserRole.INSTITUTIONAL_ADMIN this is true if the given user account has
* not the UserRole.SEB_SERVER_ADMIN (institutional administrators should not see SEB Server administrators)
* If the current user is the same as the given user account this is always true no matter if there are any
* user-account based privileges (every user shall see its own account)
*
* @param userAccount the user account the check role based view access
* @return true if the current user has role based view access to a specified user account */
default boolean hasRoleBasedUserAccountViewGrant(final UserInfo userAccount) {
final EnumSet<UserRole> userRolesOfUserAccount = userAccount.getUserRoles();
final SEBServerUser currentUser = getUserService().getCurrentUser();
final EnumSet<UserRole> userRolesOfCurrentUser = currentUser.getUserRoles();
if (userRolesOfCurrentUser.contains(UserRole.SEB_SERVER_ADMIN)) {
return true;
}
if (userRolesOfCurrentUser.contains(UserRole.INSTITUTIONAL_ADMIN)) {
return !userRolesOfUserAccount.contains(UserRole.SEB_SERVER_ADMIN);
}
if (currentUser.uuid().equals(userAccount.uuid)) {
return true;
}
return false;
}
}

View file

@ -72,6 +72,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
.andForRole(UserRole.INSTITUTIONAL_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.READ)
.withOwnerPrivilege(PrivilegeType.MODIFY)
.andForRole(UserRole.EXAM_SUPPORTER)
.withOwnerPrivilege(PrivilegeType.MODIFY)

View file

@ -160,7 +160,7 @@ public class UserDAOImpl implements UserDAO {
? predicate.and(ui -> ui.roles.contains(userRole))
: predicate;
return this.userRecordMapper
final Collection<UserInfo> userInfo = this.userRecordMapper
.selectByExample()
.where(
UserRecordDynamicSqlSupport.active,
@ -187,6 +187,9 @@ public class UserDAOImpl implements UserDAO {
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(_predicate)
.collect(Collectors.toList());
;
return userInfo;
});
}

View file

@ -174,8 +174,10 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
}
return getAll(filterMap)
.getOrThrow()
final Collection<T> all = getAll(filterMap)
.getOrThrow();
return all
.stream()
.map(Entity::toName)
.collect(Collectors.toList());
@ -341,9 +343,10 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
}
protected Result<Collection<T>> getAll(final FilterMap filterMap) {
return this.entityDAO.allMatching(
final Result<Collection<T>> allMatching = this.entityDAO.allMatching(
filterMap,
this::hasReadAccess);
return allMatching;
}
protected Result<T> notifyCreated(final T entity) {

View file

@ -8,10 +8,8 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.Valid;
@ -19,6 +17,7 @@ import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestBody;
@ -32,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
@ -46,7 +46,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
@ -101,12 +100,18 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
}
@Override
protected Result<Collection<UserInfo>> getAll(final FilterMap filterMap) {
return super.getAll(filterMap)
.map(result -> result
.stream()
.filter(this.authorization::hasRoleBasedUserAccountViewGrant)
.collect(Collectors.toList()));
protected Result<UserInfo> checkModifyAccess(final UserInfo entity) {
return super.checkModifyAccess(entity)
.map(this::checkRoleBasedEditGrant);
}
private UserInfo checkRoleBasedEditGrant(final UserInfo userInfo) {
final SEBServerUser currentUser = this.authorization.getUserService().getCurrentUser();
if (Privilege.hasRoleBasedUserAccountEditGrant(userInfo, currentUser.getUserInfo())) {
return userInfo;
} else {
throw new AccessDeniedException("No edit right grant for user: " + currentUser.getUsername());
}
}
@Override