Refactor hawkbit core and security (#2833)
* Refactor hawkbit core and security * improve access to the base core features - static * thus easiear access * and less boilerplate passing of instances Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com> * Refactor context classes * make JSON context serialization default * AccessContext * Split hawkbit-security-core to other modules and remove it --------- Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -9,8 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.protostuff.ProtostuffIOUtil;
|
||||
import io.protostuff.Schema;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -19,7 +22,6 @@ import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.AbstractServiceRemoteEvent;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -47,10 +49,9 @@ public class EventPublisherConfiguration {
|
||||
*/
|
||||
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
|
||||
ApplicationEventMulticaster applicationEventMulticaster(
|
||||
@Qualifier("asyncExecutor") final Executor executor,
|
||||
final SystemSecurityContext systemSecurityContext, final ApplicationEventFilter applicationEventFilter) {
|
||||
@Qualifier("asyncExecutor") final Executor executor, final ApplicationEventFilter applicationEventFilter) {
|
||||
final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
|
||||
new TenantAwareApplicationEventPublisher(systemSecurityContext, applicationEventFilter);
|
||||
new TenantAwareApplicationEventPublisher(applicationEventFilter);
|
||||
simpleApplicationEventMulticaster.setTaskExecutor(executor);
|
||||
return simpleApplicationEventMulticaster;
|
||||
}
|
||||
@@ -76,17 +77,14 @@ public class EventPublisherConfiguration {
|
||||
|
||||
private static class TenantAwareApplicationEventPublisher extends SimpleApplicationEventMulticaster {
|
||||
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final ApplicationEventFilter applicationEventFilter;
|
||||
|
||||
protected TenantAwareApplicationEventPublisher(
|
||||
final SystemSecurityContext systemSecurityContext, final ApplicationEventFilter applicationEventFilter) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
protected TenantAwareApplicationEventPublisher(final ApplicationEventFilter applicationEventFilter) {
|
||||
this.applicationEventFilter = applicationEventFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was overridden that not every event has to run within an own tenantAware.
|
||||
* Was overridden that not every event has to run within an own tenant.
|
||||
*/
|
||||
@Override
|
||||
public void multicastEvent(final ApplicationEvent event, final ResolvableType eventType) {
|
||||
@@ -95,19 +93,13 @@ public class EventPublisherConfiguration {
|
||||
}
|
||||
|
||||
if (event instanceof final RemoteTenantAwareEvent remoteEvent) {
|
||||
systemSecurityContext.runAsSystemAsTenant(() -> {
|
||||
super.multicastEvent(event, eventType);
|
||||
return null;
|
||||
}, remoteEvent.getTenant());
|
||||
asSystemAsTenant(remoteEvent.getTenant(), () -> super.multicastEvent(event, eventType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (event instanceof final AbstractServiceRemoteEvent<?> serviceRemoteEvent
|
||||
&& serviceRemoteEvent.getRemoteEvent() instanceof RemoteTenantAwareEvent tenantAwareEvent) {
|
||||
systemSecurityContext.runAsSystemAsTenant(() -> {
|
||||
super.multicastEvent(event, eventType);
|
||||
return null;
|
||||
}, tenantAwareEvent.getTenant());
|
||||
asSystemAsTenant(tenantAwareEvent.getTenant(), () -> super.multicastEvent(event, eventType));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,6 @@ import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEve
|
||||
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionUpdatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionAssignServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionCancelServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAttributesRequestedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||
@@ -74,6 +65,15 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionUpdatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionAssignServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionCancelServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAttributesRequestedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,8 @@ package org.eclipse.hawkbit.repository;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.im.authentication.Hierarchy;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.Hierarchy;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -47,8 +47,7 @@ public class RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("java:S3358")
|
||||
// java:S3358 better readable this way
|
||||
@SuppressWarnings("java:S3358") // java:S3358 better readable this way
|
||||
RoleHierarchy roleHierarchy(
|
||||
// if configured replaces the hierarchy completely
|
||||
@Value("${hawkbit.hierarchy:}") final String hierarchy,
|
||||
@@ -62,6 +61,9 @@ public class RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
PermissionEvaluator permissionEvaluator(final RoleHierarchy roleHierarchy) {
|
||||
// sets up global access to the role hierarchy
|
||||
Hierarchy.setRoleHierarchy(roleHierarchy);
|
||||
// and returns a custom permission evaluator
|
||||
return new DenyAllPermissionEvaluator() {
|
||||
|
||||
@Override
|
||||
@@ -72,7 +74,8 @@ public class RepositoryConfiguration {
|
||||
.replace(SpringEvalExpressions.PERMISSION_GROUP_PLACEHOLDER, permissionSupport.permissionGroup());
|
||||
|
||||
// do permissions check
|
||||
final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||
final boolean hasPermission = Hierarchy.getRoleHierarchy()
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.anyMatch(authority -> authority.equals(neededPermission));
|
||||
if (!hasPermission) {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -25,7 +27,6 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
@@ -39,19 +40,13 @@ public class RolloutStatusCache {
|
||||
|
||||
private static final TenantAwareCacheManager CACHE_MANAGER = TenantAwareCacheManager.getInstance();
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
public RolloutStatusCache(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves cached list of {@link TotalTargetCountActionStatus} of {@link Rollout}s.
|
||||
*
|
||||
* @param rollouts rolloutIds to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public Map<Long, List<TotalTargetCountActionStatus>> getRolloutStatus(final List<Long> rollouts) {
|
||||
public static Map<Long, List<TotalTargetCountActionStatus>> getRolloutStatus(final List<Long> rollouts) {
|
||||
return retrieveFromCache(rollouts, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -61,7 +56,7 @@ public class RolloutStatusCache {
|
||||
* @param rolloutId to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public List<TotalTargetCountActionStatus> getRolloutStatus(final Long rolloutId) {
|
||||
public static List<TotalTargetCountActionStatus> getRolloutStatus(final Long rolloutId) {
|
||||
return retrieveFromCache(rolloutId, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -71,7 +66,7 @@ public class RolloutStatusCache {
|
||||
* @param rolloutGroups rolloutGroupsIds to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public Map<Long, List<TotalTargetCountActionStatus>> getRolloutGroupStatus(final List<Long> rolloutGroups) {
|
||||
public static Map<Long, List<TotalTargetCountActionStatus>> getRolloutGroupStatus(final List<Long> rolloutGroups) {
|
||||
return retrieveFromCache(rolloutGroups, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@@ -81,7 +76,7 @@ public class RolloutStatusCache {
|
||||
* @param groupId to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public List<TotalTargetCountActionStatus> getRolloutGroupStatus(final Long groupId) {
|
||||
public static List<TotalTargetCountActionStatus> getRolloutGroupStatus(final Long groupId) {
|
||||
return retrieveFromCache(groupId, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@@ -91,7 +86,7 @@ public class RolloutStatusCache {
|
||||
*
|
||||
* @param put map of cached entries
|
||||
*/
|
||||
public void putRolloutStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
public static void putRolloutStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
putIntoCache(put, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -101,7 +96,7 @@ public class RolloutStatusCache {
|
||||
* @param rolloutId the cache entries belong to
|
||||
* @param status list to cache
|
||||
*/
|
||||
public void putRolloutStatus(final Long rolloutId, final List<TotalTargetCountActionStatus> status) {
|
||||
public static void putRolloutStatus(final Long rolloutId, final List<TotalTargetCountActionStatus> status) {
|
||||
putIntoCache(rolloutId, status, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -110,7 +105,7 @@ public class RolloutStatusCache {
|
||||
*
|
||||
* @param put map of cached entries
|
||||
*/
|
||||
public void putRolloutGroupStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
public static void putRolloutGroupStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
putIntoCache(put, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@@ -120,51 +115,51 @@ public class RolloutStatusCache {
|
||||
* @param groupId the cache entries belong to
|
||||
* @param status list to cache
|
||||
*/
|
||||
public void putRolloutGroupStatus(final Long groupId, final List<TotalTargetCountActionStatus> status) {
|
||||
public static void putRolloutGroupStatus(final Long groupId, final List<TotalTargetCountActionStatus> status) {
|
||||
putIntoCache(groupId, status, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@EventListener(classes = AbstractActionEvent.class)
|
||||
public void invalidateCachedTotalTargetCountActionStatus(final AbstractActionEvent event) {
|
||||
if (event.getRolloutId() != null) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getRolloutId());
|
||||
}
|
||||
|
||||
if (event.getRolloutGroupId() != null) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
cache.evict(event.getRolloutGroupId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutDeletedEvent.class)
|
||||
public void invalidateCachedTotalTargetCountOnRolloutDelete(final RolloutDeletedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getEntityId());
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutGroupDeletedEvent.class)
|
||||
public void invalidateCachedTotalTargetCountOnRolloutGroupDelete(final RolloutGroupDeletedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
cache.evict(event.getEntityId());
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutStoppedEvent.class)
|
||||
public void invalidateCachedTotalTargetCountOnRolloutStopped(final RolloutStoppedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getRolloutId());
|
||||
event.getRolloutGroupIds().forEach(
|
||||
groupId -> tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME)).evict(groupId));
|
||||
groupId -> asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME)).evict(groupId));
|
||||
}
|
||||
|
||||
private @NotNull Map<Long, List<TotalTargetCountActionStatus>> retrieveFromCache(final List<Long> ids, @NotNull final Cache cache) {
|
||||
private static @NotNull Map<Long, List<TotalTargetCountActionStatus>> retrieveFromCache(final List<Long> ids, @NotNull final Cache cache) {
|
||||
return ids.stream()
|
||||
.map(id -> cache.get(id, CachedTotalTargetCountActionStatus.class))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(CachedTotalTargetCountActionStatus::id, CachedTotalTargetCountActionStatus::status));
|
||||
}
|
||||
|
||||
private List<TotalTargetCountActionStatus> retrieveFromCache(final Long id, @NotNull final Cache cache) {
|
||||
private static List<TotalTargetCountActionStatus> retrieveFromCache(final Long id, @NotNull final Cache cache) {
|
||||
final CachedTotalTargetCountActionStatus cacheItem = cache.get(id, CachedTotalTargetCountActionStatus.class);
|
||||
if (cacheItem == null) {
|
||||
return Collections.emptyList();
|
||||
@@ -172,20 +167,19 @@ public class RolloutStatusCache {
|
||||
return cacheItem.status();
|
||||
}
|
||||
|
||||
private void putIntoCache(final Long id, final List<TotalTargetCountActionStatus> status,
|
||||
@NotNull final Cache cache) {
|
||||
private static void putIntoCache(final Long id, final List<TotalTargetCountActionStatus> status, @NotNull final Cache cache) {
|
||||
cache.put(id, new CachedTotalTargetCountActionStatus(id, status));
|
||||
}
|
||||
|
||||
private void putIntoCache(final Map<Long, List<TotalTargetCountActionStatus>> put, @NotNull final Cache cache) {
|
||||
private static void putIntoCache(final Map<Long, List<TotalTargetCountActionStatus>> put, @NotNull final Cache cache) {
|
||||
put.forEach((k, v) -> cache.put(k, new CachedTotalTargetCountActionStatus(k, v)));
|
||||
}
|
||||
|
||||
private @NotNull Cache getRolloutStatusCache() {
|
||||
private static @NotNull Cache getRolloutStatusCache() {
|
||||
return Objects.requireNonNull(CACHE_MANAGER.getCache(CACHE_RO_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
|
||||
}
|
||||
|
||||
private @NotNull Cache getGroupStatusCache() {
|
||||
private static @NotNull Cache getGroupStatusCache() {
|
||||
return Objects.requireNonNull(CACHE_MANAGER.getCache(CACHE_GR_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
|
||||
@@ -22,18 +27,12 @@ import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceE
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
abstract class AbstractEventMessageConverterTest {
|
||||
|
||||
protected final MessageConverter messageConverter;
|
||||
|
||||
Reference in New Issue
Block a user