Refactor TenantAware - remove TenantRunner and replace with standard Runnable / Callable (#2755)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -9,6 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.tenancy;
|
package org.eclipse.hawkbit.tenancy;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -30,45 +32,25 @@ public interface TenantAware {
|
|||||||
String getCurrentUsername();
|
String getCurrentUsername();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives the possibility to run a certain code under a specific given {@code tenant}. Only the given {@link TenantRunner} is executed
|
* Gives the possibility to run a certain code under a specific given {@code tenant}. Only the given {@link Callable} is executed
|
||||||
* under the specific tenant e.g. under control of an {@link ThreadLocal}. After the {@link TenantRunner} it must be ensured that the
|
* under the specific tenant e.g. under control of an {@link ThreadLocal}. After the {@link Callable} it must be ensured that the
|
||||||
* original tenant before this invocation is reset.
|
* original tenant before this invocation is reset.
|
||||||
*
|
*
|
||||||
* @param tenant the tenant which the specific code should run
|
* @param tenant the tenant which the specific code should run
|
||||||
* @param tenantRunner the runner which is implemented to run this specific code
|
* @param callable the runner which is implemented to run this specific code under the given tenant
|
||||||
* under the given tenant
|
* @return the return type of the {@link Callable}
|
||||||
* @return the return type of the {@link TenantRunner}
|
|
||||||
*/
|
*/
|
||||||
<T> T runAsTenant(String tenant, TenantRunner<T> tenantRunner);
|
<T> T runAsTenant(String tenant, Callable<T> callable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives the possibility to run a certain code under a specific given {@code tenant} and {@code username}.
|
* Gives the possibility to run a certain code under a specific given {@code tenant} and {@code username}.
|
||||||
* Only the given {@link TenantRunner} is executed under the specific tenant and user e.g. under control of an {@link ThreadLocal}.
|
* Only the given {@link Runnable} is executed under the specific tenant and user e.g. under control of an {@link ThreadLocal}.
|
||||||
* After the {@link TenantRunner} it must be ensured that the original tenant before this invocation is reset.
|
* After the {@link Runnable} it must be ensured that the original tenant before this invocation is reset.
|
||||||
*
|
*
|
||||||
* @param tenant the tenant which the specific code should run with
|
* @param tenant the tenant which the specific code should run with
|
||||||
* @param username the username which the specific code should run with
|
* @param username the username which the specific code should run with
|
||||||
* @param tenantRunner the runner which is implemented to run this specific code under the given tenant
|
|
||||||
* @return the return type of the {@link TenantRunner}
|
|
||||||
*/
|
*/
|
||||||
<T> T runAsTenantAsUser(String tenant, String username, TenantRunner<T> tenantRunner);
|
void runAsTenantAsUser(String tenant, String username, Runnable runnable);
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link TenantRunner} interface which allows to run specific code under a given tenant by using the
|
|
||||||
* {@link TenantAware#runAsTenant(String, TenantRunner)}.
|
|
||||||
*
|
|
||||||
* @param <T> the return type of the runner
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
interface TenantRunner<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to run specific code and a given tenant.
|
|
||||||
*
|
|
||||||
* @return the return of the code block running under a certain tenant
|
|
||||||
*/
|
|
||||||
T run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the tenant from the current context.
|
* Resolves the tenant from the current context.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.security.controller;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpRole;
|
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||||
@@ -49,7 +50,7 @@ public interface Authenticator {
|
|||||||
protected final TenantConfigurationManagement tenantConfigurationManagement;
|
protected final TenantConfigurationManagement tenantConfigurationManagement;
|
||||||
protected final TenantAware tenantAware;
|
protected final TenantAware tenantAware;
|
||||||
protected final SystemSecurityContext systemSecurityContext;
|
protected final SystemSecurityContext systemSecurityContext;
|
||||||
private final TenantAware.TenantRunner<Boolean> isEnabledTenantRunner;
|
private final Callable<Boolean> isEnabledGetter;
|
||||||
|
|
||||||
protected AbstractAuthenticator(
|
protected AbstractAuthenticator(
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||||
@@ -57,12 +58,12 @@ public interface Authenticator {
|
|||||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||||
this.tenantAware = tenantAware;
|
this.tenantAware = tenantAware;
|
||||||
this.systemSecurityContext = systemSecurityContext;
|
this.systemSecurityContext = systemSecurityContext;
|
||||||
isEnabledTenantRunner = () -> systemSecurityContext.runAsSystem(
|
isEnabledGetter = () -> systemSecurityContext.runAsSystem(
|
||||||
() -> tenantConfigurationManagement.getConfigurationValue(getTenantConfigurationKey(), Boolean.class).getValue());
|
() -> tenantConfigurationManagement.getConfigurationValue(getTenantConfigurationKey(), Boolean.class).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isEnabled(final ControllerSecurityToken securityToken) {
|
protected boolean isEnabled(final ControllerSecurityToken securityToken) {
|
||||||
return tenantAware.runAsTenant(securityToken.getTenant(), isEnabledTenantRunner);
|
return tenantAware.runAsTenant(securityToken.getTenant(), isEnabledGetter);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getTenantConfigurationKey();
|
protected abstract String getTenantConfigurationKey();
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.security.controller;
|
package org.eclipse.hawkbit.security.controller;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED;
|
||||||
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
@@ -30,19 +35,18 @@ public class GatewayTokenAuthenticator extends Authenticator.AbstractAuthenticat
|
|||||||
public static final String GATEWAY_SECURITY_TOKEN_AUTH_SCHEME = "GatewayToken ";
|
public static final String GATEWAY_SECURITY_TOKEN_AUTH_SCHEME = "GatewayToken ";
|
||||||
private static final int OFFSET_GATEWAY_TOKEN = GATEWAY_SECURITY_TOKEN_AUTH_SCHEME.length();
|
private static final int OFFSET_GATEWAY_TOKEN = GATEWAY_SECURITY_TOKEN_AUTH_SCHEME.length();
|
||||||
|
|
||||||
private final TenantAware.TenantRunner<String> gatewaySecurityTokenKeyConfigRunner;
|
private final Callable<String> gatewaySecurityTokenKeyGetter;
|
||||||
|
|
||||||
public GatewayTokenAuthenticator(
|
public GatewayTokenAuthenticator(
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||||
final SystemSecurityContext systemSecurityContext) {
|
final SystemSecurityContext systemSecurityContext) {
|
||||||
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||||
gatewaySecurityTokenKeyConfigRunner = () -> {
|
gatewaySecurityTokenKeyGetter = () -> {
|
||||||
log.trace("retrieving configuration value for configuration key {}",
|
log.trace("retrieving configuration value for configuration key {}", AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY);
|
||||||
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY);
|
|
||||||
|
|
||||||
return systemSecurityContext
|
return systemSecurityContext
|
||||||
.runAsSystem(() -> tenantConfigurationManagement
|
.runAsSystem(() -> tenantConfigurationManagement
|
||||||
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class)
|
.getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class)
|
||||||
.getValue());
|
.getValue());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -67,7 +71,7 @@ public class GatewayTokenAuthenticator extends Authenticator.AbstractAuthenticat
|
|||||||
final String presentedToken = authHeader.substring(OFFSET_GATEWAY_TOKEN);
|
final String presentedToken = authHeader.substring(OFFSET_GATEWAY_TOKEN);
|
||||||
|
|
||||||
// validate if the presented token is the same as the gateway token
|
// validate if the presented token is the same as the gateway token
|
||||||
return presentedToken.equals(tenantAware.runAsTenant(controllerSecurityToken.getTenant(), gatewaySecurityTokenKeyConfigRunner))
|
return presentedToken.equals(tenantAware.runAsTenant(controllerSecurityToken.getTenant(), gatewaySecurityTokenKeyGetter))
|
||||||
? authenticatedController(controllerSecurityToken.getTenant(), controllerSecurityToken.getControllerId()) : null;
|
? authenticatedController(controllerSecurityToken.getTenant(), controllerSecurityToken.getControllerId()) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +82,6 @@ public class GatewayTokenAuthenticator extends Authenticator.AbstractAuthenticat
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTenantConfigurationKey() {
|
protected String getTenantConfigurationKey() {
|
||||||
return TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED;
|
return AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.security.controller;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
@@ -47,7 +48,7 @@ public class SecurityHeaderAuthenticator extends Authenticator.AbstractAuthentic
|
|||||||
// header exists multiple times in the request for all trusted chains.
|
// header exists multiple times in the request for all trusted chains.
|
||||||
private final String sslIssuerHashBasicHeader;
|
private final String sslIssuerHashBasicHeader;
|
||||||
|
|
||||||
private final TenantAware.TenantRunner<String> sslIssuerNameConfigTenantRunner;
|
private final Callable<String> sslIssuerNameConfigGetter;
|
||||||
|
|
||||||
public SecurityHeaderAuthenticator(
|
public SecurityHeaderAuthenticator(
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||||
@@ -56,7 +57,7 @@ public class SecurityHeaderAuthenticator extends Authenticator.AbstractAuthentic
|
|||||||
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||||
this.caCommonNameHeader = caCommonNameHeader;
|
this.caCommonNameHeader = caCommonNameHeader;
|
||||||
this.sslIssuerHashBasicHeader = caAuthorityNameHeader;
|
this.sslIssuerHashBasicHeader = caAuthorityNameHeader;
|
||||||
sslIssuerNameConfigTenantRunner = () -> systemSecurityContext.runAsSystem(
|
sslIssuerNameConfigGetter = () -> systemSecurityContext.runAsSystem(
|
||||||
() -> tenantConfigurationManagement.getConfigurationValue(
|
() -> tenantConfigurationManagement.getConfigurationValue(
|
||||||
TenantConfigurationKey.AUTHENTICATION_HEADER_AUTHORITY_NAME, String.class).getValue());
|
TenantConfigurationKey.AUTHENTICATION_HEADER_AUTHORITY_NAME, String.class).getValue());
|
||||||
}
|
}
|
||||||
@@ -81,7 +82,7 @@ public class SecurityHeaderAuthenticator extends Authenticator.AbstractAuthentic
|
|||||||
|
|
||||||
final String sslIssuerHashValue = getIssuerHashHeader(
|
final String sslIssuerHashValue = getIssuerHashHeader(
|
||||||
controllerSecurityToken,
|
controllerSecurityToken,
|
||||||
tenantAware.runAsTenant(controllerSecurityToken.getTenant(), sslIssuerNameConfigTenantRunner));
|
tenantAware.runAsTenant(controllerSecurityToken.getTenant(), sslIssuerNameConfigGetter));
|
||||||
if (sslIssuerHashValue == null) {
|
if (sslIssuerHashValue == null) {
|
||||||
log.debug("The request contains the 'common name' header but trusted hash is not found");
|
log.debug("The request contains the 'common name' header but trusted hash is not found");
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public interface SystemManagement {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs consumer for each tenant as
|
* Runs consumer for each tenant as
|
||||||
* {@link TenantAware#runAsTenant(String, org.eclipse.hawkbit.tenancy.TenantAware.TenantRunner)}
|
* {@link TenantAware#runAsTenant(String, java.util.concurrent.Callable)}
|
||||||
* silently (i.e. exceptions will be logged but operations will continue for further tenants).
|
* silently (i.e. exceptions will be logged but operations will continue for further tenants).
|
||||||
*
|
*
|
||||||
* @param consumer to run as tenant
|
* @param consumer to run as tenant
|
||||||
|
|||||||
@@ -15,46 +15,37 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
|
|||||||
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
|
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of {@link RolloutApprovalStrategy}. Decides whether
|
* Default implementation of {@link RolloutApprovalStrategy}. Decides whether approval is needed based on configuration of the tenant as well
|
||||||
* approval is needed based on configuration of the tenant as well as the roles
|
* as the roles of the user who created the Rollout. Provides a no-operation implementation of
|
||||||
* of the user who created the Rollout. Provides a no-operation implementation
|
* {@link RolloutApprovalStrategy#onApprovalRequired(Rollout)}.
|
||||||
* of {@link RolloutApprovalStrategy#onApprovalRequired(Rollout)}.
|
|
||||||
*/
|
*/
|
||||||
public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
||||||
|
|
||||||
private final UserAuthoritiesResolver userAuthoritiesResolver;
|
|
||||||
|
|
||||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||||
|
|
||||||
private final SystemSecurityContext systemSecurityContext;
|
private final SystemSecurityContext systemSecurityContext;
|
||||||
|
|
||||||
DefaultRolloutApprovalStrategy(
|
DefaultRolloutApprovalStrategy(
|
||||||
final UserAuthoritiesResolver userAuthoritiesResolver,
|
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||||
final SystemSecurityContext systemSecurityContext) {
|
final SystemSecurityContext systemSecurityContext) {
|
||||||
this.userAuthoritiesResolver = userAuthoritiesResolver;
|
|
||||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||||
this.systemSecurityContext = systemSecurityContext;
|
this.systemSecurityContext = systemSecurityContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true, if rollout approval is enabled and rollout creator doesn't
|
* Returns true, if rollout approval is enabled and rollout creator doesn't have approval role. It have to be called in the user context
|
||||||
* have approval role.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isApprovalNeeded(final Rollout rollout) {
|
public boolean isApprovalNeeded(final Rollout rollout) {
|
||||||
return isApprovalEnabled() && hasNoApproveRolloutPermission(getActorAuthorities(rollout));
|
return isApprovalEnabled() && hasNoApproveRolloutPermission(
|
||||||
|
getCurrentAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@@ -85,19 +76,4 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
|||||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||||
.getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue());
|
.getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<String> getActorAuthorities(final Rollout rollout) {
|
|
||||||
// rollout state transition from CREATING to CREATED is managed by
|
|
||||||
// scheduler under SYSTEM user context, thus we get the
|
|
||||||
// user based on the properties of initially created rollout entity
|
|
||||||
if (RolloutStatus.CREATING == rollout.getStatus()) {
|
|
||||||
final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() : rollout.getCreatedBy();
|
|
||||||
if (!ObjectUtils.isEmpty(actor)) {
|
|
||||||
return systemSecurityContext.runAsSystem(() -> userAuthoritiesResolver.getUserAuthorities(rollout.getTenant(), actor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((User) getCurrentAuthentication().getPrincipal()).getAuthorities().stream()
|
|
||||||
.map(GrantedAuthority::getAuthority).toList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,6 @@ import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
|||||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -397,10 +396,9 @@ public class JpaRepositoryConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
RolloutApprovalStrategy rolloutApprovalStrategy(final UserAuthoritiesResolver userAuthoritiesResolver,
|
RolloutApprovalStrategy rolloutApprovalStrategy(
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
final TenantConfigurationManagement tenantConfigurationManagement, final SystemSecurityContext systemSecurityContext) {
|
||||||
final SystemSecurityContext systemSecurityContext) {
|
return new DefaultRolloutApprovalStrategy(tenantConfigurationManagement, systemSecurityContext);
|
||||||
return new DefaultRolloutApprovalStrategy(userAuthoritiesResolver, tenantConfigurationManagement, systemSecurityContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -155,10 +155,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
context -> // has stored context - executes it with it
|
context -> // has stored context - executes it with it
|
||||||
contextAware.runInContext(context, () -> execute0(rollout)),
|
contextAware.runInContext(context, () -> execute0(rollout)),
|
||||||
() -> // has no stored context - executes it in the tenant & user scope
|
() -> // has no stored context - executes it in the tenant & user scope
|
||||||
contextAware.runAsTenantAsUser(contextAware.getCurrentTenant(), rollout.getCreatedBy(), () -> {
|
contextAware.runAsTenantAsUser(contextAware.getCurrentTenant(), rollout.getCreatedBy(), () -> execute0(rollout)));
|
||||||
execute0(rollout);
|
|
||||||
return null;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void execute0(final Rollout rollout) {
|
private void execute0(final Rollout rollout) {
|
||||||
|
|||||||
@@ -142,10 +142,7 @@ public class AutoAssignChecker implements AutoAssignExecutor {
|
|||||||
() -> // has no stored context - executes it in the tenant & user scope
|
() -> // has no stored context - executes it in the tenant & user scope
|
||||||
contextAware.runAsTenantAsUser(
|
contextAware.runAsTenantAsUser(
|
||||||
contextAware.getCurrentTenant(),
|
contextAware.getCurrentTenant(),
|
||||||
getAutoAssignmentInitiatedBy(filterQuery), () -> {
|
getAutoAssignmentInitiatedBy(filterQuery), () -> consumer.accept(filterQuery))
|
||||||
consumer.accept(filterQuery);
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
} catch (final RuntimeException ex) {
|
} catch (final RuntimeException ex) {
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.management;
|
package org.eclipse.hawkbit.repository.jpa.management;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
|
||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||||
@@ -28,7 +29,6 @@ import java.util.function.Function;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
|
||||||
import org.eclipse.hawkbit.repository.TargetFields;
|
import org.eclipse.hawkbit.repository.TargetFields;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TenantConfigurationDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TenantConfigurationDeletedEvent;
|
||||||
@@ -248,10 +248,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
private void checkAccess(final String configurationKeyName) {
|
private void checkAccess(final String configurationKeyName) {
|
||||||
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) {
|
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) {
|
||||||
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||||
if (!systemSecurityContext.isCurrentThreadSystemCode() &&
|
if (!SystemSecurityContext.isCurrentThreadSystemCode() && !systemSecurityContext.hasPermission(READ_GATEWAY_SECURITY_TOKEN)) {
|
||||||
!systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SECURITY_TOKEN)) {
|
|
||||||
throw new InsufficientPermissionException(
|
throw new InsufficientPermissionException(
|
||||||
"Can't read gateway security token! " + SpPermission.READ_GATEWAY_SECURITY_TOKEN + " is required!");
|
"Can't read gateway security token! " + READ_GATEWAY_SECURITY_TOKEN + " is required!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
@Override
|
@Override
|
||||||
public String getSecurityToken() {
|
public String getSecurityToken() {
|
||||||
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||||
if (systemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SECURITY_TOKEN)) {
|
if (SystemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SECURITY_TOKEN)) {
|
||||||
return securityToken;
|
return securityToken;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.autoassign;
|
|||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.lenient;
|
import static org.mockito.Mockito.lenient;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -27,8 +28,8 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
|||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
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.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@@ -47,9 +48,9 @@ import org.springframework.transaction.PlatformTransactionManager;
|
|||||||
class AutoAssignCheckerTest {
|
class AutoAssignCheckerTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private TargetFilterQueryManagement targetFilterQueryManagement;
|
private TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement;
|
||||||
@Mock
|
@Mock
|
||||||
private TargetManagement targetManagement;
|
private TargetManagement<? extends Target> targetManagement;
|
||||||
@Mock
|
@Mock
|
||||||
private DeploymentManagement deploymentManagement;
|
private DeploymentManagement deploymentManagement;
|
||||||
@Mock
|
@Mock
|
||||||
@@ -57,12 +58,12 @@ class AutoAssignCheckerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private ContextAware contextAware;
|
private ContextAware contextAware;
|
||||||
|
|
||||||
private AutoAssignChecker sut;
|
private AutoAssignChecker autoAssignChecker;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void before() {
|
void before() {
|
||||||
sut = new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
|
autoAssignChecker = new AutoAssignChecker(
|
||||||
transactionManager, contextAware);
|
targetFilterQueryManagement, targetManagement, deploymentManagement, transactionManager, contextAware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,18 +76,15 @@ class AutoAssignCheckerTest {
|
|||||||
final long ds = getRandomLong();
|
final long ds = getRandomLong();
|
||||||
final TargetFilterQuery matching = mockFilterQuery(ds);
|
final TargetFilterQuery matching = mockFilterQuery(ds);
|
||||||
final TargetFilterQuery notMatching = mockFilterQuery(ds);
|
final TargetFilterQuery notMatching = mockFilterQuery(ds);
|
||||||
when(targetFilterQueryManagement.findWithAutoAssignDS(any()))
|
when(targetFilterQueryManagement.findWithAutoAssignDS(any())).thenReturn(new SliceImpl<>(Arrays.asList(notMatching, matching)));
|
||||||
.thenReturn(new SliceImpl<>(Arrays.asList(notMatching, matching)));
|
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, matching.getQuery())).thenReturn(true);
|
||||||
|
|
||||||
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, matching.getQuery()))
|
|
||||||
.thenReturn(true);
|
|
||||||
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, notMatching.getQuery()))
|
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(target, ds, notMatching.getQuery()))
|
||||||
.thenReturn(false);
|
.thenReturn(false);
|
||||||
|
|
||||||
sut.checkSingleTarget(target);
|
autoAssignChecker.checkSingleTarget(target);
|
||||||
|
|
||||||
verify(deploymentManagement).assignDistributionSets(eq(matching.getAutoAssignInitiatedBy()),
|
verify(deploymentManagement).assignDistributionSets(
|
||||||
Mockito.argThat(deployReqMatcher(target, ds)), any());
|
eq(matching.getAutoAssignInitiatedBy()), Mockito.argThat(deployReqMatcher(target, ds)), any());
|
||||||
Mockito.verifyNoMoreInteractions(deploymentManagement);
|
Mockito.verifyNoMoreInteractions(deploymentManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +116,9 @@ class AutoAssignCheckerTest {
|
|||||||
|
|
||||||
private void mockRunningAsNonSystem() {
|
private void mockRunningAsNonSystem() {
|
||||||
when(contextAware.getCurrentTenant()).thenReturn(getRandomString());
|
when(contextAware.getCurrentTenant()).thenReturn(getRandomString());
|
||||||
when(contextAware.runAsTenantAsUser(any(String.class), any(String.class), any(TenantAware.TenantRunner.class)))
|
doAnswer(i -> {
|
||||||
.thenAnswer(i -> ((TenantAware.TenantRunner) i.getArgument(2)).run());
|
((Runnable) i.getArgument(2)).run();
|
||||||
|
return null;
|
||||||
|
}).when(contextAware).runAsTenantAsUser(any(String.class), any(String.class), any(Runnable.class));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -15,6 +15,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -106,12 +107,21 @@ public class SecurityContextTenantAware implements ContextAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T runAsTenant(final String tenant, final TenantRunner<T> tenantRunner) {
|
@SuppressWarnings("java:S112") // java:S112 - it is generic class so a generic exception is fine
|
||||||
return runInContext(buildUserSecurityContext(tenant, SYSTEM_USER, SYSTEM_AUTHORITIES), tenantRunner::run);
|
public <T> T runAsTenant(final String tenant, final Callable<T> callable) {
|
||||||
|
return runInContext(buildUserSecurityContext(tenant, SYSTEM_USER, SYSTEM_AUTHORITIES), () -> {
|
||||||
|
try {
|
||||||
|
return callable.call();
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T runAsTenantAsUser(final String tenant, final String username, final TenantRunner<T> tenantRunner) {
|
public void runAsTenantAsUser(final String tenant, final String username, final Runnable runnable) {
|
||||||
Objects.requireNonNull(tenant);
|
Objects.requireNonNull(tenant);
|
||||||
Objects.requireNonNull(username);
|
Objects.requireNonNull(username);
|
||||||
|
|
||||||
@@ -119,7 +129,10 @@ public class SecurityContextTenantAware implements ContextAware {
|
|||||||
() -> authoritiesResolver.getUserAuthorities(tenant, username).stream()
|
() -> authoritiesResolver.getUserAuthorities(tenant, username).stream()
|
||||||
.map(SimpleGrantedAuthority::new)
|
.map(SimpleGrantedAuthority::new)
|
||||||
.toList());
|
.toList());
|
||||||
return runInContext(buildUserSecurityContext(tenant, username, authorities), tenantRunner::run);
|
runInContext(buildUserSecurityContext(tenant, username, authorities), () -> {
|
||||||
|
runnable.run();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -151,11 +164,11 @@ public class SecurityContextTenantAware implements ContextAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T runAsSystem(final TenantRunner<T> tenantRunner) {
|
private static <T> T runAsSystem(final Callable<T> callable) {
|
||||||
final SecurityContext currentContext = SecurityContextHolder.getContext();
|
final SecurityContext currentContext = SecurityContextHolder.getContext();
|
||||||
SystemSecurityContext.setSystemContext(currentContext);
|
SystemSecurityContext.setSystemContext(currentContext);
|
||||||
try {
|
try {
|
||||||
return MdcHandler.getInstance().callWithAuthRE(tenantRunner::run);
|
return MdcHandler.getInstance().callWithAuthRE(callable);
|
||||||
} finally {
|
} finally {
|
||||||
SecurityContextHolder.setContext(currentContext);
|
SecurityContextHolder.setContext(currentContext);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import java.io.IOException;
|
|||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -39,14 +41,15 @@ class SystemCodeAuthenticationTest {
|
|||||||
return "user";
|
return "user";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public <T> T runAsTenant(final String tenant, final TenantRunner<T> tenantRunner) {
|
public <T> T runAsTenant(final String tenant, final Callable<T> callable) {
|
||||||
return tenantRunner.run();
|
return callable.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T runAsTenantAsUser(final String tenant, final String username, final TenantRunner<T> tenantRunner) {
|
public void runAsTenantAsUser(final String tenant, final String username, final Runnable runnable) {
|
||||||
return tenantRunner.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user