check right privileges batch actions and code cleanup

This commit is contained in:
anhefti 2024-06-26 12:59:48 +02:00
parent 01bd5c5558
commit 6cc2f7b84b
13 changed files with 87 additions and 20 deletions

View file

@ -34,10 +34,10 @@ public class BatchAction implements GrantEntity {
public static final String ATTR_FAILURES = "failures";
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(
ACTION_ATTRIBUT_TARGET_STATE));
ACTION_ATTRIBUTE_TARGET_STATE));
@JsonProperty(BATCH_ACTION.ATTR_ID)
public final Long id;

View file

@ -112,7 +112,7 @@ public class SEBExamConfigBatchStateChangePopup extends AbstractBatchActionWizar
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

View file

@ -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.PrivilegeType;
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.UserRole;
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.dao.ResourceNotFoundException;
/** A service to check authorization grants for a given user for entity-types and -instances
* <p>
@ -249,6 +249,34 @@ public interface AuthorizationService {
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.
* If not a PermissionDeniedException is thrown for the given EntityType
*
@ -274,5 +302,4 @@ public interface AuthorizationService {
currentUser.getUserInfo());
}
}
}

View file

@ -10,7 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
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.web.bind.WebDataBinder;
@ -60,4 +60,10 @@ public interface UserService {
* @param authentication the Authentication context*/
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);
}

View file

@ -16,6 +16,10 @@ import java.util.Set;
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.stereotype.Service;
@ -279,6 +283,8 @@ public class AuthorizationServiceImpl implements AuthorizationService {
}
private PrivilegeBuilder addPrivilege(final EntityType entityType) {
return new PrivilegeBuilder(entityType);
}

View file

@ -15,6 +15,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
@ -37,6 +38,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
private final UserDAO userDAO;
public interface ExtractUserFromAuthenticationStrategy {
@ -45,8 +47,11 @@ public class UserServiceImpl implements UserService {
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;
}
@ -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
@Lazy
@Component

View file

@ -29,7 +29,7 @@ public interface BatchActionExec {
* 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.
*
* @param batchAction
* @param actionAttributes all attributes for batch action
* @return APIMessage if there is an consistency failure */
APIMessage checkConsistency(Map<String, String> actionAttributes);

View file

@ -21,7 +21,7 @@ public interface BatchActionService {
/** 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 */
Result<BatchAction> validate(BatchAction batchAction);

View file

@ -13,6 +13,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
@ -86,7 +87,7 @@ public class DeleteExamAction implements BatchActionExec {
@Transactional
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
return this.examDAO.byModelId(modelId)
.flatMap(this::checkWriteAccess)
.flatMap( exam -> this.checkWriteAccess(exam, batchAction.ownerId))
.flatMap(this::checkNoActiveSEBClientConnections)
.flatMap(this::deleteExamDependencies)
.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) {
this.authorization.checkWrite(entity);
}

View file

@ -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.APIMessage;
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.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
@ -21,7 +20,6 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.stream.Collectors;
@Lazy
@Component
@ -58,17 +56,24 @@ public class ExamConfigDelete implements BatchActionExec {
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
return this.configurationNodeDAO
.byModelId(modelId)
.flatMap(examConfig -> this.authorizationService.check(PrivilegeType.MODIFY, examConfig))
.flatMap(examConfig -> checkDeletionRequirements(examConfig))
.flatMap(examConfig -> this.checkWriteAccess(examConfig, batchAction.ownerId))
.flatMap(this::checkDeletionRequirements)
.flatMap(examConfig -> this.configurationNodeDAO.delete(new HashSet<>(Arrays.asList(new EntityKey(
modelId,
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){
Result<Boolean> isNotActive = this.examConfigurationMapDAO.checkNoActiveExamReferences(examConfig.id);
private Result<ConfigurationNode> checkWriteAccess(final ConfigurationNode examConfig, final String ownerId) {
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()){
return Result.ofError(new APIMessageException(
APIMessage.ErrorMessage.INTEGRITY_VALIDATION

View file

@ -83,7 +83,7 @@ public class ExamConfigStateChange implements BatchActionExec {
private ConfigurationStatus getTargetState(final Map<String, String> actionAttributes) {
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)) {
return null;
}

View file

@ -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_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.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.title=User Accounts

View file

@ -4032,7 +4032,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getBuilder(DoBatchAction.class)
.withFormParam(Domain.BATCH_ACTION.ATTR_ACTION_TYPE, BatchActionType.EXAM_CONFIG_STATE_CHANGE.name())
.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();
assertNotNull(doBatchAction);