Fix system context resolving in ACM (#2737)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,6 @@ public class RolloutScheduler {
|
||||
handleAllAsync(tenant);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
meterRegistry
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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())));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user