Skip to content

Commit

Permalink
Revert back to old JASPIC and proactive auth behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Sep 29, 2015
1 parent 0c68aa0 commit e54ca01
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 276 deletions.
Expand Up @@ -120,7 +120,7 @@ class ServletContainerDefinition extends PersistentResourceDefinition {
protected static final AttributeDefinition PROACTIVE_AUTHENTICATION =
new SimpleAttributeDefinitionBuilder(Constants.PROACTIVE_AUTHENTICATION, ModelType.BOOLEAN, true)
.setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES)
.setDefaultValue(new ModelNode(false))
.setDefaultValue(new ModelNode(true))
.setAllowExpression(true)
.build();

Expand Down
Expand Up @@ -151,7 +151,6 @@
import org.wildfly.extension.undertow.security.jacc.JACCAuthorizationManager;
import org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler;
import org.wildfly.extension.undertow.security.jaspi.JASPIAuthenticationMechanism;
import org.wildfly.extension.undertow.security.jaspi.JASPICInitialHandler;
import org.wildfly.extension.undertow.security.jaspi.JASPICSecurityContextFactory;
import org.wildfly.extension.undertow.session.CodecSessionConfigWrapper;
import org.wildfly.extension.undertow.session.SharedSessionManagerConfig;
Expand Down Expand Up @@ -467,15 +466,10 @@ private void handleJASPIMechanism(final DeploymentInfo deploymentInfo) {
if (applicationPolicy != null && JASPIAuthenticationInfo.class.isInstance(applicationPolicy.getAuthenticationInfo())) {
String authMethod = null;
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
if (loginConfig != null && loginConfig.getAuthMethods().size() > 0)
if (loginConfig != null && loginConfig.getAuthMethods().size() > 0) {
authMethod = loginConfig.getAuthMethods().get(0).getName();
deploymentInfo.addSecurityWrapper(new HandlerWrapper() {
@Override
public HttpHandler wrap(HttpHandler handler) {
return new JASPICInitialHandler(securityDomain, handler);
}
});
deploymentInfo.setJaspiAuthenticationMechanism(new JASPIAuthenticationMechanism(authMethod, securityDomain));
}
deploymentInfo.setJaspiAuthenticationMechanism(new JASPIAuthenticationMechanism(securityDomain, authMethod));
deploymentInfo.setSecurityContextFactory(new JASPICSecurityContextFactory(this.securityDomain));
}
}
Expand Down
Expand Up @@ -21,13 +21,16 @@
*/
package org.wildfly.extension.undertow.security.jaspi;

import io.undertow.security.api.AuthenticatedSessionManager;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.SecurityContext;
import io.undertow.security.idm.Account;
import io.undertow.server.ConduitWrapper;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.handlers.ServletRequestContext;
import io.undertow.util.AttachmentKey;

import io.undertow.util.ConduitFactory;
import org.jboss.security.SecurityConstants;
import org.jboss.security.auth.callback.JBossCallbackHandler;
import org.jboss.security.auth.message.GenericMessageInfo;
Expand All @@ -37,15 +40,21 @@
import org.wildfly.extension.undertow.logging.UndertowLogger;
import org.wildfly.extension.undertow.security.AccountImpl;

import javax.security.auth.Subject;
import javax.security.auth.message.AuthException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.security.Principal;
import java.util.HashSet;
import java.util.Set;

import org.jboss.security.auth.callback.JASPICallbackHandler;
import org.jboss.security.identity.Role;
import org.jboss.security.identity.RoleGroup;

import javax.security.auth.Subject;
import org.wildfly.extension.undertow.security.UndertowSecurityAttachments;
import org.xnio.conduits.StreamSinkConduit;

/**
* <p>
Expand All @@ -57,44 +66,62 @@
*/
public class JASPIAuthenticationMechanism implements AuthenticationMechanism {


private static final String JASPI_HTTP_SERVLET_LAYER = "HttpServlet";
private static final String MECHANISM_NAME = "JASPIC";
private static final String JASPI_AUTH_TYPE = "javax.servlet.http.authType";
private static final String JASPI_REGISTER_SESSION = "javax.servlet.http.registerSession";

public static final AttachmentKey<HttpServerExchange> HTTP_SERVER_EXCHANGE_ATTACHMENT_KEY = AttachmentKey.create(HttpServerExchange.class);
public static final AttachmentKey<SecurityContext> SECURITY_CONTEXT_ATTACHMENT_KEY = AttachmentKey.create(SecurityContext.class);
private static final AttachmentKey<Boolean> ALREADY_WRAPPED = AttachmentKey.create(Boolean.class);
public static final AttachmentKey<Boolean> AUTH_RUN = AttachmentKey.create(Boolean.class);

private final String configuredAuthMethod;
private final String securityDomain;
private final String configuredAuthMethod;

public JASPIAuthenticationMechanism(final String configuredAuthMethod, String securityDomain) {
this.configuredAuthMethod = configuredAuthMethod;
public JASPIAuthenticationMechanism(final String securityDomain, final String configuredAuthMethod) {
this.securityDomain = securityDomain;
this.configuredAuthMethod = configuredAuthMethod;
}

@Override
public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange, final SecurityContext sc) {
JASPICAttachment attachment = exchange.getAttachment(JASPICAttachment.ATTACHMENT_KEY);
exchange.putAttachment(AUTH_RUN, true);
final ServletRequestContext requestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
final JASPIServerAuthenticationManager sam = createJASPIAuthenticationManager();
final GenericMessageInfo messageInfo = createMessageInfo(exchange, sc);
final String applicationIdentifier = buildApplicationIdentifier(requestContext);
final JASPICallbackHandler cbh = new JASPICallbackHandler();

AuthenticationMechanismOutcome outcome;
Account authenticatedAccount = null;
UndertowLogger.ROOT_LOGGER.debugf("validateRequest for layer [%s] and applicationContextIdentifier [%s]", JASPI_HTTP_SERVLET_LAYER, applicationIdentifier);

Boolean isValid = attachment.getValid();
attachment.setValid(null);
GenericMessageInfo messageInfo = attachment.getMessageInfo();
if(isValid == null) {
isValid = createJASPIAuthenticationManager().isValid(messageInfo, new Subject(), JASPI_HTTP_SERVLET_LAYER, attachment.getApplicationIdentifier(), new JBossCallbackHandler());
Account cachedAccount = null;
final JASPICSecurityContext jaspicSecurityContext = (JASPICSecurityContext) exchange.getSecurityContext();
final AuthenticatedSessionManager sessionManager = exchange.getAttachment(AuthenticatedSessionManager.ATTACHMENT_KEY);

if (sessionManager != null) {
AuthenticatedSessionManager.AuthenticatedSession authSession = sessionManager.lookupSession(exchange);
if(authSession != null) {
cachedAccount = authSession.getAccount();
// if there is a cached account we set it in the security context so that the principal is available to
// SAM modules via request.getUserPrincipal().
if (cachedAccount != null) {
jaspicSecurityContext.setCachedAuthenticatedAccount(cachedAccount);
}
}
}

final ServletRequestContext requestContext = attachment.getRequestContext();
final JASPIServerAuthenticationManager sam = attachment.getSam();
final JASPICallbackHandler cbh = attachment.getCbh();
AuthenticationMechanismOutcome outcome = AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
Account authenticatedAccount = null;

boolean isValid = sam.isValid(messageInfo, new Subject(), JASPI_HTTP_SERVLET_LAYER, applicationIdentifier, cbh);
jaspicSecurityContext.setCachedAuthenticatedAccount(null);

if (isValid) {
// The CBH filled in the JBOSS SecurityContext, we need to create an Undertow account based on that
org.jboss.security.SecurityContext jbossSct = SecurityActions.getSecurityContext();
authenticatedAccount = createAccount(attachment.getCachedAccount(), jbossSct);
authenticatedAccount = createAccount(cachedAccount, jbossSct);
}

// authType resolution (check message info first, then check for the configured auth method, then use mech-specific name).
Expand All @@ -118,18 +145,52 @@ public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exch
sc.authenticationFailed("JASPIC authentication failed.", authType);
}

// A SAM can wrap the HTTP request/response objects - update the servlet request context with the values found in the message info.
ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
servletRequestContext.setServletRequest((HttpServletRequest) messageInfo.getRequestMessage());
servletRequestContext.setServletResponse((HttpServletResponse) messageInfo.getResponseMessage());

secureResponse(exchange, sam, messageInfo, cbh);

return outcome;

}

private JASPIServerAuthenticationManager createJASPIAuthenticationManager() {
return new JASPIServerAuthenticationManager(this.securityDomain, new JBossCallbackHandler());
}
@Override
public ChallengeResult sendChallenge(final HttpServerExchange exchange, final SecurityContext securityContext) {
return new ChallengeResult(true);
}

private boolean wasAuthExceptionThrown(HttpServerExchange exchange) {
return exchange.getAttachment(UndertowSecurityAttachments.SECURITY_CONTEXT_ATTACHMENT).getData().get(AuthException.class.getName()) != null;
}

private JASPIServerAuthenticationManager createJASPIAuthenticationManager() {
return new JASPIServerAuthenticationManager(this.securityDomain, new JBossCallbackHandler());
}

private String buildApplicationIdentifier(final ServletRequestContext attachment) {
ServletRequest servletRequest = attachment.getServletRequest();
return servletRequest.getServletContext().getVirtualServerName() + " " + servletRequest.getServletContext().getContextPath();
}

private GenericMessageInfo createMessageInfo(final HttpServerExchange exchange, final SecurityContext securityContext) {
ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);

GenericMessageInfo messageInfo = new GenericMessageInfo();

messageInfo.setRequestMessage(servletRequestContext.getServletRequest());
messageInfo.setResponseMessage(servletRequestContext.getServletResponse());

messageInfo.getMap().put("javax.security.auth.message.MessagePolicy.isMandatory", isMandatory(servletRequestContext).toString());

// additional context data, useful to provide access to Undertow resources during the modules processing
messageInfo.getMap().put(SECURITY_CONTEXT_ATTACHMENT_KEY, securityContext);
messageInfo.getMap().put(HTTP_SERVER_EXCHANGE_ATTACHMENT_KEY, exchange);

return messageInfo;
}

private Account createAccount(final Account cachedAccount, final org.jboss.security.SecurityContext jbossSct) {
if (jbossSct == null) {
throw UndertowLogger.ROOT_LOGGER.nullParamter("org.jboss.security.SecurityContext");
Expand Down Expand Up @@ -168,6 +229,32 @@ private Account createAccount(final Account cachedAccount, final org.jboss.secur
return new AccountImpl(userPrincipal, stringRoles, credential, original);
}

private void secureResponse(final HttpServerExchange exchange, final JASPIServerAuthenticationManager sam, final GenericMessageInfo messageInfo, final JASPICallbackHandler cbh) {
if(exchange.getAttachment(ALREADY_WRAPPED) != null || exchange.isResponseStarted()) {
return;
}
exchange.putAttachment(ALREADY_WRAPPED, true);
// we add a response wrapper to properly invoke the secureResponse, after processing the destination
exchange.addResponseWrapper(new ConduitWrapper<StreamSinkConduit>() {
@Override
public StreamSinkConduit wrap(final ConduitFactory<StreamSinkConduit> factory, final HttpServerExchange exchange) {
ServletRequestContext requestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
String applicationIdentifier = buildApplicationIdentifier(requestContext);

if (!wasAuthExceptionThrown(exchange)) {
UndertowLogger.ROOT_LOGGER.debugf("secureResponse for layer [%s] and applicationContextIdentifier [%s].", JASPI_HTTP_SERVLET_LAYER, applicationIdentifier);
sam.secureResponse(messageInfo, new Subject(), JASPI_HTTP_SERVLET_LAYER, applicationIdentifier, cbh);

// A SAM can unwrap the HTTP request/response objects - update the servlet request context with the values found in the message info.
ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
servletRequestContext.setServletRequest((HttpServletRequest) messageInfo.getRequestMessage());
servletRequestContext.setServletResponse((HttpServletResponse) messageInfo.getResponseMessage());
}
return factory.create();
}
});
}

/**
* <p>The authentication is mandatory if the servlet has http constraints (eg.: {@link
* javax.servlet.annotation.HttpConstraint}).</p>
Expand Down

This file was deleted.

0 comments on commit e54ca01

Please sign in to comment.