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:
Kai Zimmermann
2016-06-24 20:55:52 +02:00
committed by GitHub
2 changed files with 92 additions and 34 deletions

View File

@@ -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);
} }
} }
} }

View File

@@ -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