Fine-grained permissions (#2535)

* Fine-grained permissions

Adds support for permissions of type <permission>(/<rsql filter scope>)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>

* Apply review fixes

---------

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-07-10 13:51:49 +03:00
committed by GitHub
parent 7e8dd046e0
commit 21581c4ea4
69 changed files with 1492 additions and 1487 deletions

View File

@@ -15,7 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import lombok.SneakyThrows;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -32,7 +32,7 @@ import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultMatcher;
@ContextConfiguration(
classes = { MgmtApiConfiguration.class, RestConfiguration.class, RepositoryApplicationConfiguration.class, TestConfiguration.class })
classes = { MgmtApiConfiguration.class, RestConfiguration.class, JpaRepositoryConfiguration.class, TestConfiguration.class })
@TestPropertySource(locations = "classpath:/mgmt-test.properties")
public abstract class AbstractManagementApiIntegrationTest extends AbstractRestIntegrationTest {

View File

@@ -20,7 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.util.Base64;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.matcher.EventVerifier;
import org.eclipse.hawkbit.repository.test.util.CleanupTestExecutionListener;
@@ -67,7 +67,7 @@ import org.springframework.web.context.WebApplicationContext;
@WebAppConfiguration
@AutoConfigureMockMvc
@ContextConfiguration(classes = { MgmtApiConfiguration.class, RestConfiguration.class,
RepositoryApplicationConfiguration.class, TestConfiguration.class })
JpaRepositoryConfiguration.class, TestConfiguration.class })
/**
* Feature: Component Tests - Management API<br/>
* Story: Basic auth Userinfo Resource

View File

@@ -408,7 +408,7 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
try {
payload.put(new JSONObject().put("id", trg.getId()));
} catch (final JSONException e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
});
@@ -459,7 +459,7 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
try {
list.put(new JSONObject().put("id", target.getControllerId()));
} catch (final JSONException e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
});

View File

@@ -1912,13 +1912,13 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
private void awaitRunningState(final Long rolloutId) {
awaitRollout().until(() -> SecurityContextSwitch
.runAsPrivileged(() -> rolloutManagement.get(rolloutId).orElseThrow(NoSuchElementException::new))
.callAsPrivileged(() -> rolloutManagement.get(rolloutId).orElseThrow(NoSuchElementException::new))
.getStatus().equals(RolloutStatus.RUNNING));
}
private void awaitActionStatus(final Long actionId, final Status status) {
awaitRollout().until(() -> SecurityContextSwitch
.runAsPrivileged(() -> deploymentManagement.findAction(actionId).orElseThrow(NoSuchElementException::new))
.callAsPrivileged(() -> deploymentManagement.findAction(actionId).orElseThrow(NoSuchElementException::new))
.getStatus().equals(status));
}

View File

@@ -9,6 +9,9 @@
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.callAs;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.getAs;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
@@ -22,7 +25,6 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValueRequest;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.json.JSONObject;
@@ -132,7 +134,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* Update DefaultDistributionSetType Fails if given DistributionSetType ID does not exist.
*/
@Test
void putTenantMetadataFails() throws Exception {
void putTenantMetadataFails() throws Exception {
long oldDefaultDsType = getActualDefaultDsType();
//try an invalid input
String newDefaultDsType = new JSONObject().put("value", true).toString();
@@ -149,7 +151,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* The 'multi.assignments.enabled' property must not be changed to false.
*/
@Test
void deactivateMultiAssignment() throws Exception {
void deactivateMultiAssignment() throws Exception {
final String bodyActivate = new JSONObject().put("value", true).toString();
final String bodyDeactivate = new JSONObject().put("value", false).toString();
@@ -168,7 +170,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* The Batch configuration should not be applied, because of invalid TenantConfiguration props
*/
@Test
void changeBatchConfigurationShouldFailOnInvalidTenantConfiguration() throws Exception {
void changeBatchConfigurationShouldFailOnInvalidTenantConfiguration() throws Exception {
//in this scenario
// some TenantConfiguration are not valid,
// TenantMetadata - DefaultDSType ID is valid,
@@ -185,7 +187,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* The Batch configuration should not be applied, because of invalid TenantMetadata (DefaultDistributionSetType)
*/
@Test
void changeBatchConfigurationShouldOnInvalidTenantMetadata() throws Exception {
void changeBatchConfigurationShouldOnInvalidTenantMetadata() throws Exception {
//in this scenario
// all TenantConfiguration have valid and new values - using old values, inverted
// TenantMetadata - DefaultDSType ID is invalid
@@ -218,7 +220,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* The Batch configuration should be applied
*/
@Test
void changeBatchConfiguration() throws Exception {
void changeBatchConfiguration() throws Exception {
long updatedDistributionSetType = createTestDistributionSetType();
boolean updatedRolloutApprovalEnabled = true;
boolean updatedAuthGatewayTokenEnabled = true;
@@ -253,7 +255,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* The 'repository.actions.autoclose.enabled' property must not be modified if Multi-Assignments is enabled.
*/
@Test
void autoCloseCannotBeModifiedIfMultiAssignmentIsEnabled() throws Exception {
void autoCloseCannotBeModifiedIfMultiAssignmentIsEnabled() throws Exception {
final String bodyActivate = new JSONObject().put("value", true).toString();
final String bodyDeactivate = new JSONObject().put("value", false).toString();
@@ -280,7 +282,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* Handles DELETE request deleting a tenant specific configuration.
*/
@Test
void deleteTenantConfiguration() throws Exception {
void deleteTenantConfiguration() throws Exception {
mvc.perform(delete(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY))
.andDo(MockMvcResultPrinter.print())
@@ -291,7 +293,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
* Tests DELETE request must Fail for TenantMetadata properties.
*/
@Test
void deleteTenantMetadataFail() throws Exception {
void deleteTenantMetadataFail() throws Exception {
mvc.perform(delete(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
DEFAULT_DISTRIBUTION_SET_TYPE_KEY))
.andDo(MockMvcResultPrinter.print())
@@ -303,18 +305,15 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
*/
@Test
void getTenantConfigurationReadGWToken() throws Exception {
SecurityContextSwitch.runAs(SecurityContextSwitch.withUser("tenant_admin", SpPermission.TENANT_CONFIGURATION), () -> {
getAs(withUser("tenant_admin", SpPermission.TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(
TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY,
"123");
TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "123");
return null;
});
// 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
SecurityContextSwitch.runAs(
SecurityContextSwitch.withUser("tenant_admin", SpPermission.READ_TENANT_CONFIGURATION, SpPermission.READ_GATEWAY_SEC_TOKEN),
() -> {
callAs(withUser("tenant_admin", SpPermission.READ_TENANT_CONFIGURATION, SpPermission.READ_GATEWAY_SEC_TOKEN), () -> {
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs"))
.andDo(MockMvcResultPrinter.print())
.andDo(m -> System.out.println("-> 1: " + m.getResponse().getContentAsString()))
@@ -324,7 +323,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
return null;
});
SecurityContextSwitch.runAs(SecurityContextSwitch.withUser("tenant_read", SpPermission.READ_TENANT_CONFIGURATION), () -> {
callAs(withUser("tenant_read", SpPermission.READ_TENANT_CONFIGURATION), () -> {
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs"))
.andDo(MockMvcResultPrinter.print())
.andDo(m -> System.out.println("-> 2: " + m.getResponse().getContentAsString()))

View File

@@ -12,3 +12,5 @@
#logging.level.org.eclipse.hawkbit.rest.util.MockMvcResultPrinter=DEBUG
# Logging END
# disable spring cloud bus for tests
spring.cloud.bus.enabled=false