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)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@Schema(example = """
|
@Schema(example = """
|
||||||
{
|
{
|
||||||
"createdBy" : "system",
|
"createdBy" : "admin",
|
||||||
"createdAt" : 1682408579390,
|
"createdAt" : 1682408579390,
|
||||||
"lastModifiedBy" : "bumlux",
|
"lastModifiedBy" : "bumlux",
|
||||||
"lastModifiedAt" : 1682408579394,
|
"lastModifiedAt" : 1682408579394,
|
||||||
|
|||||||
@@ -54,11 +54,14 @@ import org.springframework.util.ObjectUtils;
|
|||||||
/**
|
/**
|
||||||
* Management service for {@link Target}s.
|
* 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>
|
public interface TargetManagement<T extends Target>
|
||||||
extends RepositoryManagement<T, TargetManagement.Create, TargetManagement.Update> {
|
extends RepositoryManagement<T, TargetManagement.Create, TargetManagement.Update> {
|
||||||
|
|
||||||
String HAS_READ_TARGET_AND_READ_ROLLOUT = HAS_READ_REPOSITORY + " and hasAuthority('READ_" + SpPermission.ROLLOUT + "')";
|
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_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_AUTO_CONFIRMATION_STATUS = "autoConfirmationStatus";
|
||||||
String DETAILS_TAGS = "tags";
|
String DETAILS_TAGS = "tags";
|
||||||
@@ -87,7 +90,7 @@ public interface TargetManagement<T extends Target>
|
|||||||
* @param targetFilterQuery to execute
|
* @param targetFilterQuery to execute
|
||||||
* @return true if it matches
|
* @return true if it matches
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(HAS_READ_TARGET_AND_READ_DISTRIBUTION_SET)
|
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||||
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
||||||
@NotNull String controllerId, long distributionSetId, @NotNull String targetFilterQuery);
|
@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
|
* @return a page of the found {@link Target}s
|
||||||
* @throws EntityNotFoundException if distribution set with given ID does not exist
|
* @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(
|
Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
|
||||||
long distributionSetId, @NotNull String rsql, @NotNull Pageable pageable);
|
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
|
* @param pageable the pageable to enhance the query for paging and sorting
|
||||||
* @return a page of the found {@link Target}s
|
* @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(
|
Slice<Target> findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||||
@NotEmpty Collection<Long> groups, @NotNull String rsql, @NotNull DistributionSetType distributionSetType,
|
@NotEmpty Collection<Long> groups, @NotNull String rsql, @NotNull DistributionSetType distributionSetType,
|
||||||
@NotNull Pageable pageable);
|
@NotNull Pageable pageable);
|
||||||
@@ -169,7 +172,7 @@ public interface TargetManagement<T extends Target>
|
|||||||
Slice<Target> findByFailedRolloutAndNotInRolloutGroups(
|
Slice<Target> findByFailedRolloutAndNotInRolloutGroups(
|
||||||
@NotNull String rolloutId, @NotEmpty Collection<Long> groups, @NotNull Pageable pageable);
|
@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(
|
Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||||
final long rolloutId, @NotNull String rsql, @NotNull DistributionSetType distributionSetType, @NotNull Pageable pageable);
|
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
|
* @return the count of found {@link Target}s
|
||||||
* @throws EntityNotFoundException if distribution set with given ID does not exist
|
* @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);
|
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
|
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible with
|
||||||
* @return count of the found {@link Target}s
|
* @return count of the found {@link Target}s
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
|
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
|
||||||
long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||||
@NotNull String rsql, @NotEmpty Collection<Long> groups, @NotNull DistributionSetType distributionSetType);
|
@NotNull String rsql, @NotEmpty Collection<Long> groups, @NotNull DistributionSetType distributionSetType);
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.acm;
|
package org.eclipse.hawkbit.repository.jpa.acm;
|
||||||
|
|
||||||
import static org.eclipse.hawkbit.security.SecurityContextTenantAware.SYSTEM_USER;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -24,6 +22,7 @@ import org.eclipse.hawkbit.ContextAware;
|
|||||||
import org.eclipse.hawkbit.repository.QueryField;
|
import org.eclipse.hawkbit.repository.QueryField;
|
||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
|
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
|
||||||
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@@ -58,8 +57,8 @@ public class DefaultAccessController<A extends Enum<A> & QueryField, T> implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Specification<T>> getAccessRules(final Operation operation) {
|
public Optional<Specification<T>> getAccessRules(final Operation operation) {
|
||||||
if (contextAware.getCurrentTenant() != null && SYSTEM_USER.equals(contextAware.getCurrentUsername())) {
|
if (SystemSecurityContext.isCurrentThreadSystemCode()) {
|
||||||
// as tenant, no restrictions
|
// 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();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +72,8 @@ public class DefaultAccessController<A extends Enum<A> & QueryField, T> implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void assertOperationAllowed(final Operation operation, final T entity) throws InsufficientPermissionException {
|
public void assertOperationAllowed(final Operation operation, final T entity) throws InsufficientPermissionException {
|
||||||
if (contextAware.getCurrentTenant() != null && SYSTEM_USER.equals(contextAware.getCurrentUsername())) {
|
if (SystemSecurityContext.isCurrentThreadSystemCode()) {
|
||||||
// as tenant, no restrictions
|
// system code - no restrictions. this runs with SYSTEM_ROLE, so no restrictions apply anyway - not scopes, but this way should be faster
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
|||||||
systemSecurityContext.runAsSystem(() -> {
|
systemSecurityContext.runAsSystem(() -> {
|
||||||
log.debug("Cancel auto assignments after ds invalidation. ID: {}", setId);
|
log.debug("Cancel auto assignments after ds invalidation. ID: {}", setId);
|
||||||
targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId);
|
targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId);
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,6 @@ public class RolloutScheduler {
|
|||||||
handleAllAsync(tenant);
|
handleAllAsync(tenant);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
meterRegistry
|
meterRegistry
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<Roll
|
|||||||
// if only the latest state is != paused then pause
|
// if only the latest state is != paused then pause
|
||||||
rolloutManagement.pauseRollout(rollout.getId());
|
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.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.integration.support.locks.LockRegistry;
|
import org.springframework.integration.support.locks.LockRegistry;
|
||||||
|
|
||||||
class AutoAssignTest extends AbstractAccessControllerManagementTest {
|
class AutoAssignTest extends AbstractAccessControllerManagementTest {
|
||||||
@@ -77,8 +76,8 @@ class AutoAssignTest extends AbstractAccessControllerManagementTest {
|
|||||||
// do the assignment
|
// do the assignment
|
||||||
assigner.run();
|
assigner.run();
|
||||||
|
|
||||||
assertThat(targetManagement.findByAssignedDistributionSet(targetFilterQuery.getAutoAssignDistributionSet().getId(), Pageable.unpaged())
|
assertThat(targetManagement.findByAssignedDistributionSet(targetFilterQuery.getAutoAssignDistributionSet().getId(), UNPAGED)
|
||||||
.map(Identifiable::getId).toList())
|
.map(Identifiable::getId).toList())
|
||||||
.as("Only updatable targets should be part of the rollout")
|
.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
|
// 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
|
// only target2 and target3 shall be assigned
|
||||||
|
|||||||
@@ -36,10 +36,7 @@ class RolloutExecutionTest extends AbstractAccessControllerManagementTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyOnlyUpdatableTargetsArePartOfRollout() {
|
void verifyOnlyUpdatableTargetsArePartOfRollout() {
|
||||||
verify(() -> systemSecurityContext.runAsSystem(() -> {
|
verify(() -> systemSecurityContext.runAsSystem(rolloutHandler::handleAll));
|
||||||
rolloutHandler.handleAll();
|
|
||||||
return null;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verify(final Runnable run) {
|
private void verify(final Runnable run) {
|
||||||
|
|||||||
@@ -106,10 +106,7 @@ class SystemExecutionTest extends AbstractAccessControllerManagementTest {
|
|||||||
|
|
||||||
final Specification mockAsSystem = mock(Specification.class);
|
final Specification mockAsSystem = mock(Specification.class);
|
||||||
for (Operation operation : Operation.values()) {
|
for (Operation operation : Operation.values()) {
|
||||||
systemSecurityContext.runAsSystem(() -> {
|
systemSecurityContext.runAsSystem(() -> accessController.appendAccessRules(operation, mockAsSystem));
|
||||||
accessController.appendAccessRules(operation, mockAsSystem);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
verifyNoInteractions(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.READ_TARGET;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_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.runAs;
|
||||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
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.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
|
|
||||||
class TargetManagementTest extends AbstractAccessControllerManagementTest {
|
class TargetManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@@ -130,57 +126,69 @@ class TargetManagementTest extends AbstractAccessControllerManagementTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@Test
|
@Test
|
||||||
void verifyReadRolloutRelated() {
|
void verifyReadCompatibleRelated() {
|
||||||
assertThat(assignDistributionSet(ds2Type2, List.of(target1Type1, target2Type2, target3Type2)).getAssigned()).isEqualTo(3);
|
// assertThat(assignDistributionSet(ds2Type2, List.of(target1Type1, target2Type2, target3Type2)).getAssigned()).isEqualTo(3);
|
||||||
prepareFinishedUpdates(ds2Type2, target1Type1, target2Type2);
|
prepareFinishedUpdates(ds2Type2, target1Type1, target2Type2);
|
||||||
final Target target1Type1 = targetManagement.get(super.target1Type1.getId());
|
final Target target1Type1 = targetManagement.get(super.target1Type1.getId());
|
||||||
runAs(withAuthorities(
|
runAs(withAuthorities(
|
||||||
READ_TARGET + "/type.id==" + targetType1.getId(),
|
READ_TARGET + "/type.id==" + targetType2.getId(), // we want to have 2 updatable targets
|
||||||
READ_DISTRIBUTION_SET,
|
READ_DISTRIBUTION_SET), () -> {
|
||||||
CREATE_ROLLOUT, READ_ROLLOUT, HANDLE_ROLLOUT), () -> {
|
|
||||||
assertThat(targetManagement.findByInstalledDistributionSet(ds2Type2.getId(), UNPAGED))
|
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))
|
assertThat(targetManagement.findByInstalledDistributionSetAndRsql(ds2Type2.getId(), "id==*", UNPAGED))
|
||||||
.extracting(Identifiable::getId).containsExactly(target1Type1.getId());
|
.extracting(Identifiable::getId).containsExactly(target2Type2.getId());
|
||||||
|
});
|
||||||
assertThat(targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
|
runAs(withAuthorities(
|
||||||
ds2Type2.getId(), "id==*", UNPAGED))
|
READ_TARGET, UPDATE_TARGET + "/type.id==" + targetType2.getId(), // we want to have 2 updatable targets
|
||||||
.hasSize(1)
|
READ_DISTRIBUTION_SET), () -> {
|
||||||
.extracting(Identifiable::getId).containsOnly(target1Type1.getId());
|
assertThat(targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(ds2Type2.getId(), "id==*", UNPAGED))
|
||||||
|
.extracting(Identifiable::getId).containsExactly(target3Type2.getId());
|
||||||
|
|
||||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
||||||
target1Type1.getControllerId(), ds2Type2.getId(), "id==*")).isTrue();
|
target1Type1.getControllerId(), ds2Type2.getId(), "id==*")).isFalse();
|
||||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
||||||
target2Type2.getControllerId(), ds2Type2.getId(), "id==*")).isFalse();
|
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(
|
assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||||
List.of(1L), "id==*", ds2Type2.getType(), UNPAGED))
|
List.of(1L), "id==*", ds2Type2.getType(), UNPAGED).stream().toList())
|
||||||
.hasSize(1)
|
.containsExactly(target1Type1);
|
||||||
.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());
|
|
||||||
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L), ds2Type2.getType()))
|
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L), ds2Type2.getType()))
|
||||||
.isEqualTo(1);
|
.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(
|
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()
|
assertThat(rolloutGroupManagement.findByRollout(runAsSystem.getId(), UNPAGED).getContent().stream()
|
||||||
.flatMap(rolloutGroup -> targetManagement.findByInRolloutGroupWithoutAction(rolloutGroup.getId(), UNPAGED)
|
.flatMap(
|
||||||
.getContent().stream()).map(Identifiable::getId).toList())
|
group -> targetManagement.findByInRolloutGroupWithoutAction(group.getId(), UNPAGED).getContent().stream()).toList())
|
||||||
.hasSize(3);
|
.hasSize(3);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -288,44 +296,4 @@ class TargetManagementTest extends AbstractAccessControllerManagementTest {
|
|||||||
Action.ActionStatusCreate.builder().actionId(action.getId()).status(Action.Status.FINISHED).build());
|
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
|
@ParameterizedTest
|
||||||
@ValueSource(strings = { "${NOW_TS}", "${OVERDUE_TS}", "${overdue_ts}" })
|
@ValueSource(strings = { "${NOW_TS}", "${OVERDUE_TS}", "${overdue_ts}" })
|
||||||
void resolveNowTimestampPlaceholder(final String placeholder) {
|
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 testString = "lhs=lt=" + placeholder;
|
||||||
|
|
||||||
final String resolvedPlaceholders = substitutor.replace(testString);
|
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());
|
final List<String> tenants = systemSecurityContext.runAsSystem(() -> systemManagement.findTenants(PAGE).getContent());
|
||||||
tenants.forEach(tenant -> {
|
tenants.forEach(tenant -> {
|
||||||
try {
|
try {
|
||||||
systemSecurityContext.runAsSystem(() -> {
|
systemSecurityContext.runAsSystem(() -> systemManagement.deleteTenant(tenant));
|
||||||
systemManagement.deleteTenant(tenant);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Error while delete tenant", e);
|
log.error("Error while delete tenant", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,6 @@
|
|||||||
package org.eclipse.hawkbit.repository.test.util;
|
package org.eclipse.hawkbit.repository.test.util;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
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.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
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.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
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.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
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.TargetTag;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
@@ -162,6 +159,7 @@ public class TestdataFactory {
|
|||||||
private final RolloutHandler rolloutHandler;
|
private final RolloutHandler rolloutHandler;
|
||||||
private final QuotaManagement quotaManagement;
|
private final QuotaManagement quotaManagement;
|
||||||
private final TenantAware tenantAware;
|
private final TenantAware tenantAware;
|
||||||
|
private final SystemSecurityContext systemSecurityContext;
|
||||||
|
|
||||||
public TestdataFactory(
|
public TestdataFactory(
|
||||||
final ControllerManagement controllerManagement, final ArtifactManagement artifactManagement,
|
final ControllerManagement controllerManagement, final ArtifactManagement artifactManagement,
|
||||||
@@ -177,7 +175,8 @@ public class TestdataFactory {
|
|||||||
final TargetTagManagement<? extends TargetTag> targetTagManagement,
|
final TargetTagManagement<? extends TargetTag> targetTagManagement,
|
||||||
final DeploymentManagement deploymentManagement,
|
final DeploymentManagement deploymentManagement,
|
||||||
final RolloutManagement rolloutManagement, final RolloutHandler rolloutHandler,
|
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.controllerManagement = controllerManagement;
|
||||||
this.softwareModuleManagement = softwareModuleManagement;
|
this.softwareModuleManagement = softwareModuleManagement;
|
||||||
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
|
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
|
||||||
@@ -195,6 +194,7 @@ public class TestdataFactory {
|
|||||||
this.rolloutHandler = rolloutHandler;
|
this.rolloutHandler = rolloutHandler;
|
||||||
this.quotaManagement = quotaManagement;
|
this.quotaManagement = quotaManagement;
|
||||||
this.tenantAware = tenantAware;
|
this.tenantAware = tenantAware;
|
||||||
|
this.systemSecurityContext = systemSecurityContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String randomString(final int len) {
|
public static String randomString(final int len) {
|
||||||
@@ -205,14 +205,6 @@ public class TestdataFactory {
|
|||||||
return randomString(len).getBytes();
|
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
|
* Creates {@link DistributionSet} in repository including three
|
||||||
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
|
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
|
||||||
@@ -1294,7 +1286,7 @@ public class TestdataFactory {
|
|||||||
if (tenant == null) {
|
if (tenant == null) {
|
||||||
throw new IllegalStateException("Tenant is 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) {
|
private Rollout reloadRollout(final Rollout rollout) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.audit;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.AuditorAware;
|
import org.springframework.data.domain.AuditorAware;
|
||||||
@@ -42,7 +43,7 @@ public class AuditContextProvider {
|
|||||||
public AuditContext getAuditContext() {
|
public AuditContext getAuditContext() {
|
||||||
return new AuditContext(
|
return new AuditContext(
|
||||||
Optional.ofNullable(resolver.resolveTenant()).orElse("n/a"),
|
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) {}
|
public record AuditContext(String tenant, String username) {}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class MdcHandler {
|
|||||||
|
|
||||||
final String user = springSecurityAuditorAware
|
final String user = springSecurityAuditorAware
|
||||||
.getCurrentAuditor()
|
.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);
|
.orElse(null);
|
||||||
|
|
||||||
return callWithTenantAndUser0(callable, tenant, user);
|
return callWithTenantAndUser0(callable, tenant, user);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
|||||||
*/
|
*/
|
||||||
public class SecurityContextTenantAware implements ContextAware {
|
public class SecurityContextTenantAware implements ContextAware {
|
||||||
|
|
||||||
|
// Note! no system user shall be used as a regular user!
|
||||||
public static final String SYSTEM_USER = "system";
|
public static final String SYSTEM_USER = "system";
|
||||||
|
|
||||||
private static final Collection<? extends GrantedAuthority> SYSTEM_AUTHORITIES = List.of(new SimpleGrantedAuthority(SpRole.SYSTEM_ROLE));
|
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 TenantAwareUser principal;
|
||||||
private final TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails;
|
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) {
|
final Collection<? extends GrantedAuthority> authorities) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
principal = new TenantAwareUser(username, username, authorities, tenant);
|
principal = new TenantAwareUser(username, username, authorities, tenant);
|
||||||
|
|||||||
@@ -54,6 +54,22 @@ public class SystemSecurityContext {
|
|||||||
this.roleHierarchy = roleHierarchy;
|
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
|
* 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
|
* 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.
|
* @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;
|
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user