Fix action delete access control - to require only target update (not delete also) (#2767)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -320,22 +320,22 @@ public interface DeploymentManagement extends PermissionSupport {
|
|||||||
void deleteActionsByIds(List<Long> actionIds);
|
void deleteActionsByIds(List<Long> actionIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes actions in scope of the target ONLY by list of action ids.
|
* Deletes actions in scope of the controllerId ONLY by list of action ids.
|
||||||
*
|
*
|
||||||
* @param target - target controllerId
|
* @param controllerId - controllerId controllerId
|
||||||
* @param actionsIds - list of action ids to be deleted
|
* @param actionsIds - list of action ids to be deleted
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
||||||
void deleteTargetActionsByIds(final String target, final List<Long> actionsIds);
|
void deleteTargetActionsByIds(final String controllerId, final List<Long> actionsIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes target actions and leaves the LAST N actions in the action history only.
|
* Deletes controllerId actions and leaves the LAST N actions in the action history only.
|
||||||
*
|
*
|
||||||
* @param target - target controllerId
|
* @param controllerId - controllerId controllerId
|
||||||
* @param keepLast - number of actions to be left/kept (NOT deleted)
|
* @param keepLast - number of actions to be left/kept (NOT deleted)
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
||||||
void deleteOldestTargetActions(final String target, final int keepLast);
|
void deleteOldestTargetActions(final String controllerId, final int keepLast);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the status of inactive scheduled {@link Action}s for the specified {@link Target}s to {@link Status#CANCELED}
|
* Sets the status of inactive scheduled {@link Action}s for the specified {@link Target}s to {@link Status#CANCELED}
|
||||||
@@ -413,5 +413,5 @@ public interface DeploymentManagement extends PermissionSupport {
|
|||||||
void cancelActionsForDistributionSet(final ActionCancellationType cancelationType, final DistributionSet set);
|
void cancelActionsForDistributionSet(final ActionCancellationType cancelationType, final DistributionSet set);
|
||||||
|
|
||||||
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
|
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||||
void handleMaxAssignmentsExceeded(Long targetId, Long requested, AssignmentQuotaExceededException ex);
|
void handleMaxAssignmentsExceeded(Long targetId, Long requested, AssignmentQuotaExceededException quotaExceededException);
|
||||||
}
|
}
|
||||||
@@ -385,14 +385,13 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void deleteScheduledActions(final JpaRollout rollout, final Slice<JpaAction> scheduledActions) {
|
private void deleteScheduledActions(final JpaRollout rollout, final Slice<JpaAction> scheduledActions) {
|
||||||
final boolean hasScheduledActions = scheduledActions.getNumberOfElements() > 0;
|
if (scheduledActions.getNumberOfElements() > 0) {
|
||||||
|
// has scheduled actions - delete them
|
||||||
if (hasScheduledActions) {
|
|
||||||
try {
|
try {
|
||||||
final List<Long> actionIds = StreamSupport.stream(scheduledActions.spliterator(), false)
|
final List<Long> actionIds = StreamSupport.stream(scheduledActions.spliterator(), false)
|
||||||
.map(Action::getId)
|
.map(Action::getId)
|
||||||
.toList();
|
.toList();
|
||||||
actionRepository.deleteByIdIn(actionIds);
|
actionRepository.deleteAllById(actionIds);
|
||||||
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
|
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
|
||||||
.publishEvent(new RolloutUpdatedEvent(rollout)));
|
.publishEvent(new RolloutUpdatedEvent(rollout)));
|
||||||
} catch (final RuntimeException e) {
|
} catch (final RuntimeException e) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public class AccessControllerConfiguration {
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Optional<Specification<JpaAction>> getAccessRules(final Operation operation) {
|
public Optional<Specification<JpaAction>> getAccessRules(final Operation operation) {
|
||||||
return targetAccessController.getAccessRules(operation).map(targetSpec -> (actionRoot, query, cb) -> {
|
return targetAccessController.getAccessRules(map(operation)).map(targetSpec -> (actionRoot, query, cb) -> {
|
||||||
final Join<JpaAction, JpaTarget> targetJoin = actionRoot.join(JpaAction_.target);
|
final Join<JpaAction, JpaTarget> targetJoin = actionRoot.join(JpaAction_.target);
|
||||||
final EntityType<JpaTarget> targetModel = query.from(JpaTarget.class).getModel();
|
final EntityType<JpaTarget> targetModel = query.from(JpaTarget.class).getModel();
|
||||||
final Root<JpaTarget> targetRoot = (Root<JpaTarget>) Proxy.newProxyInstance(
|
final Root<JpaTarget> targetRoot = (Root<JpaTarget>) Proxy.newProxyInstance(
|
||||||
@@ -101,7 +101,15 @@ public class AccessControllerConfiguration {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void assertOperationAllowed(final Operation operation, final JpaAction entity) throws InsufficientPermissionException {
|
public void assertOperationAllowed(final Operation operation, final JpaAction entity) throws InsufficientPermissionException {
|
||||||
targetAccessController.assertOperationAllowed(operation, entity.getTarget());
|
targetAccessController.assertOperationAllowed(map(operation), entity.getTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
// all CREATE/UPDATE/DELETE action operations are mapped to UPDATE_TARGET permissions / actions
|
||||||
|
private static Operation map(final Operation actionOperation) {
|
||||||
|
return switch (actionOperation) {
|
||||||
|
case READ -> Operation.READ;
|
||||||
|
case CREATE, UPDATE, DELETE -> Operation.UPDATE;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import jakarta.persistence.criteria.JoinType;
|
import jakarta.persistence.criteria.JoinType;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.function.TriConsumer;
|
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
@@ -29,6 +28,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
|||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement.MaxAssignmentsExceededInfo;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
@@ -67,7 +67,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
private final BooleanSupplier multiAssignmentsConfig;
|
private final BooleanSupplier multiAssignmentsConfig;
|
||||||
private final BooleanSupplier confirmationFlowConfig;
|
private final BooleanSupplier confirmationFlowConfig;
|
||||||
private final RepositoryProperties repositoryProperties;
|
private final RepositoryProperties repositoryProperties;
|
||||||
private final TriConsumer<Long, Long, AssignmentQuotaExceededException> maxAssignmentExceededHandler;
|
private final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler;
|
||||||
|
|
||||||
@SuppressWarnings("java:S107")
|
@SuppressWarnings("java:S107")
|
||||||
AbstractDsAssignmentStrategy(
|
AbstractDsAssignmentStrategy(
|
||||||
@@ -76,7 +76,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
|
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
|
||||||
final TriConsumer<Long, Long, AssignmentQuotaExceededException> maxAssignmentExceededHandler) {
|
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
|
||||||
this.targetRepository = targetRepository;
|
this.targetRepository = targetRepository;
|
||||||
this.afterCommit = afterCommit;
|
this.afterCommit = afterCommit;
|
||||||
this.actionRepository = actionRepository;
|
this.actionRepository = actionRepository;
|
||||||
@@ -96,7 +96,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
|
|
||||||
// create the action
|
// create the action
|
||||||
return optTarget.map(target -> {
|
return optTarget.map(target -> {
|
||||||
assertActionsPerTargetQuota(target, 1);
|
assertActionsPerTargetQuota(target);
|
||||||
final JpaAction actionForTarget = new JpaAction();
|
final JpaAction actionForTarget = new JpaAction();
|
||||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||||
@@ -144,7 +144,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
*
|
*
|
||||||
* @param targetsIds to override {@link Action}s
|
* @param targetsIds to override {@link Action}s
|
||||||
*/
|
*/
|
||||||
protected List<Long> overrideObsoleteUpdateActions(final Collection<Long> targetsIds) {
|
protected void overrideObsoleteUpdateActions(final Collection<Long> targetsIds) {
|
||||||
// Figure out if there are potential target/action combinations that
|
// Figure out if there are potential target/action combinations that
|
||||||
// need to be considered for cancellation
|
// need to be considered for cancellation
|
||||||
final List<JpaAction> activeActions = actionRepository.findAll((root, query, cb) -> {
|
final List<JpaAction> activeActions = actionRepository.findAll((root, query, cb) -> {
|
||||||
@@ -157,22 +157,18 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<Long> targetIds = activeActions.stream().map(action -> {
|
activeActions.forEach(action -> {
|
||||||
action.setStatus(Status.CANCELING);
|
action.setStatus(Status.CANCELING);
|
||||||
|
|
||||||
// document that the status has been retrieved
|
// document that the status has been retrieved
|
||||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update"));
|
RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update"));
|
||||||
actionRepository.save(action);
|
actionRepository.save(action);
|
||||||
|
});
|
||||||
return action.getTarget().getId();
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
if (!activeActions.isEmpty()) {
|
if (!activeActions.isEmpty()) {
|
||||||
cancelAssignDistributionSetEvent(Collections.unmodifiableList(activeActions));
|
cancelAssignDistributionSetEvent(Collections.unmodifiableList(activeActions));
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,7 +179,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
*
|
*
|
||||||
* @param targetsIds to override {@link Action}s
|
* @param targetsIds to override {@link Action}s
|
||||||
*/
|
*/
|
||||||
protected List<Long> closeObsoleteUpdateActions(final Collection<Long> targetsIds) {
|
protected void closeObsoleteUpdateActions(final Collection<Long> targetsIds) {
|
||||||
// Figure out if there are potential target/action combinations that
|
// Figure out if there are potential target/action combinations that
|
||||||
// need to be considered for cancellation
|
// need to be considered for cancellation
|
||||||
final List<JpaAction> activeActions = actionRepository.findAll((root, query, cb) -> {
|
final List<JpaAction> activeActions = actionRepository.findAll((root, query, cb) -> {
|
||||||
@@ -195,7 +191,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return activeActions.stream().map(action -> {
|
activeActions.forEach(action -> {
|
||||||
action.setStatus(Status.CANCELED);
|
action.setStatus(Status.CANCELED);
|
||||||
action.setActive(false);
|
action.setActive(false);
|
||||||
|
|
||||||
@@ -203,10 +199,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
|
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
|
||||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "close obsolete action due to new update"));
|
RepositoryConstants.SERVER_MESSAGE_PREFIX + "close obsolete action due to new update"));
|
||||||
actionRepository.save(action);
|
actionRepository.save(action);
|
||||||
|
});
|
||||||
return action.getTarget().getId();
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -237,10 +230,6 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
*/
|
*/
|
||||||
abstract List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long distributionSetId);
|
abstract List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long distributionSetId);
|
||||||
|
|
||||||
/**
|
|
||||||
* @param set
|
|
||||||
* @param targets
|
|
||||||
*/
|
|
||||||
abstract void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets);
|
abstract void sendTargetUpdatedEvents(final DistributionSet set, final List<JpaTarget> targets);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,9 +249,8 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
* such actions existed.
|
* such actions existed.
|
||||||
*
|
*
|
||||||
* @param targetIds to cancel actions for
|
* @param targetIds to cancel actions for
|
||||||
* @return {@link Set} of {@link Target#getId()}s
|
|
||||||
*/
|
*/
|
||||||
abstract Set<Long> cancelActiveActions(List<List<Long>> targetIds);
|
abstract void cancelActiveActions(List<List<Long>> targetIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels actions that can be canceled (i.e.
|
* Cancels actions that can be canceled (i.e.
|
||||||
@@ -274,8 +262,6 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
*/
|
*/
|
||||||
abstract void closeActiveActions(List<List<Long>> targetIds);
|
abstract void closeActiveActions(List<List<Long>> targetIds);
|
||||||
|
|
||||||
abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult);
|
|
||||||
|
|
||||||
abstract void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults);
|
abstract void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults);
|
||||||
|
|
||||||
private static String getActionMessage(final Action action) {
|
private static String getActionMessage(final Action action) {
|
||||||
@@ -297,12 +283,12 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
.publishEvent(new CancelTargetAssignmentEvent(tenant, actions)));
|
.publishEvent(new CancelTargetAssignmentEvent(tenant, actions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertActionsPerTargetQuota(final Target target, final long requested) {
|
private void assertActionsPerTargetQuota(final Target target) {
|
||||||
final int quota = quotaManagement.getMaxActionsPerTarget();
|
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||||
try {
|
try {
|
||||||
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class, actionRepository::countByTargetId);
|
QuotaHelper.assertAssignmentQuota(target.getId(), 1, quota, Action.class, Target.class, actionRepository::countByTargetId);
|
||||||
} catch (AssignmentQuotaExceededException ex) {
|
} catch (AssignmentQuotaExceededException e) {
|
||||||
maxAssignmentExceededHandler.accept(target.getId(), requested, ex);
|
maxAssignmentExceededHandler.accept(new MaxAssignmentsExceededInfo(target.getId(), 1, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -37,8 +38,6 @@ import jakarta.persistence.criteria.Root;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.collections4.ListUtils;
|
import org.apache.commons.collections4.ListUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
|
||||||
import org.apache.commons.lang3.function.TriConsumer;
|
|
||||||
import org.eclipse.hawkbit.repository.ActionFields;
|
import org.eclipse.hawkbit.repository.ActionFields;
|
||||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
@@ -162,6 +161,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
this.targetRepository = targetRepository;
|
this.targetRepository = targetRepository;
|
||||||
this.auditorProvider = auditorProvider;
|
this.auditorProvider = auditorProvider;
|
||||||
this.txManager = txManager;
|
this.txManager = txManager;
|
||||||
|
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentsExceededHandler = maxAssignmentsExceededInfo ->
|
||||||
|
handleMaxAssignmentsExceeded(
|
||||||
|
maxAssignmentsExceededInfo.targetId,
|
||||||
|
maxAssignmentsExceededInfo.requested,
|
||||||
|
maxAssignmentsExceededInfo.quotaExceededException);
|
||||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
||||||
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
|
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
|
||||||
maxAssignmentsExceededHandler);
|
maxAssignmentsExceededHandler);
|
||||||
@@ -409,14 +413,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void deleteAction(final long actionId) {
|
public void deleteAction(final long actionId) {
|
||||||
log.info("Deleting action {}", actionId);
|
log.info("Deleting action {}", actionId);
|
||||||
actionRepository.getAccessController().ifPresent(accessController -> {
|
|
||||||
// check update access
|
|
||||||
if (ObjectUtils.isEmpty(actionRepository.findAll(accessController.appendAccessRules(
|
|
||||||
AccessController.Operation.UPDATE, ((root, q, cb) -> cb.equal(root.get(AbstractJpaBaseEntity_.id), actionId)))))) {
|
|
||||||
// could be also InsufficientPermissionException but for security reasons we do not reveal that the entity exists
|
|
||||||
throw new EntityNotFoundException(Action.class, actionId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
actionRepository.deleteById(actionId);
|
actionRepository.deleteById(actionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,56 +420,41 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void deleteActionsByRsql(final String rsql) {
|
public void deleteActionsByRsql(final String rsql) {
|
||||||
log.info("Deleting actions matching rsql {}", rsql);
|
log.info("Deleting actions matching rsql {}", rsql);
|
||||||
final Specification<JpaAction> specification = QLSupport.getInstance().buildSpec(rsql, ActionFields.class);
|
actionRepository.delete(QLSupport.getInstance().buildSpec(rsql, ActionFields.class));
|
||||||
actionRepository.delete(
|
|
||||||
actionRepository.getAccessController()
|
|
||||||
.map(accessController -> accessController.appendAccessRules(AccessController.Operation.UPDATE, specification))
|
|
||||||
.orElse(specification));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteActionsByIds(final List<Long> actionIds) {
|
public void deleteActionsByIds(final List<Long> actionIds) {
|
||||||
log.info("Deleting actions with ids {}", actionIds);
|
log.info("Deleting actions with ids {}", actionIds);
|
||||||
actionRepository.getAccessController().ifPresent(accessController ->
|
actionRepository.deleteAllById(actionIds);
|
||||||
actionRepository.findAll(
|
|
||||||
accessController.appendAccessRules(AccessController.Operation.UPDATE, ActionSpecifications.byIdIn(actionIds))));
|
|
||||||
actionRepository.deleteByIdIn(actionIds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteTargetActionsByIds(final String target, final List<Long> actionsIds) {
|
public void deleteTargetActionsByIds(final String controllerId, final List<Long> actionsIds) {
|
||||||
log.info("Delete actions for target {} with action ids {}", target, actionsIds);
|
log.info("Delete actions for target {} with action ids {}", controllerId, actionsIds);
|
||||||
targetRepository.getAccessController()
|
actionRepository.delete(ActionSpecifications.byControllerIdAndIdIn(controllerId, actionsIds));
|
||||||
.ifPresent(accessController ->
|
|
||||||
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, targetRepository.getByControllerId(target)));
|
|
||||||
actionRepository.delete(ActionSpecifications.byControllerIdAndIdIn(target, actionsIds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteOldestTargetActions(final String target, final int keepLast) {
|
public void deleteOldestTargetActions(final String controllerId, final int keepLast) {
|
||||||
final JpaTarget jpaTarget = targetRepository.findByControllerId(target)
|
final JpaTarget target = targetRepository.findByControllerId(controllerId).orElseThrow(EntityNotFoundException::new);
|
||||||
.orElseThrow(EntityNotFoundException::new);
|
// check access to target since deletion will be executed via native query
|
||||||
targetRepository.getAccessController().ifPresent(accessController ->
|
targetRepository.getAccessController().ifPresent(accessController ->
|
||||||
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, jpaTarget));
|
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||||
|
final long targetActions = actionRepository.countByTargetId(target.getId());
|
||||||
final long targetActions = actionRepository.countByTargetId(jpaTarget.getId());
|
|
||||||
|
|
||||||
long oldestToDelete;
|
|
||||||
if (targetActions > keepLast) {
|
if (targetActions > keepLast) {
|
||||||
oldestToDelete = targetActions - keepLast;
|
final long oldestToDelete = targetActions - keepLast;
|
||||||
} else {
|
deleteOldestTargetActions(target.getId(), (int) oldestToDelete);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
deleteOldestTargetActions(jpaTarget.getId(), (int) oldestToDelete);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||||
@Retryable(retryFor = {
|
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
|
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
|
||||||
if (!isMultiAssignmentsEnabled()) {
|
if (!isMultiAssignmentsEnabled()) {
|
||||||
targetRepository.getAccessController().ifPresent(v -> {
|
targetRepository.getAccessController().ifPresent(v -> {
|
||||||
@@ -598,12 +579,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public record MaxAssignmentsExceededInfo(long targetId, long requested, AssignmentQuotaExceededException quotaExceededException) {}
|
||||||
public void handleMaxAssignmentsExceeded(final Long targetId, final Long requested, final AssignmentQuotaExceededException ex) {
|
|
||||||
maxAssignmentsExceededHandler.accept(targetId, requested, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final TriConsumer<Long, Long, AssignmentQuotaExceededException> maxAssignmentsExceededHandler = (targetId, requested, quotaExceededException) -> {
|
@Override
|
||||||
|
public void handleMaxAssignmentsExceeded(
|
||||||
|
final Long targetId, final Long requested, final AssignmentQuotaExceededException quotaExceededException) {
|
||||||
int actionsPurgePercentage = getActionsPurgePercentage();
|
int actionsPurgePercentage = getActionsPurgePercentage();
|
||||||
int quota = quotaManagement.getMaxActionsPerTarget();
|
int quota = quotaManagement.getMaxActionsPerTarget();
|
||||||
if (actionsPurgePercentage > 0 && actionsPurgePercentage < 100) {
|
if (actionsPurgePercentage > 0 && actionsPurgePercentage < 100) {
|
||||||
@@ -622,7 +602,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
} else {
|
} else {
|
||||||
throw quotaExceededException;
|
throw quotaExceededException;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the first n target actions of a target
|
* Deletes the first n target actions of a target
|
||||||
@@ -630,7 +612,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
* @param targetId - target id
|
* @param targetId - target id
|
||||||
* @param oldestToDelete - number of oldest actions to be deleted
|
* @param oldestToDelete - number of oldest actions to be deleted
|
||||||
*/
|
*/
|
||||||
public void deleteOldestTargetActions(long targetId, int oldestToDelete) {
|
private void deleteOldestTargetActions(long targetId, int oldestToDelete) {
|
||||||
// Workaround for the case where JPQL or Criteria API do not support LIMIT
|
// Workaround for the case where JPQL or Criteria API do not support LIMIT
|
||||||
log.info("Deleting last {} actions of target {}", oldestToDelete, targetId);
|
log.info("Deleting last {} actions of target {}", oldestToDelete, targetId);
|
||||||
final String SQL = "DELETE FROM sp_action WHERE id IN(" +
|
final String SQL = "DELETE FROM sp_action WHERE id IN(" +
|
||||||
@@ -641,7 +623,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
" LIMIT " + oldestToDelete
|
" LIMIT " + oldestToDelete
|
||||||
+ ") AS sub"
|
+ ") AS sub"
|
||||||
+ ")";
|
+ ")";
|
||||||
Query query = entityManager.createNativeQuery(SQL);
|
final Query query = entityManager.createNativeQuery(SQL);
|
||||||
query.setParameter("target", targetId);
|
query.setParameter("target", targetId);
|
||||||
query.executeUpdate();
|
query.executeUpdate();
|
||||||
}
|
}
|
||||||
@@ -912,7 +894,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
} catch (final AssignmentQuotaExceededException ex) {
|
} catch (final AssignmentQuotaExceededException ex) {
|
||||||
targetRepository.findByControllerId(controllerId).ifPresentOrElse(
|
targetRepository.findByControllerId(controllerId).ifPresentOrElse(
|
||||||
// assume requested are always smaller than int size
|
// assume requested are always smaller than int size
|
||||||
target -> maxAssignmentsExceededHandler.accept(target.getId(), requested, ex),
|
target -> handleMaxAssignmentsExceeded(target.getId(), requested, ex),
|
||||||
() -> {
|
() -> {
|
||||||
throw new EntityNotFoundException(Target.class, controllerId);
|
throw new EntityNotFoundException(Target.class, controllerId);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,25 +9,21 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.management;
|
package org.eclipse.hawkbit.repository.jpa.management;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.apache.commons.collections4.ListUtils;
|
import org.apache.commons.collections4.ListUtils;
|
||||||
import org.apache.commons.lang3.function.TriConsumer;
|
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
|
||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement.MaxAssignmentsExceededInfo;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
@@ -53,7 +49,7 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
|
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
|
||||||
final TriConsumer<Long, Long, AssignmentQuotaExceededException> maxAssignmentExceededHandler) {
|
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
|
||||||
super(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
super(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
||||||
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
|
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
|
||||||
}
|
}
|
||||||
@@ -128,8 +124,7 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
|
public void cancelActiveActions(final List<List<Long>> targetIds) {
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -137,14 +132,8 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
// Not supported by offline case
|
// Not supported by offline case
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) {
|
|
||||||
// no need to send deployment events in the offline case
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
||||||
// no need to send deployment events in the offline case
|
// no need to send deployment events in the offline case
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9,30 +9,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.management;
|
package org.eclipse.hawkbit.repository.jpa.management;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.collections4.ListUtils;
|
import org.apache.commons.collections4.ListUtils;
|
||||||
import org.apache.commons.lang3.function.TriConsumer;
|
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
|
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
|
||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement.MaxAssignmentsExceededInfo;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
@@ -59,7 +54,7 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
|
||||||
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
|
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
|
||||||
final TriConsumer<Long, Long, AssignmentQuotaExceededException> maxAssignmentExceededHandler) {
|
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
|
||||||
super(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
super(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
||||||
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
|
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
|
||||||
}
|
}
|
||||||
@@ -129,7 +124,8 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
|
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds,
|
||||||
|
final String currentUser) {
|
||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
targetIds.forEach(targetIdsChunk -> {
|
targetIds.forEach(targetIdsChunk -> {
|
||||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||||
@@ -154,8 +150,8 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
|
public void cancelActiveActions(final List<List<Long>> targetIds) {
|
||||||
return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream).collect(Collectors.toSet());
|
targetIds.forEach(this::overrideObsoleteUpdateActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,15 +159,6 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
targetIds.forEach(this::closeObsoleteUpdateActions);
|
targetIds.forEach(this::closeObsoleteUpdateActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) {
|
|
||||||
if (isMultiAssignmentsEnabled()) {
|
|
||||||
sendDeploymentEvents(Collections.singletonList(assignmentResult));
|
|
||||||
} else {
|
|
||||||
sendDistributionSetAssignedEvent(assignmentResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
public void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
|
||||||
if (isMultiAssignmentsEnabled()) {
|
if (isMultiAssignmentsEnabled()) {
|
||||||
@@ -190,7 +177,7 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sendCancellationMessages(final List<JpaAction> actions, final String tenant) {
|
void sendCancellationMessages(final List<JpaAction> actions, final String tenant) {
|
||||||
if(isMultiAssignmentsEnabled()) {
|
if (isMultiAssignmentsEnabled()) {
|
||||||
sendMultiActionCancelEvent(tenant, Collections.unmodifiableList(actions));
|
sendMultiActionCancelEvent(tenant, Collections.unmodifiableList(actions));
|
||||||
} else {
|
} else {
|
||||||
actions.forEach(this::cancelAssignDistributionSetEvent);
|
actions.forEach(this::cancelAssignDistributionSetEvent);
|
||||||
@@ -224,12 +211,10 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
sendMultiActionAssignEvent(tenant, filteredActions);
|
sendMultiActionAssignEvent(tenant, filteredActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DistributionSetAssignmentResult sendDistributionSetAssignedEvent(
|
private void sendDistributionSetAssignedEvent(final DistributionSetAssignmentResult assignmentResult) {
|
||||||
final DistributionSetAssignmentResult assignmentResult) {
|
|
||||||
final List<Action> filteredActions = filterCancellations(assignmentResult.getAssignedEntity()).toList();
|
final List<Action> filteredActions = filterCancellations(assignmentResult.getAssignedEntity()).toList();
|
||||||
final DistributionSet set = assignmentResult.getDistributionSet();
|
final DistributionSet set = assignmentResult.getDistributionSet();
|
||||||
sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions);
|
sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions);
|
||||||
return assignmentResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendTargetAssignDistributionSetEvent(final String tenant, final long distributionSetId,
|
private void sendTargetAssignDistributionSetEvent(final String tenant, final long distributionSetId,
|
||||||
|
|||||||
@@ -320,15 +320,4 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Query("UPDATE JpaAction a SET a.externalRef = :externalRef WHERE a.id = :actionId")
|
@Query("UPDATE JpaAction a SET a.externalRef = :externalRef WHERE a.id = :actionId")
|
||||||
void updateExternalRef(@Param("actionId") Long actionId, @Param("externalRef") String externalRef);
|
void updateExternalRef(@Param("actionId") Long actionId, @Param("externalRef") String externalRef);
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes all actions with the given IDs.
|
|
||||||
*
|
|
||||||
* @param actionIDs the IDs of the actions to be deleted.
|
|
||||||
*/
|
|
||||||
@Modifying
|
|
||||||
@Transactional
|
|
||||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
|
||||||
@Query("DELETE FROM JpaAction a WHERE a.id IN ?1")
|
|
||||||
void deleteByIdIn(Collection<Long> actionIDs);
|
|
||||||
}
|
}
|
||||||
@@ -43,11 +43,6 @@ public final class ActionSpecifications {
|
|||||||
cb.equal(root.get(JpaAction_.active), true));
|
cb.equal(root.get(JpaAction_.active), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Specification<JpaAction> byIdIn(final List<Long> actionIds) {
|
|
||||||
return ((root, query, cb) ->
|
|
||||||
root.get(AbstractJpaBaseEntity_.id).in(actionIds));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Specification<JpaAction> byTargetControllerId(final String controllerId) {
|
public static Specification<JpaAction> byTargetControllerId(final String controllerId) {
|
||||||
return (root, query, cb) -> cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId);
|
return (root, query, cb) -> cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,12 +53,12 @@ public final class TargetSpecifications {
|
|||||||
/**
|
/**
|
||||||
* {@link Specification} for retrieving {@link Target}s including {@link TargetTag}s.
|
* {@link Specification} for retrieving {@link Target}s including {@link TargetTag}s.
|
||||||
*
|
*
|
||||||
* @param controllerIDs to search for
|
* @param controllerIds to search for
|
||||||
* @return the {@link Target} {@link Specification}
|
* @return the {@link Target} {@link Specification}
|
||||||
*/
|
*/
|
||||||
public static Specification<JpaTarget> byControllerIdWithTagsInJoin(final Collection<String> controllerIDs) {
|
public static Specification<JpaTarget> byControllerIdWithTagsInJoin(final Collection<String> controllerIds) {
|
||||||
return (targetRoot, query, cb) -> {
|
return (targetRoot, query, cb) -> {
|
||||||
final Predicate predicate = targetRoot.get(JpaTarget_.controllerId).in(controllerIDs);
|
final Predicate predicate = targetRoot.get(JpaTarget_.controllerId).in(controllerIds);
|
||||||
targetRoot.fetch(JpaTarget_.tags, JoinType.LEFT);
|
targetRoot.fetch(JpaTarget_.tags, JoinType.LEFT);
|
||||||
query.distinct(true);
|
query.distinct(true);
|
||||||
return predicate;
|
return predicate;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa.acm;
|
package org.eclipse.hawkbit.repository.jpa.acm;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET;
|
||||||
@@ -56,17 +58,17 @@ class DeploymentManagementTest extends AbstractAccessControllerManagementTest {
|
|||||||
void verifyActionVisibility() {
|
void verifyActionVisibility() {
|
||||||
final String controllerId = target1Type1.getControllerId();
|
final String controllerId = target1Type1.getControllerId();
|
||||||
verify(
|
verify(
|
||||||
assignedId -> {
|
actionId -> {
|
||||||
assertThat(deploymentManagement.findActionsAll(UNPAGED)).isEmpty();
|
assertThat(deploymentManagement.findActionsAll(UNPAGED)).isEmpty();
|
||||||
assertThat(deploymentManagement.findAction(assignedId)).isEmpty();
|
assertThat(deploymentManagement.findAction(actionId)).isEmpty();
|
||||||
assertThatThrownBy(() -> deploymentManagement.findActionWithDetails(assignedId))
|
assertThatThrownBy(() -> deploymentManagement.findActionWithDetails(actionId))
|
||||||
.isInstanceOf(InsufficientPermissionException.class);
|
.isInstanceOf(InsufficientPermissionException.class);
|
||||||
assertThat(deploymentManagement.findActions("id==*", UNPAGED)).isEmpty();
|
assertThat(deploymentManagement.findActions("id==*", UNPAGED)).isEmpty();
|
||||||
assertThatThrownBy(() -> deploymentManagement.findActionsByTarget(controllerId, UNPAGED))
|
assertThatThrownBy(() -> deploymentManagement.findActionsByTarget(controllerId, UNPAGED))
|
||||||
.isInstanceOf(EntityNotFoundException.class);
|
.isInstanceOf(EntityNotFoundException.class);
|
||||||
assertThatThrownBy(() -> deploymentManagement.findActionsByTarget("id==*", controllerId, UNPAGED))
|
assertThatThrownBy(() -> deploymentManagement.findActionsByTarget("id==*", controllerId, UNPAGED))
|
||||||
.isInstanceOf(EntityNotFoundException.class);
|
.isInstanceOf(EntityNotFoundException.class);
|
||||||
assertThatThrownBy(() -> deploymentManagement.findActionStatusByAction(assignedId, UNPAGED))
|
assertThatThrownBy(() -> deploymentManagement.findActionStatusByAction(actionId, UNPAGED))
|
||||||
.isInstanceOf(EntityNotFoundException.class);
|
.isInstanceOf(EntityNotFoundException.class);
|
||||||
assertThatThrownBy(() -> deploymentManagement.findActiveActionsByTarget(controllerId, UNPAGED))
|
assertThatThrownBy(() -> deploymentManagement.findActiveActionsByTarget(controllerId, UNPAGED))
|
||||||
.isInstanceOf(EntityNotFoundException.class);
|
.isInstanceOf(EntityNotFoundException.class);
|
||||||
@@ -76,16 +78,16 @@ class DeploymentManagementTest extends AbstractAccessControllerManagementTest {
|
|||||||
assertThatThrownBy(() -> deploymentManagement.hasPendingCancellations(targetId)).isInstanceOf(
|
assertThatThrownBy(() -> deploymentManagement.hasPendingCancellations(targetId)).isInstanceOf(
|
||||||
EntityNotFoundException.class);
|
EntityNotFoundException.class);
|
||||||
},
|
},
|
||||||
assignedId -> {
|
actionId -> {
|
||||||
assertThat(deploymentManagement.findActionsAll(UNPAGED)).hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
assertThat(deploymentManagement.findActionsAll(UNPAGED)).hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
||||||
assertThat(deploymentManagement.findAction(assignedId)).hasValueSatisfying(this::assertActionOfTarget1Type1);
|
assertThat(deploymentManagement.findAction(actionId)).hasValueSatisfying(this::assertActionOfTarget1Type1);
|
||||||
assertThat(deploymentManagement.findActionWithDetails(assignedId)).hasValueSatisfying(this::assertActionOfTarget1Type1);
|
assertThat(deploymentManagement.findActionWithDetails(actionId)).hasValueSatisfying(this::assertActionOfTarget1Type1);
|
||||||
assertThat(deploymentManagement.findActions("id==*", UNPAGED)).hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
assertThat(deploymentManagement.findActions("id==*", UNPAGED)).hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
||||||
assertThat(deploymentManagement.findActionsByTarget(controllerId, UNPAGED)).hasSize(1)
|
assertThat(deploymentManagement.findActionsByTarget(controllerId, UNPAGED)).hasSize(1)
|
||||||
.allMatch(this::isActionOfTarget1Type1);
|
.allMatch(this::isActionOfTarget1Type1);
|
||||||
assertThat(deploymentManagement.findActionsByTarget("id==*", controllerId, UNPAGED))
|
assertThat(deploymentManagement.findActionsByTarget("id==*", controllerId, UNPAGED))
|
||||||
.hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
.hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
||||||
assertThat(deploymentManagement.findActionStatusByAction(assignedId, UNPAGED))
|
assertThat(deploymentManagement.findActionStatusByAction(actionId, UNPAGED))
|
||||||
.hasSize(1).allMatch(actionStatus -> actionStatus.getStatus().equals(Action.Status.RUNNING));
|
.hasSize(1).allMatch(actionStatus -> actionStatus.getStatus().equals(Action.Status.RUNNING));
|
||||||
assertThat(deploymentManagement.findActiveActionsByTarget(controllerId, UNPAGED))
|
assertThat(deploymentManagement.findActiveActionsByTarget(controllerId, UNPAGED))
|
||||||
.hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
.hasSize(1).allMatch(this::isActionOfTarget1Type1);
|
||||||
@@ -96,24 +98,83 @@ class DeploymentManagementTest extends AbstractAccessControllerManagementTest {
|
|||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifyDeleteActionById() {
|
||||||
|
verify(
|
||||||
|
null,
|
||||||
|
actionId -> assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||||
|
.isThrownBy(() -> deploymentManagement.deleteAction(actionId)),
|
||||||
|
actionId -> assertThatNoException().isThrownBy(() -> deploymentManagement.deleteAction(actionId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifyDeleteActionsById() {
|
||||||
|
verify(
|
||||||
|
null,
|
||||||
|
actionId -> {
|
||||||
|
final List<Long> actionIds = List.of(actionId);
|
||||||
|
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||||
|
.isThrownBy(() -> deploymentManagement.deleteActionsByIds(actionIds));
|
||||||
|
},
|
||||||
|
actionId -> assertThatNoException().isThrownBy(() -> deploymentManagement.deleteActionsByIds(List.of(actionId))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifyDeleteActionByRSQL() {
|
||||||
|
verify(
|
||||||
|
null,
|
||||||
|
actionId -> {
|
||||||
|
assertThatNoException().isThrownBy(() -> deploymentManagement.deleteActionsByRsql("id==" + actionId));
|
||||||
|
// no exception but the action shall not be deleted
|
||||||
|
assertThat(deploymentManagement.findAction(actionId)).isPresent();
|
||||||
|
},
|
||||||
|
actionId -> {
|
||||||
|
assertThatNoException().isThrownBy(() -> deploymentManagement.deleteActionsByRsql("id==" + actionId));
|
||||||
|
// the action shall be deleted
|
||||||
|
assertThat(deploymentManagement.findAction(actionId)).isEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifyDeleteTargetActionsById() {
|
||||||
|
verify(
|
||||||
|
null,
|
||||||
|
actionId -> {
|
||||||
|
final String controllerId = deploymentManagement.findAction(actionId)
|
||||||
|
.map(Action::getTarget).map(Target::getControllerId).orElseThrow();
|
||||||
|
final List<Long> actionIds = List.of(actionId, -1L);
|
||||||
|
assertThatNoException().isThrownBy(() -> deploymentManagement.deleteTargetActionsByIds(controllerId, actionIds));
|
||||||
|
// no exception but the action shall not be deleted
|
||||||
|
assertThat(deploymentManagement.findAction(actionId)).isPresent();
|
||||||
|
},
|
||||||
|
actionId -> {
|
||||||
|
final String controllerId = deploymentManagement.findAction(actionId)
|
||||||
|
.map(Action::getTarget).map(Target::getControllerId).orElseThrow();
|
||||||
|
assertThatNoException().isThrownBy(
|
||||||
|
() -> deploymentManagement.deleteTargetActionsByIds(controllerId, List.of(actionId, -1L)));
|
||||||
|
// the action shall be deleted
|
||||||
|
assertThat(deploymentManagement.findAction(actionId)).isEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyCancellation() {
|
void verifyCancellation() {
|
||||||
verify(
|
verify(
|
||||||
assignedId -> assertThatThrownBy(() -> deploymentManagement.cancelAction(assignedId))
|
actionId -> assertThatThrownBy(() -> deploymentManagement.cancelAction(actionId))
|
||||||
.isInstanceOf(EntityNotFoundException.class),
|
.isInstanceOf(EntityNotFoundException.class),
|
||||||
assignedId -> assertThatThrownBy(() -> deploymentManagement.cancelAction(assignedId))
|
actionId -> assertThatThrownBy(() -> deploymentManagement.cancelAction(actionId))
|
||||||
.isInstanceOf(InsufficientPermissionException.class),
|
.isInstanceOf(InsufficientPermissionException.class),
|
||||||
assignedId -> assertThat(deploymentManagement.cancelAction(assignedId).getId()).isEqualTo(assignedId));
|
actionId -> assertThat(deploymentManagement.cancelAction(actionId).getId()).isEqualTo(actionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyCancellationByDistributionSetId() {
|
void verifyCancellationByDistributionSetId() {
|
||||||
verify(
|
verify(
|
||||||
assignedId -> {
|
actionId -> {
|
||||||
deploymentManagement.cancelActionsForDistributionSet(ActionCancellationType.FORCE, ds1Type1);
|
deploymentManagement.cancelActionsForDistributionSet(ActionCancellationType.FORCE, ds1Type1);
|
||||||
assertThat(deploymentManagement.findAction(assignedId)).isEmpty();
|
assertThat(deploymentManagement.findAction(actionId)).isEmpty();
|
||||||
},
|
},
|
||||||
assignedId -> assertThat(deploymentManagement.findAction(assignedId))
|
actionId -> assertThat(deploymentManagement.findAction(actionId))
|
||||||
.hasValueSatisfying(action -> assertThat(action.getStatus()).isEqualTo(Action.Status.RUNNING)),
|
.hasValueSatisfying(action -> assertThat(action.getStatus()).isEqualTo(Action.Status.RUNNING)),
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
@@ -121,37 +182,37 @@ class DeploymentManagementTest extends AbstractAccessControllerManagementTest {
|
|||||||
@Test
|
@Test
|
||||||
void verifyForceActionIsNotAllowed() {
|
void verifyForceActionIsNotAllowed() {
|
||||||
verify(
|
verify(
|
||||||
assignedId -> assertThatThrownBy(() -> deploymentManagement.forceTargetAction(assignedId))
|
actionId -> assertThatThrownBy(() -> deploymentManagement.forceTargetAction(actionId))
|
||||||
.isInstanceOf(EntityNotFoundException.class),
|
.isInstanceOf(EntityNotFoundException.class),
|
||||||
assignedId -> assertThatThrownBy(() -> deploymentManagement.forceTargetAction(assignedId))
|
actionId -> assertThatThrownBy(() -> deploymentManagement.forceTargetAction(actionId))
|
||||||
.isInstanceOf(InsufficientPermissionException.class),
|
.isInstanceOf(InsufficientPermissionException.class),
|
||||||
assignedId -> assertThat(deploymentManagement.forceTargetAction(assignedId).getActionType())
|
actionId -> assertThat(deploymentManagement.forceTargetAction(actionId).getActionType())
|
||||||
.isEqualTo(Action.ActionType.FORCED));
|
.isEqualTo(Action.ActionType.FORCED));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verify(final Consumer<Long> noRead, final Consumer<Long> noUpdate, final Consumer<Long> readAndUpdate) {
|
private void verify(final Consumer<Long> noRead, final Consumer<Long> readNoUpdate, final Consumer<Long> readAndUpdate) {
|
||||||
final Long assignedId = systemSecurityContext.runAsSystem(() -> {
|
final Long actionId = systemSecurityContext.runAsSystem(() -> {
|
||||||
final List<Action> assignedEntity = assignDistributionSet(ds1Type1.getId(), target1Type1.getControllerId()).getAssignedEntity();
|
final List<Action> actions = assignDistributionSet(ds1Type1.getId(), target1Type1.getControllerId()).getAssignedEntity();
|
||||||
assertThat(assignedEntity).hasSize(1).allMatch(action -> action.getTarget().getId().equals(target1Type1.getId()));
|
assertThat(actions).hasSize(1).allMatch(action -> action.getTarget().getId().equals(target1Type1.getId()));
|
||||||
return assignedEntity.get(0);
|
return actions.get(0);
|
||||||
}).getId();
|
}).getId();
|
||||||
|
|
||||||
if (noRead != null) {
|
if (noRead != null) {
|
||||||
// no read permission
|
// no read permission
|
||||||
runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType2.getId(), UPDATE_TARGET + "/type.id==" + targetType2.getId()),
|
runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType2.getId(), UPDATE_TARGET + "/type.id==" + targetType2.getId()),
|
||||||
() -> noRead.accept(assignedId));
|
() -> noRead.accept(actionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noUpdate != null) {
|
if (readNoUpdate != null) {
|
||||||
// read but no update permission
|
// read but no update permission
|
||||||
runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType1.getId(), UPDATE_TARGET + "/type.id==" + targetType2.getId()),
|
runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType1.getId(), UPDATE_TARGET + "/type.id==" + targetType2.getId()),
|
||||||
() -> noUpdate.accept(assignedId));
|
() -> readNoUpdate.accept(actionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readAndUpdate != null) {
|
if (readAndUpdate != null) {
|
||||||
// read and update permissions
|
// read and update permissions
|
||||||
runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType1.getId(), UPDATE_TARGET + "/type.id==" + targetType1.getId()),
|
runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType1.getId(), UPDATE_TARGET + "/type.id==" + targetType1.getId()),
|
||||||
() -> readAndUpdate.accept(assignedId));
|
() -> readAndUpdate.accept(actionId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.acm;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_PREFIX;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_PREFIX;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_TARGET;
|
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_PREFIX;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_PREFIX;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_TARGET;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_TARGET;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET;
|
||||||
@@ -23,6 +22,7 @@ import static org.eclipse.hawkbit.im.authentication.SpPermission.SOFTWARE_MODULE
|
|||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SOFTWARE_MODULE_TYPE;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.SOFTWARE_MODULE_TYPE;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.TARGET_TYPE;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.TARGET_TYPE;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_PREFIX;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_PREFIX;
|
||||||
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET;
|
||||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
|
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -92,17 +92,22 @@ class SystemExecutionTest extends AbstractAccessControllerManagementTest {
|
|||||||
() -> verifyAccessController(distributionSetTypeAccessController));
|
() -> verifyAccessController(distributionSetTypeAccessController));
|
||||||
runAs(withAuthorities(READ_TARGET, DELETE_TARGET + "/type.id==1"),
|
runAs(withAuthorities(READ_TARGET, DELETE_TARGET + "/type.id==1"),
|
||||||
() -> verifyAccessController(targetAccessController));
|
() -> verifyAccessController(targetAccessController));
|
||||||
runAs(withAuthorities(UPDATE_PREFIX + TARGET_TYPE + "/id==1"), () -> verifyAccessController(targetTypeAccessController));
|
runAs(withAuthorities(CREATE_PREFIX + TARGET_TYPE + "/id==1"), () -> verifyAccessController(targetTypeAccessController));
|
||||||
runAs(withAuthorities(CREATE_TARGET + "/type.id==1"), () -> verifyAccessController(actionAccessController));
|
// Action Access Controller maps CREATE/UPDATE/DELETE to UPDATE - so only UPDATE (or READ scope) is relevant
|
||||||
|
// and update will be called for every of the mapped - so 3 times
|
||||||
|
runAs(withAuthorities(UPDATE_TARGET + "/type.id==1"), () -> verifyAccessController(actionAccessController, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
private void verifyAccessController(final AccessController<?> accessController) {
|
private void verifyAccessController(final AccessController<?> accessController) {
|
||||||
|
verifyAccessController(accessController, 1);
|
||||||
|
}
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
private void verifyAccessController(final AccessController<?> accessController, final int times) {
|
||||||
final Specification mock = mock(Specification.class);
|
final Specification mock = mock(Specification.class);
|
||||||
for (final Operation operation : Operation.values()) {
|
for (final Operation operation : Operation.values()) {
|
||||||
accessController.appendAccessRules(operation, mock);
|
accessController.appendAccessRules(operation, mock);
|
||||||
}
|
}
|
||||||
verify(mock, times(1)).and(any()); // once for every access controller is scoped only
|
verify(mock, times(times)).and(any()); // once for every access controller is scoped only
|
||||||
|
|
||||||
final Specification mockAsSystem = mock(Specification.class);
|
final Specification mockAsSystem = mock(Specification.class);
|
||||||
for (Operation operation : Operation.values()) {
|
for (Operation operation : Operation.values()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user