Merge pull request elastic/elasticsearch#2860 from rjernst/deguice12

Internal: Simplify SecurityContext dependencies

Original commit: elastic/x-pack-elasticsearch@74d0036e80
This commit is contained in:
Ryan Ernst 2016-07-19 09:05:26 -07:00 committed by GitHub
commit 4552df11da
6 changed files with 44 additions and 111 deletions

View File

@ -248,6 +248,8 @@ public class Security implements ActionPlugin, IngestPlugin {
return Collections.emptyList(); return Collections.emptyList();
} }
List<Object> components = new ArrayList<>(); List<Object> components = new ArrayList<>();
final SecurityContext securityContext = new SecurityContext(settings, threadPool, cryptoService);
components.add(securityContext);
final SSLConfiguration.Global globalSslConfig = new SSLConfiguration.Global(settings); final SSLConfiguration.Global globalSslConfig = new SSLConfiguration.Global(settings);
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, globalSslConfig, resourceWatcherService); final ClientSSLService clientSSLService = new ClientSSLService(settings, env, globalSslConfig, resourceWatcherService);

View File

@ -5,110 +5,58 @@
*/ */
package org.elasticsearch.xpack.security; package org.elasticsearch.xpack.security;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.threadpool.ThreadPool;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.Callable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.user.User;
/** /**
* * A lightweight utility that can find the current user and authentication information for the local thread.
*/ */
public interface SecurityContext { public class SecurityContext {
void executeAs(User user, Runnable runnable);
<V> V executeAs(User user, Callable<V> callable);
User getUser();
Authentication getAuthentication();
default boolean hasAuthentication() {
return getAuthentication() != null;
}
class Insecure implements SecurityContext {
public static final Insecure INSTANCE = new Insecure();
private Insecure() {
}
@Override
public void executeAs(User user, Runnable runnable) {
runnable.run();
}
@Override
public <V> V executeAs(User user, Callable<V> callable) {
try {
return callable.call();
} catch (Exception e) {
throw new ElasticsearchException(e);
}
}
@Override
public User getUser() {
return null;
}
@Override
public Authentication getAuthentication() {
return null;
}
}
class Secure implements SecurityContext {
private final ESLogger logger;
private final ThreadContext threadContext; private final ThreadContext threadContext;
private final AuthenticationService authcService; private final CryptoService cryptoService;
private final boolean signUserHeader;
@Inject /**
public Secure(ThreadPool threadPool, AuthenticationService authcService) { * Creates a new security context.
* If cryptoService is null, security is disabled and {@link #getUser()}
* and {@link #getAuthentication()} will always return null.
*/
public SecurityContext(Settings settings, ThreadPool threadPool, CryptoService cryptoService) {
this.logger = Loggers.getLogger(getClass(), settings);
this.threadContext = threadPool.getThreadContext(); this.threadContext = threadPool.getThreadContext();
this.authcService = authcService; this.cryptoService = cryptoService;
this.signUserHeader = InternalAuthenticationService.SIGN_USER_HEADER.get(settings);
} }
public void executeAs(User user, Runnable runnable) { /** Returns the current user information, or null if the current request has no authentication info. */
try (ThreadContext.StoredContext ctx = threadContext.stashContext()) {
setUser(user);
runnable.run();
}
}
public <V> V executeAs(User user, Callable<V> callable) {
try (ThreadContext.StoredContext ctx = threadContext.stashContext()) {
setUser(user);
return callable.call();
} catch (Exception e) {
throw new ElasticsearchException(e);
}
}
@Override
public User getUser() { public User getUser() {
Authentication authentication = authcService.getCurrentAuthentication(); Authentication authentication = getAuthentication();
return authentication == null ? null : authentication.getUser(); return authentication == null ? null : authentication.getUser();
} }
@Override /** Returns the authentication information, or null if the current request has no authentication info. */
public Authentication getAuthentication() { public Authentication getAuthentication() {
return authcService.getCurrentAuthentication(); if (cryptoService == null) {
return null;
} }
private void setUser(User user) {
try { try {
authcService.attachUserIfMissing(user); return Authentication.readFromContext(threadContext, cryptoService, signUserHeader);
} catch (IOException | IllegalArgumentException e) { } catch (IOException e) {
throw new ElasticsearchException("failed to attach user to request", e); // TODO: this seems bogus, the only way to get an ioexception here is from a corrupt or tampered
} // auth header, which should be be audited?
logger.error("failed to read authentication", e);
return null;
} }
} }
} }

View File

@ -27,11 +27,7 @@ public class SecurityModule extends AbstractSecurityModule {
XPackPlugin.bindFeatureSet(binder(), SecurityFeatureSet.class); XPackPlugin.bindFeatureSet(binder(), SecurityFeatureSet.class);
if (securityEnabled) { if (securityEnabled) {
bind(SecurityContext.Secure.class).asEagerSingleton();
bind(SecurityContext.class).to(SecurityContext.Secure.class);
bind(SecurityLifecycleService.class).asEagerSingleton(); bind(SecurityLifecycleService.class).asEagerSingleton();
} else {
bind(SecurityContext.class).toInstance(SecurityContext.Insecure.INSTANCE);
} }
} }

View File

@ -92,7 +92,7 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil
// only restore the context if it is not empty. This is needed because sometimes a response is sent to the user // only restore the context if it is not empty. This is needed because sometimes a response is sent to the user
// and then a cleanup action is executed (like for search without a scroll) // and then a cleanup action is executed (like for search without a scroll)
final ThreadContext.StoredContext original = threadContext.newStoredContext(); final ThreadContext.StoredContext original = threadContext.newStoredContext();
final boolean restoreOriginalContext = securityContext.hasAuthentication(); final boolean restoreOriginalContext = securityContext.getAuthentication() != null;
try { try {
if (licenseState.authenticationAndAuthorizationEnabled()) { if (licenseState.authenticationAndAuthorizationEnabled()) {
if (AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, action)) { if (AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, action)) {

View File

@ -58,6 +58,4 @@ public interface AuthenticationService {
* @param user The user to be attached if the header is missing * @param user The user to be attached if the header is missing
*/ */
void attachUserIfMissing(User user) throws IOException, IllegalArgumentException; void attachUserIfMissing(User user) throws IOException, IllegalArgumentException;
Authentication getCurrentAuthentication();
} }

View File

@ -82,17 +82,6 @@ public class InternalAuthenticationService extends AbstractComponent implements
authentication.writeToContextIfMissing(threadContext, cryptoService, signUserHeader); authentication.writeToContextIfMissing(threadContext, cryptoService, signUserHeader);
} }
@Override
public Authentication getCurrentAuthentication() {
try {
Authentication authentication = Authentication.readFromContext(threadContext, cryptoService, signUserHeader);
return authentication == null ? null : authentication;
} catch (IOException e) {
logger.error("failed to read authentication", e);
return null;
}
}
Authenticator createAuthenticator(RestRequest request) { Authenticator createAuthenticator(RestRequest request) {
return new Authenticator(request); return new Authenticator(request);
} }