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: package org.josso.gateway.signon;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.apache.struts.action.*;
026: import org.josso.Lookup;
027: import org.josso.auth.Credential;
028: import org.josso.auth.exceptions.AuthenticationFailureException;
029: import org.josso.gateway.SSOContext;
030: import org.josso.gateway.SSOGateway;
031: import org.josso.gateway.SSOWebConfiguration;
032: import org.josso.gateway.assertion.AssertionManager;
033: import org.josso.gateway.assertion.AuthenticationAssertion;
034: import org.josso.gateway.session.SSOSession;
035: import org.josso.gateway.session.exceptions.NoSuchSessionException;
036: import org.josso.gateway.session.service.SSOSessionManager;
037:
038: import javax.servlet.http.Cookie;
039: import javax.servlet.http.HttpServletRequest;
040: import javax.servlet.http.HttpServletResponse;
041: import javax.servlet.http.HttpSession;
042: import java.net.URL;
043:
044: /**
045: * Base login action extended by concrete actions associated to specific authentication schemes.
046: * This actions controls the auth. process and invokes subclasses methods (template method).
047: *
048: * @author <a href="mailto:sgonzalez@josso.org">Sebastian Gonzalez Oyuela</a>
049: * @version $Id: LoginAction.java 508 2008-02-18 13:32:29Z sgonzalez $
050: */
051:
052: public abstract class LoginAction extends SignonBaseAction {
053:
054: public static final String JOSSO_CMD_LOGIN = "login";
055:
056: private static final Log logger = LogFactory
057: .getLog(LoginAction.class);
058:
059: /**
060: * Executes the propper login method based on sso_command request parameter.
061: */
062: public ActionForward execute(ActionMapping mapping,
063: ActionForm form, HttpServletRequest request,
064: HttpServletResponse response) throws Exception {
065:
066: // Get current SSO Command ...
067: String cmd = getSSOCmd(request);
068:
069: if (canRelay(request))
070: return relay(mapping, form, request, response);
071:
072: if (cmd == null) {
073: return askForLogin(mapping, form, request, response);
074: }
075:
076: return login(mapping, form, request, response);
077: }
078:
079: /**
080: * Ask the user for login information.
081: */
082: protected ActionForward askForLogin(ActionMapping mapping,
083: ActionForm form, HttpServletRequest request,
084: HttpServletResponse response) {
085:
086: try {
087: storeSsoParameters(request);
088: // Ask user for login information.
089: SSOWebConfiguration cfg = Lookup.getInstance()
090: .lookupSSOWebConfiguration();
091:
092: if (cfg.getCustomLoginURL() != null) {
093: // The authentication interface is not the default ...
094: logger.debug("Redirecting to custom login : "
095: + cfg.getCustomLoginURL());
096:
097: response.sendRedirect(response.encodeRedirectURL(cfg
098: .getCustomLoginURL()));
099: return null;
100:
101: }
102:
103: return mapping.findForward("login-page");
104:
105: } catch (Exception e) {
106: // Fatal error , redirect to default error page ...
107: logger.error(e.getMessage(), e);
108: ActionErrors errors = new ActionErrors();
109: errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(
110: "sso.error", e.getMessage() != null ? e
111: .getMessage() : e.toString()));
112: saveErrors(request, errors);
113: return mapping.findForward("error");
114: }
115: }
116:
117: /**
118: * Logins the user in the SSO infrastructure
119: *
120: */
121: protected ActionForward login(ActionMapping mapping,
122: ActionForm form, HttpServletRequest request,
123: HttpServletResponse response) {
124:
125: Credential[] c = null;
126: try {
127:
128: SSOContext ctx = getNewSSOContext(request);
129: c = getCredentials(request);
130: SSOGateway g = getSSOGateway();
131:
132: SSOWebConfiguration cfg = Lookup.getInstance()
133: .lookupSSOWebConfiguration();
134:
135: try {
136:
137: storeSsoParameters(request);
138:
139: HttpSession httpSession = request.getSession();
140:
141: // 1 - Handle Outbound relaying by generating an assertion for the authentication request
142: SSOSession session = null;
143:
144: AuthenticationAssertion authAssertion = g
145: .assertIdentity(c, ctx.getScheme(), ctx);
146: session = authAssertion.getSSOSession();
147: Cookie ssoCookie = newJossoCookie(request
148: .getContextPath(), session.getId());
149: response.addCookie(ssoCookie);
150:
151: if (logger.isDebugEnabled())
152: logger
153: .debug("[login()], authentication successfull.");
154:
155: // 2 - Restore BACK TO URL ...
156: String back_to = (String) httpSession
157: .getAttribute(KEY_JOSSO_BACK_TO);
158: if (back_to == null) {
159:
160: if (logger.isDebugEnabled())
161: logger
162: .debug("[login()], No 'BACK TO' URL found in session "
163: + httpSession.getId());
164:
165: if (logger.isDebugEnabled())
166: logger
167: .debug("[login()], Using configured 'BACK TO' URL : "
168: + cfg.getLoginBackToURL());
169: back_to = cfg.getLoginBackToURL();
170: }
171:
172: if (back_to == null) {
173:
174: // No back to URL received or configured ... use configured success page.
175:
176: logger
177: .warn("No 'BACK TO' URL received or configured ... using default forward rule !");
178:
179: String username = session.getUsername();
180: ActionErrors msg = new ActionErrors();
181: msg.add(ActionErrors.GLOBAL_ERROR, new ActionError(
182: "sso.login.success", username));
183: msg.add(ActionErrors.GLOBAL_ERROR, new ActionError(
184: "sso.info.session", session.getId()));
185: saveErrors(request, msg);
186:
187: // Return to controller.
188: return mapping.findForward("login-result");
189: }
190:
191: // 3 - Redirect the user to the propper page
192: String backToURLWithAssertion = back_to
193: + (back_to.indexOf("?") >= 0 ? "&" : "?")
194: + "josso_assertion_id=" + authAssertion.getId();
195:
196: httpSession.setAttribute(KEY_JOSSO_BACK_TO,
197: backToURLWithAssertion);
198: back_to = backToURLWithAssertion;
199:
200: // Remove this attributes once used
201: httpSession.removeAttribute(KEY_JOSSO_BACK_TO);
202: httpSession.removeAttribute(KEY_JOSSO_ON_ERROR);
203:
204: // We're going back to the partner app.
205: if (logger.isDebugEnabled())
206: logger.debug("[login()], Redirecting user to : "
207: + back_to);
208:
209: response.sendRedirect(response
210: .encodeRedirectURL(back_to));
211:
212: return null; // No forward is needed, we perfomed a 'sendRedirect'.
213:
214: } catch (AuthenticationFailureException e) {
215:
216: if (logger.isDebugEnabled())
217: logger.debug(e.getMessage(), e);
218:
219: // Invalid login attempt, redirect to ON ERROR URL, if any.
220: String on_error = (String) request.getSession(true)
221: .getAttribute(KEY_JOSSO_ON_ERROR);
222:
223: if (on_error == null) {
224: // Check for a configured custom login url
225: on_error = cfg.getCustomLoginURL();
226: }
227:
228: if (on_error != null) {
229:
230: // TODO : Improve error information handling, this could be managed with an outbound mechanism, like assertions.
231:
232: // Add error type and received username to ERROR URL.
233: on_error += on_error.indexOf("?") >= 0 ? "&" : "?"
234: + "josso_error_type=" + e.getErrorType();
235: on_error += "&josso_username="
236: + g.getPrincipalName(ctx.getScheme(), c);
237:
238: response.sendRedirect(response
239: .encodeRedirectURL(on_error));
240: if (logger.isDebugEnabled())
241: logger
242: .debug("[login()], authentication failure. Redirecting user to : "
243: + on_error);
244:
245: return null; // No forward is needed, we perfomed a 'sendRedirect'.
246: }
247:
248: ActionErrors errors = new ActionErrors();
249: errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(
250: "sso.login.failed"));
251:
252: saveErrors(request, errors);
253: return mapping.findForward("login-page");
254: }
255:
256: } catch (Exception e) {
257:
258: // Fatal error ...
259: logger.error(e.getMessage(), e);
260: ActionErrors errors = new ActionErrors();
261: errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(
262: "sso.error", e.getMessage() != null ? e
263: .getMessage() : e.toString()));
264: saveErrors(request, errors);
265: return mapping.findForward("error");
266: }
267:
268: }
269:
270: /**
271: * Relay using a previously opened and valid SSO session.
272: *
273: */
274: protected ActionForward relay(ActionMapping mapping,
275: ActionForm form, HttpServletRequest request,
276: HttpServletResponse response) {
277:
278: try {
279:
280: SSOGateway g = getSSOGateway();
281: storeSsoParameters(request);
282:
283: SSOWebConfiguration cfg = Lookup.getInstance()
284: .lookupSSOWebConfiguration();
285: HttpSession httpSession = request.getSession();
286:
287: // Recover session and create a new assertion.
288: String sessionId = getJossoSessionId(request);
289: SSOSession session = g.findSession(sessionId);
290: AuthenticationAssertion authAssertion = g
291: .assertIdentity(sessionId);
292:
293: if (logger.isDebugEnabled())
294: logger.debug("[relay()], authentication successfull.");
295:
296: // 2 - Restore BACK TO URL ...
297: String back_to = (String) httpSession
298: .getAttribute(KEY_JOSSO_BACK_TO);
299: if (back_to == null) {
300: logger
301: .debug("[relay()], No 'BACK TO' URL found in session, using configured URL : "
302: + cfg.getLoginBackToURL());
303: back_to = cfg.getLoginBackToURL();
304: }
305:
306: if (back_to == null) {
307:
308: // No back to URL received or configured ... use configured success page.
309:
310: logger
311: .warn("No 'BACK TO' URL received or configured ... using default forward rule !");
312:
313: String username = session.getUsername();
314: ActionErrors msg = new ActionErrors();
315: msg.add(ActionErrors.GLOBAL_ERROR, new ActionError(
316: "sso.login.success", username));
317: msg.add(ActionErrors.GLOBAL_ERROR, new ActionError(
318: "sso.info.session", session.getId()));
319: saveErrors(request, msg);
320:
321: // Return to controller.
322: return mapping.findForward("login-result");
323: }
324:
325: // 3 - Redirect the user to the propper page
326: String backToURLWithAssertion = back_to
327: + (back_to.indexOf("?") >= 0 ? "&" : "?")
328: + "josso_assertion_id=" + authAssertion.getId();
329:
330: httpSession.setAttribute(KEY_JOSSO_BACK_TO,
331: backToURLWithAssertion);
332: back_to = backToURLWithAssertion;
333:
334: // Remove this attributes once used
335: httpSession.removeAttribute(KEY_JOSSO_BACK_TO);
336: httpSession.removeAttribute(KEY_JOSSO_ON_ERROR);
337:
338: // We're going back to the partner app.
339: if (logger.isDebugEnabled())
340: logger.debug("[relay()], Redirecting user to : "
341: + back_to);
342:
343: response.sendRedirect(response.encodeRedirectURL(back_to));
344:
345: return null; // No forward is needed, we perfomed a 'sendRedirect'.
346:
347: } catch (Exception e) {
348:
349: // Fatal error ...
350: logger.error(e.getMessage(), e);
351: ActionErrors errors = new ActionErrors();
352: errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(
353: "sso.error", e.getMessage() != null ? e
354: .getMessage() : e.toString()));
355: saveErrors(request, errors);
356: return mapping.findForward("error");
357: }
358: }
359:
360: /**
361: * Check if the request can be relayed to the requesting party without having to reauthenticate.
362: *
363: * @param request
364: * @return true if a relay can be achieved or false in case a new authentication assertion must be issued.
365: */
366: protected boolean canRelay(HttpServletRequest request) {
367: boolean canRelay = false;
368:
369: try {
370: String jossoSessionId = getJossoSessionId(request);
371: if (jossoSessionId != null) {
372: SSOSessionManager ssoSessionManager = Lookup
373: .getInstance().lookupSecurityDomain()
374: .getSessionManager();
375: SSOSession s = ssoSessionManager
376: .getSession(jossoSessionId);
377: if (s != null)
378: canRelay = true;
379: }
380:
381: } catch (NoSuchSessionException e) {
382: // Ingore this error .... we probably got an old SESSION id ...
383: if (logger.isDebugEnabled())
384: logger.debug(e.getMessage());
385:
386: } catch (Exception e) {
387: logger.error(e.getMessage(), e);
388: }
389:
390: return canRelay;
391: }
392:
393: /**
394: * This method stores sso parameters in session.
395: * @param request
396: */
397: protected void storeSsoParameters(HttpServletRequest request) {
398: // Get a session
399: HttpSession s = request.getSession(true);
400:
401: // Store back_to url, if present.
402: String back_to = request.getParameter(PARAM_JOSSO_BACK_TO);
403: if (back_to != null && !"".equals(back_to)) {
404: s.setAttribute(KEY_JOSSO_BACK_TO, back_to);
405: if (logger.isDebugEnabled())
406: logger
407: .debug("[storeSsoParameters()] Storing back-to url in session : "
408: + back_to + " (" + s.getId() + ")");
409: }
410:
411: // Store on_error url if present.
412: String on_error = request.getParameter(PARAM_JOSSO_ON_ERROR);
413: if (on_error != null && !"".equals(on_error)) {
414: s.setAttribute(KEY_JOSSO_ON_ERROR, on_error);
415: if (logger.isDebugEnabled())
416: logger
417: .debug("[storeSsoParameters()] Storing on-error url in session : "
418: + on_error + " (" + s.getId() + ")");
419: }
420:
421: }
422:
423: }
|