Apply role hierarchy in hasPermission checks (#1675)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -127,8 +127,9 @@ public class SecurityAutoConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public SystemSecurityContext systemSecurityContext(final TenantAware tenantAware) {
|
public SystemSecurityContext systemSecurityContext(
|
||||||
return new SystemSecurityContext(tenantAware);
|
final TenantAware tenantAware, final RoleHierarchy roleHierarchy) {
|
||||||
|
return new SystemSecurityContext(tenantAware, roleHierarchy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityChecker;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||||
@@ -65,6 +64,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
|||||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||||
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
@@ -320,8 +320,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getSecurityToken() {
|
public String getSecurityToken() {
|
||||||
if (SystemSecurityContextHolder.getInstance().getSystemSecurityContext().isCurrentThreadSystemCode()
|
final SystemSecurityContext systemSecurityContext =
|
||||||
|| SecurityChecker.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
|
SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||||
|
if (systemSecurityContext.isCurrentThreadSystemCode() ||
|
||||||
|
systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
|
||||||
return securityToken;
|
return securityToken;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
|
||||||
*
|
|
||||||
* This program and the accompanying materials are made
|
|
||||||
* available under the terms of the Eclipse Public License 2.0
|
|
||||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
|
||||||
*/
|
|
||||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper class which allows to do runtime security checks.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public final class SecurityChecker {
|
|
||||||
|
|
||||||
private SecurityChecker() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the current {@link SecurityContext} for the permission.
|
|
||||||
*
|
|
||||||
* @param permission
|
|
||||||
* the permission to check against the security context
|
|
||||||
* @return {@code true} if the given permission is present in the security
|
|
||||||
* context otherwise {@code false}
|
|
||||||
*/
|
|
||||||
public static boolean hasPermission(final String permission) {
|
|
||||||
final SecurityContext context = SecurityContextHolder.getContext();
|
|
||||||
if (context != null) {
|
|
||||||
final Authentication authentication = context.getAuthentication();
|
|
||||||
if (authentication != null) {
|
|
||||||
for (final GrantedAuthority authority : authentication.getAuthorities()) {
|
|
||||||
if (authority.getAuthority().equals(permission)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,7 @@ import jakarta.validation.ConstraintViolationException;
|
|||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||||
|
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||||
import org.eclipse.hawkbit.repository.FilterParams;
|
import org.eclipse.hawkbit.repository.FilterParams;
|
||||||
import org.eclipse.hawkbit.repository.Identifiable;
|
import org.eclipse.hawkbit.repository.Identifiable;
|
||||||
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
|
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
|
||||||
@@ -189,6 +190,14 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final String securityTokenWithReadPermission = SecurityContextSwitch.runAs(
|
final String securityTokenWithReadPermission = SecurityContextSwitch.runAs(
|
||||||
SecurityContextSwitch.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN),
|
SecurityContextSwitch.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN),
|
||||||
createdTarget::getSecurityToken);
|
createdTarget::getSecurityToken);
|
||||||
|
// retrieve security token only with ROLE_TARGET_ADMIN permission
|
||||||
|
final String securityTokenWithTargetAdminPermission = SecurityContextSwitch.runAs(
|
||||||
|
SecurityContextSwitch.withUser("OnlyTargetAdminPermission", false, SpRole.TARGET_ADMIN),
|
||||||
|
createdTarget::getSecurityToken);
|
||||||
|
// retrieve security token only with ROLE_TENANT_ADMIN permission
|
||||||
|
final String securityTokenWithTenantAdminPermission = SecurityContextSwitch.runAs(
|
||||||
|
SecurityContextSwitch.withUser("OnlyTenantAdminPermission", false, SpRole.TENANT_ADMIN),
|
||||||
|
createdTarget::getSecurityToken);
|
||||||
|
|
||||||
// retrieve security token as system code execution
|
// retrieve security token as system code execution
|
||||||
final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(createdTarget::getSecurityToken);
|
final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(createdTarget::getSecurityToken);
|
||||||
@@ -199,6 +208,8 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
assertThat(createdTarget.getSecurityToken()).isEqualTo("token");
|
assertThat(createdTarget.getSecurityToken()).isEqualTo("token");
|
||||||
assertThat(securityTokenWithReadPermission).isNotNull();
|
assertThat(securityTokenWithReadPermission).isNotNull();
|
||||||
|
assertThat(securityTokenWithTargetAdminPermission).isNotNull();
|
||||||
|
assertThat(securityTokenWithTenantAdminPermission).isNotNull();
|
||||||
assertThat(securityTokenAsSystemCode).isNotNull();
|
assertThat(securityTokenAsSystemCode).isNotNull();
|
||||||
|
|
||||||
assertThat(securityTokenWithoutPermission).isNull();
|
assertThat(securityTokenWithoutPermission).isNull();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.cache.DefaultDownloadIdCache;
|
|||||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||||
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
||||||
import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter;
|
import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter;
|
||||||
|
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||||
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||||
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
|
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
|
||||||
@@ -67,6 +68,7 @@ import org.springframework.integration.support.locks.DefaultLockRegistry;
|
|||||||
import org.springframework.integration.support.locks.LockRegistry;
|
import org.springframework.integration.support.locks.LockRegistry;
|
||||||
import org.springframework.messaging.converter.MessageConverter;
|
import org.springframework.messaging.converter.MessageConverter;
|
||||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||||
import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
|
import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
@@ -106,7 +108,9 @@ public class TestConfiguration implements AsyncConfigurer {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SystemSecurityContext systemSecurityContext(final TenantAware tenantAware) {
|
SystemSecurityContext systemSecurityContext(final TenantAware tenantAware) {
|
||||||
return new SystemSecurityContext(tenantAware);
|
final RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
|
||||||
|
hierarchy.setHierarchy(SpRole.DEFAULT_ROLE_HIERARCHY);
|
||||||
|
return new SystemSecurityContext(tenantAware, hierarchy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.security;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@@ -29,6 +31,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextImpl;
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Service which provide to run system code.
|
* A Service which provide to run system code.
|
||||||
@@ -37,15 +40,26 @@ import org.springframework.security.core.context.SecurityContextImpl;
|
|||||||
public class SystemSecurityContext {
|
public class SystemSecurityContext {
|
||||||
|
|
||||||
private final TenantAware tenantAware;
|
private final TenantAware tenantAware;
|
||||||
|
private final RoleHierarchy roleHierarchy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Autowired constructor.
|
* Autowired constructor.
|
||||||
*
|
*
|
||||||
* @param tenantAware
|
* @param tenantAware the tenant aware bean to retrieve the current tenant
|
||||||
* the tenant aware bean to retrieve the current tenant
|
|
||||||
*/
|
*/
|
||||||
public SystemSecurityContext(final TenantAware tenantAware) {
|
public SystemSecurityContext(final TenantAware tenantAware) {
|
||||||
|
this(tenantAware, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autowired constructor.
|
||||||
|
*
|
||||||
|
* @param tenantAware the tenant aware bean to retrieve the current tenant
|
||||||
|
* @param roleHierarchy the roleHierarchy that is applied
|
||||||
|
*/
|
||||||
|
public SystemSecurityContext(final TenantAware tenantAware, final RoleHierarchy roleHierarchy) {
|
||||||
this.tenantAware = tenantAware;
|
this.tenantAware = tenantAware;
|
||||||
|
this.roleHierarchy = roleHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,6 +174,27 @@ public class SystemSecurityContext {
|
|||||||
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
|
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasPermission(final String permission) {
|
||||||
|
final SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
if (context != null) {
|
||||||
|
final Authentication authentication = context.getAuthentication();
|
||||||
|
if (authentication != null) {
|
||||||
|
Collection<? extends GrantedAuthority> grantedAuthorities = authentication.getAuthorities();
|
||||||
|
if (!ObjectUtils.isEmpty(grantedAuthorities)) {
|
||||||
|
if (roleHierarchy != null) {
|
||||||
|
grantedAuthorities = roleHierarchy.getReachableGrantedAuthorities(grantedAuthorities);
|
||||||
|
}
|
||||||
|
for (final GrantedAuthority authority : grantedAuthorities) {
|
||||||
|
if (authority.getAuthority().equals(permission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void setCustomSecurityContext(final String tenantId, final Object principal,
|
private void setCustomSecurityContext(final String tenantId, final Object principal,
|
||||||
final Collection<? extends GrantedAuthority> authorities) {
|
final Collection<? extends GrantedAuthority> authorities) {
|
||||||
final AnonymousAuthenticationToken authenticationToken = new AnonymousAuthenticationToken(
|
final AnonymousAuthenticationToken authenticationToken = new AnonymousAuthenticationToken(
|
||||||
|
|||||||
Reference in New Issue
Block a user