/* * JOSSO: Java Open Single Sign-On * * Copyright 2004-2009, Atricore, Inc. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.josso.jb5.agent; import java.io.IOException; import java.security.Principal; import javax.security.auth.Subject; import javax.security.auth.message.callback.CallerPrincipalCallback; import javax.security.auth.message.callback.PasswordValidationCallback; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import org.apache.catalina.Realm; import org.apache.catalina.Session; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.SecurityConstraint; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.security.ServerAuthenticationManager; import org.jboss.security.auth.message.GenericMessageInfo; import org.jboss.web.tomcat.security.jaspi.TomcatJASPIAuthenticator; import org.josso.agent.Lookup; import org.josso.agent.http.HttpSSOAgent; import org.josso.agent.http.WebAccessControlUtil; import org.josso.gateway.Constants; import org.josso.jaspi.agent.JASPICallbackHandler; /** * JOSSO authenticator that does JSR-196 (JASPI) authentication. */ public class JOSSOJASPIAuthenticator extends TomcatJASPIAuthenticator { private static final Log log = LogFactory .getLog(JOSSOJASPIAuthenticator.class); private String messageLayer = "HttpServlet"; @Override public void invoke(Request request, Response response) throws IOException, ServletException { if (log.isDebugEnabled()) log.debug("Security checking request " + request.getMethod() + " " + request.getRequestURI()); LoginConfig config = this.context.getLoginConfig(); // Have we got a cached authenticated Principal to record? Principal principal = request.getUserPrincipal(); if (principal == null) { Session session = request.getSessionInternal(false); if (session != null) { if (!jossoCookieExists(request)) { session.setPrincipal(null); } principal = session.getPrincipal(); if (principal != null) { if (log.isDebugEnabled()) log.debug("We have cached auth type " + session.getAuthType() + " for principal " + session.getPrincipal()); request.setAuthType(session.getAuthType()); request.setUserPrincipal(principal); } } } Realm realm = this.context.getRealm(); // Is this request URI subject to a security constraint? SecurityConstraint[] constraints = realm.findSecurityConstraints( request, this.context); // Enforce any user data constraint for this security constraint if (log.isDebugEnabled()) { log.debug(" Calling hasUserDataPermission()"); } if (!realm.hasUserDataPermission(request, response, constraints)) { if (log.isDebugEnabled()) { log.debug(" Failed hasUserDataPermission()"); } /* * ASSERT: Authenticator already set the appropriate HTTP status * code, so we do not have to do anything special */ return; } if (!authenticate(request, response, config)) { if (log.isDebugEnabled()) { log.debug(" Failed authenticate()"); } /* * ASSERT: Authenticator already set the appropriate HTTP status * code, so we do not have to do anything special */ return; } if (log.isDebugEnabled()) { log.debug(" Calling accessControl()"); } if (!realm.hasResourcePermission(request, response, constraints, this.context)) { if (log.isDebugEnabled()) { log.debug(" Failed accessControl()"); } /* * ASSERT: AccessControl method has already set the appropriate HTTP * status code, so we do not have to do anything special */ return; } // Any and all specified constraints have been satisfied if (log.isDebugEnabled()) { log.debug(" Successfully passed all security constraints"); } getNext().invoke(request, response); } @Override protected boolean authenticate(Request request, Response response, LoginConfig config) throws IOException { boolean result = false; String authMethod = config.getAuthMethod(); // Have we already authenticated someone? Principal principal = request.getUserPrincipal(); if (principal != null) { log.trace("Already authenticated '" + principal.getName() + "'"); //return true; } Realm realm = this.context.getRealm(); // Is this request URI subject to a security constraint? SecurityConstraint[] constraints = realm.findSecurityConstraints( request, this.context); if (!jossoCookieExists(request) && principal == null && constraints != null && constraints.length > 0) { boolean authRequired = true; for (int i = 0; i < constraints.length && authRequired; i++) { if (!constraints[i].getAuthConstraint()) { authRequired = false; } else if (!constraints[i].getAllRoles()) { String[] roles = constraints[i].findAuthRoles(); if (roles == null || roles.length == 0) { authRequired = false; } } } if (authRequired) { forwardToLoginPage(request, response, config); return false; } } GenericMessageInfo messageInfo = new GenericMessageInfo(); messageInfo.setRequestMessage(request); messageInfo.setResponseMessage(response); // Put bits of information needed by tomcat server auth modules messageInfo.getMap().put("CACHE", cache); JASPICallbackHandler cbh = new JASPICallbackHandler(); Subject subject = new Subject(); ServerAuthenticationManager sam = getServerAuthenticationManager(); if (sam != null) { result = sam.isValid(messageInfo, subject, messageLayer, cbh); } // The Authentication process has been a success. We need to register // the principal, username, password with the container if (result) { PasswordValidationCallback pvc = cbh.getPasswordValidationCallback(); CallerPrincipalCallback cpcb = cbh.getCallerPrincipalCallback(); if (pvc != null && cpcb != null) { this.register(request, response, cpcb.getPrincipal(), authMethod, pvc.getUsername(), new String(pvc .getPassword())); JBossSecurityAssociationActions.setPrincipalInfo(cpcb.getPrincipal(), new String(pvc.getPassword()), subject); } } return result; } /** * Register an authenticated Principal and authentication type in our * request and session. * * @param request * The servlet request we are processing * @param response * The servlet response we are generating * @param principal * The authenticated Principal to be registered * @param authType * The authentication type to be registered * @param username * Username used to authenticate (if any) * @param password * Password used to authenticate (if any) */ protected void register(Request request, Response response, Principal principal, String authType, String username, String password) { if (log.isTraceEnabled()) { String name = (principal == null) ? "none" : principal.getName(); log.trace("Authenticated '" + name + "' with type '" + authType + "'"); } // Cache the authentication information in our request request.setAuthType(authType); request.setUserPrincipal(principal); // Cache the authentication information in our session, if any Session session = request.getSessionInternal(false); if (session != null && cache) { session.setAuthType(authType); session.setPrincipal(principal); } } /** * Called to forward to the login page. * * @param request Request we are processing * @param response Response we are creating * @param config Login configuration describing * how authentication should be performed */ protected void forwardToLoginPage(Request request, Response response, LoginConfig config) { RequestDispatcher disp = context.getServletContext() .getRequestDispatcher(config.getLoginPage()); try { Lookup lookup = Lookup.getInstance(); lookup.init("josso-agent-config.xml"); HttpSSOAgent agent = (HttpSSOAgent) lookup.lookupSSOAgent(); agent.setAttribute(request.getRequest(), response.getResponse(), WebAccessControlUtil.KEY_JOSSO_SAVED_REQUEST_URI, getRequestURI(request)); disp.forward(request.getRequest(), response.getResponse()); response.finishResponse(); } catch (Throwable t) { log.warn("Unexpected error forwarding to login page", t); } } /** * Returns request URI. * * @param request request * @return request URI. */ protected String getRequestURI(Request request) { StringBuffer requestURI = new StringBuffer(request.getRequestURI()); if (request.getQueryString() != null) { requestURI.append('?'); requestURI.append(request.getQueryString()); } return requestURI.toString(); } /** * Checks if josso cookie exists. * * @param request request * @return true if josso cookie exists, false otherwise */ protected boolean jossoCookieExists(Request request) { boolean jossoCookieExists = false; Cookie cookies[] = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (Constants.JOSSO_SINGLE_SIGN_ON_COOKIE.equals(cookie.getName())) { if (cookie.getValue() != null && !cookie.getValue().equals("-")) { jossoCookieExists = true; } break; } } } return jossoCookieExists; } }