Finalize and polish fine-grained permission (#2660)

* Remove _REPOSITORY_ permissions -> replaced with _SOFTWARE_MODULE_, _SOFTWARE_MODULE_TYPE_, _DISTRIBUTION_SET_, _DISTRIBUTION_SET_TYPE_ permissions
* Still kept _ROLE_REPOSITORY_ADMIN_ role granting all repository fine-graned permissions
* Added dedicated _TARGET_TYPE_ permission set - the _TARGET_ permissions just grant _READ_TARGET_TYPE_ (analogically _SOFTWARE_MODULE_ permissions grant _READ_SOFTWARE_MODULE_TYPE_ and _DISTRIBUTION_SET_ grants _READ_DISTRIBUTON_SET_TYPE_
* Hierarcy is not configurable - could be completely replaced by setting spring application property org.eclipse.hawkbit.hierarchy or could be extended by adding rules using org.eclipse.hawkbit.hierarchy.ext

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-09-09 15:42:11 +03:00
committed by GitHub
parent f2e6344775
commit ae3a004da0
16 changed files with 182 additions and 219 deletions

View File

@@ -17,7 +17,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
@@ -88,7 +88,6 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
private final DeploymentManagement deployManagement; private final DeploymentManagement deployManagement;
private final SystemManagement systemManagement; private final SystemManagement systemManagement;
private final MgmtDistributionSetMapper mgmtDistributionSetMapper; private final MgmtDistributionSetMapper mgmtDistributionSetMapper;
private final SystemSecurityContext systemSecurityContext;
private final TenantConfigHelper tenantConfigHelper; private final TenantConfigHelper tenantConfigHelper;
@SuppressWarnings("java:S107") @SuppressWarnings("java:S107")
@@ -112,7 +111,6 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
this.tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement); this.tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
this.mgmtDistributionSetMapper = mgmtDistributionSetMapper; this.mgmtDistributionSetMapper = mgmtDistributionSetMapper;
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext;
} }
@Override @Override
@@ -144,23 +142,27 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
public ResponseEntity<List<MgmtDistributionSet>> createDistributionSets(final List<MgmtDistributionSetRequestBodyPost> sets) { public ResponseEntity<List<MgmtDistributionSet>> createDistributionSets(final List<MgmtDistributionSetRequestBodyPost> sets) {
log.debug("creating {} distribution sets", sets.size()); log.debug("creating {} distribution sets", sets.size());
// set default Ds type if ds type is null // set default Ds type if ds type is null
final String defaultDsKey = systemSecurityContext.runAsSystem(systemManagement.getTenantMetadata().getDefaultDsType()::getKey); final String defaultDsKey = systemManagement.getTenantMetadata().getDefaultDsType().getKey();
sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(defaultDsKey)); sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(defaultDsKey));
//check if there is already deleted DS Type // check if target ds types exist and are not deleted, also caches them
for (final MgmtDistributionSetRequestBodyPost ds : sets) { final Map<String, DistributionSetType> dsTypeKeyToDsType = sets.stream()
final Optional<? extends DistributionSetType> opt = distributionSetTypeManagement.findByKey(ds.getType()); .map(MgmtDistributionSetRequestBodyPost::getType)
opt.ifPresent(dsType -> { .distinct()
if (dsType.isDeleted()) { .collect(Collectors.toMap(
final String text = "Cannot create Distribution Set from type with key {0}. Distribution Set Type already deleted!"; Function.identity(),
final String message = MessageFormat.format(text, dsType.getKey()); dsTypeKey ->
throw new ValidationException(message); distributionSetTypeManagement.findByKey(dsTypeKey).map(dsType -> {
} if (dsType.isDeleted()) {
}); throw new ValidationException(MessageFormat.format(
} "Cannot create Distribution Set from type with key {0}. Distribution Set Type already deleted!",
dsTypeKey));
}
return dsType;
}).orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, dsTypeKey))));
final Collection<? extends DistributionSet> createdDSets = distributionSetManagement final Collection<? extends DistributionSet> createdDSets =
.create(mgmtDistributionSetMapper.fromRequest(sets)); distributionSetManagement.create(mgmtDistributionSetMapper.fromRequest(sets, defaultDsKey, dsTypeKeyToDsType));
log.debug("{} distribution sets created, return status {}", sets.size(), HttpStatus.CREATED); log.debug("{} distribution sets created, return status {}", sets.size(), HttpStatus.CREATED);
return new ResponseEntity<>(MgmtDistributionSetMapper.toResponseDistributionSets(createdDSets), HttpStatus.CREATED); return new ResponseEntity<>(MgmtDistributionSetMapper.toResponseDistributionSets(createdDSets), HttpStatus.CREATED);

View File

@@ -17,6 +17,7 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -63,8 +64,38 @@ public class MgmtDistributionSetMapper {
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
} }
public List<DistributionSetManagement.Create> fromRequest(final Collection<MgmtDistributionSetRequestBodyPost> sets) { public List<DistributionSetManagement.Create> fromRequest(
return sets.stream().map(this::fromRequest).toList(); final Collection<MgmtDistributionSetRequestBodyPost> sets,
final String defaultDsKey, final Map<String, DistributionSetType> dsTypeKeyToDsType) {
return sets.stream().<DistributionSetManagement.Create>map(dsRest -> {
final Set<Long> modules = new HashSet<>();
if (dsRest.getOs() != null) {
modules.add(dsRest.getOs().getId());
}
if (dsRest.getApplication() != null) {
modules.add(dsRest.getApplication().getId());
}
if (dsRest.getRuntime() != null) {
modules.add(dsRest.getRuntime().getId());
}
if (dsRest.getModules() != null) {
dsRest.getModules().forEach(module -> modules.add(module.getId()));
}
// distribution set type, if null by the REST call shall be replaced with the default tenant DS type
final String dsTypeKey = Objects.requireNonNull(dsRest.getType(), "Distribution set type must not be null");
final DistributionSetType dsType = dsTypeKeyToDsType.get(dsTypeKey);
if (dsType == null) {
// type should never null, cache is prefilled with all types
throw new EntityNotFoundException(DistributionSetType.class, defaultDsKey);
}
return DistributionSetManagement.Create.builder()
.type(dsType)
.name(dsRest.getName()).version(dsRest.getVersion())
.description(dsRest.getDescription())
.modules(findSoftwareModuleWithExceptionIfNotFound(modules))
.requiredMigrationStep(dsRest.getRequiredMigrationStep())
.build();
}).toList();
} }
public static MgmtDistributionSet toResponse(final DistributionSet distributionSet) { public static MgmtDistributionSet toResponse(final DistributionSet distributionSet) {
@@ -170,36 +201,6 @@ public class MgmtDistributionSetMapper {
return sets.stream().map(MgmtDistributionSetMapper::toResponse).toList(); return sets.stream().map(MgmtDistributionSetMapper::toResponse).toList();
} }
private DistributionSetManagement.Create fromRequest(final MgmtDistributionSetRequestBodyPost dsRest) {
final Set<Long> modules = new HashSet<>();
if (dsRest.getOs() != null) {
modules.add(dsRest.getOs().getId());
}
if (dsRest.getApplication() != null) {
modules.add(dsRest.getApplication().getId());
}
if (dsRest.getRuntime() != null) {
modules.add(dsRest.getRuntime().getId());
}
if (dsRest.getModules() != null) {
dsRest.getModules().forEach(module -> modules.add(module.getId()));
}
return DistributionSetManagement.Create.builder()
.type(Optional.ofNullable(dsRest.getType())
// if the type is supplied the type MUST exist
.map(typeKey -> distributionSetTypeManagement
.findByKey(typeKey)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeKey)))
.map(DistributionSetType.class::cast)
// if here, the type is not supplied, use the default type
.orElseGet(() -> systemManagement.getTenantMetadata().getDefaultDsType()))
.name(dsRest.getName()).version(dsRest.getVersion())
.description(dsRest.getDescription())
.modules(findSoftwareModuleWithExceptionIfNotFound(modules))
.requiredMigrationStep(dsRest.getRequiredMigrationStep())
.build();
}
private Set<? extends SoftwareModule> findSoftwareModuleWithExceptionIfNotFound(final Set<Long> softwareModuleIds) { private Set<? extends SoftwareModule> findSoftwareModuleWithExceptionIfNotFound(final Set<Long> softwareModuleIds) {
if (CollectionUtils.isEmpty(softwareModuleIds)) { if (CollectionUtils.isEmpty(softwareModuleIds)) {
return Collections.emptySet(); return Collections.emptySet();

View File

@@ -73,7 +73,6 @@ class MgmtTargetTypeResourceTest extends AbstractManagementApiIntegrationTest {
principal = "targetTypeTester", allSpPermissions = true, principal = "targetTypeTester", allSpPermissions = true,
removeFromAllPermission = { removeFromAllPermission = {
SpPermission.CREATE_TARGET, SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET, SpPermission.DELETE_TARGET, SpPermission.CREATE_TARGET, SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET, SpPermission.DELETE_TARGET,
SpPermission.CREATE_REPOSITORY, SpPermission.READ_REPOSITORY, SpPermission.UPDATE_REPOSITORY, SpPermission.DELETE_REPOSITORY,
SpPermission.READ_TARGET_TYPE }) SpPermission.READ_TARGET_TYPE })
void getTargetTypesWithoutPermission() throws Exception { void getTargetTypesWithoutPermission() throws Exception {
mvc.perform(get(TARGETTYPES_ENDPOINT).accept(MediaType.APPLICATION_JSON)) mvc.perform(get(TARGETTYPES_ENDPOINT).accept(MediaType.APPLICATION_JSON))

View File

@@ -28,6 +28,16 @@ import org.springframework.http.HttpStatus;
*/ */
class PreAuthorizeEnabledTest extends AbstractSecurityTest { class PreAuthorizeEnabledTest extends AbstractSecurityTest {
/**
* Tests whether request succeed if a role is granted for the user
*/
@Test
@WithUser(authorities = { SpPermission.READ_DISTRIBUTION_SET }, autoCreateTenant = false)
void successIfHasRole() throws Exception {
mvc.perform(get("/rest/v1/distributionsets")).andExpect(result ->
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()));
}
/** /**
* Tests whether request fail if a role is forbidden for the user * Tests whether request fail if a role is forbidden for the user
*/ */
@@ -38,23 +48,12 @@ class PreAuthorizeEnabledTest extends AbstractSecurityTest {
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value()));
} }
/**
* Tests whether request succeed if a role is granted for the user
*/
@Test
@WithUser(authorities = { SpPermission.READ_REPOSITORY }, autoCreateTenant = false)
void successIfHasRole() throws Exception {
mvc.perform(get("/rest/v1/distributionsets")).andExpect(result ->
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()));
}
/** /**
* Tests whether request returns distribution set if a role with scope is granted for the user * Tests whether request returns distribution set if a role with scope is granted for the user
*/ */
@Test @Test
@WithUser(authorities = { @WithUser(authorities = {
SpPermission.CREATE_REPOSITORY, "CREATE_DISTRIBUTION_SET", "READ_DISTRIBUTION_SET_TYPE",
SpPermission.READ_REPOSITORY,
SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne" }, autoCreateTenant = false) SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne" }, autoCreateTenant = false)
void successIfHasRoleWithScope() throws Exception { void successIfHasRoleWithScope() throws Exception {
createDsOne("successIfHasRoleWithScope"); createDsOne("successIfHasRoleWithScope");
@@ -69,8 +68,7 @@ class PreAuthorizeEnabledTest extends AbstractSecurityTest {
*/ */
@Test @Test
@WithUser(authorities = { @WithUser(authorities = {
SpPermission.CREATE_REPOSITORY, "CREATE_DISTRIBUTION_SET", "READ_DISTRIBUTION_SET_TYPE",
SpPermission.READ_REPOSITORY,
SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne2" }, autoCreateTenant = false) SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne2" }, autoCreateTenant = false)
void failIfHasNoForbiddingScope() throws Exception { void failIfHasNoForbiddingScope() throws Exception {
createDsOne("failIfHasNoForbiddingScope"); createDsOne("failIfHasNoForbiddingScope");
@@ -99,8 +97,7 @@ class PreAuthorizeEnabledTest extends AbstractSecurityTest {
mvc.perform(get("/rest/v1/system/configs")).andExpect(result -> { mvc.perform(get("/rest/v1/system/configs")).andExpect(result -> {
// returns default DS type because of READ_TARGET // returns default DS type because of READ_TARGET
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value());
assertThat(new ObjectMapper().reader().readValue(result.getResponse().getContentAsString(), HashMap.class)) assertThat(new ObjectMapper().reader().readValue(result.getResponse().getContentAsString(), HashMap.class)).hasSize(1);
.hasSize(1);
}); });
} }

View File

@@ -28,6 +28,16 @@ import org.springframework.http.HttpStatus;
*/ */
class PreAuthorizeEnabledTest extends AbstractSecurityTest { class PreAuthorizeEnabledTest extends AbstractSecurityTest {
/**
* Tests whether request succeed if a role is granted for the user
*/
@Test
@WithUser(authorities = { SpPermission.READ_DISTRIBUTION_SET }, autoCreateTenant = false)
void successIfHasRole() throws Exception {
mvc.perform(get("/rest/v1/distributionsets"))
.andExpect(result -> assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()));
}
/** /**
* Tests whether request fail if a role is forbidden for the user * Tests whether request fail if a role is forbidden for the user
*/ */
@@ -38,23 +48,12 @@ class PreAuthorizeEnabledTest extends AbstractSecurityTest {
.andExpect(result -> assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); .andExpect(result -> assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value()));
} }
/**
* Tests whether request succeed if a role is granted for the user
*/
@Test
@WithUser(authorities = { SpPermission.READ_REPOSITORY }, autoCreateTenant = false)
void successIfHasRole() throws Exception {
mvc.perform(get("/rest/v1/distributionsets"))
.andExpect(result -> assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()));
}
/** /**
* Tests whether request returns distribution set if a role with scope is granted for the user * Tests whether request returns distribution set if a role with scope is granted for the user
*/ */
@Test @Test
@WithUser(authorities = { @WithUser(authorities = {
SpPermission.CREATE_REPOSITORY, "CREATE_DISTRIBUTION_SET", "READ_DISTRIBUTION_SET_TYPE",
SpPermission.READ_REPOSITORY,
SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne" }, autoCreateTenant = false) SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne" }, autoCreateTenant = false)
void successIfHasRoleWithScope() throws Exception { void successIfHasRoleWithScope() throws Exception {
createDsOne("successIfHasRoleWithScope"); createDsOne("successIfHasRoleWithScope");
@@ -69,8 +68,7 @@ class PreAuthorizeEnabledTest extends AbstractSecurityTest {
*/ */
@Test @Test
@WithUser(authorities = { @WithUser(authorities = {
SpPermission.CREATE_REPOSITORY, "CREATE_DISTRIBUTION_SET", "READ_DISTRIBUTION_SET_TYPE",
SpPermission.READ_REPOSITORY,
SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne2" }, autoCreateTenant = false) SpPermission.READ_DISTRIBUTION_SET + "/name==DsOne2" }, autoCreateTenant = false)
void failIfHasNoForbiddingScope() throws Exception { void failIfHasNoForbiddingScope() throws Exception {
createDsOne("failIfHasNoForbiddingScope"); createDsOne("failIfHasNoForbiddingScope");
@@ -100,8 +98,7 @@ class PreAuthorizeEnabledTest extends AbstractSecurityTest {
.andExpect(result -> { .andExpect(result -> {
// returns default DS type because of READ_TARGET // returns default DS type because of READ_TARGET
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value());
assertThat(new ObjectMapper().reader().readValue(result.getResponse().getContentAsString(), HashMap.class)) assertThat(new ObjectMapper().reader().readValue(result.getResponse().getContentAsString(), HashMap.class)).hasSize(1);
.hasSize(1);
}); });
} }

View File

@@ -62,7 +62,7 @@ public interface ArtifactManagement extends PermissionSupport {
* @param isEncrypted flag to indicate if artifact is encrypted. * @param isEncrypted flag to indicate if artifact is encrypted.
* @return loaded {@link StoredArtifactInfo} * @return loaded {@link StoredArtifactInfo}
*/ */
@PreAuthorize("hasAuthority('" + SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT + "')" + " or " + SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize("hasAuthority('DOWNLOAD_REPOSITORY_ARTIFACT') or hasAuthority('" + SpPermission.SOFTWARE_MODULE_DOWNLOAD_ARTIFACT + "')" + " or " + SpringEvalExpressions.IS_CONTROLLER)
ArtifactStream getArtifactStream(@NotEmpty String sha1Hash, long softwareModuleId, final boolean isEncrypted); ArtifactStream getArtifactStream(@NotEmpty String sha1Hash, long softwareModuleId, final boolean isEncrypted);
/** /**

View File

@@ -74,7 +74,7 @@ public interface SystemManagement {
/** /**
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()} * @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()}
*/ */
@PreAuthorize("hasAuthority('" + SpPermission.READ_REPOSITORY + "')" + " or " @PreAuthorize("hasAuthority('" + SpPermission.READ_DISTRIBUTION_SET + "')" + " or "
+ "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or " + "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or "
+ "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or " + "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or "
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
@@ -83,7 +83,7 @@ public interface SystemManagement {
/** /**
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()} without details ({@link TenantMetaData#getDefaultDsType()}) * @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()} without details ({@link TenantMetaData#getDefaultDsType()})
*/ */
@PreAuthorize("hasAuthority('" + SpPermission.READ_REPOSITORY + "')" + " or " @PreAuthorize("hasAuthority('" + SpPermission.READ_DISTRIBUTION_SET + "')" + " or "
+ "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or " + "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or "
+ "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or " + "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or "
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)

View File

@@ -19,6 +19,7 @@ import org.aopalliance.intercept.MethodInvocation;
import org.eclipse.hawkbit.im.authentication.Hierarchy; import org.eclipse.hawkbit.im.authentication.Hierarchy;
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties; import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@@ -38,6 +39,7 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.util.ObjectUtils;
import org.springframework.util.function.SingletonSupplier; import org.springframework.util.function.SingletonSupplier;
/** /**
@@ -52,8 +54,16 @@ public class RepositoryConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
static RoleHierarchy roleHierarchy() { @SuppressWarnings("java:S3358") // java:S3358 better readable this way
return RoleHierarchyImpl.fromHierarchy(Hierarchy.DEFAULT); RoleHierarchy roleHierarchy(
// if configured replaces the hierarchy completely
@Value("${org.eclipse.hawkbit.hierarchy:}") final String hierarchy,
// if the "hierarchy" property is empty, and this property is configured it is appended to the default hierarchy
@Value("${org.eclipse.hawkbit.hierarchy.ext:}") final String hierarchyExt) {
return RoleHierarchyImpl.fromHierarchy(
ObjectUtils.isEmpty(hierarchy)
? (ObjectUtils.isEmpty(hierarchyExt) ? Hierarchy.DEFAULT : Hierarchy.DEFAULT + hierarchyExt)
: hierarchy);
} }
@Bean @Bean

View File

@@ -13,7 +13,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
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_REPOSITORY;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET; import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_DISTRIBUTION_SET; import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET; import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET;
@@ -61,7 +60,6 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
final Action permittedAction = testdataFactory.performAssignment(permitted); final Action permittedAction = testdataFactory.performAssignment(permitted);
runAs(withUser("user", runAs(withUser("user",
READ_REPOSITORY,
READ_DISTRIBUTION_SET + "/id==" + permitted.getId(), READ_DISTRIBUTION_SET + "/id==" + permitted.getId(),
READ_TARGET +"/controllerId==" + permittedAction.getTarget().getControllerId()), () -> { READ_TARGET +"/controllerId==" + permittedAction.getTarget().getControllerId()), () -> {
final Long permittedActionId = permitted.getId(); final Long permittedActionId = permitted.getId();
@@ -114,7 +112,6 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
final SoftwareModule swModule = testdataFactory.createSoftwareModuleOs(); final SoftwareModule swModule = testdataFactory.createSoftwareModuleOs();
runAs(withUser("user", runAs(withUser("user",
READ_REPOSITORY,
READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(), READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(),
UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId()), () -> { UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId()), () -> {
// verify distributionSetManagement#assignSoftwareModules // verify distributionSetManagement#assignSoftwareModules
@@ -177,7 +174,6 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
distributionSetManagement.assignTag(Arrays.asList(permitted.getId(), readOnly.getId(), hidden.getId()), dsTagId); distributionSetManagement.assignTag(Arrays.asList(permitted.getId(), readOnly.getId(), hidden.getId()), dsTagId);
runAs(withUser("user", runAs(withUser("user",
READ_REPOSITORY,
READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(), READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(),
UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId()), () -> { UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId()), () -> {
assertThat(distributionSetManagement.findByTag(dsTagId, Pageable.unpaged()).get().map(Identifiable::getId) assertThat(distributionSetManagement.findByTag(dsTagId, Pageable.unpaged()).get().map(Identifiable::getId)
@@ -245,7 +241,6 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
.create(TargetFilterQueryManagement.Create.builder().name("test").query("id==*").build()); .create(TargetFilterQueryManagement.Create.builder().name("test").query("id==*").build());
runAs(withUser("user", runAs(withUser("user",
READ_REPOSITORY,
READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(), READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(),
UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId(), UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId(),
// read / update target needed to update target filter query // read / update target needed to update target filter query

View File

@@ -13,7 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_ROLLOUT; import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_ROLLOUT;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_REPOSITORY; import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_ROLLOUT; import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_ROLLOUT;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET; import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET; import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET;
@@ -126,8 +126,7 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
runAs(withUser("user", runAs(withUser("user",
READ_TARGET + "/controllerId==" + permittedTarget.getControllerId() + " or controllerId==" + readOnlyTargetControllerId, READ_TARGET + "/controllerId==" + permittedTarget.getControllerId() + " or controllerId==" + readOnlyTargetControllerId,
UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId(), UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId()),
READ_REPOSITORY),
() -> { () -> {
// verify targetManagement#findByTag // verify targetManagement#findByTag
assertThat( assertThat(
@@ -194,9 +193,9 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
.getControllerId(); .getControllerId();
runAs(withUser("user", runAs(withUser("user",
READ_DISTRIBUTION_SET,
READ_TARGET + "/controllerId==" + permittedTarget.getControllerId(), READ_TARGET + "/controllerId==" + permittedTarget.getControllerId(),
UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId(), UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () -> {
READ_REPOSITORY), () -> {
final Long dsId = ds.getId(); final Long dsId = ds.getId();
assertThat(assignDistributionSet(dsId, permittedTarget.getControllerId()).getAssigned()).isEqualTo(1); assertThat(assignDistributionSet(dsId, permittedTarget.getControllerId()).getAssigned()).isEqualTo(1);
// assigning of not allowed target behaves as not found // assigning of not allowed target behaves as not found
@@ -220,9 +219,9 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
.create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build()); .create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build());
runAs(withUser("user", runAs(withUser("user",
READ_DISTRIBUTION_SET,
READ_TARGET + "/controllerId==" + manageableTarget.getControllerId() + " or controllerId==" + readOnlyTarget.getControllerId(), READ_TARGET + "/controllerId==" + manageableTarget.getControllerId() + " or controllerId==" + readOnlyTarget.getControllerId(),
UPDATE_TARGET + "/controllerId==" + manageableTarget.getControllerId(), UPDATE_TARGET + "/controllerId==" + manageableTarget.getControllerId()), () -> {
READ_REPOSITORY), () -> {
final Long firstDsId = firstDs.getId(); final Long firstDsId = firstDs.getId();
// assignment is permitted for manageableTarget // assignment is permitted for manageableTarget
assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned()).isEqualTo(1); assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned()).isEqualTo(1);
@@ -253,10 +252,10 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
final List<Target> hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5"); final List<Target> hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5");
runAs(withUser("user", runAs(withUser("user",
READ_DISTRIBUTION_SET,
READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" + READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" +
" or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")", " or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")",
UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")", UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")",
READ_REPOSITORY,
CREATE_ROLLOUT, READ_ROLLOUT), () -> { CREATE_ROLLOUT, READ_ROLLOUT), () -> {
final Rollout rollout = testdataFactory.createRolloutByVariables( final Rollout rollout = testdataFactory.createRolloutByVariables(
"testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5"); "testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5");
@@ -299,7 +298,7 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" + READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" +
" or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")", " or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")",
UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")", UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")",
READ_REPOSITORY + "/id==" + distributionSet.getId()), () -> { READ_DISTRIBUTION_SET + "/id==" + distributionSet.getId()), () -> {
targetFilterQueryManagement.updateAutoAssignDS( targetFilterQueryManagement.updateAutoAssignDS(
new AutoAssignDistributionSetUpdate(targetFilterQuery.getId()).ds(distributionSet.getId())); new AutoAssignDistributionSetUpdate(targetFilterQuery.getId()).ds(distributionSet.getId()));

View File

@@ -400,7 +400,8 @@ class ArtifactManagementTest extends AbstractJpaIntegrationTest {
*/ */
@Test @Test
@WithUser(allSpPermissions = true, removeFromAllPermission = { @WithUser(allSpPermissions = true, removeFromAllPermission = {
SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT, SpRole.CONTROLLER_ROLE, SpRole.CONTROLLER_ROLE_ANONYMOUS }) SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT, SpPermission.SOFTWARE_MODULE_DOWNLOAD_ARTIFACT,
SpRole.CONTROLLER_ROLE, SpRole.CONTROLLER_ROLE_ANONYMOUS })
void getArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() { void getArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
assertThatExceptionOfType(InsufficientPermissionException.class) assertThatExceptionOfType(InsufficientPermissionException.class)
.as("Should not have worked with missing permission.") .as("Should not have worked with missing permission.")

View File

@@ -16,7 +16,6 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
@@ -56,21 +55,20 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAutoAssignment"); final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAutoAssignment");
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation( final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE); Collections.singletonList(invalidationTestData.distributionSet().getId()), ActionCancellationType.NONE);
final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation); final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation);
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0); assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0);
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
rolloutHandler.handleAll(); rolloutHandler.handleAll();
assertThat(targetFilterQueryManagement.find(invalidationTestData.getTargetFilterQuery().getId()).get() assertThat(targetFilterQueryManagement.find(invalidationTestData.targetFilterQuery().getId()).orElseThrow()
.getAutoAssignDistributionSet()).isNull(); .getAutoAssignDistributionSet()).isNull();
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus()) assertThat(rolloutRepository.findById(invalidationTestData.rollout().getId()).orElseThrow().getStatus())
.isNotIn(RolloutStatus.STOPPING, RolloutStatus.FINISHED); .isNotIn(RolloutStatus.STOPPING, RolloutStatus.FINISHED);
for (final Target target : invalidationTestData.getTargets()) { for (final Target target : invalidationTestData.targets()) {
// if status is pending, the assignment has not been canceled // if status is pending, the assignment has not been canceled
assertThat(targetRepository.findById(target.getId()).get().getUpdateStatus()) assertThat(targetRepository.findById(target.getId()).orElseThrow().getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
.isEqualTo(TargetUpdateStatus.PENDING);
assertThat(findActionsByTarget(target)).hasSize(1); assertThat(findActionsByTarget(target)).hasSize(1);
assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.RUNNING); assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.RUNNING);
} }
@@ -81,27 +79,25 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
*/ */
@Test @Test
void verifyInvalidateDistributionSetDoesNotStopRollouts() { void verifyInvalidateDistributionSetDoesNotStopRollouts() {
final InvalidationTestData invalidationTestData = createInvalidationTestData( final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopRollouts");
"verifyInvalidateDistributionSetStopRollouts");
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation( final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE); Collections.singletonList(invalidationTestData.distributionSet().getId()), ActionCancellationType.NONE);
final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation); final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation);
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0); assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0);
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
rolloutHandler.handleAll(); rolloutHandler.handleAll();
assertThat(targetFilterQueryManagement.find(invalidationTestData.getTargetFilterQuery().getId()).get() assertThat(targetFilterQueryManagement.find(invalidationTestData.targetFilterQuery().getId()).orElseThrow()
.getAutoAssignDistributionSet()).isNull(); .getAutoAssignDistributionSet()).isNull();
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus()) assertThat(rolloutRepository.findById(invalidationTestData.rollout().getId()).orElseThrow().getStatus())
.isEqualTo(RolloutStatus.READY); .isEqualTo(RolloutStatus.READY);
assertNoScheduledActionsExist(invalidationTestData.getRollout()); assertNoScheduledActionsExist(invalidationTestData.rollout());
for (final Target target : invalidationTestData.getTargets()) { for (final Target target : invalidationTestData.targets()) {
// if status is pending, the assignment has not been canceled // if status is pending, the assignment has not been canceled
assertThat( assertThat(targetRepository.findById(invalidationTestData.targets().get(0).getId()).orElseThrow().getUpdateStatus())
targetRepository.findById(invalidationTestData.getTargets().get(0).getId()).get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.PENDING); .isEqualTo(TargetUpdateStatus.PENDING);
assertThat(findActionsByTarget(target)).hasSize(1); assertThat(findActionsByTarget(target)).hasSize(1);
assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.RUNNING); assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.RUNNING);
@@ -113,23 +109,22 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
*/ */
@Test @Test
void verifyInvalidateDistributionSetStopAllAndForceCancel() { void verifyInvalidateDistributionSetStopAllAndForceCancel() {
final InvalidationTestData invalidationTestData = createInvalidationTestData( final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAllAndForceCancel");
"verifyInvalidateDistributionSetStopAllAndForceCancel");
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation( final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.FORCE); Collections.singletonList(invalidationTestData.distributionSet().getId()), ActionCancellationType.FORCE);
final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation); final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation);
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1); assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1);
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
rolloutHandler.handleAll(); rolloutHandler.handleAll();
assertThat(targetFilterQueryManagement.find(invalidationTestData.getTargetFilterQuery().getId()).get() assertThat(targetFilterQueryManagement.find(invalidationTestData.targetFilterQuery().getId()).orElseThrow()
.getAutoAssignDistributionSet()).isNull(); .getAutoAssignDistributionSet()).isNull();
// rollout should be deleted when force invalidation // rollout should be deleted when force invalidation
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId())).isEmpty(); assertThat(rolloutRepository.findById(invalidationTestData.rollout().getId())).isEmpty();
assertNoScheduledActionsExist(invalidationTestData.getRollout()); assertNoScheduledActionsExist(invalidationTestData.rollout());
assertAllRolloutActionsAreCancelled(invalidationTestData.getRollout()); assertAllRolloutActionsAreCancelled(invalidationTestData.rollout());
} }
/** /**
@@ -140,19 +135,18 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAll"); final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAll");
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation( final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.SOFT); Collections.singletonList(invalidationTestData.distributionSet().getId()), ActionCancellationType.SOFT);
final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation); final DistributionSetInvalidationCount distributionSetInvalidationCount = countEntitiesForInvalidation(distributionSetInvalidation);
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1); assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1);
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
assertThat(targetFilterQueryManagement.find(invalidationTestData.getTargetFilterQuery().getId()).get() assertThat(targetFilterQueryManagement.find(invalidationTestData.targetFilterQuery().getId()).orElseThrow()
.getAutoAssignDistributionSet()).isNull(); .getAutoAssignDistributionSet()).isNull();
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus()) assertThat(rolloutRepository.findById(invalidationTestData.rollout().getId()).orElseThrow().getStatus())
.isIn(RolloutStatus.STOPPING, RolloutStatus.FINISHED); .isIn(RolloutStatus.STOPPING, RolloutStatus.FINISHED);
for (final Target target : invalidationTestData.getTargets()) { for (final Target target : invalidationTestData.targets()) {
assertThat(targetRepository.findById(target.getId()).get().getUpdateStatus()) assertThat(targetRepository.findById(target.getId()).orElseThrow().getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
.isEqualTo(TargetUpdateStatus.PENDING);
assertThat(findActionsByTarget(target)).hasSize(1); assertThat(findActionsByTarget(target)).hasSize(1);
assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.CANCELING); assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.CANCELING);
} }
@@ -182,74 +176,63 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
void verifyInvalidateInvalidatedDistributionSetDontThrowsException() { void verifyInvalidateInvalidatedDistributionSetDontThrowsException() {
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet(); final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
distributionSetInvalidationManagement.invalidateDistributionSet( distributionSetInvalidationManagement.invalidateDistributionSet(
new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()), new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()), ActionCancellationType.SOFT));
ActionCancellationType.SOFT));
} }
/** /**
* Verify that a user that has authority READ_REPOSITORY and UPDATE_REPOSITORY is allowed to invalidate a distribution set * Verify that a user that has authority READ_DISTRIBUTION_SET and UPDATE_DISTRIBUTION_SET is allowed to invalidate a distribution set
*/ */
@Test @Test
@WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY" }) @WithUser(authorities = { "READ_DISTRIBUTION_SET", "UPDATE_DISTRIBUTION_SET" })
void verifyInvalidateWithReadAndUpdateRepoAuthority() { void verifyInvalidateWithReadAndUpdateRepoAuthority() {
final InvalidationTestData invalidationTestData = systemSecurityContext final InvalidationTestData invalidationTestData = systemSecurityContext
.runAsSystem(() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAuthority")); .runAsSystem(() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAuthority"));
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation( distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE)); Collections.singletonList(invalidationTestData.distributionSet().getId()), ActionCancellationType.NONE));
assertThat( assertThat(distributionSetRepository.findById(invalidationTestData.distributionSet().getId()).orElseThrow().isValid()).isFalse();
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
.isFalse();
} }
/** /**
* Verify that a user that has authority READ_REPOSITORY, UPDATE_REPOSITORY and UPDATE_TARGET is allowed to invalidate a distribution set only without canceling rollouts * Verify that a user that has authority READ_DISTRIBUTION_SET, UPDATE_DISTRIBUTION_SET and UPDATE_TARGET is allowed to invalidate a distribution set only without canceling rollouts
*/ */
@Test @Test
@WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY", "UPDATE_TARGET" }) @WithUser(authorities = { "READ_DISTRIBUTION_SET", "UPDATE_DISTRIBUTION_SET", "UPDATE_TARGET" })
void verifyInvalidateWithReadAndUpdateRepoAndUpdateTargetAuthority() { void verifyInvalidateWithReadAndUpdateRepoAndUpdateTargetAuthority() {
final InvalidationTestData invalidationTestData = systemSecurityContext.runAsSystem( final InvalidationTestData invalidationTestData = systemSecurityContext.runAsSystem(
() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority")); () -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority"));
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation( final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
List.of(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.SOFT); List.of(invalidationTestData.distributionSet().getId()), ActionCancellationType.SOFT);
assertThatExceptionOfType(InsufficientPermissionException.class) assertThatExceptionOfType(InsufficientPermissionException.class)
.as("Insufficient permission exception expected") .as("Insufficient permission exception expected")
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation)); .isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation));
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation( distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE)); Collections.singletonList(invalidationTestData.distributionSet().getId()), ActionCancellationType.NONE));
assertThat( assertThat(distributionSetRepository.findById(invalidationTestData.distributionSet().getId()).orElseThrow().isValid()).isFalse();
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
.isFalse();
} }
/** /**
* Verify that a user that has authority READ_REPOSITORY, UPDATE_REPOSITORY, UPDATE_ROLLOUT and UPDATE_TARGET is allowed to invalidate a distribution * Verify that a user that has authority READ_DISTRIBUTION_SET, UPDATE_DISTRIBUTION_SET, UPDATE_ROLLOUT and UPDATE_TARGET is allowed to invalidate a distribution
*/ */
@Test @Test
@WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY", "UPDATE_TARGET", "UPDATE_ROLLOUT" }) @WithUser(authorities = { "READ_DISTRIBUTION_SET", "UPDATE_DISTRIBUTION_SET", "UPDATE_TARGET", "UPDATE_ROLLOUT" })
void verifyInvalidateWithReadAndUpdateRepoAndUpdateTargetAndUpdateRolloutAuthority() { void verifyInvalidateWithReadAndUpdateRepoAndUpdateTargetAndUpdateRolloutAuthority() {
final InvalidationTestData invalidationTestData = systemSecurityContext.runAsSystem( final InvalidationTestData invalidationTestData = systemSecurityContext.runAsSystem(
() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority")); () -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority"));
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation( distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.SOFT)); List.of(invalidationTestData.distributionSet().getId()), ActionCancellationType.SOFT));
assertThat( assertThat(distributionSetRepository.findById(invalidationTestData.distributionSet().getId()).orElseThrow().isValid()).isFalse();
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
.isFalse();
} }
private void assertNoScheduledActionsExist(final Rollout rollout) { private void assertNoScheduledActionsExist(final Rollout rollout) {
assertThat( assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Status.SCHEDULED).getTotalElements()).isZero();
actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Status.SCHEDULED).getTotalElements())
.isZero();
} }
private void assertAllRolloutActionsAreCancelled(final Rollout rollout) { private void assertAllRolloutActionsAreCancelled(final Rollout rollout) {
assertThat( assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Status.CANCELED).getTotalElements()).isZero();
actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Status.CANCELED).getTotalElements())
.isZero();
} }
private InvalidationTestData createInvalidationTestData(final String testName) { private InvalidationTestData createInvalidationTestData(final String testName) {
@@ -276,8 +259,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
return actionRepository.findAll(ActionSpecifications.byTargetControllerId(target.getControllerId())); return actionRepository.findAll(ActionSpecifications.byTargetControllerId(target.getControllerId()));
} }
private DistributionSetInvalidationCount countEntitiesForInvalidation( private DistributionSetInvalidationCount countEntitiesForInvalidation(final DistributionSetInvalidation distributionSetInvalidation) {
final DistributionSetInvalidation distributionSetInvalidation) {
return systemSecurityContext.runAsSystem(() -> { return systemSecurityContext.runAsSystem(() -> {
final Collection<Long> setIds = distributionSetInvalidation.getDistributionSetIds(); final Collection<Long> setIds = distributionSetInvalidation.getDistributionSetIds();
final long rolloutsCount = distributionSetInvalidation.getActionCancellationType() != ActionCancellationType.NONE ? countRolloutsForInvalidation(setIds) : 0; final long rolloutsCount = distributionSetInvalidation.getActionCancellationType() != ActionCancellationType.NONE ? countRolloutsForInvalidation(setIds) : 0;
@@ -317,22 +299,6 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
.sum(); .sum();
} }
@Data private record InvalidationTestData(
private static class InvalidationTestData { DistributionSet distributionSet, List<Target> targets, TargetFilterQuery targetFilterQuery, Rollout rollout) {}
private final DistributionSet distributionSet;
private final List<Target> targets;
private final TargetFilterQuery targetFilterQuery;
private final Rollout rollout;
public InvalidationTestData(
final DistributionSet distributionSet, final List<Target> targets,
final TargetFilterQuery targetFilterQuery, final Rollout rollout) {
super();
this.distributionSet = distributionSet;
this.targets = targets;
this.targetFilterQuery = targetFilterQuery;
this.rollout = rollout;
}
}
} }

View File

@@ -1393,10 +1393,10 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
final WithUser userWithoutHandleRollout = SecurityContextSwitch.withUser( final WithUser userWithoutHandleRollout = SecurityContextSwitch.withUser(
"user_without_handle_rollout", "user_without_handle_rollout",
SpPermission.READ_REPOSITORY, SpPermission.READ_TARGET, SpPermission.CREATE_ROLLOUT); SpPermission.READ_DISTRIBUTION_SET, SpPermission.READ_TARGET, SpPermission.CREATE_ROLLOUT);
final WithUser userWithHandleRollout = SecurityContextSwitch.withUser( final WithUser userWithHandleRollout = SecurityContextSwitch.withUser(
"user_with_handle_rollout", "user_with_handle_rollout",
SpPermission.READ_REPOSITORY, SpPermission.READ_TARGET, SpPermission.CREATE_ROLLOUT, SpPermission.HANDLE_ROLLOUT); SpPermission.READ_DISTRIBUTION_SET, SpPermission.READ_TARGET, SpPermission.CREATE_ROLLOUT, SpPermission.HANDLE_ROLLOUT);
final WithUser userWithSystemRole = SecurityContextSwitch.withUser( final WithUser userWithSystemRole = SecurityContextSwitch.withUser(
"user_with_system_role", "user_with_system_role",
SpRole.SYSTEM_ROLE); SpRole.SYSTEM_ROLE);

View File

@@ -21,7 +21,6 @@ public class Hierarchy {
SpPermission.SOFTWARE_MODULE_HIERARCHY + SpPermission.SOFTWARE_MODULE_HIERARCHY +
SpPermission.DISTRIBUTION_SET_HIERARCHY + SpPermission.DISTRIBUTION_SET_HIERARCHY +
SpPermission.TENANT_CONFIGURATION_HIERARCHY + SpPermission.TENANT_CONFIGURATION_HIERARCHY +
SpRole.DEFAULT_ROLE_HIERARCHY + SpRole.DEFAULT_ROLE_HIERARCHY;
SpPermission.REPOSITORY_HIERARCHY;
// @formatter:on // @formatter:on
} }

View File

@@ -67,11 +67,14 @@ public final class SpPermission {
public static final String READ_DISTRIBUTION_SET = READ_PREFIX + DISTRIBUTION_SET; public static final String READ_DISTRIBUTION_SET = READ_PREFIX + DISTRIBUTION_SET;
public static final String UPDATE_DISTRIBUTION_SET = UPDATE_PREFIX + DISTRIBUTION_SET; public static final String UPDATE_DISTRIBUTION_SET = UPDATE_PREFIX + DISTRIBUTION_SET;
public static final String CREATE_REPOSITORY = "CREATE_REPOSITORY"; /**
public static final String READ_REPOSITORY = "READ_REPOSITORY"; * Deprecated since 0.10.0, use {@link #SOFTWARE_MODULE_DOWNLOAD_ARTIFACT} instead
public static final String UPDATE_REPOSITORY = "UPDATE_REPOSITORY"; *
public static final String DELETE_REPOSITORY = "DELETE_REPOSITORY"; * @deprecated since 0.10.0, use {@link #SOFTWARE_MODULE_DOWNLOAD_ARTIFACT} instead
*/
@Deprecated(since = "0.10.0", forRemoval = true)
public static final String DOWNLOAD_REPOSITORY_ARTIFACT = "DOWNLOAD_REPOSITORY_ARTIFACT"; public static final String DOWNLOAD_REPOSITORY_ARTIFACT = "DOWNLOAD_REPOSITORY_ARTIFACT";
public static final String SOFTWARE_MODULE_DOWNLOAD_ARTIFACT = SOFTWARE_MODULE + "_DOWNLOAD_ARTIFACT";
/** /**
* Permission to read the tenant settings. * Permission to read the tenant settings.
@@ -124,27 +127,6 @@ public final class SpPermission {
TENANT_CONFIGURATION + IMPLY_UPDATE + TENANT_CONFIGURATION + "\n" + TENANT_CONFIGURATION + IMPLY_UPDATE + TENANT_CONFIGURATION + "\n" +
TENANT_CONFIGURATION + IMPLY_DELETE + TENANT_CONFIGURATION + "\n" + TENANT_CONFIGURATION + IMPLY_DELETE + TENANT_CONFIGURATION + "\n" +
TENANT_CONFIGURATION + " > " + READ_GATEWAY_SECURITY_TOKEN + "\n"; TENANT_CONFIGURATION + " > " + READ_GATEWAY_SECURITY_TOKEN + "\n";
public static final String REPOSITORY_HIERARCHY =
CREATE_REPOSITORY + IMPLY_CREATE + TARGET_TYPE + "\n" +
READ_REPOSITORY + IMPLY_READ + TARGET_TYPE + "\n" +
UPDATE_REPOSITORY + IMPLY_UPDATE + TARGET_TYPE + "\n" +
DELETE_REPOSITORY + IMPLY_DELETE + TARGET_TYPE + "\n" +
CREATE_REPOSITORY + IMPLY_CREATE + SOFTWARE_MODULE + "\n" +
READ_REPOSITORY + IMPLY_READ + SOFTWARE_MODULE + "\n" +
UPDATE_REPOSITORY + IMPLY_UPDATE + SOFTWARE_MODULE + "\n" +
DELETE_REPOSITORY + IMPLY_DELETE + SOFTWARE_MODULE + "\n" +
CREATE_REPOSITORY + IMPLY_CREATE + SOFTWARE_MODULE_TYPE + "\n" +
READ_REPOSITORY + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
UPDATE_REPOSITORY + IMPLY_UPDATE + SOFTWARE_MODULE_TYPE + "\n" +
DELETE_REPOSITORY + IMPLY_DELETE + SOFTWARE_MODULE_TYPE + "\n" +
CREATE_REPOSITORY + IMPLY_CREATE + DISTRIBUTION_SET + "\n" +
READ_REPOSITORY + IMPLY_READ + DISTRIBUTION_SET + "\n" +
UPDATE_REPOSITORY + IMPLY_UPDATE + DISTRIBUTION_SET + "\n" +
DELETE_REPOSITORY + IMPLY_DELETE + DISTRIBUTION_SET + "\n" +
CREATE_REPOSITORY + IMPLY_CREATE + DISTRIBUTION_SET_TYPE + "\n" +
READ_REPOSITORY + IMPLY_READ + DISTRIBUTION_SET_TYPE + "\n" +
UPDATE_REPOSITORY + IMPLY_UPDATE + DISTRIBUTION_SET_TYPE + "\n" +
DELETE_REPOSITORY + IMPLY_DELETE + DISTRIBUTION_SET_TYPE + "\n";
// @formatter:on // @formatter:on
private static final SingletonSupplier<List<String>> ALL_AUTHORITIES = SingletonSupplier.of(() -> { private static final SingletonSupplier<List<String>> ALL_AUTHORITIES = SingletonSupplier.of(() -> {
@@ -163,7 +145,7 @@ public final class SpPermission {
// special // special
allPermissions.add(READ_TARGET_SECURITY_TOKEN); allPermissions.add(READ_TARGET_SECURITY_TOKEN);
allPermissions.add(READ_GATEWAY_SECURITY_TOKEN); allPermissions.add(READ_GATEWAY_SECURITY_TOKEN);
allPermissions.add(DOWNLOAD_REPOSITORY_ARTIFACT); allPermissions.add(SOFTWARE_MODULE_DOWNLOAD_ARTIFACT);
allPermissions.add(APPROVE_ROLLOUT); allPermissions.add(APPROVE_ROLLOUT);
allPermissions.add(HANDLE_ROLLOUT); allPermissions.add(HANDLE_ROLLOUT);
@@ -176,6 +158,9 @@ public final class SpPermission {
// system permission, (!) take care with // system permission, (!) take care with
allPermissions.add(SYSTEM_ADMIN); allPermissions.add(SYSTEM_ADMIN);
// add deprecated permissions
allPermissions.add(DOWNLOAD_REPOSITORY_ARTIFACT);
return Collections.unmodifiableList(allPermissions); return Collections.unmodifiableList(allPermissions);
}); });

View File

@@ -46,6 +46,24 @@ public final class SpRole {
TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET_TYPE + LINE_BREAK + TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET_TYPE + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.UPDATE_TARGET_TYPE + LINE_BREAK + TARGET_ADMIN + IMPLIES + SpPermission.UPDATE_TARGET_TYPE + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.DELETE_TARGET_TYPE + LINE_BREAK; TARGET_ADMIN + IMPLIES + SpPermission.DELETE_TARGET_TYPE + LINE_BREAK;
public static final String REPOSITORY_ADMIN_HIERARCHY =
REPOSITORY_ADMIN + IMPLIES + SpPermission.CREATE_PREFIX + SpPermission.SOFTWARE_MODULE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_PREFIX + SpPermission.SOFTWARE_MODULE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_PREFIX + SpPermission.SOFTWARE_MODULE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.DELETE_PREFIX + SpPermission.SOFTWARE_MODULE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.SOFTWARE_MODULE_DOWNLOAD_ARTIFACT + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.CREATE_PREFIX + SpPermission.SOFTWARE_MODULE_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_PREFIX + SpPermission.SOFTWARE_MODULE_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_PREFIX + SpPermission.SOFTWARE_MODULE_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.DELETE_PREFIX + SpPermission.SOFTWARE_MODULE_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.CREATE_PREFIX + SpPermission.DISTRIBUTION_SET + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_PREFIX + SpPermission.DISTRIBUTION_SET + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_PREFIX + SpPermission.DISTRIBUTION_SET + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.DELETE_PREFIX + SpPermission.DISTRIBUTION_SET + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.CREATE_PREFIX + SpPermission.DISTRIBUTION_SET_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_PREFIX + SpPermission.DISTRIBUTION_SET_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_PREFIX + SpPermission.DISTRIBUTION_SET_TYPE + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.DELETE_PREFIX + SpPermission.DISTRIBUTION_SET_TYPE + LINE_BREAK;
public static final String ROLLOUT_ADMIN_HIERARCHY = public static final String ROLLOUT_ADMIN_HIERARCHY =
ROLLOUT_ADMIN + IMPLIES + SpPermission.CREATE_ROLLOUT + LINE_BREAK + ROLLOUT_ADMIN + IMPLIES + SpPermission.CREATE_ROLLOUT + LINE_BREAK +
ROLLOUT_ADMIN + IMPLIES + SpPermission.READ_ROLLOUT + LINE_BREAK + ROLLOUT_ADMIN + IMPLIES + SpPermission.READ_ROLLOUT + LINE_BREAK +
@@ -56,23 +74,17 @@ public final class SpRole {
public static final String TENANT_ADMIN_HIERARCHY = public static final String TENANT_ADMIN_HIERARCHY =
TENANT_ADMIN + IMPLIES + TARGET_ADMIN + LINE_BREAK + TENANT_ADMIN + IMPLIES + TARGET_ADMIN + LINE_BREAK +
TENANT_ADMIN + IMPLIES + REPOSITORY_ADMIN + LINE_BREAK + TENANT_ADMIN + IMPLIES + REPOSITORY_ADMIN + LINE_BREAK +
TENANT_ADMIN + IMPLIES + SpPermission.TENANT_CONFIGURATION + LINE_BREAK + TENANT_ADMIN + IMPLIES + ROLLOUT_ADMIN + LINE_BREAK +
TENANT_ADMIN + IMPLIES + ROLLOUT_ADMIN + LINE_BREAK; TENANT_ADMIN + IMPLIES + SpPermission.TENANT_CONFIGURATION + LINE_BREAK;
public static final String SYSTEM_ROLE_HIERARCHY = public static final String SYSTEM_ROLE_HIERARCHY =
SYSTEM_ROLE + IMPLIES + TENANT_ADMIN + LINE_BREAK + SYSTEM_ROLE + IMPLIES + TENANT_ADMIN + LINE_BREAK +
SYSTEM_ROLE + IMPLIES + SpPermission.SYSTEM_ADMIN + LINE_BREAK; SYSTEM_ROLE + IMPLIES + SpPermission.SYSTEM_ADMIN + LINE_BREAK;
public static final String REPOSITORY_ADMIN_HIERARCHY =
REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_REPOSITORY + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_REPOSITORY + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.CREATE_REPOSITORY + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.DELETE_REPOSITORY + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT + LINE_BREAK;
public static final String DEFAULT_ROLE_HIERARCHY = public static final String DEFAULT_ROLE_HIERARCHY =
TARGET_ADMIN_HIERARCHY + TARGET_ADMIN_HIERARCHY +
REPOSITORY_ADMIN_HIERARCHY +
ROLLOUT_ADMIN_HIERARCHY + ROLLOUT_ADMIN_HIERARCHY +
TENANT_ADMIN_HIERARCHY + TENANT_ADMIN_HIERARCHY +
SYSTEM_ROLE_HIERARCHY + SYSTEM_ROLE_HIERARCHY;
REPOSITORY_ADMIN_HIERARCHY;
// @formatter:on // @formatter:on
} }