Merge pull request #224 from bsinno/fix_asychronous_tenant_aware_access
use SecurityContext as thread local cause it's delegated through threads
This commit is contained in:
@@ -8,13 +8,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.springframework.security.core.Authentication;
|
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.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link TenantAware} implemenation which retrieves the ID of the tenant from
|
* A {@link TenantAware} implemenation which retrieves the ID of the tenant from
|
||||||
@@ -22,15 +24,9 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
* {@link Authentication#getDetails()} which holds the
|
* {@link Authentication#getDetails()} which holds the
|
||||||
* {@link TenantAwareAuthenticationDetails} object.
|
* {@link TenantAwareAuthenticationDetails} object.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class SecurityContextTenantAware implements TenantAware {
|
public class SecurityContextTenantAware implements TenantAware {
|
||||||
|
|
||||||
private static final ThreadLocal<String> TENANT_THREAD_LOCAL = new ThreadLocal<>();
|
|
||||||
private static final ThreadLocal<AtomicInteger> RUN_AS_DEPTH = new ThreadLocal<>();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
@@ -38,9 +34,6 @@ public class SecurityContextTenantAware implements TenantAware {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getCurrentTenant() {
|
public String getCurrentTenant() {
|
||||||
if (TENANT_THREAD_LOCAL.get() != null) {
|
|
||||||
return TENANT_THREAD_LOCAL.get();
|
|
||||||
}
|
|
||||||
final SecurityContext context = SecurityContextHolder.getContext();
|
final SecurityContext context = SecurityContextHolder.getContext();
|
||||||
if (context.getAuthentication() != null) {
|
if (context.getAuthentication() != null) {
|
||||||
final Object authDetails = context.getAuthentication().getDetails();
|
final Object authDetails = context.getAuthentication().getDetails();
|
||||||
@@ -51,29 +44,88 @@ public class SecurityContextTenantAware implements TenantAware {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see hawkbit.server.tenancy.TenantAware#runAsTenant(java.lang.String,
|
|
||||||
* java.util.concurrent.Callable)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T runAsTenant(final String tenant, final TenantRunner<T> callable) {
|
public <T> T runAsTenant(final String tenant, final TenantRunner<T> callable) {
|
||||||
AtomicInteger runAsDepth = RUN_AS_DEPTH.get();
|
final SecurityContext originalContext = SecurityContextHolder.getContext();
|
||||||
if (runAsDepth == null) {
|
|
||||||
runAsDepth = new AtomicInteger(1);
|
|
||||||
RUN_AS_DEPTH.set(runAsDepth);
|
|
||||||
} else {
|
|
||||||
runAsDepth.incrementAndGet();
|
|
||||||
}
|
|
||||||
TENANT_THREAD_LOCAL.set(tenant);
|
|
||||||
try {
|
try {
|
||||||
|
SecurityContextHolder.setContext(buildSecurityContext(tenant));
|
||||||
return callable.run();
|
return callable.run();
|
||||||
} finally {
|
} finally {
|
||||||
if (runAsDepth.decrementAndGet() <= 0) {
|
SecurityContextHolder.setContext(originalContext);
|
||||||
RUN_AS_DEPTH.remove();
|
}
|
||||||
TENANT_THREAD_LOCAL.remove();
|
}
|
||||||
}
|
|
||||||
|
private SecurityContext buildSecurityContext(final String tenant) {
|
||||||
|
final SecurityContextImpl securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(
|
||||||
|
new AuthenticationDelegate(SecurityContextHolder.getContext().getAuthentication(), tenant));
|
||||||
|
return securityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Authentication} implementation to delegate to an existing
|
||||||
|
* {@link Authentication} object except setting the details specifically for
|
||||||
|
* a specific tenant.
|
||||||
|
*/
|
||||||
|
private class AuthenticationDelegate implements Authentication {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Authentication delegate;
|
||||||
|
private final TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails;
|
||||||
|
|
||||||
|
private AuthenticationDelegate(final Authentication delegate, final String tenant) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
tenantAwareAuthenticationDetails = new TenantAwareAuthenticationDetails(tenant, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object another) {
|
||||||
|
return delegate.equals(another);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return delegate.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return delegate.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return delegate.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return delegate.getAuthorities();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return delegate.getCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDetails() {
|
||||||
|
return tenantAwareAuthenticationDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return delegate.getPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAuthenticated() {
|
||||||
|
return delegate.isAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
|
||||||
|
delegate.setAuthenticated(isAuthenticated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class SystemSecurityContext {
|
|||||||
logger.debug("entering system code execution");
|
logger.debug("entering system code execution");
|
||||||
return tenantAware.runAsTenant(tenantAware.getCurrentTenant(), () -> {
|
return tenantAware.runAsTenant(tenantAware.getCurrentTenant(), () -> {
|
||||||
try {
|
try {
|
||||||
setSystemContext();
|
setSystemContext(oldContext);
|
||||||
return callable.call();
|
return callable.call();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
throw Throwables.propagate(e);
|
throw Throwables.propagate(e);
|
||||||
@@ -93,9 +93,10 @@ public class SystemSecurityContext {
|
|||||||
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
|
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setSystemContext() {
|
private static void setSystemContext(final SecurityContext oldContext) {
|
||||||
|
final Authentication oldAuthentication = oldContext.getAuthentication();
|
||||||
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
|
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
|
||||||
securityContextImpl.setAuthentication(new SystemCodeAuthentication());
|
securityContextImpl.setAuthentication(new SystemCodeAuthentication(oldAuthentication));
|
||||||
SecurityContextHolder.setContext(securityContextImpl);
|
SecurityContextHolder.setContext(securityContextImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +105,11 @@ public class SystemSecurityContext {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final List<SimpleGrantedAuthority> AUTHORITIES = Collections
|
private static final List<SimpleGrantedAuthority> AUTHORITIES = Collections
|
||||||
.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.SYSTEM_ROLE));
|
.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.SYSTEM_ROLE));
|
||||||
|
private final Authentication oldAuthentication;
|
||||||
|
|
||||||
|
private SystemCodeAuthentication(final Authentication oldAuthentication) {
|
||||||
|
this.oldAuthentication = oldAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -117,17 +123,17 @@ public class SystemSecurityContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public Object getCredentials() {
|
||||||
return null;
|
return oldAuthentication != null ? oldAuthentication.getCredentials() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getDetails() {
|
public Object getDetails() {
|
||||||
return null;
|
return oldAuthentication != null ? oldAuthentication.getDetails() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getPrincipal() {
|
public Object getPrincipal() {
|
||||||
return null;
|
return oldAuthentication != null ? oldAuthentication.getPrincipal() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user