Add fine grained sm/ds type permission (#2649)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-09-04 15:05:32 +03:00
committed by GitHub
parent 2e97d67489
commit 2c995b3665
17 changed files with 139 additions and 74 deletions

View File

@@ -431,7 +431,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
*/ */
@Test @Test
@WithUser(allSpPermissions = false, authorities = { SpPermission.READ_TARGET, SpPermission.CREATE_TARGET, @WithUser(allSpPermissions = false, authorities = { SpPermission.READ_TARGET, SpPermission.CREATE_TARGET,
SpPermission.READ_TARGET_SEC_TOKEN }) SpPermission.READ_TARGET_SECURITY_TOKEN })
void securityTokenIsInResponseWithCorrectPermission() throws Exception { void securityTokenIsInResponseWithCorrectPermission() throws Exception {
final String knownControllerId = "knownControllerId"; final String knownControllerId = "knownControllerId";

View File

@@ -71,7 +71,9 @@ class MgmtTargetTypeResourceTest extends AbstractManagementApiIntegrationTest {
@Test @Test
@WithUser( @WithUser(
principal = "targetTypeTester", allSpPermissions = true, principal = "targetTypeTester", allSpPermissions = true,
removeFromAllPermission = { SpPermission.READ_TARGET, SpPermission.READ_TARGET_TYPE }) removeFromAllPermission = {
SpPermission.CREATE_TARGET, SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET, SpPermission.DELETE_TARGET,
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))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())

View File

@@ -314,7 +314,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
// TODO - should be able to read with TENANT_CONFIGURATION but somehow here the role hierarchy doesn't play // TODO - should be able to read with TENANT_CONFIGURATION but somehow here the role hierarchy doesn't play
// checked in mgmt / update server runtime PreAuthorizeEnabledTest // checked in mgmt / update server runtime PreAuthorizeEnabledTest
callAs(withUser("tenant_admin", SpPermission.READ_TENANT_CONFIGURATION, SpPermission.READ_GATEWAY_SEC_TOKEN), () -> { callAs(withUser("tenant_admin", SpPermission.READ_TENANT_CONFIGURATION, SpPermission.READ_GATEWAY_SECURITY_TOKEN), () -> {
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs")) mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs"))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andDo(m -> System.out.println("-> 1: " + m.getResponse().getContentAsString())) .andDo(m -> System.out.println("-> 1: " + m.getResponse().getContentAsString()))

View File

@@ -41,7 +41,7 @@ public interface DistributionSetTypeManagement<T extends DistributionSetType>
@Override @Override
default String permissionGroup() { default String permissionGroup() {
return SpPermission.DISTRIBUTION_SET; return SpPermission.DISTRIBUTION_SET_TYPE;
} }
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)

View File

@@ -35,7 +35,7 @@ public interface SoftwareModuleTypeManagement<T extends SoftwareModuleType>
@Override @Override
default String permissionGroup() { default String permissionGroup() {
return SpPermission.SOFTWARE_MODULE; return SpPermission.SOFTWARE_MODULE_TYPE;
} }
/** /**

View File

@@ -46,19 +46,17 @@ public interface SystemManagement {
Page<String> findTenants(@NotNull Pageable pageable); Page<String> findTenants(@NotNull Pageable pageable);
/** /**
* Runs consumer for each teant as * Runs consumer for each tenant as
* {@link TenantAware#runAsTenant(String, org.eclipse.hawkbit.tenancy.TenantAware.TenantRunner)} * {@link TenantAware#runAsTenant(String, org.eclipse.hawkbit.tenancy.TenantAware.TenantRunner)}
* sliently (i.e. exceptions will be logged but operations will continue for * silently (i.e. exceptions will be logged but operations will continue for further tenants).
* further tenants).
* *
* @param consumer to run as teanant * @param consumer to run as tenant
*/ */
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
void forEachTenant(Consumer<String> consumer); void forEachTenant(Consumer<String> consumer);
/** /**
* Calculated system usage statistics, both overall for the entire system * Calculated system usage statistics, both overall for the entire system and per tenant;
* and per tenant;
* *
* @return SystemUsageReport of the current system * @return SystemUsageReport of the current system
*/ */

View File

@@ -61,7 +61,7 @@ public interface Target extends NamedEntity, Identifiable<Long> {
/** /**
* @return the securityToken if the current security context contains the necessary permission * @return the securityToken if the current security context contains the necessary permission
* {@link org.eclipse.hawkbit.im.authentication.SpPermission#READ_TARGET_SEC_TOKEN} * {@link org.eclipse.hawkbit.im.authentication.SpPermission#READ_TARGET_SECURITY_TOKEN}
* or the current context is executed as system code, otherwise {@code null}. * or the current context is executed as system code, otherwise {@code null}.
*/ */
String getSecurityToken(); String getSecurityToken();

View File

@@ -9,10 +9,17 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.acm; package org.eclipse.hawkbit.repository.jpa.acm;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.DistributionSetFields; import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetTypeFields; import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -26,18 +33,36 @@ public class DefaultAccessControllerConfiguration {
@Bean @Bean
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.target.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "hawkbit.acm.access-controller.target.enabled", havingValue = "true", matchIfMissing = true)
AccessController<JpaTarget> targetAccessController() { AccessController<JpaTarget> targetAccessController() {
return new DefaultAccessController<>(TargetFields.class, "TARGET"); return new DefaultAccessController<>(TargetFields.class, SpPermission.TARGET);
} }
@Bean @Bean
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.target-type.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "hawkbit.acm.access-controller.target-type.enabled", havingValue = "true")
AccessController<JpaTargetType> targetTypeAccessController() { AccessController<JpaTargetType> targetTypeAccessController() {
return new DefaultAccessController<>(TargetTypeFields.class, "TARGET_TYPE"); return new DefaultAccessController<>(TargetTypeFields.class, SpPermission.TARGET_TYPE);
}
@Bean
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.software-module.enabled", havingValue = "true", matchIfMissing = true)
AccessController<JpaSoftwareModule> softwareModuleAccessController() {
return new DefaultAccessController<>(SoftwareModuleFields.class, SpPermission.SOFTWARE_MODULE);
}
@Bean
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.software-module-type.enabled", havingValue = "true")
AccessController<JpaSoftwareModuleType> softwareModuleTypeAccessController() {
return new DefaultAccessController<>(SoftwareModuleTypeFields.class, SpPermission.SOFTWARE_MODULE_TYPE);
} }
@Bean @Bean
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.distribution-set.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "hawkbit.acm.access-controller.distribution-set.enabled", havingValue = "true", matchIfMissing = true)
AccessController<JpaDistributionSet> distributionSetAccessController() { AccessController<JpaDistributionSet> distributionSetAccessController() {
return new DefaultAccessController<>(DistributionSetFields.class, "DISTRIBUTION_SET"); return new DefaultAccessController<>(DistributionSetFields.class, SpPermission.DISTRIBUTION_SET);
}
@Bean
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.distribution-set-type.enabled", havingValue = "true")
AccessController<JpaDistributionSetType> distributionSetTypeAccessController() {
return new DefaultAccessController<>(DistributionSetTypeFields.class, SpPermission.DISTRIBUTION_SET_TYPE);
} }
} }

View File

@@ -51,7 +51,6 @@ import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.PollingTime; import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
@@ -249,9 +248,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) { if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) {
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext(); final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
if (!systemSecurityContext.isCurrentThreadSystemCode() && if (!systemSecurityContext.isCurrentThreadSystemCode() &&
!systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SEC_TOKEN)) { !systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SECURITY_TOKEN)) {
throw new InsufficientPermissionException( throw new InsufficientPermissionException(
"Can't read gateway security token! " + SpPermission.READ_GATEWAY_SEC_TOKEN + " is required!"); "Can't read gateway security token! " + SpPermission.READ_GATEWAY_SECURITY_TOKEN + " is required!");
} }
} }
} }

View File

@@ -216,7 +216,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
@Override @Override
public String getSecurityToken() { public String getSecurityToken() {
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext(); final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
if (systemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) { if (systemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SECURITY_TOKEN)) {
return securityToken; return securityToken;
} }
return null; return null;

View File

@@ -31,12 +31,14 @@ import org.eclipse.hawkbit.repository.model.TargetType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
/** /**
* Feature: Component Tests - Access Control<br/> * Feature: Component Tests - Access Control<br/>
* Story: Test Target Type Access Controller * Story: Test Target Type Access Controller
*/ */
@ContextConfiguration(classes = { DefaultAccessControllerConfiguration.class }) @ContextConfiguration(classes = { DefaultAccessControllerConfiguration.class })
@TestPropertySource(properties = { "hawkbit.acm.access-controller.target-type.enabled=true" })
class TargetTypeAccessControllerTest extends AbstractJpaIntegrationTest { class TargetTypeAccessControllerTest extends AbstractJpaIntegrationTest {
/** /**
@@ -92,8 +94,8 @@ class TargetTypeAccessControllerTest extends AbstractJpaIntegrationTest {
final TargetType readOnlyTargetType = targetTypeManagement.create(Create.builder().name("type2").build()); final TargetType readOnlyTargetType = targetTypeManagement.create(Create.builder().name("type2").build());
runAs(withUser("user", runAs(withUser("user",
READ_TARGET_TYPE + "/id==" + manageableTargetType.getId() + " or id==" + readOnlyTargetType.getId(), READ_TARGET_TYPE + "/id==" + manageableTargetType.getId() + " or id==" + readOnlyTargetType.getId(),
DELETE_TARGET_TYPE + "/id==" + manageableTargetType.getId()), () -> { DELETE_TARGET_TYPE + "/id==" + manageableTargetType.getId()), () -> {
// delete the manageableTargetType // delete the manageableTargetType
targetTypeManagement.delete(manageableTargetType.getId()); targetTypeManagement.delete(manageableTargetType.getId());
@@ -113,8 +115,8 @@ class TargetTypeAccessControllerTest extends AbstractJpaIntegrationTest {
final TargetType readOnlyTargetType = targetTypeManagement.create(Create.builder().name("type2").build()); final TargetType readOnlyTargetType = targetTypeManagement.create(Create.builder().name("type2").build());
runAs(withUser("user", runAs(withUser("user",
READ_TARGET_TYPE + "/id==" + manageableTargetType.getId() + " or id==" + readOnlyTargetType.getId(), READ_TARGET_TYPE + "/id==" + manageableTargetType.getId() + " or id==" + readOnlyTargetType.getId(),
UPDATE_TARGET_TYPE + "/id==" + manageableTargetType.getId()), () -> { UPDATE_TARGET_TYPE + "/id==" + manageableTargetType.getId()), () -> {
// update the manageableTargetType // update the manageableTargetType
targetTypeManagement.update(Update.builder().id(manageableTargetType.getId()) targetTypeManagement.update(Update.builder().id(manageableTargetType.getId())
.name(manageableTargetType.getName() + "/new").description("newDesc").build()); .name(manageableTargetType.getName() + "/new").description("newDesc").build());

View File

@@ -139,7 +139,7 @@ class TargetManagementTest extends AbstractRepositoryManagementWithMetadataTest<
// retrieve security token only with READ_TARGET_SEC_TOKEN permission // retrieve security token only with READ_TARGET_SEC_TOKEN permission
final String securityTokenWithReadPermission = SecurityContextSwitch.getAs( final String securityTokenWithReadPermission = SecurityContextSwitch.getAs(
SecurityContextSwitch.withUser("OnlyTargetReadPermission", SpPermission.READ_TARGET_SEC_TOKEN), SecurityContextSwitch.withUser("OnlyTargetReadPermission", SpPermission.READ_TARGET_SECURITY_TOKEN),
createdTarget::getSecurityToken); createdTarget::getSecurityToken);
// retrieve security token only with ROLE_TARGET_ADMIN permission // retrieve security token only with ROLE_TARGET_ADMIN permission
final String securityTokenWithTargetAdminPermission = SecurityContextSwitch.getAs( final String securityTokenWithTargetAdminPermission = SecurityContextSwitch.getAs(

View File

@@ -108,8 +108,7 @@ class MultiTenancyEntityTest extends AbstractJpaIntegrationTest {
*/ */
@Test @Test
@WithUser(tenantId = "mytenant", autoCreateTenant = false, allSpPermissions = true) @WithUser(tenantId = "mytenant", autoCreateTenant = false, allSpPermissions = true)
void getTenanatMetdata() throws Exception { void getTenantMetdata() throws Exception {
// logged in tenant mytenant - check if tenant default data is // logged in tenant mytenant - check if tenant default data is
// autogenerated // autogenerated
assertThat(distributionSetTypeManagement.findAll(PAGE)).isEmpty(); assertThat(distributionSetTypeManagement.findAll(PAGE)).isEmpty();

View File

@@ -15,9 +15,13 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Hierarchy { public class Hierarchy {
// @formatter:off
public static final String DEFAULT = public static final String DEFAULT =
SpPermission.TARGET_HIERARCHY + SpPermission.TARGET_HIERARCHY +
SpPermission.REPOSITORY_HIERARCHY + SpPermission.REPOSITORY_HIERARCHY +
SpPermission.SOFTWARE_MODULE_HIERARCHY +
SpPermission.DISTRIBUTION_SET_HIERARCHY +
SpPermission.TENANT_CONFIGURATION_HIERARCHY + SpPermission.TENANT_CONFIGURATION_HIERARCHY +
SpRole.DEFAULT_ROLE_HIERARCHY; SpRole.DEFAULT_ROLE_HIERARCHY;
// @formatter:on
} }

View File

@@ -9,8 +9,6 @@
*/ */
package org.eclipse.hawkbit.im.authentication; package org.eclipse.hawkbit.im.authentication;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -46,7 +44,9 @@ public final class SpPermission {
public static final String TARGET = "TARGET"; public static final String TARGET = "TARGET";
public static final String TARGET_TYPE = "TARGET_TYPE"; public static final String TARGET_TYPE = "TARGET_TYPE";
public static final String SOFTWARE_MODULE = "SOFTWARE_MODULE"; public static final String SOFTWARE_MODULE = "SOFTWARE_MODULE";
public static final String SOFTWARE_MODULE_TYPE = "SOFTWARE_MODULE_TYPE";
public static final String DISTRIBUTION_SET = "DISTRIBUTION_SET"; public static final String DISTRIBUTION_SET = "DISTRIBUTION_SET";
public static final String DISTRIBUTION_SET_TYPE = "DISTRIBUTION_SET_TYPE";
public static final String ROLLOUT = "ROLLOUT"; public static final String ROLLOUT = "ROLLOUT";
public static final String TENANT_CONFIGURATION = "TENANT_CONFIGURATION"; public static final String TENANT_CONFIGURATION = "TENANT_CONFIGURATION";
@@ -55,12 +55,10 @@ public final class SpPermission {
public static final String UPDATE_TARGET = UPDATE_PREFIX + TARGET; public static final String UPDATE_TARGET = UPDATE_PREFIX + TARGET;
public static final String DELETE_TARGET = DELETE_PREFIX + TARGET; public static final String DELETE_TARGET = DELETE_PREFIX + TARGET;
/** /**
* Permission to read the target security token. The security token is security * Permission to read the target security token. The security token is security concerned and should be protected. So the combination
* concerned and should be protected. So the combination * {@linkplain #READ_TARGET} and {@code READ_TARGET_SEC_TOKEN} is necessary to be able to read the security token of a target.
* {@linkplain #READ_TARGET} and {@code READ_TARGET_SEC_TOKEN} is necessary to
* be able to read the security token of a target.
*/ */
public static final String READ_TARGET_SEC_TOKEN = READ_TARGET + "_SECURITY_TOKEN"; public static final String READ_TARGET_SECURITY_TOKEN = READ_TARGET + "_SECURITY_TOKEN";
public static final String READ_TARGET_TYPE = READ_PREFIX + TARGET_TYPE; public static final String READ_TARGET_TYPE = READ_PREFIX + TARGET_TYPE;
public static final String UPDATE_TARGET_TYPE = UPDATE_PREFIX + TARGET_TYPE; public static final String UPDATE_TARGET_TYPE = UPDATE_PREFIX + TARGET_TYPE;
@@ -73,21 +71,19 @@ public final class SpPermission {
public static final String UPDATE_REPOSITORY = "UPDATE_REPOSITORY"; public static final String UPDATE_REPOSITORY = "UPDATE_REPOSITORY";
public static final String CREATE_REPOSITORY = "CREATE_REPOSITORY"; public static final String CREATE_REPOSITORY = "CREATE_REPOSITORY";
public static final String DELETE_REPOSITORY = "DELETE_REPOSITORY"; public static final String DELETE_REPOSITORY = "DELETE_REPOSITORY";
public static final String DOWNLOAD_REPOSITORY_ARTIFACT = "DOWNLOAD_REPOSITORY_ARTIFACT"; public static final String DOWNLOAD_REPOSITORY_ARTIFACT = "DOWNLOAD_REPOSITORY_ARTIFACT";
/** /**
* Permission to read the tenant settings. * Permission to read the tenant settings.
*/ */
public static final String READ_TENANT_CONFIGURATION = READ_PREFIX + TENANT_CONFIGURATION; public static final String READ_TENANT_CONFIGURATION = READ_PREFIX + TENANT_CONFIGURATION;
/** /**
* Permission to read the gateway security token. The gateway security token is security * Permission to read the gateway security token. The gateway security token is security
* concerned and should be protected. So in addition to {@linkplain #READ_TENANT_CONFIGURATION}, * concerned and should be protected. So in addition to {@linkplain #READ_TENANT_CONFIGURATION},
* {@code READ_GATEWAY_SEC_TOKEN} is necessary to read gateway security token. {@link #TENANT_CONFIGURATION} * {@code READ_GATEWAY_SEC_TOKEN} is necessary to read gateway security token. {@link #TENANT_CONFIGURATION}
* implies both permissions - so it is sufficient to read the gateway security token. * implies both permissions - so it is sufficient to read the gateway security token.
*/ */
public static final String READ_GATEWAY_SEC_TOKEN = "READ_GATEWAY_SECURITY_TOKEN"; public static final String READ_GATEWAY_SECURITY_TOKEN = "READ_GATEWAY_SECURITY_TOKEN";
public static final String CREATE_ROLLOUT = CREATE_PREFIX + ROLLOUT; public static final String CREATE_ROLLOUT = CREATE_PREFIX + ROLLOUT;
public static final String READ_ROLLOUT = READ_PREFIX + ROLLOUT; public static final String READ_ROLLOUT = READ_PREFIX + ROLLOUT;
@@ -101,44 +97,81 @@ public final class SpPermission {
/** Permission to administrate the system on a global, i.e. tenant independent scale. That includes the deletion of tenants. */ /** Permission to administrate the system on a global, i.e. tenant independent scale. That includes the deletion of tenants. */
public static final String SYSTEM_ADMIN = "SYSTEM_ADMIN"; public static final String SYSTEM_ADMIN = "SYSTEM_ADMIN";
public static final String TARGET_HIERARCHY = """ public static final String IMPLY_CREATE = " > " + CREATE_PREFIX;
CREATE_TARGET > CREATE_TARGET_TYPE public static final String IMPLY_READ = " > " + READ_PREFIX;
READ_TARGET > READ_TARGET_TYPE public static final String IMPLY_UPDATE = " > " + UPDATE_PREFIX;
UPDATE_TARGET > UPDATE_TARGET_TYPE public static final String IMPLY_DELETE = " > " + DELETE_PREFIX;
DELETE_TARGET > DELETE_TARGET_TYPE
""";
public static final String REPOSITORY_HIERARCHY = """
CREATE_REPOSITORY > CREATE_DISTRIBUTION_SET
READ_REPOSITORY > READ_DISTRIBUTION_SET
UPDATE_REPOSITORY > UPDATE_DISTRIBUTION_SET
DELETE_REPOSITORY > DELETE_DISTRIBUTION_SET
CREATE_REPOSITORY > CREATE_SOFTWARE_MODULE
READ_REPOSITORY > READ_SOFTWARE_MODULE
UPDATE_REPOSITORY > UPDATE_SOFTWARE_MODULE
DELETE_REPOSITORY > DELETE_SOFTWARE_MODULE
""";
public static final String TENANT_CONFIGURATION_HIERARCHY = """
TENANT_CONFIGURATION > CREATE_TENANT_CONFIGURATION
TENANT_CONFIGURATION > READ_TENANT_CONFIGURATION
TENANT_CONFIGURATION > UPDATE_TENANT_CONFIGURATION
TENANT_CONFIGURATION > DELETE_TENANT_CONFIGURATION
TENANT_CONFIGURATION > READ_GATEWAY_SECURITY_TOKEN
""";
// @formatter:off
public static final String TARGET_HIERARCHY =
CREATE_TARGET + IMPLY_READ + TARGET_TYPE + "\n" +
READ_TARGET + IMPLY_READ + TARGET_TYPE + "\n" +
UPDATE_TARGET + IMPLY_READ + TARGET_TYPE + "\n" +
DELETE_TARGET + IMPLY_READ + TARGET_TYPE + "\n";
public static final String REPOSITORY_HIERARCHY =
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";
public static final String SOFTWARE_MODULE_HIERARCHY =
CREATE_PREFIX + SOFTWARE_MODULE + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
READ_PREFIX + SOFTWARE_MODULE + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
UPDATE_PREFIX + SOFTWARE_MODULE + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
DELETE_PREFIX + SOFTWARE_MODULE + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n";
public static final String DISTRIBUTION_SET_HIERARCHY =
CREATE_PREFIX + DISTRIBUTION_SET + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
READ_PREFIX + DISTRIBUTION_SET + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
UPDATE_PREFIX + DISTRIBUTION_SET + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n" +
DELETE_PREFIX + DISTRIBUTION_SET + IMPLY_READ + SOFTWARE_MODULE_TYPE + "\n";
public static final String TENANT_CONFIGURATION_HIERARCHY =
TENANT_CONFIGURATION + IMPLY_CREATE + TENANT_CONFIGURATION + "\n" +
TENANT_CONFIGURATION + IMPLY_READ + TENANT_CONFIGURATION + "\n" +
TENANT_CONFIGURATION + IMPLY_UPDATE + TENANT_CONFIGURATION + "\n" +
TENANT_CONFIGURATION + IMPLY_DELETE + TENANT_CONFIGURATION + "\n" +
TENANT_CONFIGURATION + IMPLY_CREATE + READ_GATEWAY_SECURITY_TOKEN + "\n";
// @formatter:on
private static final SingletonSupplier<List<String>> ALL_AUTHORITIES = SingletonSupplier.of(() -> { private static final SingletonSupplier<List<String>> ALL_AUTHORITIES = SingletonSupplier.of(() -> {
final List<String> allPermissions = new ArrayList<>(); final List<String> allPermissions = new ArrayList<>();
final Field[] declaredFields = SpPermission.class.getDeclaredFields();
for (final Field field : declaredFields) { // groups with access, canonical
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && for (final String group : new String[] {
String.class.equals(field.getType()) && !field.getName().endsWith("_HIERARCHY")) { TARGET, TARGET_TYPE,
try { SOFTWARE_MODULE, SOFTWARE_MODULE_TYPE, DISTRIBUTION_SET, DISTRIBUTION_SET_TYPE,
final String role = (String) field.get(null); ROLLOUT,
allPermissions.add(role); TENANT_CONFIGURATION }) {
} catch (final IllegalAccessException e) { for (final String access_prefix : new String[] { CREATE_PREFIX, READ_PREFIX, UPDATE_PREFIX, DELETE_PREFIX }) {
log.error(e.getMessage(), e); allPermissions.add(access_prefix + group);
}
} }
} }
// special
allPermissions.add(READ_TARGET_SECURITY_TOKEN);
allPermissions.add(READ_GATEWAY_SECURITY_TOKEN);
allPermissions.add(DOWNLOAD_REPOSITORY_ARTIFACT);
allPermissions.add(APPROVE_ROLLOUT);
allPermissions.add(HANDLE_ROLLOUT);
// coarse-grained - maybe to be deprecated
for (final String access_prefix : new String[] { CREATE_PREFIX, READ_PREFIX, UPDATE_PREFIX, DELETE_PREFIX }) {
allPermissions.add(access_prefix + "REPOSITORY");
}
allPermissions.add(TENANT_CONFIGURATION);
// system permission, (!) take care with
allPermissions.add(SYSTEM_ADMIN);
return Collections.unmodifiableList(allPermissions); return Collections.unmodifiableList(allPermissions);
}); });

View File

@@ -36,10 +36,14 @@ public final class SpRole {
private static final String LINE_BREAK = "\n"; private static final String LINE_BREAK = "\n";
public static final String TARGET_ADMIN_HIERARCHY = public static final String TARGET_ADMIN_HIERARCHY =
TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET + LINE_BREAK + TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET_SEC_TOKEN + LINE_BREAK + TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET_SECURITY_TOKEN + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.UPDATE_TARGET + LINE_BREAK + TARGET_ADMIN + IMPLIES + SpPermission.UPDATE_TARGET + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.CREATE_TARGET + LINE_BREAK + TARGET_ADMIN + IMPLIES + SpPermission.CREATE_TARGET + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.DELETE_TARGET + LINE_BREAK; TARGET_ADMIN + IMPLIES + SpPermission.DELETE_TARGET + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.READ_TARGET_TYPE + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.UPDATE_TARGET_TYPE + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.CREATE_PREFIX + SpPermission.TARGET_TYPE + LINE_BREAK +
TARGET_ADMIN + IMPLIES + SpPermission.DELETE_TARGET_TYPE + LINE_BREAK;
public static final String REPOSITORY_ADMIN_HIERARCHY = public static final String REPOSITORY_ADMIN_HIERARCHY =
REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_REPOSITORY + LINE_BREAK + REPOSITORY_ADMIN + IMPLIES + SpPermission.READ_REPOSITORY + LINE_BREAK +
REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_REPOSITORY + LINE_BREAK + REPOSITORY_ADMIN + IMPLIES + SpPermission.UPDATE_REPOSITORY + LINE_BREAK +

View File

@@ -66,8 +66,7 @@ public class StaticAuthenticationProvider extends DaoAuthenticationProvider {
: new TenantAwareUser(username, password, credentials, user.getTenant())); : new TenantAwareUser(username, password, credentials, user.getTenant()));
}); });
if (securityProperties != null && securityProperties.getUser() != null && if (securityProperties != null && securityProperties.getUser() != null && !securityProperties.getUser().isPasswordGenerated()) {
!securityProperties.getUser().isPasswordGenerated()) {
// explicitly setup system user - add is as a regular (non-tenant scoped) user // explicitly setup system user - add is as a regular (non-tenant scoped) user
userPrincipals.add(new User( userPrincipals.add(new User(
securityProperties.getUser().getName(), securityProperties.getUser().getName(),