All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.omnifaces.security.jaspic.factory.OmniServerAuthContext Maven / Gradle / Ivy

/*
 * Copyright 2013 OmniFaces.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.omnifaces.security.jaspic.factory;

import static java.util.Collections.unmodifiableList;
import static javax.security.auth.message.AuthStatus.FAILURE;
import static javax.security.auth.message.AuthStatus.SEND_CONTINUE;
import static javax.security.auth.message.AuthStatus.SUCCESS;
import static org.omnifaces.security.jaspic.Jaspic.LOGGEDIN_ROLES;
import static org.omnifaces.security.jaspic.Jaspic.LOGGEDIN_USERNAME;
import static org.omnifaces.security.jaspic.Jaspic.isAuthenticationRequest;
import static org.omnifaces.security.jaspic.Jaspic.isExplicitAuthCall;
import static org.omnifaces.security.jaspic.Jaspic.isLogoutRequest;
import static org.omnifaces.security.jaspic.Jaspic.isProtectedResource;
import static org.omnifaces.security.jaspic.Jaspic.isRegisterSession;
import static org.omnifaces.security.jaspic.Jaspic.notifyContainerAboutLogin;
import static org.omnifaces.security.jaspic.Utils.getBaseURL;
import static org.omnifaces.security.jaspic.Utils.redirect;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.config.ServerAuthContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.omnifaces.security.jaspic.AuthResult;
import org.omnifaces.security.jaspic.Jaspic;
import org.omnifaces.security.jaspic.config.AuthStacks;
import org.omnifaces.security.jaspic.config.Module;
import org.omnifaces.security.jaspic.request.RequestDataDAO;

/**
 * The Server Authentication Context is an extra (required) indirection between the Application Server and the actual Server Authentication Module
 * (SAM). This can be used to encapsulate any number of SAMs and either select one at run-time, invoke them all in order, etc.
 * 

* This auth context implements the algorithm for a JAAS/PAM like stacking of auth modules and simulates some JASPIC MR2 (Java EE 7) * features for use in a JASPIC MR1 (Java EE 6) environment such as delivering a logout call to the auth modules and automatically * creating an authentication session. *

* This auth context is also instrumental on working around the CDI limitation of JASPIC; a protected request is always redirected * to a public resource, where a Filter does an explicit call for authentication. Container calls are not delegated to the SAMs, only * explicit calls are. This ensures at the cost of an extra redirect that SAMs always execute within a context where CDI, EJB etc is available * and where forwards on the passed-in request instance work. *

* Note: As explained above, parts of this implementation are redundant with JASPIC 1.0 MR2. Hopefully the Filter workaround will * be redundant too with some future version of JASPIC. * * @author Arjan Tijms * */ public class OmniServerAuthContext implements ServerAuthContext { private static final String AUTHENTICATOR_SESSION_NAME = "org.omnifaces.security.jaspic.Authenticator"; private static final String AUTH_METHOD_SESSION_NAME = "org.omnifaces.security.jaspic.AuthMethod"; private AuthStacks stacks; private CallbackHandler handler; private final RequestDataDAO requestDAO = new RequestDataDAO(); public OmniServerAuthContext(CallbackHandler handler, AuthStacks stacks) throws AuthException { this.stacks = stacks; this.handler = handler; for (List modules : stacks.getModuleStacks().values()) { for (Module module : modules) { module.getServerAuthModule().initialize(null, null, handler, Collections. emptyMap()); } } } @Override public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { AuthStatus status = doValidateRequest(messageInfo, clientSubject, serviceSubject); Jaspic.setLastStatus((HttpServletRequest) messageInfo.getRequestMessage(), status); return status; } public AuthStatus doValidateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage(); AuthStatus status = checkSpecialCases(request, response, messageInfo, clientSubject); if (status != null) { return status; } boolean requiredFailed = false; AuthResult finalAuthResult = new AuthResult(); try { for (Module module : getModuleStack(request)) { AuthResult authResult = Jaspic.validateRequest(module.getServerAuthModule(), messageInfo, clientSubject, serviceSubject); if (authResult.getAuthStatus() == FAILURE) { throw new IllegalStateException("Servlet Container Profile SAM should not return status FAILURE. This is for CLIENT SAMs only"); } finalAuthResult.add(authResult); switch (module.getControlFlag()) { case REQUIRED: if (authResult.isFailed()) { requiredFailed = true; } break; case REQUISITE: if (authResult.isFailed()) { return finalAuthResult.throwOrFail(); } break; case SUFFICIENT: if (!authResult.isFailed() && !requiredFailed) { return authResult.getAuthStatus(); } break; } } return finalAuthResult.throwOrReturnStatus(); } finally { if (!finalAuthResult.isFailed() && isRegisterSession(messageInfo)) { saveAuthentication(request); } } } @Override public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { AuthStatus authStatus = null; for (Module module : getModuleStack((HttpServletRequest) messageInfo.getRequestMessage())) { authStatus = module.getServerAuthModule().secureResponse(messageInfo, serviceSubject); } return authStatus; } @Override public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { for (Module module : getModuleStack((HttpServletRequest) messageInfo.getRequestMessage())) { // tmp module.getServerAuthModule().cleanSubject(messageInfo, subject); } } private AuthStatus checkSpecialCases(HttpServletRequest request, HttpServletResponse response, MessageInfo messageInfo, Subject clientSubject) throws AuthException { // Check if this is a logout request. // // With JASPIC 1.0MR1 request#logout is not profiled and the call with most runtimes will not reach this auth context. // So OmniSecurity uses a hacky workaround where Jaspic.logout actually calls authenticate with a request attribute that we // check here and manually divert to cleanSubject. // // With JASPIC 1.0MR2 request#logout will directly go to cleanSubject if (isLogoutRequest(request)) { cleanSubject(messageInfo, clientSubject); return SEND_CONTINUE; } // Check to see if we're already authenticated. // // With JASPIC 1.0MR1, the container doesn't remember authentication data between requests and we thus have to // re-authenticate before every request. It's important to skip this step if authentication is explicitly requested, otherwise // we risk re-authenticating instead of processing a new login request. // // With JASPIC 1.0MR2, the container takes care of this detail if so requested. if (!isAuthenticationRequest(request) && canReAuthenticate(request, clientSubject, handler)) { return SUCCESS; } if (!isExplicitAuthCall(request)) { // Check to see if this request is to a protected resource // // We'll save the current request here, so we can redirect to the original URL after // authentication succeeds and when we start processing that URL wrap the request // with one containing the original headers, cookies, etc. if (isProtectedResource(messageInfo)) { requestDAO.save(request); redirect(response, getBaseURL(request) + "/login.xhtml"); return SEND_CONTINUE; // End request processing for this request and don't try to process the handler } // No login request and no protected resource. Just continue. return SUCCESS; } return null; } private List getModuleStack(HttpServletRequest request) { String authMethod = Jaspic.getAuthParameters(request).getAuthMethod(); if (authMethod == null) { authMethod = (String) request.getSession().getAttribute(AUTH_METHOD_SESSION_NAME); if (authMethod == null) { authMethod = stacks.getDefaultStackName(); } } request.getSession().setAttribute(AUTH_METHOD_SESSION_NAME, authMethod); return stacks.getModuleStacks().get(authMethod); } @SuppressWarnings("unchecked") private void saveAuthentication(HttpServletRequest request) { request.getSession().setAttribute( AUTHENTICATOR_SESSION_NAME, new AuthenticationData( (String) request.getAttribute(LOGGEDIN_USERNAME), (List) request.getAttribute(LOGGEDIN_ROLES) ) ); } private boolean canReAuthenticate(HttpServletRequest request, Subject clientSubject, CallbackHandler handler) { HttpSession session = request.getSession(false); if (session != null) { AuthenticationData authenticationData = (AuthenticationData) session.getAttribute(AUTHENTICATOR_SESSION_NAME); if (authenticationData != null) { notifyContainerAboutLogin(clientSubject, handler, authenticationData.getUserName(), authenticationData.getApplicationRoles()); return true; } } return false; } private class AuthenticationData { private final String username; private final List applicationRoles; public AuthenticationData(String username, List applicationRoles) { this.username = username; this.applicationRoles = unmodifiableList(new ArrayList<>(applicationRoles)); } public String getUserName() { return username; } public List getApplicationRoles() { return applicationRoles; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy