check right privileges batch actions and code cleanup
This commit is contained in:
parent
01bd5c5558
commit
6cc2f7b84b
13 changed files with 87 additions and 20 deletions
|
@ -34,10 +34,10 @@ public class BatchAction implements GrantEntity {
|
||||||
|
|
||||||
public static final String ATTR_FAILURES = "failures";
|
public static final String ATTR_FAILURES = "failures";
|
||||||
public static final String FINISHED_FLAG = "_FINISHED";
|
public static final String FINISHED_FLAG = "_FINISHED";
|
||||||
public static final String ACTION_ATTRIBUT_TARGET_STATE = "batchActionTargetState";
|
public static final String ACTION_ATTRIBUTE_TARGET_STATE = "batchActionTargetState";
|
||||||
|
|
||||||
private static final Set<String> ACTION_ATTRIBUTES = new HashSet<>(Arrays.asList(
|
private static final Set<String> ACTION_ATTRIBUTES = new HashSet<>(Arrays.asList(
|
||||||
ACTION_ATTRIBUT_TARGET_STATE));
|
ACTION_ATTRIBUTE_TARGET_STATE));
|
||||||
|
|
||||||
@JsonProperty(BATCH_ACTION.ATTR_ID)
|
@JsonProperty(BATCH_ACTION.ATTR_ID)
|
||||||
public final Long id;
|
public final Long id;
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class SEBExamConfigBatchStateChangePopup extends AbstractBatchActionWizar
|
||||||
throw new IllegalArgumentException("missing " + ATTR_SELECTED_TARGET_STATE + " from pageContext");
|
throw new IllegalArgumentException("missing " + ATTR_SELECTED_TARGET_STATE + " from pageContext");
|
||||||
}
|
}
|
||||||
|
|
||||||
batchActionRequestBuilder.withFormParam(BatchAction.ACTION_ATTRIBUT_TARGET_STATE, targetStateName);
|
batchActionRequestBuilder.withFormParam(BatchAction.ACTION_ATTRIBUTE_TARGET_STATE, targetStateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,11 @@ 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.Privilege;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
||||||
|
|
||||||
/** A service to check authorization grants for a given user for entity-types and -instances
|
/** A service to check authorization grants for a given user for entity-types and -instances
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -249,6 +249,34 @@ public interface AuthorizationService {
|
||||||
return check(PrivilegeType.WRITE, entity);
|
return check(PrivilegeType.WRITE, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default <E extends GrantEntity> Result<E> checkForUser(
|
||||||
|
final PrivilegeType privilegeType,
|
||||||
|
final E entity,
|
||||||
|
final String ownerId) {
|
||||||
|
|
||||||
|
final UserInfo userInfo = getUserService().getUser(ownerId);
|
||||||
|
if (userInfo == null) {
|
||||||
|
return Result.ofError(new ResourceNotFoundException(EntityType.USER, ownerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasGrant(
|
||||||
|
privilegeType,
|
||||||
|
entity.entityType(),
|
||||||
|
entity.getInstitutionId(),
|
||||||
|
entity.getOwnerId(),
|
||||||
|
userInfo.uuid,
|
||||||
|
userInfo.institutionId,
|
||||||
|
userInfo.getUserRoles())) {
|
||||||
|
|
||||||
|
throw new PermissionDeniedException(
|
||||||
|
entity,
|
||||||
|
privilegeType,
|
||||||
|
getUserService().getCurrentUser().getUserInfo().uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.of(entity);
|
||||||
|
}
|
||||||
|
|
||||||
/** Checks if the current user has a specified role.
|
/** Checks if the current user has a specified role.
|
||||||
* If not a PermissionDeniedException is thrown for the given EntityType
|
* If not a PermissionDeniedException is thrown for the given EntityType
|
||||||
*
|
*
|
||||||
|
@ -274,5 +302,4 @@ public interface AuthorizationService {
|
||||||
currentUser.getUserInfo());
|
currentUser.getUserInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
||||||
|
@ -60,4 +60,10 @@ public interface UserService {
|
||||||
* @param authentication the Authentication context*/
|
* @param authentication the Authentication context*/
|
||||||
void setAuthenticationIfAbsent(Authentication authentication);
|
void setAuthenticationIfAbsent(Authentication authentication);
|
||||||
|
|
||||||
|
/** Gets the user for model id / uuid or null if not exists or not active
|
||||||
|
*
|
||||||
|
* @param userId The user uuid
|
||||||
|
* @return UserInfo for the user
|
||||||
|
*/
|
||||||
|
UserInfo getUser(String userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -279,6 +283,8 @@ public class AuthorizationServiceImpl implements AuthorizationService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private PrivilegeBuilder addPrivilege(final EntityType entityType) {
|
private PrivilegeBuilder addPrivilege(final EntityType entityType) {
|
||||||
return new PrivilegeBuilder(entityType);
|
return new PrivilegeBuilder(entityType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
@ -37,6 +38,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||||
public class UserServiceImpl implements UserService {
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
|
||||||
|
private final UserDAO userDAO;
|
||||||
|
|
||||||
|
|
||||||
public interface ExtractUserFromAuthenticationStrategy {
|
public interface ExtractUserFromAuthenticationStrategy {
|
||||||
|
@ -45,8 +47,11 @@ public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
private final Collection<ExtractUserFromAuthenticationStrategy> extractStrategies;
|
private final Collection<ExtractUserFromAuthenticationStrategy> extractStrategies;
|
||||||
|
|
||||||
public UserServiceImpl(final Collection<ExtractUserFromAuthenticationStrategy> extractStrategies) {
|
public UserServiceImpl(
|
||||||
|
final UserDAO userDAO,
|
||||||
|
final Collection<ExtractUserFromAuthenticationStrategy> extractStrategies) {
|
||||||
|
|
||||||
|
this.userDAO = userDAO;
|
||||||
this.extractStrategies = extractStrategies;
|
this.extractStrategies = extractStrategies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +113,21 @@ public class UserServiceImpl implements UserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserInfo getUser(final String userId) {
|
||||||
|
final UserInfo user = this.userDAO
|
||||||
|
.byModelId(userId)
|
||||||
|
.onError(error -> log.error("Failed to find user for id: {} error: {}", userId, error.getMessage()))
|
||||||
|
.getOr(null);
|
||||||
|
|
||||||
|
if (user != null && !user.isActive()) {
|
||||||
|
log.warn("Try to get inactive user for processing: {}", userId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. OAuth2Authentication strategy
|
// 1. OAuth2Authentication strategy
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
|
|
@ -29,7 +29,7 @@ public interface BatchActionExec {
|
||||||
* This shall check whether the needed attributes are available for a proper processing of the actions.
|
* This shall check whether the needed attributes are available for a proper processing of the actions.
|
||||||
* This is called just before a new batch action is created and processing is started.
|
* This is called just before a new batch action is created and processing is started.
|
||||||
*
|
*
|
||||||
* @param batchAction
|
* @param actionAttributes all attributes for batch action
|
||||||
* @return APIMessage if there is an consistency failure */
|
* @return APIMessage if there is an consistency failure */
|
||||||
APIMessage checkConsistency(Map<String, String> actionAttributes);
|
APIMessage checkConsistency(Map<String, String> actionAttributes);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public interface BatchActionService {
|
||||||
|
|
||||||
/** Validates a given batch action.
|
/** Validates a given batch action.
|
||||||
*
|
*
|
||||||
* @param batchAction
|
* @param batchAction the batch action data
|
||||||
* @return Result refer to the BatchAction or to an error when happened */
|
* @return Result refer to the BatchAction or to an error when happened */
|
||||||
Result<BatchAction> validate(BatchAction batchAction);
|
Result<BatchAction> validate(BatchAction batchAction);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
@ -86,7 +87,7 @@ public class DeleteExamAction implements BatchActionExec {
|
||||||
@Transactional
|
@Transactional
|
||||||
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
||||||
return this.examDAO.byModelId(modelId)
|
return this.examDAO.byModelId(modelId)
|
||||||
.flatMap(this::checkWriteAccess)
|
.flatMap( exam -> this.checkWriteAccess(exam, batchAction.ownerId))
|
||||||
.flatMap(this::checkNoActiveSEBClientConnections)
|
.flatMap(this::checkNoActiveSEBClientConnections)
|
||||||
.flatMap(this::deleteExamDependencies)
|
.flatMap(this::deleteExamDependencies)
|
||||||
.flatMap(this::deleteExamWithRefs)
|
.flatMap(this::deleteExamWithRefs)
|
||||||
|
@ -137,7 +138,7 @@ public class DeleteExamAction implements BatchActionExec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<Exam> checkWriteAccess(final Exam entity) {
|
private Result<Exam> checkWriteAccess(final Exam entity, final String ownerId) {
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
this.authorization.checkWrite(entity);
|
this.authorization.checkWrite(entity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType;
|
import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.BatchAction;
|
import ch.ethz.seb.sebserver.gbl.model.BatchAction;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
|
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
|
@ -21,7 +20,6 @@ import org.springframework.stereotype.Component;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -58,17 +56,24 @@ public class ExamConfigDelete implements BatchActionExec {
|
||||||
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
||||||
return this.configurationNodeDAO
|
return this.configurationNodeDAO
|
||||||
.byModelId(modelId)
|
.byModelId(modelId)
|
||||||
.flatMap(examConfig -> this.authorizationService.check(PrivilegeType.MODIFY, examConfig))
|
.flatMap(examConfig -> this.checkWriteAccess(examConfig, batchAction.ownerId))
|
||||||
.flatMap(examConfig -> checkDeletionRequirements(examConfig))
|
.flatMap(this::checkDeletionRequirements)
|
||||||
.flatMap(examConfig -> this.configurationNodeDAO.delete(new HashSet<>(Arrays.asList(new EntityKey(
|
.flatMap(examConfig -> this.configurationNodeDAO.delete(new HashSet<>(Arrays.asList(new EntityKey(
|
||||||
modelId,
|
modelId,
|
||||||
EntityType.CONFIGURATION_NODE))))
|
EntityType.CONFIGURATION_NODE))))
|
||||||
)
|
)
|
||||||
.map(res -> res.stream().collect(Collectors.toList()).get(0));
|
.map(res -> res.stream().toList().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<ConfigurationNode> checkDeletionRequirements(ConfigurationNode examConfig){
|
private Result<ConfigurationNode> checkWriteAccess(final ConfigurationNode examConfig, final String ownerId) {
|
||||||
Result<Boolean> isNotActive = this.examConfigurationMapDAO.checkNoActiveExamReferences(examConfig.id);
|
if (examConfig != null) {
|
||||||
|
this.authorizationService.checkWrite(examConfig);
|
||||||
|
}
|
||||||
|
return Result.of(examConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<ConfigurationNode> checkDeletionRequirements(final ConfigurationNode examConfig){
|
||||||
|
final Result<Boolean> isNotActive = this.examConfigurationMapDAO.checkNoActiveExamReferences(examConfig.id);
|
||||||
if(!isNotActive.getOrThrow()){
|
if(!isNotActive.getOrThrow()){
|
||||||
return Result.ofError(new APIMessageException(
|
return Result.ofError(new APIMessageException(
|
||||||
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
|
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class ExamConfigStateChange implements BatchActionExec {
|
||||||
|
|
||||||
private ConfigurationStatus getTargetState(final Map<String, String> actionAttributes) {
|
private ConfigurationStatus getTargetState(final Map<String, String> actionAttributes) {
|
||||||
try {
|
try {
|
||||||
final String targetStateString = actionAttributes.get(BatchAction.ACTION_ATTRIBUT_TARGET_STATE);
|
final String targetStateString = actionAttributes.get(BatchAction.ACTION_ATTRIBUTE_TARGET_STATE);
|
||||||
if (StringUtils.isBlank(targetStateString)) {
|
if (StringUtils.isBlank(targetStateString)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,6 +245,8 @@ sebserver.useraccount.role.EXAM_ADMIN=Exam Administrator
|
||||||
sebserver.useraccount.role.EXAM_ADMIN.tooltip=An exam administrator has overall read privileges for the institution but cannot see or manage other user accounts.<br/>An exam administrator is able to create new SEB configurations and import and setup exams.
|
sebserver.useraccount.role.EXAM_ADMIN.tooltip=An exam administrator has overall read privileges for the institution but cannot see or manage other user accounts.<br/>An exam administrator is able to create new SEB configurations and import and setup exams.
|
||||||
sebserver.useraccount.role.EXAM_SUPPORTER=Exam Supporter
|
sebserver.useraccount.role.EXAM_SUPPORTER=Exam Supporter
|
||||||
sebserver.useraccount.role.EXAM_SUPPORTER.tooltip=An exam supporter can only see and edit the own user account<br/> and monitor exams for that he/she was attached by an exam administrator.
|
sebserver.useraccount.role.EXAM_SUPPORTER.tooltip=An exam supporter can only see and edit the own user account<br/> and monitor exams for that he/she was attached by an exam administrator.
|
||||||
|
sebserver.useraccount.role.TEACHER=Teacher
|
||||||
|
sebserver.useraccount.role.TEACHER.tooltip=A teacher can only see and edit the own user account<br/> and monitor exams for that he/she has been imported or was attached by an exam administrator
|
||||||
|
|
||||||
sebserver.useraccount.list.empty=No user account can be found. Please adapt the filter or create a new user account
|
sebserver.useraccount.list.empty=No user account can be found. Please adapt the filter or create a new user account
|
||||||
sebserver.useraccount.list.title=User Accounts
|
sebserver.useraccount.list.title=User Accounts
|
||||||
|
|
|
@ -4032,7 +4032,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||||
.getBuilder(DoBatchAction.class)
|
.getBuilder(DoBatchAction.class)
|
||||||
.withFormParam(Domain.BATCH_ACTION.ATTR_ACTION_TYPE, BatchActionType.EXAM_CONFIG_STATE_CHANGE.name())
|
.withFormParam(Domain.BATCH_ACTION.ATTR_ACTION_TYPE, BatchActionType.EXAM_CONFIG_STATE_CHANGE.name())
|
||||||
.withFormParam(BATCH_ACTION.ATTR_SOURCE_IDS, config.getModelId())
|
.withFormParam(BATCH_ACTION.ATTR_SOURCE_IDS, config.getModelId())
|
||||||
.withFormParam(BatchAction.ACTION_ATTRIBUT_TARGET_STATE, ConfigurationStatus.CONSTRUCTION.name())
|
.withFormParam(BatchAction.ACTION_ATTRIBUTE_TARGET_STATE, ConfigurationStatus.CONSTRUCTION.name())
|
||||||
.call();
|
.call();
|
||||||
|
|
||||||
assertNotNull(doBatchAction);
|
assertNotNull(doBatchAction);
|
||||||
|
|
Loading…
Reference in a new issue