diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutExecutor.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutExecutor.java index e027b742f..3f386e013 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutExecutor.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutExecutor.java @@ -22,6 +22,9 @@ import org.springframework.security.access.prepost.PreAuthorize; public interface RolloutExecutor { /** + * This execution should only be triggered by the system as a background job and + * not available via API. + * * Process rollout based on its current {@link Rollout#getStatus()}. * * For {@link RolloutStatus#CREATING} that means creating the @@ -29,9 +32,9 @@ public interface RolloutExecutor { * {@link RolloutStatus#READY}. * * For {@link RolloutStatus#READY} that means switching to - * {@link RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is set - * and time of calling this method is beyond this point in time. This auto - * start mechanism is optional. Call {@link #start(Long)} otherwise. + * {@link RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is set and + * time of calling this method is beyond this point in time. This auto start + * mechanism is optional. Call {@link #start(Long)} otherwise. * * For {@link RolloutStatus#STARTING} that means starting the first * {@link RolloutGroup}s in line and when finished switch to @@ -45,8 +48,7 @@ public interface RolloutExecutor { * rollout was already {@link RolloutStatus#RUNNING} which results in status * change {@link RolloutStatus#DELETED} or hard delete from the persistence * otherwise. - * */ - @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE) void execute(Rollout rollout); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java index 7b1de49f3..ecde4b63d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java @@ -69,9 +69,11 @@ public interface TargetManagement { /** * Counts number of targets with the given distribution set assigned. * - * @param distributionSetId to search for + * @param distributionSetId + * to search for * @return number of found {@link Target}s. - * @throws EntityNotFoundException if distribution set with given ID does not exist + * @throws EntityNotFoundException + * if distribution set with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @@ -81,8 +83,8 @@ public interface TargetManagement { * Count {@link Target}s for all the given filter parameters. * * @param filterParams - * the filters to apply; only filters are enabled that have - * non-null value; filters are AND-gated + * the filters to apply; only filters are enabled that have non-null + * value; filters are AND-gated * * @return the found number {@link Target}s * @@ -95,21 +97,25 @@ public interface TargetManagement { /** * Get the count of targets with the given distribution set id. * - * @param distributionSetId to search for + * @param distributionSetId + * to search for * @return number of found {@link Target}s. - * @throws EntityNotFoundException if distribution set with given ID does not exist + * @throws EntityNotFoundException + * if distribution set with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) long countByInstalledDistributionSet(long distributionSetId); /** - * Checks if there is already a {@link Target} that has the given - * distribution set Id assigned or installed. + * Checks if there is already a {@link Target} that has the given distribution + * set Id assigned or installed. * - * @param distributionSetId to search for + * @param distributionSetId + * to search for * @return true if a {@link Target} exists. - * @throws EntityNotFoundException if distribution set with given ID does not exist + * @throws EntityNotFoundException + * if distribution set with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @@ -126,23 +132,47 @@ public interface TargetManagement { long countByRsql(@NotEmpty String rsqlParam); /** - * Count all targets for given {@link TargetFilterQuery} and that are - * compatible with the passed {@link DistributionSetType}. + * Count {@link TargetFilterQuery}s for given target filter query with UPDATE permission. * * @param rsqlParam * filter definition in RSQL syntax - * @param distributionSetId + * @return the found number of {@link Target}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + long countByRsqlAndUpdatable(@NotEmpty String rsqlParam); + + /** + * Count all targets for given {@link TargetFilterQuery} and that are compatible + * with the passed {@link DistributionSetType}. + * + * @param rsqlParam + * filter definition in RSQL syntax + * @param distributionSetIdTypeId * ID of the {@link DistributionSetType} the targets need to be * compatible with * @return the found number of{@link Target}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - long countByRsqlAndCompatible(@NotEmpty String rsqlParam, @NotNull Long distributionSetId); + long countByRsqlAndCompatible(@NotEmpty String rsqlParam, @NotNull Long distributionSetIdTypeId); /** - * Count all targets with failed actions for specific Rollout - * and that are compatible with the passed {@link DistributionSetType} - * and created after given timestamp + * Count all targets for given {@link TargetFilterQuery} and that are compatible + * with the passed {@link DistributionSetType} and UPDATE permission. + * + * @param rsqlParam + * filter definition in RSQL syntax + * @param distributionSetIdTypeId + * ID of the {@link DistributionSetType} the targets need to be + * compatible with + * @return the found number of{@link Target}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + long countByRsqlAndCompatibleAndUpdatable(@NotEmpty String rsqlParam, @NotNull Long distributionSetIdTypeId); + + /** + * Count all targets with failed actions for specific Rollout and that are + * compatible with the passed {@link DistributionSetType} and created after + * given timestamp * * @param rolloutId * rolloutId of the rollout to be retried. @@ -185,18 +215,17 @@ public interface TargetManagement { * @throws EntityAlreadyExistsException * given target already exists. * @throws ConstraintViolationException - * if fields are not filled as specified. Check - * {@link TargetCreate} for field constraints. + * if fields are not filled as specified. Check {@link TargetCreate} + * for field constraints. * */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) Target create(@NotNull @Valid TargetCreate create); /** - * creates multiple {@link Target}s. If the given {@link Target}s - * already exists in the DB an {@link EntityAlreadyExistsException} is - * thrown. {@link Target}s contain all objects of the parameter targets, - * including duplicates. + * creates multiple {@link Target}s. If the given {@link Target}s already exists + * in the DB an {@link EntityAlreadyExistsException} is thrown. {@link Target}s + * contain all objects of the parameter targets, including duplicates. * * @param creates * to be created. @@ -205,8 +234,8 @@ public interface TargetManagement { * @throws EntityAlreadyExistsException * of one of the given targets already exist. * @throws ConstraintViolationException - * if fields are not filled as specified. Check - * {@link TargetCreate} for field constraints. + * if fields are not filled as specified. Check {@link TargetCreate} + * for field constraints. */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) List create(@NotNull @Valid Collection creates); @@ -253,12 +282,12 @@ public interface TargetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) Slice findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(@NotNull Pageable pageRequest, - long distributionSetId, @NotNull String rsqlParam); + long distributionSetId, @NotNull String rsqlParam); /** - * Counts all targets for all the given parameter {@link TargetFilterQuery} - * and that don't have the specified distribution set in their action - * history and are compatible with the passed {@link DistributionSetType}. + * Counts all targets for all the given parameter {@link TargetFilterQuery} and + * that don't have the specified distribution set in their action history and + * are compatible with the passed {@link DistributionSetType}. * * @param distributionSetId * id of the {@link DistributionSet} @@ -273,9 +302,9 @@ public interface TargetManagement { long countByRsqlAndNonDSAndCompatibleAndUpdatable(long distributionSetId, @NotNull String rsqlParam); /** - * Finds all targets for all the given parameter {@link TargetFilterQuery} - * and that are not assigned to one of the {@link RolloutGroup}s and are - * compatible with the passed {@link DistributionSetType}. + * Finds all targets for all the given parameter {@link TargetFilterQuery} and + * that are not assigned to one of the {@link RolloutGroup}s and are compatible + * with the passed {@link DistributionSetType}. * * @param pageRequest * the pageRequest to enhance the query for paging and sorting @@ -284,8 +313,8 @@ public interface TargetManagement { * @param rsqlParam * filter definition in RSQL syntax * @param distributionSetType - * type of the {@link DistributionSet} the targets must be - * compatible withs + * type of the {@link DistributionSet} the targets must be compatible + * withs * @return a page of the found {@link Target}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @@ -294,9 +323,9 @@ public interface TargetManagement { @NotNull DistributionSetType distributionSetType); /** - * Finds all targets with failed actions for specific Rollout - * and that are not assigned to one of the retried {@link RolloutGroup}s and are - * compatible with the passed {@link DistributionSetType}. + * Finds all targets with failed actions for specific Rollout and that are not + * assigned to one of the retried {@link RolloutGroup}s and are compatible with + * the passed {@link DistributionSetType}. * * @param pageRequest * the pageRequest to enhance the query for paging and sorting @@ -308,12 +337,12 @@ public interface TargetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) Slice findByFailedRolloutAndNotInRolloutGroups(@NotNull Pageable pageRequest, - @NotEmpty Collection groups, @NotNull String rolloutId); + @NotEmpty Collection groups, @NotNull String rolloutId); /** - * Counts all targets for all the given parameter {@link TargetFilterQuery} - * and that are not assigned to one of the {@link RolloutGroup}s and are - * compatible with the passed {@link DistributionSetType}. + * Counts all targets for all the given parameter {@link TargetFilterQuery} and + * that are not assigned to one of the {@link RolloutGroup}s and are compatible + * with the passed {@link DistributionSetType}. * * @param groups * the list of {@link RolloutGroup}s @@ -329,9 +358,9 @@ public interface TargetManagement { @NotNull String rsqlParam, @NotNull DistributionSetType distributionSetType); /** - * Counts all targets with failed actions for specific Rollout - * and that are not assigned to one of the {@link RolloutGroup}s and are - * compatible with the passed {@link DistributionSetType}. + * Counts all targets with failed actions for specific Rollout and that are not + * assigned to one of the {@link RolloutGroup}s and are compatible with the + * passed {@link DistributionSetType}. * * @param groups * the list of {@link RolloutGroup}s @@ -343,8 +372,8 @@ public interface TargetManagement { long countByFailedRolloutAndNotInRolloutGroups(@NotEmpty Collection groups, @NotNull String rolloutId); /** - * Finds all targets of the provided {@link RolloutGroup} that have no - * Action for the RolloutGroup. + * Finds all targets of the provided {@link RolloutGroup} that have no Action + * for the RolloutGroup. * * @param pageRequest * the pageRequest to enhance the query for paging and sorting @@ -376,8 +405,8 @@ public interface TargetManagement { Page findByAssignedDistributionSet(@NotNull Pageable pageReq, long distributionSetId); /** - * Retrieves {@link Target}s by the assigned {@link DistributionSet} - * possible including additional filtering based on the given {@code spec}. + * Retrieves {@link Target}s by the assigned {@link DistributionSet} possible + * including additional filtering based on the given {@code spec}. * * @param pageReq * page parameter @@ -420,14 +449,14 @@ public interface TargetManagement { Optional getByControllerID(@NotEmpty String controllerId); /** - * Filter {@link Target}s for all the given parameters. If all parameters - * except pageable are null, all available {@link Target}s are returned. + * Filter {@link Target}s for all the given parameters. If all parameters except + * pageable are null, all available {@link Target}s are returned. * * @param pageable * page parameters * @param filterParams - * the filters to apply; only filters are enabled that have - * non-null value; filters are AND-gated + * the filters to apply; only filters are enabled that have non-null + * value; filters are AND-gated * * @return the found {@link Target}s * @@ -454,8 +483,8 @@ public interface TargetManagement { Page findByInstalledDistributionSet(@NotNull Pageable pageReq, long distributionSetId); /** - * retrieves {@link Target}s by the installed {@link DistributionSet} - * including additional filtering based on the given {@code spec}. + * retrieves {@link Target}s by the installed {@link DistributionSet} including + * additional filtering based on the given {@code spec}. * * @param pageReq * page parameter @@ -480,8 +509,7 @@ public interface TargetManagement { @NotNull String rsqlParam); /** - * Retrieves the {@link Target} which have a certain - * {@link TargetUpdateStatus}. + * Retrieves the {@link Target} which have a certain {@link TargetUpdateStatus}. * * @param pageable * page parameter @@ -629,9 +657,9 @@ public interface TargetManagement { /** * Initiates {@link TargetType} assignment to given {@link Target}s. If some - * targets in the list have the {@link TargetType} not yet assigned, they - * will get assigned. If all targets are already of that type, there will be - * no un-assignment. + * targets in the list have the {@link TargetType} not yet assigned, they will + * get assigned. If all targets are already of that type, there will be no + * un-assignment. * * @param controllerIds * to set the type to @@ -647,8 +675,8 @@ public interface TargetManagement { TargetTypeAssignmentResult assignType(@NotEmpty Collection controllerIds, @NotNull Long typeId); /** - * Initiates {@link TargetType} un-assignment to given {@link Target}s. The - * type of the targets will be set to {@code null} + * Initiates {@link TargetType} un-assignment to given {@link Target}s. The type + * of the targets will be set to {@code null} * * @param controllerIds * to remove the type from @@ -710,8 +738,8 @@ public interface TargetManagement { * @throws EntityNotFoundException * if given target does not exist * @throws ConstraintViolationException - * if fields are not filled as specified. Check - * {@link TargetUpdate} for field constraints. + * if fields are not filled as specified. Check {@link TargetUpdate} + * for field constraints. */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) Target update(@NotNull @Valid TargetUpdate update); @@ -797,30 +825,29 @@ public interface TargetManagement { boolean existsByControllerId(@NotEmpty String controllerId); /** - * Verify if a target matches a specific target filter query, does not have - * a specific DS already assigned and is compatible with it. + * Verify if a target matches a specific target filter query, does not have a + * specific DS already assigned and is compatible with it. * * @param controllerId * of the {@link org.eclipse.hawkbit.repository.model.Target} to * check * @param distributionSetId * of the - * {@link org.eclipse.hawkbit.repository.model.DistributionSet} - * to consider + * {@link org.eclipse.hawkbit.repository.model.DistributionSet} to + * consider * @param targetFilterQuery * to execute * @return true if it matches */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) - boolean isTargetMatchingQueryAndDSNotAssignedAndCompatible(@NotNull String controllerId, long distributionSetId, - @NotNull String targetFilterQuery); + boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(@NotNull String controllerId, + long distributionSetId, @NotNull String targetFilterQuery); /** * Creates a list of target meta data entries. * * @param controllerId - * {@link Target} controller id the metadata has to be created - * for + * {@link Target} controller id the metadata has to be created for * @param metadata * the meta data entries to create or update * @return the updated or created target metadata entries @@ -829,12 +856,12 @@ public interface TargetManagement { * if given target does not exist * * @throws EntityAlreadyExistsException - * in case one of the metadata entry already exists for the - * specific key + * in case one of the metadata entry already exists for the specific + * key * * @throws AssignmentQuotaExceededException - * if the maximum number of {@link MetaData} entries is exceeded - * for the addressed {@link Target} + * if the maximum number of {@link MetaData} entries is exceeded for + * the addressed {@link Target} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) List createMetaData(@NotEmpty String controllerId, @NotEmpty Collection metadata); @@ -928,15 +955,13 @@ public interface TargetManagement { * Updates a target meta data value if corresponding entry exists. * * @param controllerId - * {@link Target} controller id of the metadata entry to be - * updated + * {@link Target} controller id of the metadata entry to be updated * @param metadata * meta data entry to be updated * @return the updated meta data entry * * @throws EntityNotFoundException - * in case the metadata entry does not exist and cannot be - * updated + * in case the metadata entry does not exist and cannot be updated */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) TargetMetadata updateMetadata(@NotEmpty String controllerId, @NotNull MetaData metadata); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java index 63fffd5f6..a2b6d6548 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaManagementHelper.java @@ -15,7 +15,10 @@ import java.util.Optional; import javax.persistence.EntityManager; +import org.eclipse.hawkbit.repository.jpa.acm.AccessController; import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity; +import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity; +import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository; import org.eclipse.hawkbit.repository.jpa.repository.NoCountSliceRepository; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.springframework.data.domain.Page; @@ -26,6 +29,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -54,6 +58,9 @@ public final class JpaManagementHelper { } public static Specification combineWithAnd(final List> specList) { + if (ObjectUtils.isEmpty(specList)) { + return Specification.where(null); + } return specList.size() == 1 ? specList.get(0) : SpecificationsBuilder.combineWithAnd(specList); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java index 5f6ab6bb3..168527308 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java @@ -520,10 +520,10 @@ public class JpaRolloutExecutor implements RolloutExecutor { final String baseFilter = RolloutHelper.getTargetFilterQuery(rollout); final String groupTargetFilter; - if (StringUtils.isEmpty(group.getTargetFilterQuery())) { - groupTargetFilter = baseFilter; - } else { + if (StringUtils.hasText(group.getTargetFilterQuery())) { groupTargetFilter = baseFilter + ";" + group.getTargetFilterQuery(); + } else { + groupTargetFilter = baseFilter; } final List readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout.getRolloutGroups(), diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index 06a9e01ba..cd5ce9836 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -101,6 +101,7 @@ import org.eclipse.hawkbit.repository.jpa.management.JpaTargetTagManagement; import org.eclipse.hawkbit.repository.jpa.management.JpaTargetTypeManagement; import org.eclipse.hawkbit.repository.jpa.management.JpaTenantConfigurationManagement; import org.eclipse.hawkbit.repository.jpa.management.JpaTenantStatsManagement; +import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; @@ -1038,13 +1039,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { final DistributionSetManagement distributionSetManagement, final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement, final TargetFilterQueryManagement targetFilterQueryManagement, final ActionRepository actionRepository, - final PlatformTransactionManager txManager, - final RepositoryProperties repositoryProperties, final TenantAware tenantAware, - final LockRegistry lockRegistry) { + final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties, + final TenantAware tenantAware, final LockRegistry lockRegistry, + final SystemSecurityContext systemSecurityContext) { return new JpaDistributionSetInvalidationManagement(distributionSetManagement, rolloutManagement, - deploymentManagement, targetFilterQueryManagement, actionRepository, - txManager, repositoryProperties, tenantAware, - lockRegistry); + deploymentManagement, targetFilterQueryManagement, actionRepository, txManager, repositoryProperties, + tenantAware, lockRegistry, systemSecurityContext); } /** @@ -1080,10 +1080,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { @Autowired(required = false) final AccessController distributionSetTypeAccessController, @Autowired(required = false) final AccessController distributionSetAccessController, @Autowired(required = false) final AccessController targetTypeAccessControlManager, - @Autowired(required = false) final AccessController targetAccessControlManager) { + @Autowired(required = false) final AccessController targetAccessControlManager, + @Autowired(required = false) final AccessController actionAccessController) { return new BeanPostProcessor() { @Override - public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException { + public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) + throws BeansException { if (bean instanceof LocalArtifactRepository repo) { return repo.withACM(artifactAccessController); } else if (bean instanceof SoftwareModuleTypeRepository repo) { @@ -1098,6 +1100,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { return repo.withACM(targetTypeAccessControlManager); } else if (bean instanceof TargetRepository repo) { return repo.withACM(targetAccessControlManager); + } else if (bean instanceof ActionRepository repo) { + return repo.withACM(actionAccessController); } return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java index 0293666d6..7b3c0c746 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java @@ -119,7 +119,7 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor { LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} for device {} started", getContextAware().getCurrentTenant(), targetFilterQuery.getId(), controllerId); try { - final boolean controllerIdMatches = targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible( + final boolean controllerIdMatches = targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable( controllerId, targetFilterQuery.getAutoAssignDistributionSet().getId(), targetFilterQuery.getQuery()); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java index 7aa1c6f47..0cc67fd99 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java @@ -308,7 +308,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont throwExceptionIfTargetDoesNotExist(controllerId); throwExceptionIfSoftwareModuleDoesNotExist(moduleId); - // TODO AC - REVIEW // it used to perform 3-table join query // @Query("Select a from JpaAction a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul.id = :module order by a.id desc") // final List actions = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java index 5fcc2e5bd..eca4eb209 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java @@ -158,14 +158,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl private final RetryTemplate retryTemplate; public JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository, - final DistributionSetManagement distributionSetManagement, - final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository, - final ActionStatusRepository actionStatusRepository, final AuditorAware auditorProvider, - final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit, - final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager, - final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement, - final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final Database database, - final RepositoryProperties repositoryProperties) { + final DistributionSetManagement distributionSetManagement, + final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository, + final ActionStatusRepository actionStatusRepository, final AuditorAware auditorProvider, + final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit, + final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager, + final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement, + final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final Database database, + final RepositoryProperties repositoryProperties) { super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties); this.entityManager = entityManager; this.distributionSetRepository = distributionSetRepository; @@ -240,10 +240,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl deploymentRequests = deploymentRequests.stream().distinct().toList(); checkForMultiAssignment(deploymentRequests); checkQuotaForAssignment(deploymentRequests); - // validates READ access to deployment sets, throws exception if deployment set is not accessible + // validates READ access to deployment sets, throws exception if deployment set + // is not accessible checkForTargetTypeCompatibility(deploymentRequests); // filters only targets that are updatable - // TODO - should assignments that contain non-existing/allowed devices be allowed anyway? return filterByTargetUpdatable(deploymentRequests); } @@ -313,16 +313,12 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } private List filterByTargetUpdatable(final List deploymentRequests) { - final List controllerIds = - deploymentRequests.stream() - .map(DeploymentRequest::getControllerId) - .distinct() - .toList(); + final List controllerIds = deploymentRequests.stream().map(DeploymentRequest::getControllerId) + .distinct().toList(); - final List found = targetRepository.findAll( - AccessController.Operation.UPDATE, - TargetSpecifications.hasControllerIdIn(controllerIds) - ).stream().map(JpaTarget::getControllerId).toList(); + final List found = targetRepository + .findAll(AccessController.Operation.UPDATE, TargetSpecifications.hasControllerIdIn(controllerIds)) + .stream().map(JpaTarget::getControllerId).toList(); if (found.size() != controllerIds.size()) { return deploymentRequests.stream() .filter(deploymentRequest -> found.contains(deploymentRequest.getControllerId())).toList(); @@ -384,8 +380,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl final List existingTargetIds = Lists.partition(providedTargetIds, Constants.MAX_ENTRIES_IN_STATEMENT) .stream() - .map(ids -> targetRepository.findAll( - AccessController.Operation.UPDATE, TargetSpecifications.hasControllerIdIn(ids))) + .map(ids -> targetRepository.findAll(AccessController.Operation.UPDATE, + TargetSpecifications.hasControllerIdIn(ids))) .flatMap(List::stream).map(JpaTarget::getControllerId).toList(); final List targetEntities = assignmentStrategy.findTargetsForAssignment(existingTargetIds, @@ -490,9 +486,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl public void cancelInactiveScheduledActionsForTargets(final List targetIds) { if (!isMultiAssignmentsEnabled()) { targetRepository.getAccessController().ifPresent(v -> { - if (targetRepository.count(AccessController.Operation.UPDATE, TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) { - throw new EntityNotFoundException(Target.class, targetIds); - } + if (targetRepository.count(AccessController.Operation.UPDATE, + TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) { + throw new EntityNotFoundException(Target.class, targetIds); + } }); actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED); } else { @@ -776,24 +773,21 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl @Override public Optional findAction(final long actionId) { - return actionRepository - .findById(actionId) + return actionRepository.findById(actionId) .filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))) .map(JpaAction.class::cast); } @Override public Optional findActionWithDetails(final long actionId) { - return actionRepository - .findWithDetailsById(actionId) + return actionRepository.findWithDetailsById(actionId) .filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))); } @Override public Slice findActionsByTarget(final String controllerId, final Pageable pageable) { assertTargetReadAllowed(controllerId); - return actionRepository - .findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable) + return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable) .map(Action.class::cast); } @@ -853,8 +847,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl @Retryable(include = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public Action forceTargetAction(final long actionId) { - final JpaAction action = actionRepository.findById(actionId) - .map(this::assertTargetUpdateAllowed) + final JpaAction action = actionRepository.findById(actionId).map(this::assertTargetUpdateAllowed) .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); if (!action.isForcedOrTimeForced()) { @@ -878,7 +871,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl return actionStatusRepository.countByActionId(actionId); } - // action is already got and there are checked read permissions - do not check permissions + // action is already got and there are checked read permissions - do not check + // permissions // and UI which is to be removed @Override public Page findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) { @@ -921,15 +915,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl return JpaManagementHelper.countBySpec(actionRepository, specList); } - // TODO - return via Mgmt API all actions (event for targets the use has no access - check if should and could - // be limited @Override public Slice findActionsAll(final Pageable pageable) { return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null); } - // TODO - return via Mgmt API all actions (event for targets the use has no access - check if should and could - // be limited @Override public Slice findActions(final String rsqlParam, final Pageable pageable) { final List> specList = List.of( @@ -983,8 +973,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl public boolean hasPendingCancellations(final Long targetId) { // target access checked in assertTargetReadAllowed assertTargetReadAllowed(targetId); - return actionRepository.exists( - ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING)); + return actionRepository + .exists(ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING)); } private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(final Database database) { @@ -1039,29 +1029,33 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl @Override @Transactional - public void cancelActionsForDistributionSet( - final CancelationType cancelationType, final DistributionSet distributionSet) { - actionRepository - .findAll(ActionSpecifications.byDistributionSetIdAndActiveAndStatusIsNot(distributionSet.getId(), Status.CANCELING)) + public void cancelActionsForDistributionSet(final CancelationType cancelationType, + final DistributionSet distributionSet) { + actionRepository.findAll(ActionSpecifications + .byDistributionSetIdAndActiveAndStatusIsNot(distributionSet.getId(), Status.CANCELING)) .forEach(action -> { try { assertTargetUpdateAllowed(action); cancelAction(action.getId()); LOG.debug("Action {} canceled", action.getId()); } catch (final InsufficientPermissionException e) { - // no access - skip it + LOG.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e); + } catch (final EntityNotFoundException e) { + LOG.trace("Could not cancel action {} due to entity not found exception.", action.getId(), e); } }); if (cancelationType == CancelationType.FORCE) { - actionRepository - .findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId())) + actionRepository.findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId())) .forEach(action -> { try { assertTargetUpdateAllowed(action); forceQuitAction(action.getId()); LOG.debug("Action {} force canceled", action.getId()); } catch (final InsufficientPermissionException e) { - // no access - skip it + LOG.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e); + } catch (final EntityNotFoundException e) { + LOG.trace("Could not cancel action {} due to entity not found exception.", action.getId(), + e); } }); } @@ -1080,9 +1074,12 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } private JpaAction assertTargetUpdateAllowed(final JpaAction action) { - if (!targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))) { + targetRepository.findOne(TargetSpecifications.hasId(action.getTarget().getId())).ifPresentOrElse(target -> { + targetRepository.getAccessController() + .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); + }, () -> { throw new EntityNotFoundException(Action.class, action); - } + }); return action; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java index 53af6acb0..36511c81b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java @@ -27,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation; import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType; import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount; +import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,13 +51,14 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet private final RepositoryProperties repositoryProperties; private final TenantAware tenantAware; private final LockRegistry lockRegistry; + private final SystemSecurityContext systemSecurityContext; public JpaDistributionSetInvalidationManagement(final DistributionSetManagement distributionSetManagement, final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement, final TargetFilterQueryManagement targetFilterQueryManagement, final ActionRepository actionRepository, - final PlatformTransactionManager txManager, - final RepositoryProperties repositoryProperties, final TenantAware tenantAware, - final LockRegistry lockRegistry) { + final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties, + final TenantAware tenantAware, final LockRegistry lockRegistry, + final SystemSecurityContext systemSecurityContext) { this.distributionSetManagement = distributionSetManagement; this.rolloutManagement = rolloutManagement; this.deploymentManagement = deploymentManagement; @@ -66,13 +68,13 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet this.repositoryProperties = repositoryProperties; this.tenantAware = tenantAware; this.lockRegistry = lockRegistry; + this.systemSecurityContext = systemSecurityContext; } @Override public void invalidateDistributionSet(final DistributionSetInvalidation distributionSetInvalidation) { LOG.debug("Invalidate distribution sets {}", distributionSetInvalidation.getDistributionSetIds()); final String tenant = tenantAware.getCurrentTenant(); - if (shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(), distributionSetInvalidation.isCancelRollouts())) { final String handlerId = JpaRolloutManagement.createRolloutLockKey(tenant); @@ -95,6 +97,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet // no lock is needed as no rollout will be stopped invalidateDistributionSetsInTransaction(distributionSetInvalidation, tenant); } + } private void invalidateDistributionSetsInTransaction(final DistributionSetInvalidation distributionSetInvalidation, @@ -110,17 +113,25 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet final boolean cancelRollouts) { final DistributionSet set = distributionSetManagement.getValidAndComplete(setId); distributionSetManagement.invalidate(set); - LOG.debug("Distribution set {} set to invalid", setId); + LOG.debug("Distribution set {} marked as invalid.", setId); + // rollout cancellation should only be permitted with UPDATE_ROLLOUT permission if (shouldRolloutsBeCanceled(cancelationType, cancelRollouts)) { + LOG.debug("Cancel rollouts after ds invalidation. ID: {}", setId); rolloutManagement.cancelRolloutsForDistributionSet(set); } - if (cancelationType != CancelationType.NONE) { - deploymentManagement.cancelActionsForDistributionSet(cancelationType, set); - } + // Do run as system to ensure all actions (even invisible) are canceled due to invalidation. + systemSecurityContext.runAsSystem(() -> { + if (cancelationType != CancelationType.NONE) { + LOG.debug("Cancel actions after ds invalidation. ID: {}", setId); + deploymentManagement.cancelActionsForDistributionSet(cancelationType, set); + } - targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId); + LOG.debug("Cancel auto assignments after ds invalidation. ID: {}", setId); + targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId); + return null; + }); } private static boolean shouldRolloutsBeCanceled(final CancelationType cancelationType, @@ -131,13 +142,16 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet @Override public DistributionSetInvalidationCount countEntitiesForInvalidation( final DistributionSetInvalidation distributionSetInvalidation) { - final Collection setIds = distributionSetInvalidation.getDistributionSetIds(); - final long rolloutsCount = shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(), - distributionSetInvalidation.isCancelRollouts()) ? countRolloutsForInvalidation(setIds) : 0; - final long autoAssignmentsCount = countAutoAssignmentsForInvalidation(setIds); - final long actionsCount = countActionsForInvalidation(setIds, distributionSetInvalidation.getCancelationType()); + return systemSecurityContext.runAsSystem(() -> { + final Collection setIds = distributionSetInvalidation.getDistributionSetIds(); + final long rolloutsCount = shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(), + distributionSetInvalidation.isCancelRollouts()) ? countRolloutsForInvalidation(setIds) : 0; + final long autoAssignmentsCount = countAutoAssignmentsForInvalidation(setIds); + final long actionsCount = countActionsForInvalidation(setIds, + distributionSetInvalidation.getCancelationType()); - return new DistributionSetInvalidationCount(rolloutsCount, autoAssignmentsCount, actionsCount); + return new DistributionSetInvalidationCount(rolloutsCount, autoAssignmentsCount, actionsCount); + }); } private long countRolloutsForInvalidation(final Collection setIds) { @@ -163,7 +177,9 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet } private long countActionsForSoftInvalidation(final Collection setIds) { - return setIds.stream().mapToLong(distributionSet -> actionRepository - .countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, Status.CANCELING)).sum(); + return setIds.stream() + .mapToLong(distributionSet -> actionRepository + .countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, Status.CANCELING)) + .sum(); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java index f501affd0..ba23f485f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java @@ -786,7 +786,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { public void invalidate(final DistributionSet distributionSet) { final JpaDistributionSet jpaSet = (JpaDistributionSet) distributionSet; jpaSet.invalidate(); - distributionSetRepository.save(AccessController.Operation.DELETE, jpaSet); + distributionSetRepository.save(jpaSet); } private JpaDistributionSet getById(final long id) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java index 96fcfdc92..4ff027df1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java @@ -96,6 +96,8 @@ import org.springframework.validation.annotation.Validated; import com.google.common.collect.Lists; +import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd; + /** * JPA implementation of {@link TargetManagement}. * @@ -130,7 +132,6 @@ public class JpaTargetManagement implements TargetManagement { private final Database database; - public JpaTargetManagement(final EntityManager entityManager, final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository, @@ -138,8 +139,8 @@ public class JpaTargetManagement implements TargetManagement { final RolloutGroupRepository rolloutGroupRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder, - final TenantAware tenantAware, - final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { + final TenantAware tenantAware, final VirtualPropertyReplacer virtualPropertyReplacer, + final Database database) { this.entityManager = entityManager; this.distributionSetManagement = distributionSetManagement; this.quotaManagement = quotaManagement; @@ -161,8 +162,7 @@ public class JpaTargetManagement implements TargetManagement { } private JpaTarget getByControllerIdAndThrowIfNotFound(final String controllerId) { - return targetRepository - .findOne(TargetSpecifications.hasControllerId(controllerId)) + return targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId)) .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); } @@ -255,6 +255,9 @@ public class JpaTargetManagement implements TargetManagement { final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository, getByControllerIdAndThrowIfNotFound(controllerId)); + targetRepository.getAccessController() + .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); + targetMetadataRepository.deleteById(metadata.getId()); // target update event is set to ignore "lastModifiedAt" field, so it is // not send automatically within the touch() method @@ -312,17 +315,14 @@ public class JpaTargetManagement implements TargetManagement { .orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId)); return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, - List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), TargetFields.class, - virtualPropertyReplacer, database))); + List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), TargetFields.class, + virtualPropertyReplacer, database))); } @Override public Slice findByRsql(final Pageable pageable, final String targetFilterQuery) { - return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, - List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, - virtualPropertyReplacer, database))); + return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, List.of(RSQLUtility + .buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database))); } @Override @@ -481,7 +481,7 @@ public class JpaTargetManagement implements TargetManagement { final TargetTag tag = targetTagRepository.findByNameEquals(tagName) .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagName)); final List allTargets = targetRepository - .findAll(AccessController.Operation.UPDATE, TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); + .findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); if (allTargets.size() < controllerIds.size()) { throw new EntityNotFoundException(Target.class, controllerIds, allTargets.stream().map(Target::getControllerId).toList()); @@ -492,6 +492,7 @@ public class JpaTargetManagement implements TargetManagement { // all are already assigned -> unassign if (alreadyAssignedTargets.size() == allTargets.size()) { + alreadyAssignedTargets.forEach(target -> target.removeTag(tag)); return new TargetTagAssignmentResult(0, Collections.emptyList(), Collections.unmodifiableList(alreadyAssignedTargets), tag); @@ -501,9 +502,7 @@ public class JpaTargetManagement implements TargetManagement { // some or none are assigned -> assign allTargets.forEach(target -> target.addTag(tag)); final TargetTagAssignmentResult result = new TargetTagAssignmentResult(alreadyAssignedTargets.size(), - Collections - .unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList())), - Collections.emptyList(), tag); + targetRepository.saveAll(allTargets), Collections.emptyList(), tag); // no reason to persist the tag entityManager.detach(tag); @@ -528,7 +527,7 @@ public class JpaTargetManagement implements TargetManagement { targetsWithoutSameType.forEach(target -> target.setTargetType(type)); final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(targetsWithSameType.size(), - targetsWithoutSameType.stream().map(targetRepository::save).toList(), Collections.emptyList(), type); + targetRepository.saveAll(targetsWithoutSameType), Collections.emptyList(), type); // no reason to persist the type entityManager.detach(type); @@ -566,12 +565,15 @@ public class JpaTargetManagement implements TargetManagement { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public List assignTag(final Collection controllerIds, final long tagId) { final List allTargets = targetRepository - .findAll(AccessController.Operation.UPDATE, TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); + .findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); if (allTargets.size() < controllerIds.size()) { throw new EntityNotFoundException(Target.class, controllerIds, allTargets.stream().map(Target::getControllerId).toList()); } + targetRepository.getAccessController() + .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, allTargets)); + final JpaTargetTag tag = targetTagRepository.findById(tagId) .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagId)); @@ -619,6 +621,11 @@ public class JpaTargetManagement implements TargetManagement { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public Target assignType(final String controllerId, final Long targetTypeId) { final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); + + targetRepository.getAccessController().ifPresent(acm -> { + acm.assertOperationAllowed(AccessController.Operation.UPDATE, target); + }); + final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId); target.setTargetType(targetType); return targetRepository.save(target); @@ -626,7 +633,7 @@ public class JpaTargetManagement implements TargetManagement { @Override public Slice findByFilterOrderByLinkedDistributionSet(final Pageable pageable, - final long orderByDistributionSetId, final FilterParams filterParams) { + final long orderByDistributionSetId, final FilterParams filterParams) { // remove default sort from pageable to not overwrite sorted spec final OffsetBasedPageRequest unsortedPage = new OffsetBasedPageRequest(pageable.getOffset(), pageable.getPageSize(), Sort.unsorted()); @@ -661,41 +668,42 @@ public class JpaTargetManagement implements TargetManagement { @Override public Slice findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(final Pageable pageRequest, - final long distributionSetId, final String targetFilterQuery) { + final long distributionSetId, final String targetFilterQuery) { final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final Long distSetTypeId = jpaDistributionSet.getType().getId(); - return targetRepository.findAllWithoutCount( - AccessController.Operation.UPDATE, - JpaManagementHelper.combineWithAnd(List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, - database), - TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), - TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))), - pageRequest).map(Target.class::cast); + return targetRepository + .findAllWithoutCount(AccessController.Operation.UPDATE, + combineWithAnd(List.of( + RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, + virtualPropertyReplacer, database), + TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), + TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))), + pageRequest) + .map(Target.class::cast); } @Override public Slice findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable( final Pageable pageRequest, final Collection groups, final String targetFilterQuery, final DistributionSetType dsType) { - return targetRepository.findAllWithoutCount( - AccessController.Operation.UPDATE, - JpaManagementHelper.combineWithAnd(List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, - database), - TargetSpecifications.isNotInRolloutGroups(groups), - TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))), - pageRequest).map(Target.class::cast); + return targetRepository + .findAllWithoutCount(AccessController.Operation.UPDATE, + combineWithAnd(List.of( + RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, + virtualPropertyReplacer, database), + TargetSpecifications.isNotInRolloutGroups(groups), + TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))), + pageRequest) + .map(Target.class::cast); } @Override public Slice findByFailedRolloutAndNotInRolloutGroups(Pageable pageRequest, Collection groups, - String rolloutId) { + String rolloutId) { final List> specList = Arrays.asList( - TargetSpecifications.failedActionsForRollout(rolloutId), - TargetSpecifications.isNotInRolloutGroups(groups) - ); + TargetSpecifications.failedActionsForRollout(rolloutId), + TargetSpecifications.isNotInRolloutGroups(groups)); return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageRequest, specList); } @@ -712,34 +720,34 @@ public class JpaTargetManagement implements TargetManagement { @Override public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(final Collection groups, - final String targetFilterQuery, final DistributionSetType dsType) { - return targetRepository.count(AccessController.Operation.UPDATE, JpaManagementHelper.combineWithAnd( - List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, - database), - TargetSpecifications.isNotInRolloutGroups(groups), - TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId())))); + final String targetFilterQuery, final DistributionSetType dsType) { + return targetRepository.count(AccessController.Operation.UPDATE, + combineWithAnd(List.of( + RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, + virtualPropertyReplacer, database), + TargetSpecifications.isNotInRolloutGroups(groups), + TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId())))); } @Override public long countByFailedRolloutAndNotInRolloutGroups(Collection groups, String rolloutId) { final List> specList = Arrays.asList( - TargetSpecifications.failedActionsForRollout(rolloutId), - TargetSpecifications.isNotInRolloutGroups(groups)); + TargetSpecifications.failedActionsForRollout(rolloutId), + TargetSpecifications.isNotInRolloutGroups(groups)); return JpaManagementHelper.countBySpec(targetRepository, specList); } @Override public long countByRsqlAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, - final String targetFilterQuery) { + final String targetFilterQuery) { final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final Long distSetTypeId = jpaDistributionSet.getType().getId(); - return targetRepository.count(AccessController.Operation.UPDATE, JpaManagementHelper.combineWithAnd( - List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, - database), + return targetRepository.count(AccessController.Operation.UPDATE, + combineWithAnd(List.of( + RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, + virtualPropertyReplacer, database), TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId)))); } @@ -798,27 +806,38 @@ public class JpaTargetManagement implements TargetManagement { @Override public long countByRsql(final String targetFilterQuery) { - return JpaManagementHelper.countBySpec( - targetRepository, - List.of( - RSQLUtility.buildRsqlSpecification( - targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database))); + return JpaManagementHelper.countBySpec(targetRepository, List.of(RSQLUtility + .buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database))); } @Override - public long countByRsqlAndCompatible(final String targetFilterQuery, final Long distributionSetId) { - final List> specList = List.of( - RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, - database), - TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetId)); + public long countByRsqlAndUpdatable(String targetFilterQuery) { + final List> specList = List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery, + TargetFields.class, virtualPropertyReplacer, database)); + return targetRepository.count(AccessController.Operation.UPDATE, combineWithAnd(specList)); + } + + @Override + public long countByRsqlAndCompatible(final String targetFilterQuery, final Long distributionSetIdTypeId) { + final List> specList = List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery, + TargetFields.class, virtualPropertyReplacer, database), + TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId)); return JpaManagementHelper.countBySpec(targetRepository, specList); } + @Override + public long countByRsqlAndCompatibleAndUpdatable(String targetFilterQuery, Long distributionSetIdTypeId) { + final List> specList = List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery, + TargetFields.class, virtualPropertyReplacer, database), + TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId)); + return targetRepository.count(AccessController.Operation.UPDATE, combineWithAnd(specList)); + } + @Override public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) { - final List> specList = List.of( - TargetSpecifications.failedActionsForRollout(rolloutId)); + final List> specList = List + .of(TargetSpecifications.failedActionsForRollout(rolloutId)); return JpaManagementHelper.countBySpec(targetRepository, specList); } @@ -856,6 +875,8 @@ public class JpaTargetManagement implements TargetManagement { @Override public void requestControllerAttributes(final String controllerId) { final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); + targetRepository.getAccessController() + .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); target.setRequestControllerAttributes(true); eventPublisherHolder.getEventPublisher() .publishEvent(new TargetAttributesRequestedEvent(tenantAware.getCurrentTenant(), target.getId(), @@ -876,7 +897,7 @@ public class JpaTargetManagement implements TargetManagement { } @Override - public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatible(final String controllerId, + public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(final String controllerId, final long distributionSetId, final String targetFilterQuery) { RSQLUtility.validateRsqlFor(targetFilterQuery, TargetFields.class); final DistributionSet ds = distributionSetManagement.get(distributionSetId) @@ -891,7 +912,7 @@ public class JpaTargetManagement implements TargetManagement { final Specification combinedSpecification = Objects .requireNonNull(SpecificationsBuilder.combineWithAnd(specList)); - return targetRepository.exists(combinedSpecification); + return targetRepository.exists(AccessController.Operation.UPDATE, combinedSpecification); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java index c5547b79e..5aef2becc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java @@ -27,7 +27,6 @@ import javax.transaction.Transactional; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -70,13 +69,18 @@ public class BaseEntityRepositoryACM if (method.getName().startsWith("find") || method.getName().startsWith("get")) { final Object result = method.invoke(repository, args); if (Iterable.class.isAssignableFrom(method.getReturnType())) { - for (final T e : ((Iterable) result)) { - accessController.assertOperationAllowed(AccessController.Operation.READ, e); + for (final Object e : (Iterable) result) { + if (repository.getDomainClass().isAssignableFrom(e.getClass())) { + accessController.assertOperationAllowed(AccessController.Operation.READ, (T) e); + } } - } else if (Optional.class.isAssignableFrom(method.getReturnType())) { - return ((Optional)result).filter(t -> isOperationAllowed(AccessController.Operation.READ, t, accessController)); + } else if (Optional.class.isAssignableFrom(method.getReturnType()) && ((Optional) result) + .filter(value -> repository.getDomainClass().isAssignableFrom(value.getClass())) + .isPresent()) { + return ((Optional) result).filter( + t -> isOperationAllowed(AccessController.Operation.READ, t, accessController)); } else if (repository.getDomainClass().isAssignableFrom(method.getReturnType())) { - accessController.assertOperationAllowed(AccessController.Operation.READ, (T)result); + accessController.assertOperationAllowed(AccessController.Operation.READ, (T) result); } return result; } else if ("toString".equals(method.getName()) && method.getParameterCount() == 0) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java index a02d94277..9f755b18d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java @@ -74,9 +74,9 @@ class AutoAssignCheckerTest { when(targetFilterQueryManagement.findWithAutoAssignDS(any())) .thenReturn(new SliceImpl<>(Arrays.asList(notMatching, matching))); - when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, matching.getQuery())) + when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, matching.getQuery())) .thenReturn(true); - when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, notMatching.getQuery())) + when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, notMatching.getQuery())) .thenReturn(false); sut.checkSingleTarget(target); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java index 3eca57e94..147481e70 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java @@ -203,18 +203,18 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe } @Test - @Description("Verify that a user that has authority READ_REPOSITORY and UPDATE_REPOSITORY is not allowed to invalidate a distribution set") + @Description("Verify that a user that has authority READ_REPOSITORY and UPDATE_REPOSITORY is allowed to invalidate a distribution set") @WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY" }) void verifyInvalidateWithReadAndUpdateRepoAuthority() { final InvalidationTestData invalidationTestData = systemSecurityContext .runAsSystem(() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAuthority")); - assertThatExceptionOfType(InsufficientPermissionException.class) - .as("Insufficient permission exception expected") - .isThrownBy(() -> distributionSetInvalidationManagement - .invalidateDistributionSet(new DistributionSetInvalidation( - Collections.singletonList(invalidationTestData.getDistributionSet().getId()), - CancelationType.NONE, false))); + distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation( + Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE, + false)); + assertThat( + distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid()) + .isFalse(); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java index 0514007d8..94c97ce49 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java @@ -1267,7 +1267,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet(); final String filter = "metadata.key1==target1-value1"; - assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(), + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target.getControllerId(), ds.getId(), filter)).isTrue(); } @@ -1278,7 +1278,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet(); final String filter = "metadata.key==not_existing"; - assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(), + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target.getControllerId(), ds.getId(), filter)).isFalse(); } @@ -1292,7 +1292,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { assignDistributionSet(ds2, target); final String filter = "name==*"; - assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(), + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target.getControllerId(), ds1.getId(), filter)).isFalse(); } @@ -1303,7 +1303,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final Target target = testdataFactory.createTarget("target", "target", type.getId()); final DistributionSet ds = testdataFactory.createDistributionSet(); - assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(), + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target.getControllerId(), ds.getId(), "name==*")).isFalse(); } @@ -1314,9 +1314,9 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final Long ds = testdataFactory.createDistributionSet().getId(); assertThatExceptionOfType(RSQLParameterSyntaxException.class).isThrownBy(() -> targetManagement - .isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, "invalid_syntax")); + .isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, "invalid_syntax")); assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class).isThrownBy(() -> targetManagement - .isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, "invalid_field==1")); + .isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, "invalid_field==1")); } @Test @@ -1324,7 +1324,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { void matchesFilterTargetNotExists() { final DistributionSet ds = testdataFactory.createDistributionSet(); - assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible("notExisting", ds.getId(), + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable("notExisting", ds.getId(), "name==*")).isFalse(); } @@ -1334,7 +1334,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final String target = testdataFactory.createTarget().getControllerId(); assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy( - () -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, 123, "name==*")); + () -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, 123, "name==*")); } private void validateFoundTargetsByRsql(final String rsqlFilter, final String... controllerIds) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/window/layouts/AddRolloutWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/window/layouts/AddRolloutWindowLayout.java index 29a962e0b..ce7118e0d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/window/layouts/AddRolloutWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/window/layouts/AddRolloutWindowLayout.java @@ -134,14 +134,13 @@ public class AddRolloutWindowLayout extends AbstractRolloutWindowLayout { } private Long getTotalTargets(final String filterQuery, final Long distSetTypeId) { - // TODO AC - Check for updatable targets only if (StringUtils.isEmpty(filterQuery)) { return null; } if (distSetTypeId == null) { - return targetManagement.countByRsql(filterQuery); + return targetManagement.countByRsqlAndUpdatable(filterQuery); } - return targetManagement.countByRsqlAndCompatible(filterQuery, distSetTypeId); + return targetManagement.countByRsqlAndCompatibleAndUpdatable(filterQuery, distSetTypeId); } private boolean isSimpleGroupsTabSelected() {