001: /*
002: * JOSSO: Java Open Single Sign-On
003: *
004: * Copyright 2004-2008, Atricore, Inc.
005: *
006: * This is free software; you can redistribute it and/or modify it
007: * under the terms of the GNU Lesser General Public License as
008: * published by the Free Software Foundation; either version 2.1 of
009: * the License, or (at your option) any later version.
010: *
011: * This software is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this software; if not, write to the Free
018: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
020: */
021:
022: package org.josso.servlet.agent;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.josso.Lookup;
027: import org.josso.agent.*;
028:
029: import javax.servlet.*;
030: import javax.servlet.http.Cookie;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033: import javax.servlet.http.HttpSession;
034: import java.io.IOException;
035: import java.util.HashMap;
036: import java.util.Map;
037:
038: /**
039: * JOSSO Servlet Filter for Generic SSO Agent, this replaces the Valve in tomcat or other container specific components.
040: * The fillter will handle web logic to authenticate, login and logout users.
041: *
042: * Date: Nov 27, 2007
043: * Time: 9:28:53 AM
044: *
045: * @author <a href="mailto:sgonzalez@josso.org">Sebastian Gonzalez Oyuela</a>
046: */
047: public class GenericServletSSOAgentFilter implements Filter {
048:
049: public static final String KEY_JOSSO_SAVED_REQUEST = "org.josso.servlet.agent.savedRequest";
050:
051: private static final String KEY_SESSION_MAP = "org.josso.servlet.agent.sessionMap";
052:
053: /**
054: * One agent instance for all applications.
055: */
056: private AbstractSSOAgent _agent;
057:
058: /**
059: * Logger
060: */
061: private static final Log log = LogFactory
062: .getLog(GenericServletSSOAgentFilter.class);
063:
064: public GenericServletSSOAgentFilter() {
065:
066: }
067:
068: public void init(FilterConfig filterConfig) throws ServletException {
069: // Validate and update our current component state
070: ServletContext ctx = filterConfig.getServletContext();
071: ctx.setAttribute(KEY_SESSION_MAP, new HashMap());
072:
073: if (_agent == null) {
074:
075: try {
076:
077: Lookup lookup = Lookup.getInstance();
078: lookup.init("josso-agent-config.xml"); // For spring compatibility ...
079:
080: // We need at least an abstract SSO Agent
081: _agent = (AbstractSSOAgent) lookup.lookupSSOAgent();
082: if (log.isDebugEnabled())
083: _agent.setDebug(1);
084: _agent.start();
085: } catch (Exception e) {
086: throw new ServletException(
087: "Error starting SSO Agent : " + e.getMessage(),
088: e);
089: }
090:
091: }
092:
093: }
094:
095: public void doFilter(ServletRequest request,
096: ServletResponse response, FilterChain filterChain)
097: throws IOException, ServletException {
098: HttpServletRequest hreq = (HttpServletRequest) request;
099: HttpServletResponse hres = (HttpServletResponse) response;
100:
101: if (log.isDebugEnabled())
102: log.debug("Processing : " + hreq.getContextPath());
103:
104: try {
105: // ------------------------------------------------------------------
106: // Check with the agent if this context should be processed.
107: // ------------------------------------------------------------------
108: String contextPath = hreq.getContextPath();
109:
110: // In catalina, the empty context is considered the root context
111: if ("".equals(contextPath))
112: contextPath = "/";
113:
114: if (!_agent.isPartnerApp(contextPath)) {
115: filterChain.doFilter(hreq, hres);
116: if (log.isDebugEnabled())
117: log.debug("Context is not a josso partner app : "
118: + hreq.getContextPath());
119:
120: return;
121: }
122:
123: // ------------------------------------------------------------------
124: // Check if the partner application required the login form
125: // ------------------------------------------------------------------
126: if (log.isDebugEnabled())
127: log.debug("Checking if its a josso_login_request for '"
128: + hreq.getRequestURI() + "'");
129:
130: if (hreq.getRequestURI()
131: .endsWith(Constants.JOSSO_LOGIN_URI)) {
132:
133: if (log.isDebugEnabled())
134: log.debug("josso_login_request received for uri '"
135: + hreq.getRequestURI() + "'");
136:
137: String loginUrl = _agent.getGatewayLoginUrl();
138: String onErrorUrl = _agent.getGatewayLoginErrorUrl();
139:
140: // TODO: this must be configurable
141: // TODO: improve URL handling
142:
143: String backto = _agent.buildBackToURL(hreq,
144: Constants.JOSSO_SECURITY_CHECK_URI);
145:
146: loginUrl = loginUrl
147: + "?josso_back_to="
148: + backto
149: + (onErrorUrl != null ? "&josso_on_error="
150: + onErrorUrl : "");
151:
152: if (log.isDebugEnabled())
153: log.debug("Redirecting to login url '" + loginUrl
154: + "'");
155:
156: hres.sendRedirect(hres.encodeRedirectURL(loginUrl));
157:
158: return;
159:
160: }
161:
162: // ------------------------------------------------------------------
163: // Check if the partner application required a logout
164: // ------------------------------------------------------------------
165: if (log.isDebugEnabled())
166: log
167: .debug("Checking if its a josso_logout request for '"
168: + hreq.getRequestURI() + "'");
169:
170: if (hreq.getRequestURI().endsWith(
171: Constants.JOSSO_LOGOUT_URI)) {
172:
173: if (log.isDebugEnabled())
174: log.debug("josso_logout request received for uri '"
175: + hreq.getRequestURI() + "'");
176:
177: String backto = _agent.buildBackToURL(hreq, "/");
178: String logoutUrl = _agent.getGatewayLogoutUrl()
179: + (backto != null ? "?josso_back_to=" + backto
180: : "");
181:
182: if (log.isDebugEnabled())
183: log.debug("Redirecting to logout url '" + logoutUrl
184: + "'");
185:
186: // Clear previous COOKIE ...
187: Cookie ssoCookie = _agent.newJossoCookie(hreq
188: .getContextPath(), "-");
189: hres.addCookie(ssoCookie);
190:
191: hres.sendRedirect(hres.encodeRedirectURL(logoutUrl));
192:
193: return;
194:
195: }
196:
197: // ------------------------------------------------------------------
198: // Check for the single sign on cookie
199: // ------------------------------------------------------------------
200: if (log.isDebugEnabled())
201: log.debug("Checking for SSO cookie");
202: Cookie cookie = null;
203: Cookie cookies[] = hreq.getCookies();
204: if (cookies == null)
205: cookies = new Cookie[0];
206: for (int i = 0; i < cookies.length; i++) {
207: if (org.josso.gateway.Constants.JOSSO_SINGLE_SIGN_ON_COOKIE
208: .equals(cookies[i].getName())) {
209: cookie = cookies[i];
210: break;
211: }
212: }
213: if (cookie == null) {
214: if (log.isDebugEnabled())
215: log
216: .debug("SSO cookie is not present, checking for outbound relaying");
217:
218: if (!(hreq.getRequestURI().endsWith(
219: Constants.JOSSO_SECURITY_CHECK_URI) && hreq
220: .getParameter("josso_assertion_id") != null)) {
221: log
222: .debug("SSO cookie not present and relaying was not requested, skipping");
223: filterChain.doFilter(hreq, hres);
224: return;
225: }
226:
227: }
228:
229: // ------------------------------------------------------------------
230: // Check if this URI is subject to SSO protection
231: // ------------------------------------------------------------------
232:
233: // There are some web-resources to ignore.
234: String[] ignoredWebResources = _agent.getPartnerAppConfig(
235: contextPath).getIgnoredWebRources();
236:
237: if (ignoredWebResources != null
238: && ignoredWebResources.length > 0)
239: log
240: .warn("IGNORED WEB RESOURCES NOT SUPPORTED BY THIS AGENT ... ");
241:
242: /**
243: * TODO : Not supported !!!!
244:
245: if (log.isDebugEnabled())
246: log.debug("Found [" + (ignoredWebResources!= null ? ignoredWebResources.length+"" : "no") + "] ignored web resources ");
247:
248:
249: if (ignoredWebResources != null && ignoredWebResources.length > 0) {
250:
251: Realm realm = request.getContext().getRealm();
252: SecurityConstraint [] constraints
253: = realm.findSecurityConstraints(request, request.getContext());
254:
255: if ((constraints != null)) {
256:
257: for (int i = 0; i < ignoredWebResources.length; i++) {
258:
259: // For each ignored web resource, find a web resource collection that matchs.
260: String ignoredWebResource = ignoredWebResources[i];
261: for (int j = 0; j < constraints.length; j++) {
262:
263: // Find a matching web resource collection for each ignored web resources
264: SecurityConstraint constraint = constraints[j];
265: if (constraint.findCollection(ignoredWebResource) != null) {
266:
267: // We should ignore this URI, it's not subject to SSO protection.
268: if (log.isDebugEnabled())
269: log.debug("Not subject to SSO protection : web-resource-name:" + ignoredWebResource);
270: getNext().invoke(request, response);
271: return;
272: }
273: }
274: }
275: }
276: } */
277:
278: // This URI should be protected by SSO, go on ...
279: String jossoSessionId = (cookie == null) ? null : cookie
280: .getValue();
281: HttpSession session = hreq.getSession(true);
282:
283: if (log.isDebugEnabled())
284: log.debug("Session is: " + session);
285:
286: GenericServletLocalSession localSession = new GenericServletLocalSession(
287: session);
288:
289: // ------------------------------------------------------------------
290: // Invoke the SSO Agent
291: // ------------------------------------------------------------------
292: if (log.isDebugEnabled())
293: log.debug("Executing agent...");
294:
295: // ------------------------------------------------------------------
296: // Check if a user has been authenitcated and should be checked by the agent.
297: // ------------------------------------------------------------------
298: if (log.isDebugEnabled())
299: log
300: .debug("Checking if its a josso_security_check for '"
301: + hreq.getRequestURI() + "'");
302:
303: if (hreq.getRequestURI().endsWith(
304: Constants.JOSSO_SECURITY_CHECK_URI)
305: && hreq.getParameter("josso_assertion_id") != null) {
306:
307: if (log.isDebugEnabled())
308: log.debug("josso_security_check received for uri '"
309: + hreq.getRequestURI() + "' assertion id '"
310: + hreq.getParameter("josso_assertion_id"));
311:
312: String assertionId = hreq
313: .getParameter(Constants.JOSSO_ASSERTION_ID_PARAMETER);
314:
315: GenericServletSSOAgentRequest relayRequest;
316:
317: if (log.isDebugEnabled())
318: log
319: .debug("Outbound relaying requested for assertion id ["
320: + assertionId + "]");
321:
322: relayRequest = (GenericServletSSOAgentRequest) doMakeSSOAgentRequest(
323: SSOAgentRequest.ACTION_RELAY, null,
324: localSession, assertionId, hreq, hres);
325:
326: SingleSignOnEntry entry = _agent
327: .processRequest(relayRequest);
328:
329: if (log.isDebugEnabled())
330: log
331: .debug("Outbound relaying succesfull for assertion id ["
332: + assertionId + "]");
333:
334: if (log.isDebugEnabled())
335: log.debug("Assertion id [" + assertionId
336: + "] mapped to SSO session id ["
337: + entry.ssoId + "]");
338:
339: // The cookie is valid to for the partner application only ... in the future each partner app may
340: // store a different auth. token (SSO SESSION) value
341: cookie = _agent.newJossoCookie(hreq.getContextPath(),
342: entry.ssoId);
343: hres.addCookie(cookie);
344:
345: // Redirect the user to the original request URI (which will cause
346: // the original request to be restored)
347:
348: String requestURI = savedRequestURL(session);
349: if (requestURI == null) {
350:
351: // If no saved request is found, redirect to the partner app root :
352: requestURI = hreq
353: .getRequestURI()
354: .substring(
355: 0,
356: (hreq.getRequestURI().length() - Constants.JOSSO_SECURITY_CHECK_URI
357: .length()));
358:
359: // If we're behind a reverse proxy, we have to alter the URL ... this was not necessary on tomcat 5.0 ?!
360: String singlePointOfAccess = _agent
361: .getSinglePointOfAccess();
362: if (singlePointOfAccess != null) {
363: requestURI = singlePointOfAccess + requestURI;
364: } else {
365: String reverseProxyHost = hreq
366: .getHeader(org.josso.gateway.Constants.JOSSO_REVERSE_PROXY_HEADER);
367: if (reverseProxyHost != null) {
368: requestURI = reverseProxyHost + requestURI;
369: }
370: }
371:
372: if (log.isDebugEnabled())
373: log.debug("No saved request found, using : '"
374: + requestURI + "'");
375: }
376:
377: if (log.isDebugEnabled())
378: log.debug("Redirecting to original '" + requestURI
379: + "'");
380:
381: hres.sendRedirect(hres.encodeRedirectURL(requestURI));
382:
383: return;
384: }
385:
386: SSOAgentRequest r = doMakeSSOAgentRequest(
387: SSOAgentRequest.ACTION_ESTABLISH_SECURITY_CONTEXT,
388: jossoSessionId, localSession, null, hreq, hres);
389: SingleSignOnEntry entry = _agent.processRequest(r);
390:
391: if (log.isDebugEnabled())
392: log.debug("Executed agent.");
393:
394: // Get session map for this servlet context.
395: Map sessionMap = (Map) hreq.getSession()
396: .getServletContext().getAttribute(KEY_SESSION_MAP);
397: if (sessionMap.get(localSession.getWrapped()) == null) {
398: // the local session is new so, make the valve listen for its events so that it can
399: // map them to local session events.
400: // TODO : Not supported ... session.addSessionListener(this);
401: sessionMap.put(session, localSession);
402: }
403:
404: // ------------------------------------------------------------------
405: // Has a valid user already been authenticated?
406: // ------------------------------------------------------------------
407: if (log.isDebugEnabled())
408: log.debug("Process request for '"
409: + hreq.getRequestURI() + "'");
410:
411: if (entry != null) {
412: if (log.isDebugEnabled())
413: log.debug("Principal '" + entry.principal
414: + "' has already been authenticated");
415: // TODO : Not supported
416: // (request).setAuthType(entry.authType);
417: // (request).setUserPrincipal(entry.principal);
418:
419: }
420:
421: // propagate the login and logout URLs to
422: // partner applications.
423: hreq.setAttribute("org.josso.agent.gateway-login-url",
424: _agent.getGatewayLoginUrl());
425: hreq.setAttribute("org.josso.agent.gateway-logout-url",
426: _agent.getGatewayLogoutUrl());
427: hreq.setAttribute("org.josso.agent.ssoSessionid",
428: jossoSessionId);
429:
430: // ------------------------------------------------------------------
431: // Invoke the next Valve in our pipeline
432: // ------------------------------------------------------------------
433: filterChain.doFilter(hreq, hres);
434: } finally {
435: if (log.isDebugEnabled())
436: log.debug("Processed : " + hreq.getContextPath());
437: }
438: }
439:
440: public void destroy() {
441: // Validate and update our current component state
442: if (_agent != null) {
443: _agent.stop();
444: _agent = null;
445: }
446:
447: }
448:
449: /**
450: * Return the request URI (with the corresponding query string, if any)
451: * from the saved request so that we can redirect to it.
452: *
453: * @param session Our current session
454: */
455: private String savedRequestURL(HttpSession session) {
456:
457: return (String) session
458: .getAttribute(JOSSOGenericServletUtil.KEY_JOSSO_SAVED_REQUEST_URI);
459:
460: }
461:
462: /**
463: * Creates a new request
464: */
465: protected SSOAgentRequest doMakeSSOAgentRequest(int action,
466: String sessionId, LocalSession session, String assertionId,
467: HttpServletRequest hreq, HttpServletResponse hres) {
468: GenericServletSSOAgentRequest r = new GenericServletSSOAgentRequest(
469: action, sessionId, session, assertionId);
470: r.setRequest(hreq);
471: r.setResponse(hres);
472:
473: return r;
474:
475: }
476:
477: }
|