Fix system context resolving in ACM (#2737)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-10-10 12:02:16 +03:00
committed by GitHub
parent e7d9ee7990
commit 3447ac3b1b
17 changed files with 97 additions and 129 deletions

View File

@@ -32,7 +32,7 @@ import org.eclipse.hawkbit.mgmt.json.model.MgmtTypeEntity;
@JsonIgnoreProperties(ignoreUnknown = true)
@Schema(example = """
{
"createdBy" : "system",
"createdBy" : "admin",
"createdAt" : 1682408579390,
"lastModifiedBy" : "bumlux",
"lastModifiedAt" : 1682408579394,

View File

@@ -54,11 +54,14 @@ import org.springframework.util.ObjectUtils;
/**
* Management service for {@link Target}s.
*/
@SuppressWarnings("java:S1192") // java:S1192 nothing meaningful to add + would be interface constant
public interface TargetManagement<T extends Target>
extends RepositoryManagement<T, TargetManagement.Create, TargetManagement.Update> {
String HAS_READ_TARGET_AND_READ_ROLLOUT = HAS_READ_REPOSITORY + " and hasAuthority('READ_" + SpPermission.ROLLOUT + "')";
String HAS_UPDATE_TARGET_AND_READ_ROLLOUT = HAS_UPDATE_REPOSITORY + " and hasAuthority('READ_" + SpPermission.ROLLOUT + "')";
String HAS_READ_TARGET_AND_READ_DISTRIBUTION_SET = HAS_READ_REPOSITORY + " and hasAuthority('READ_" + SpPermission.DISTRIBUTION_SET + "')";
String HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET = HAS_UPDATE_REPOSITORY + " and hasAuthority('READ_" + SpPermission.DISTRIBUTION_SET + "')";
String DETAILS_AUTO_CONFIRMATION_STATUS = "autoConfirmationStatus";
String DETAILS_TAGS = "tags";
@@ -87,7 +90,7 @@ public interface TargetManagement<T extends Target>
* @param targetFilterQuery to execute
* @return true if it matches
*/
@PreAuthorize(HAS_READ_TARGET_AND_READ_DISTRIBUTION_SET)
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
@NotNull String controllerId, long distributionSetId, @NotNull String targetFilterQuery);
@@ -137,7 +140,7 @@ public interface TargetManagement<T extends Target>
* @return a page of the found {@link Target}s
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_READ_TARGET_AND_READ_DISTRIBUTION_SET)
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
long distributionSetId, @NotNull String rsql, @NotNull Pageable pageable);
@@ -151,7 +154,7 @@ public interface TargetManagement<T extends Target>
* @param pageable the pageable to enhance the query for paging and sorting
* @return a page of the found {@link Target}s
*/
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
Slice<Target> findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
@NotEmpty Collection<Long> groups, @NotNull String rsql, @NotNull DistributionSetType distributionSetType,
@NotNull Pageable pageable);
@@ -169,7 +172,7 @@ public interface TargetManagement<T extends Target>
Slice<Target> findByFailedRolloutAndNotInRolloutGroups(
@NotNull String rolloutId, @NotEmpty Collection<Long> groups, @NotNull Pageable pageable);
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
final long rolloutId, @NotNull String rsql, @NotNull DistributionSetType distributionSetType, @NotNull Pageable pageable);
@@ -293,7 +296,7 @@ public interface TargetManagement<T extends Target>
* @return the count of found {@link Target}s
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_READ_TARGET_AND_READ_DISTRIBUTION_SET)
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
long countByRsqlAndNonDsAndCompatibleAndUpdatable(long distributionSetId, @NotNull String rsql);
/**
@@ -305,7 +308,7 @@ public interface TargetManagement<T extends Target>
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible with
* @return count of the found {@link Target}s
*/
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
@NotNull String rsql, @NotEmpty Collection<Long> groups, @NotNull DistributionSetType distributionSetType);

View File

@@ -9,8 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.acm;
import static org.eclipse.hawkbit.security.SecurityContextTenantAware.SYSTEM_USER;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
@@ -24,6 +22,7 @@ import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.GrantedAuthority;
@@ -58,8 +57,8 @@ public class DefaultAccessController<A extends Enum<A> & QueryField, T> implemen
@Override
public Optional<Specification<T>> getAccessRules(final Operation operation) {
if (contextAware.getCurrentTenant() != null && SYSTEM_USER.equals(contextAware.getCurrentUsername())) {
// as tenant, no restrictions
if (SystemSecurityContext.isCurrentThreadSystemCode()) {
// system code - no restrictions. this runs with SYSTEM_ROLE, so no restrictions apply anyway - not scopes, but this way should be faster
return Optional.empty();
}
@@ -73,8 +72,8 @@ public class DefaultAccessController<A extends Enum<A> & QueryField, T> implemen
@Override
public void assertOperationAllowed(final Operation operation, final T entity) throws InsufficientPermissionException {
if (contextAware.getCurrentTenant() != null && SYSTEM_USER.equals(contextAware.getCurrentUsername())) {
// as tenant, no restrictions
if (SystemSecurityContext.isCurrentThreadSystemCode()) {
// system code - no restrictions. this runs with SYSTEM_ROLE, so no restrictions apply anyway - not scopes, but this way should be faster
return;
}

View File

@@ -134,7 +134,6 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
systemSecurityContext.runAsSystem(() -> {
log.debug("Cancel auto assignments after ds invalidation. ID: {}", setId);
targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId);
return null;
});
}
}

View File

@@ -73,7 +73,6 @@ public class RolloutScheduler {
handleAllAsync(tenant);
}
});
return null;
});
meterRegistry

View File

@@ -61,7 +61,6 @@ public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<Roll
// if only the latest state is != paused then pause
rolloutManagement.pauseRollout(rollout.getId());
}
return null;
});
}
}

View File

@@ -27,7 +27,6 @@ import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.integration.support.locks.LockRegistry;
class AutoAssignTest extends AbstractAccessControllerManagementTest {
@@ -77,8 +76,8 @@ class AutoAssignTest extends AbstractAccessControllerManagementTest {
// do the assignment
assigner.run();
assertThat(targetManagement.findByAssignedDistributionSet(targetFilterQuery.getAutoAssignDistributionSet().getId(), Pageable.unpaged())
.map(Identifiable::getId).toList())
assertThat(targetManagement.findByAssignedDistributionSet(targetFilterQuery.getAutoAssignDistributionSet().getId(), UNPAGED)
.map(Identifiable::getId).toList())
.as("Only updatable targets should be part of the rollout")
// all targets are distribution set type 2 compatible, but since user has UPDATE_TARGET only for targets of type 2
// only target2 and target3 shall be assigned

View File

@@ -36,10 +36,7 @@ class RolloutExecutionTest extends AbstractAccessControllerManagementTest {
@Test
void verifyOnlyUpdatableTargetsArePartOfRollout() {
verify(() -> systemSecurityContext.runAsSystem(() -> {
rolloutHandler.handleAll();
return null;
}));
verify(() -> systemSecurityContext.runAsSystem(rolloutHandler::handleAll));
}
private void verify(final Runnable run) {

View File

@@ -106,10 +106,7 @@ class SystemExecutionTest extends AbstractAccessControllerManagementTest {
final Specification mockAsSystem = mock(Specification.class);
for (Operation operation : Operation.values()) {
systemSecurityContext.runAsSystem(() -> {
accessController.appendAccessRules(operation, mockAsSystem);
return null;
});
systemSecurityContext.runAsSystem(() -> accessController.appendAccessRules(operation, mockAsSystem));
}
verifyNoInteractions(mockAsSystem);
}

View File

@@ -21,7 +21,6 @@ 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.UPDATE_TARGET;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser;
import java.util.Arrays;
import java.util.List;
@@ -37,12 +36,9 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable;
class TargetManagementTest extends AbstractAccessControllerManagementTest {
@@ -130,57 +126,69 @@ class TargetManagementTest extends AbstractAccessControllerManagementTest {
});
}
@Disabled
@Test
void verifyReadRolloutRelated() {
assertThat(assignDistributionSet(ds2Type2, List.of(target1Type1, target2Type2, target3Type2)).getAssigned()).isEqualTo(3);
void verifyReadCompatibleRelated() {
// assertThat(assignDistributionSet(ds2Type2, List.of(target1Type1, target2Type2, target3Type2)).getAssigned()).isEqualTo(3);
prepareFinishedUpdates(ds2Type2, target1Type1, target2Type2);
final Target target1Type1 = targetManagement.get(super.target1Type1.getId());
runAs(withAuthorities(
READ_TARGET + "/type.id==" + targetType1.getId(),
READ_DISTRIBUTION_SET,
CREATE_ROLLOUT, READ_ROLLOUT, HANDLE_ROLLOUT), () -> {
READ_TARGET + "/type.id==" + targetType2.getId(), // we want to have 2 updatable targets
READ_DISTRIBUTION_SET), () -> {
assertThat(targetManagement.findByInstalledDistributionSet(ds2Type2.getId(), UNPAGED))
.extracting(Identifiable::getId).containsExactly(target1Type1.getId());
.extracting(Identifiable::getId).containsExactly(target2Type2.getId());
assertThat(targetManagement.findByInstalledDistributionSetAndRsql(ds2Type2.getId(), "id==*", UNPAGED))
.extracting(Identifiable::getId).containsExactly(target1Type1.getId());
assertThat(targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
ds2Type2.getId(), "id==*", UNPAGED))
.hasSize(1)
.extracting(Identifiable::getId).containsOnly(target1Type1.getId());
.extracting(Identifiable::getId).containsExactly(target2Type2.getId());
});
runAs(withAuthorities(
READ_TARGET, UPDATE_TARGET + "/type.id==" + targetType2.getId(), // we want to have 2 updatable targets
READ_DISTRIBUTION_SET), () -> {
assertThat(targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(ds2Type2.getId(), "id==*", UNPAGED))
.extracting(Identifiable::getId).containsExactly(target3Type2.getId());
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
target1Type1.getControllerId(), ds2Type2.getId(), "id==*")).isTrue();
target1Type1.getControllerId(), ds2Type2.getId(), "id==*")).isFalse();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
target2Type2.getControllerId(), ds2Type2.getId(), "id==*")).isFalse();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
target3Type2.getControllerId(), ds2Type2.getId(), "id==*")).isTrue();
});
}
@Test
void verifyReadRolloutRelated() {
final Target target1Type1 = targetManagement.get(super.target1Type1.getId());
runAs(withAuthorities(
READ_TARGET, UPDATE_TARGET + "/type.id==" + targetType1.getId(),
READ_DISTRIBUTION_SET,
CREATE_ROLLOUT, READ_ROLLOUT, HANDLE_ROLLOUT), () -> {
assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
List.of(1L), "id==*", ds2Type2.getType(), UNPAGED))
.hasSize(1)
.extracting(Identifiable::getId).containsOnly(target1Type1.getId());
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L),
ds2Type2.getType())).isEqualTo(1);
final Rollout rollout = testdataFactory.createRolloutByVariables(
"testRollout", "testDescription", 3, "id==*", ds2Type2, "50", "5");
final List<Long> foundTargetIds = rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream()
.flatMap(rolloutGroup -> targetManagement.findByInRolloutGroupWithoutAction(rolloutGroup.getId(), UNPAGED)
.getContent().stream()).map(Identifiable::getId).toList();
assertThat(foundTargetIds).hasSize(1).contains(target1Type1.getId());
assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
List.of(1L), "id==*", ds2Type2.getType(), UNPAGED))
.hasSize(1)
.extracting(Identifiable::getId).containsOnly(target1Type1.getId());
List.of(1L), "id==*", ds2Type2.getType(), UNPAGED).stream().toList())
.containsExactly(target1Type1);
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L), ds2Type2.getType()))
.isEqualTo(1);
final Rollout rollout = testdataFactory.createRolloutByVariables("testRollout", "testDescription", 3, "id==*", ds2Type2, "50", "5");
final List<Long> groups = rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream().
map(Identifiable::getId).toList();
assertThat(groups.stream().flatMap(
group -> targetManagement.findByInRolloutGroupWithoutAction(group, UNPAGED).get()).toList())
.containsExactly(target1Type1);
assertThat(groups.stream().flatMap(
group -> rolloutGroupManagement.findTargetsOfRolloutGroup(group, UNPAGED).get()).toList())
.containsExactly(target1Type1);
assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(groups, "id==*", ds2Type2.getType(), UNPAGED))
.isEmpty();
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", groups, ds2Type2.getType()))
.isZero();
// as system in context - doesn't apply scopes
final Rollout runAsSystem = systemSecurityContext.runAsSystem(
() -> testdataFactory.createRolloutByVariables("testRollout", "testDescription", 3, "id==*", ds2Type2, "50", "5"));
() -> testdataFactory.createRolloutByVariables(
"testRolloutAsSystem", "testDescriptionAsSystem", 3, "id==*", ds2Type2, "50", "5"));
assertThat(rolloutGroupManagement.findByRollout(runAsSystem.getId(), UNPAGED).getContent().stream()
.flatMap(rolloutGroup -> targetManagement.findByInRolloutGroupWithoutAction(rolloutGroup.getId(), UNPAGED)
.getContent().stream()).map(Identifiable::getId).toList())
.flatMap(
group -> targetManagement.findByInRolloutGroupWithoutAction(group.getId(), UNPAGED).getContent().stream()).toList())
.hasSize(3);
});
}
@@ -288,44 +296,4 @@ class TargetManagementTest extends AbstractAccessControllerManagementTest {
Action.ActionStatusCreate.builder().actionId(action.getId()).status(Action.Status.FINISHED).build());
});
}
/**
* Verifies only manageable targets are part of the rollout
*/
@Test
void verifyRolloutTargetScope() {
final DistributionSet ds = testdataFactory.createDistributionSet("myDs");
distributionSetManagement.lock(ds);
final String[] updateTargetControllerIds = { "update1", "update2", "update3" };
final List<Target> updateTargets = testdataFactory.createTargets(updateTargetControllerIds);
final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" };
final List<Target> readTargets = testdataFactory.createTargets(readTargetControllerIds);
final List<Target> hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5");
runAs(withUser("user",
READ_DISTRIBUTION_SET,
READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" +
" or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")",
UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")",
CREATE_ROLLOUT, READ_ROLLOUT), () -> {
final Rollout rollout = testdataFactory.createRolloutByVariables(
"testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5");
assertThat(rollout.getTotalTargets()).isEqualTo(updateTargets.size());
final List<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent();
assertThat(content).hasSize(updateTargets.size());
final List<Target> rolloutTargets = content.stream().flatMap(
group -> rolloutGroupManagement.findTargetsOfRolloutGroup(group.getId(), Pageable.unpaged()).get())
.toList();
assertThat(rolloutTargets).hasSize(updateTargets.size()).allMatch(
target -> updateTargets.stream().anyMatch(readTarget -> readTarget.getId().equals(target.getId())))
.noneMatch(target -> readTargets.stream()
.anyMatch(readTarget -> readTarget.getId().equals(target.getId())))
.noneMatch(target -> hiddenTargets.stream()
.anyMatch(readTarget -> readTarget.getId().equals(target.getId())));
});
}
}

View File

@@ -91,7 +91,7 @@ class VirtualPropertyResolverTest {
@ParameterizedTest
@ValueSource(strings = { "${NOW_TS}", "${OVERDUE_TS}", "${overdue_ts}" })
void resolveNowTimestampPlaceholder(final String placeholder) {
when(securityContext.runAsSystem(Mockito.any())).thenAnswer(a -> ((Callable<?>) a.getArgument(0)).call());
when(securityContext.runAsSystem(Mockito.any(Callable.class))).thenAnswer(a -> ((Callable<?>) a.getArgument(0)).call());
final String testString = "lhs=lt=" + placeholder;
final String resolvedPlaceholders = substitutor.replace(testString);

View File

@@ -53,10 +53,7 @@ public class CleanupTestExecutionListener extends AbstractTestExecutionListener
final List<String> tenants = systemSecurityContext.runAsSystem(() -> systemManagement.findTenants(PAGE).getContent());
tenants.forEach(tenant -> {
try {
systemSecurityContext.runAsSystem(() -> {
systemManagement.deleteTenant(tenant);
return null;
});
systemSecurityContext.runAsSystem(() -> systemManagement.deleteTenant(tenant));
} catch (final Exception e) {
log.error("Error while delete tenant", e);
}

View File

@@ -10,9 +10,6 @@
package org.eclipse.hawkbit.repository.test.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.im.authentication.SpRole.SYSTEM_ROLE;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUserAndTenant;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -57,7 +54,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -78,6 +74,7 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.PageRequest;
@@ -162,6 +159,7 @@ public class TestdataFactory {
private final RolloutHandler rolloutHandler;
private final QuotaManagement quotaManagement;
private final TenantAware tenantAware;
private final SystemSecurityContext systemSecurityContext;
public TestdataFactory(
final ControllerManagement controllerManagement, final ArtifactManagement artifactManagement,
@@ -177,7 +175,8 @@ public class TestdataFactory {
final TargetTagManagement<? extends TargetTag> targetTagManagement,
final DeploymentManagement deploymentManagement,
final RolloutManagement rolloutManagement, final RolloutHandler rolloutHandler,
final QuotaManagement quotaManagement, final TenantAware tenantAware) {
final QuotaManagement quotaManagement,
final TenantAware tenantAware, final SystemSecurityContext systemSecurityContext) {
this.controllerManagement = controllerManagement;
this.softwareModuleManagement = softwareModuleManagement;
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
@@ -195,6 +194,7 @@ public class TestdataFactory {
this.rolloutHandler = rolloutHandler;
this.quotaManagement = quotaManagement;
this.tenantAware = tenantAware;
this.systemSecurityContext = systemSecurityContext;
}
public static String randomString(final int len) {
@@ -205,14 +205,6 @@ public class TestdataFactory {
return randomString(len).getBytes();
}
public Action performAssignment(final DistributionSet distributionSet) {
final Target target = createTarget(randomString(5));
final DeploymentRequest deploymentRequest = new DeploymentRequest(
target.getControllerId(), distributionSet.getId(), ActionType.FORCED, 0, null, null, null, null, false);
deploymentManagement.assignDistributionSets(Collections.singletonList(deploymentRequest));
return deploymentManagement.findActionsByTarget(target.getControllerId(), Pageable.unpaged()).getContent().get(0);
}
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
@@ -1294,7 +1286,7 @@ public class TestdataFactory {
if (tenant == null) {
throw new IllegalStateException("Tenant is null");
}
runAs(withUserAndTenant("system", tenant, false, false, false, SYSTEM_ROLE), rolloutHandler::handleAll);
systemSecurityContext.runAsSystem(rolloutHandler::handleAll);
}
private Rollout reloadRollout(final Rollout rollout) {

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.audit;
import java.util.Optional;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.AuditorAware;
@@ -42,7 +43,7 @@ public class AuditContextProvider {
public AuditContext getAuditContext() {
return new AuditContext(
Optional.ofNullable(resolver.resolveTenant()).orElse("n/a"),
Optional.ofNullable(auditorAware).flatMap(AuditorAware::getCurrentAuditor).orElse("system"));
Optional.ofNullable(auditorAware).flatMap(AuditorAware::getCurrentAuditor).orElse(SecurityContextTenantAware.SYSTEM_USER));
}
public record AuditContext(String tenant, String username) {}

View File

@@ -84,7 +84,7 @@ public class MdcHandler {
final String user = springSecurityAuditorAware
.getCurrentAuditor()
.filter(username -> !username.equals("system")) // null and system are the same - system user
.filter(username -> !username.equals(SecurityContextTenantAware.SYSTEM_USER)) // null and system are the same - system user
.orElse(null);
return callWithTenantAndUser0(callable, tenant, user);

View File

@@ -40,6 +40,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
*/
public class SecurityContextTenantAware implements ContextAware {
// Note! no system user shall be used as a regular user!
public static final String SYSTEM_USER = "system";
private static final Collection<? extends GrantedAuthority> SYSTEM_AUTHORITIES = List.of(new SimpleGrantedAuthority(SpRole.SYSTEM_ROLE));
@@ -181,7 +182,8 @@ public class SecurityContextTenantAware implements ContextAware {
private final TenantAwareUser principal;
private final TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails;
private AuthenticationDelegate(final Authentication delegate, final String tenant, final String username,
private AuthenticationDelegate(
final Authentication delegate, final String tenant, final String username,
final Collection<? extends GrantedAuthority> authorities) {
this.delegate = delegate;
principal = new TenantAwareUser(username, username, authorities, tenant);

View File

@@ -54,6 +54,22 @@ public class SystemSecurityContext {
this.roleHierarchy = roleHierarchy;
}
/**
* Runs a given {@link Runnable} within a system security context, which is permitted to call secured system code. Often the system needs
* to call secured methods by its own without relying on the current security context e.g. if the current security context does not contain
* the necessary permission it's necessary to execute code as system code to execute necessary methods and functionality. <br/>
* The security context will be switched to the system code and back after the callable is called. <br/>
* The system code is executed for a current tenant by using the {@link TenantAware#getCurrentTenant()}.
*
* @param runnable the runnable to call within the system security context
*/
public void runAsSystem(final Runnable runnable) {
runAsSystemAsTenant(() -> {
runnable.run();
return null;
}, tenantAware.getCurrentTenant());
}
/**
* Runs a given {@link Callable} within a system security context, which is permitted to call secured system code. Often the system needs
* to call secured methods by its own without relying on the current security context e.g. if the current security context does not contain
@@ -120,7 +136,7 @@ public class SystemSecurityContext {
/**
* @return {@code true} if the current running code is running as system code block.
*/
public boolean isCurrentThreadSystemCode() {
public static boolean isCurrentThreadSystemCode() {
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
}