CDCClientServlet.java revision 0785b28d0aa9b62b04ed457a37d2191f45ae0d72
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/**
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The contents of this file are subject to the terms
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * of the Common Development and Distribution License
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * (the License). You may not use this file except in
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * compliance with the License.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * You can obtain a copy of the License at
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * https://opensso.dev.java.net/public/CDDLv1.0.html or
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * opensso/legal/CDDLv1.0.txt
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * See the License for the specific language governing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * permission and limitations under the License.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * When distributing Covered Code, include this CDDL
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Header Notice in each file and include the License file
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * at opensso/legal/CDDLv1.0.txt.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If applicable, add the following below the CDDL Header,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * with the fields enclosed by brackets [] replaced by
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * your own identifying information:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * "Portions Copyrighted [year] [name of copyright owner]"
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * $Id: CDCClientServlet.java,v 1.6 2009/01/12 18:57:12 madan_ranganath Exp $
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Portions Copyrighted 2010-2014 ForgeRock AS.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpackage com.iplanet.services.cdc.client;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.servlet.*;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.servlet.http.*;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.shared.encode.URLEncDec;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.shared.debug.Debug;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.shared.configuration.SystemPropertiesManager;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.iplanet.am.util.SystemProperties;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.iplanet.dpro.session.Session;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.iplanet.dpro.session.SessionID;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.iplanet.dpro.session.SessionException;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.shared.encode.CookieUtils;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.iplanet.sso.SSOException;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.iplanet.sso.SSOToken;
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbingsimport com.iplanet.sso.SSOTokenManager;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.io.BufferedReader;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.io.IOException;
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Lunaimport java.io.InputStreamReader;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.io.PrintWriter;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.net.ConnectException;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.net.HttpURLConnection;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.net.URL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.util.ArrayList;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.util.HashSet;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.util.StringTokenizer;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.util.Enumeration;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.shared.Constants;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport java.util.Set;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/**
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The <code>CDCClientServlet</code> is the heart of the Cross Domain Single Signon mechanism of OpenAM in the DMZ
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * along with the distributed auth.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <br/>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The following is the algorithm used by the program:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <ol>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <li>If request does not contain SSO related cookie or policy has generated some advices then redirect request to
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the distributed auth service.</li>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <li>If request contains SSO related cookie and no advices.</li>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <ul>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <li>Tunnel the Request to OpenAM.</li>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * <li>Send the received AuthNResponse as Form POST to the original request requested using the goto parameter in
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the query string.</li>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * </ul>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * </ol>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpublic class CDCClientServlet extends HttpServlet {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final ArrayList adviceParams = new ArrayList();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final Set<String> INVALID_SET = new HashSet<String>();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final Set<String> VALID_LOGIN_URIS = new HashSet<String>();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String LEFT_ANGLE = "<";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String RIGHT_ANGLE = ">";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String URLENC_RIGHT_ANGLE = "%3e";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String URLENC_LEFT_ANGLE = "%3c";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String URLENC_JAVASCRIPT = "javascript%3a";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String JAVASCRIPT = "javascript:";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String DELIM = ",";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String DEBUG_FILE_NAME = "amCDC";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final char QUESTION_MARK = '?';
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final char AMPERSAND = '&';
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final char EQUAL_TO = '=';
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final char SEMI_COLON = ';';
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final char SPACE = ' ';
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee private static final String GOTO_PARAMETER = "goto";
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee private static final String TARGET_PARAMETER = "TARGET";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String CDCURI = "/cdcservlet";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String LOGIN_URI = "loginURI";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static String cdcAuthURI;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String AUTHURI = "/UI/Login";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String RESPONSE_HEADER_ALERT =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "X-DSAME-Assertion-Form";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String RESPONSE_HEADER_ALERT_VALUE =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "true";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String FORBIDDEN_STR_MATCH = "#403x";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String SERVER_ERROR_STR_MATCH = "#500x";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster static Debug debug = Debug.getInstance(DEBUG_FILE_NAME);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster static {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster initConfig();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static SSOTokenManager tokenManager;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static String sessionServiceName = "iPlanetAMSessionService";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static String authURLCookieName;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static String authURLCookieDomain;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static String deployDescriptor;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean serverMode = Boolean.valueOf(System.getProperty(
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster Constants.SERVER_MODE, SystemProperties.get(Constants.SERVER_MODE,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "false"))).booleanValue();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static boolean cookieEncoding =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SystemProperties.getAsBoolean(Constants.AM_COOKIE_ENCODE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /**
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param config the ServletConfig object that contains configutation
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * information for this servlet.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception ServletException if an exception occurs that interrupts
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet's normal operation.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void init(ServletConfig config) throws ServletException {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster super.init(config);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (debug.messageEnabled()) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("CDCClientServlet.init:CDCClientServlet "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster +"Initializing...");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster try {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tokenManager = SSOTokenManager.getInstance();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } catch (SSOException ssoe) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("CDCClientServlet.init:unable to get SSOTokenManager",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ssoe);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster authURLCookieName = SystemProperties.get(
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster Constants.AUTH_UNIQUE_COOKIE_NAME, "sunIdentityServerAuthNServer");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster authURLCookieDomain = SystemProperties.get(
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster Constants.AUTH_UNIQUE_COOKIE_DOMAIN, "");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster deployDescriptor = SystemProperties.get(
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster Constants.AM_DISTAUTH_DEPLOYMENT_DESCRIPTOR, "/distauth");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /**
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Handles the HTTP GET request.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param request an HttpServletRequest object that contains the request
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the client has made of the servlet.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param response an HttpServletResponse object that contains the response
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet sends to the client.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception ServletException if an input or output error is detected when
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet handles the GET request
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception IOException if the request for the GET could not be handled
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster @Override
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void doGet(HttpServletRequest request, HttpServletResponse response)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster throws ServletException, IOException {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster doGetPost(request, response);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /**
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Handles the HTTP POST request.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param request an HttpServletRequest object that contains the request
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the client has made of the servlet.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param response an HttpServletResponse object that contains the response
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet sends to the client.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception ServletException if an input or output error is detected when
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet handles the GET request
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception IOException if the request for the GET could not be handled
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster @Override
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void doPost(HttpServletRequest request, HttpServletResponse
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster response) throws ServletException, IOException
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster doGetPost(request, response);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /**
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The method redirects the user to the authentication module if he is not
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * authenticated; else redirects him back to the original referrer.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param request an HttpServletRequest object that contains the request
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the client has made of the servlet.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @param response an HttpServletResponse object that contains the response
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet sends to the client.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception ServletException if an input or output error is detected when
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the servlet handles the GET request
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * @exception IOException if the request for the GET could not be handled
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private void doGetPost(HttpServletRequest request,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster HttpServletResponse response) throws ServletException, IOException {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (debug.messageEnabled()) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("CDCClientServlet.doGetPost:Query String received= "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + request.getQueryString());
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String gotoParameter = request.getParameter(GOTO_PARAMETER);
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee String targetParameter = request.getParameter(TARGET_PARAMETER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (targetParameter == null) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster targetParameter =
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee request.getParameter(TARGET_PARAMETER.toLowerCase());
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // if check if goto ot target have invalid strings, to avoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // accepting invalid injected javascript.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((gotoParameter != null ) || (targetParameter != null)) {
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna if (debug.messageEnabled()) {
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna debug.message("CDCClientServlet:doGetPost():validating goto: " + gotoParameter
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna + " and target: " + targetParameter);
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna }
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna for (String invalidStr : INVALID_SET) {
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna if (gotoParameter != null && gotoParameter.toLowerCase().contains(invalidStr)) {
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna showError(response, SERVER_ERROR_STR_MATCH + "GOTO parameter has invalid characters");
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna return;
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna }
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna if (targetParameter != null && targetParameter.toLowerCase().contains(invalidStr)) {
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna showError(response, SERVER_ERROR_STR_MATCH + "TARGET parameter has invalid characters");
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna return;
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna }
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna }
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna }
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna /* Steps to be done
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna * 1. If no SSOToken or policy advice present , forward to
61b4091b1d49fc8b736ee1e150a1b76c14f05c91David Luna * authentication.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * 2. If SSOToken is valid tunnel request to the backend AM's
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee * CDCServlet and Form POST the received response to the agent.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Check for a valid SSOToken in the request. If SSOToken is not found
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee // or if the token is invalid, redirect the user for authentication.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Also re-direct if there are policy advices in the query string
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SSOToken token = getSSOToken(request, response);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // collect advices in parsedRequestParams[0] String and rest of params
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // other than original goto url in parsedRequestParams[1] String.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String[] parsedRequestParams = parseRequestParams(request);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((token == null) || (parsedRequestParams[0] != null)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Redirect to authentication
redirectForAuthentication(request, response, parsedRequestParams[0], parsedRequestParams[1]);
} else {
// tunnel request to AM
// send the request to the CDCServlet of AM where the session
// was created.
sendAuthnRequest(request, response, token);
}
}
/**
* This the main method of this servlet which takes in the request
* opens a URLConnection to the CDCServlet endpoint in the
* OpenAM, and tunnels the request content to it.
* It parses the Response received and if the HTTP_STATUS is "HTTP_OK"
* or "HTTP_MOVED_TEMP" POSTs the received Liberty Authn Response to the
* goto URL specified in the original request.
*/
private void sendAuthnRequest(HttpServletRequest request,
HttpServletResponse response, SSOToken token)
throws ServletException, IOException
{
SessionID sessid = new SessionID(request);
URL CDCServletURL = null;
URL sessionServiceURL = null;
try {
sessionServiceURL = Session.getSessionServiceURL(sessid);
} catch (SessionException se) {
debug.error("CDCClientServlet.sendAuthnRequest: Cannot locate"
+" OpenAM instance to forward to.", se);
showError(response,
"Cannot locate OpenAM instance to forward to");
}
if (sessionServiceURL == null) {
showError(response,
"Cannot locate OpenAM instance to forward to");
}
// replace "sessionservice" by cdcservlet in obtained URL
// we use naming so that we get the URL of the exact server
// where the session is located and get the right deployment
// descriptor.
String sessionServiceURLString = sessionServiceURL.toString();
int serviceNameIndex = sessionServiceURLString.lastIndexOf("/",
sessionServiceURLString.length() - 2); // avoiding trailing "/"
// if any
StringBuilder buffer = new StringBuilder(150);
buffer.append(sessionServiceURLString.substring(0,serviceNameIndex))
.append(CDCURI)
.append(QUESTION_MARK)
.append(request.getQueryString()); // add query string to
// CDCServletURL
CDCServletURL = new URL (buffer.toString());
// save the go to URL of the agent side to ultimately
// POST to.
try {
HttpURLConnection connection =
(HttpURLConnection)CDCServletURL.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type",
"text/html;charset=UTF-8");
connection.setDoOutput( true );
connection.setUseCaches( false );
// replay cookies
String strCookies = getCookiesFromRequest(request);
if (strCookies != null)
{ if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnRequest:Setting "
+"cookies = " + strCookies);
}
connection.setRequestProperty("Cookie", strCookies);
}
// dont wish to follow redirect to agent, since
// the response needs to go via the CDCClientServlet.
HttpURLConnection.setFollowRedirects(false);
// Receiving input from CDCServlet on the AM server instance
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnRequest:Getting "
+"response back from "+CDCServletURL);
debug.message("CDCClientServlet.sendAuthnRequest:Response "
+"Code " + connection.getResponseCode());
debug.message("CDCClientServlet.sendAuthnRequest:Response "
+"Message= " + connection.getResponseMessage());
}
// Check response code
if ((connection.getResponseCode() == HttpURLConnection.HTTP_OK) ||
(connection.getResponseCode() ==
HttpURLConnection.HTTP_MOVED_TEMP))
{
/**
* Read the response back from CDCServlet, got a redirect
* since this response contains the "LARES" ( Liberty
* authn response, which needs to be posted back to the
* dest url (agent).
*/
StringBuilder inBuf = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
int len;
char[] buf = new char[1024];
while((len = in.read(buf,0,buf.length)) != -1) {
inBuf.append(buf,0,len);
}
String inString = inBuf.toString();
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnRequest:"
+"Received response data = " + inString);
}
// put the received Liberty Auth Response
// in the servlet's response.
sendAuthnResponse(request, response, inString);
} else {
debug.error("CDCClientServlet.sendAuthnRequest: Response "
+"code NOT OK/MOVED_TEMP ");
showError(response, "ERROR: Received HTTP error code "
+ connection.getResponseCode()+" from "
+ CDCServletURL);
}
} catch (ConnectException ce) {
// Debug the exception
if (debug.warningEnabled()) {
debug.warning("CDCClientServlet.sendAuthnRequest: "
+"Connection Exception to " + CDCServletURL, ce);
}
showError(response, "Could not connect to CDCServlet at "
+ CDCServletURL+":"+ce.getMessage());
}
}
// Get cookies string from HTTP request object
private String getCookiesFromRequest(HttpServletRequest request) {
Cookie cookies[] = CookieUtils.getCookieArrayFromReq(request);
// above call would return pure sid in iPlanetDirectoryPro cookie
// independent of container encoding
StringBuilder cookieStr = null;
String strCookies = null;
if (cookies != null) {
for (int nCookie = 0; nCookie < cookies.length; nCookie++) {
String cookieName = cookies[nCookie].getName();
String cookieVal = cookies[nCookie].getValue();
if (cookieName.equals(CookieUtils.getAmCookieName()) &&
cookieEncoding) {
cookieVal = URLEncDec.encode(cookieVal);
}
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.getCookiesFromRequest:"
+"Cookie name = " + cookieName);
debug.message("CDCClientServlet.getCookiesFromRequest:"
+"Cookie val= "+ cookieVal);
}
if (cookieStr == null) {
cookieStr = new StringBuilder();
} else {
cookieStr.append(SEMI_COLON).append(SPACE);
}
cookieStr.append(cookieName).append(EQUAL_TO).append(cookieVal);
}
}
if (cookieStr != null) {
strCookies = cookieStr.toString();
}
return strCookies;
}
/**
* Gathers the parameters in the request as a HTTP URL string.
* to form request parameters and policy advice String array.
* It collects all the parameters from the original request except
* the original goto url and any advice parameters.
* Note: All the paramters will be url decoded by default., we should
* make sure that these values are encoded again
*
* @param request an HttpServletRequest object that contains the request
* the client has made of the servlet.
* @return An String array, index 0 is policy advice, index 1 is rest of the
* request parameters
*/
private String[] parseRequestParams(HttpServletRequest request) {
StringBuilder adviceList = null;
StringBuilder parameterString = new StringBuilder(100);
for (Enumeration e = request.getParameterNames(); e.hasMoreElements();){
String paramName = (String)e.nextElement();
if (adviceParams.contains(paramName.toLowerCase())) {
if (adviceList == null) {
adviceList = new StringBuilder();
} else {
adviceList.append(AMPERSAND);
}
String[] values = request.getParameterValues(paramName);
for (int i = 0; values != null && i < values.length; i++) {
adviceList.append(paramName).append(EQUAL_TO)
.append(values[i]);
}
} else {
if (!paramName.equals(GOTO_PARAMETER)) {
String[] values = request.getParameterValues(paramName);
for (int i = 0; values != null && i < values.length; i++) {
parameterString.append(AMPERSAND).append(paramName)
.append(EQUAL_TO).append(URLEncDec.encode(values[i]));
}
}
}
}
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.parseRequestParams:"
+"Advice List is = " + adviceList);
debug.message("CDCClientServlet.parseRequestParams:"
+"Parameter String is = " + parameterString.toString());
}
String policyAdviceList;
String requestParams;
if (adviceList == null) {
policyAdviceList= null;
} else {
policyAdviceList = adviceList.toString();
}
if (parameterString.length() > 0 ) {
requestParams= (parameterString.deleteCharAt(0).toString());
} else {
requestParams = parameterString.toString();
}
return new String[] { policyAdviceList, requestParams };
}
/**
* Redirects the HTTP request to the Authentication module.
* It gets the authentication url from <code>SystemProperties</code>.
* @param request an HttpServletRequest object that contains the request
* the client has made of the servlet.
* @param response an HttpServletResponse object that contains the response
* the servlet sends to the client.
* @exception IOException If an input or output exception occurs
*/
private void redirectForAuthentication(HttpServletRequest request,
HttpServletResponse response, String policyAdviceList, String requestParams)
throws IOException {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication: "
+"requestURL="+request.getRequestURL());
}
StringBuilder redirectURL = new StringBuilder(100);
StringBuilder gotoURL = new StringBuilder(100);
// Check if user has authenticated to another OpenAM
// instance
String authURL = null;
Cookie authCookie =
CookieUtils.getCookieFromReq(request,authURLCookieName);
if (authCookie != null) {
authURL = CookieUtils.getCookieValue(authCookie);
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication: "
+"got an authenticated URL= " + authURL);
}
}
try {
if (authURL == null || authURL.length() == 0 ||
!authURL.toLowerCase().startsWith("http") ||
policyAdviceList != null) {
String finalURL = request.getParameter(GOTO_PARAMETER);
if(finalURL == null || finalURL.equals("")) {
finalURL = request.getParameter(TARGET_PARAMETER);
}
if(finalURL == null || finalURL.equals("")) {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication: "
+"goto or target parameter is missing in the request.");
}
showError(response, SERVER_ERROR_STR_MATCH);
return;
}
gotoURL.append(deployDescriptor).append(CDCURI)
.append(QUESTION_MARK).append(TARGET_PARAMETER)
.append(EQUAL_TO).append(URLEncDec.encode(finalURL))
.append(AMPERSAND).append(requestParams);
// Construct the login URL
String loginURI = request.getParameter(LOGIN_URI);
String cdcUri;
if (loginURI != null && !loginURI.isEmpty() && isValidCDCURI(loginURI)) {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication:found " + LOGIN_URI + "="
+ loginURI);
}
cdcUri = loginURI;
} else {
cdcUri = cdcAuthURI;
}
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication: Login URI is set to = " + cdcUri);
}
if (cdcUri.indexOf(QUESTION_MARK) == -1) {
redirectURL.append(cdcUri).append(QUESTION_MARK);
} else {
redirectURL.append(cdcUri).append(AMPERSAND);
}
if (policyAdviceList != null) {
redirectURL.append(policyAdviceList).append(AMPERSAND);
}
redirectURL.append(GOTO_PARAMETER)
.append(EQUAL_TO)
.append(URLEncDec.encode(gotoURL.toString()));
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication"
+":redirectURL before dispatching is="+redirectURL);
}
RequestDispatcher dispatcher =
request.getRequestDispatcher(redirectURL.toString());
dispatcher.forward(request, response);
} else {
// Redirect the user to the authenticated URL
redirectURL.append(authURL).append(deployDescriptor)
.append(CDCURI).append(QUESTION_MARK)
.append(request.getQueryString());
//Reset the cookie value to null, to avoid continuous loop
// when a load balancer is used
if (authCookie != null) {
authCookie.setValue("");
response.addCookie(authCookie);
}
response.sendRedirect(redirectURL.toString());
}
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.redirectForAuthentication:"
+"Forwarding for authentication to= " + redirectURL);
}
} catch (IOException ex) {
debug.error("CDCClientServlet.redirectForAuthentication: Failed "
+"in forwarding to Authentication service. IOException", ex);
showError(response, "Could for forward to authentication service:"
+ex.getMessage());
} catch (ServletException se) {
debug.error("CDCClientServlet.redirectForAuthentication : Failed " +
"in forwarding to Authentication service. ServletException", se);
showError(response, "Could for forward to authentication service:"
+se.getMessage());
} catch (IllegalStateException ie) {
debug.error("CDCClientServlet.redirectForAuthentication : Failed "
+"in forwarding to Authentication service. Illegal state", ie);
showError(response, "Could for forward to authentication service:"
+ie.getMessage());
}
}
/**
* Shows Application Error message to the user.
* @param response an HttpServletResponse object that contains the response
* the servlet sends to the client.
* @param msg Message to be displayed.
*/
private void showError(HttpServletResponse response, String msg)
throws IOException {
ServletOutputStream out = null;
if (msg == null || msg.equals("") || msg.contains(SERVER_ERROR_STR_MATCH)) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
try {
out = response.getOutputStream();
out.println(msg);
out.flush();
} catch (IOException e) {
debug.error("CDCClientServlet.showError::Could not show error "
+"message to the user " + e);
} finally {
try {
out.close();
} catch (IOException ignore) {}
}
}
/**
* Returns the SSOToken of the user. If user has not authenticated
* re-directs the user to login page
*/
private SSOToken getSSOToken(HttpServletRequest request,
HttpServletResponse response) throws IOException {
SSOToken token = null;
try {
/* SSOTokenManager.createSSOToken() throws an SSOException if the
* token is not valid, so for a invalid token manager.isValidToken()
* will never get executed for an invalid token.
*/
if (((token = tokenManager.createSSOToken(request)) == null) ||
!tokenManager.isValidToken(token)) {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.getSSOToken:SSOToken is "
+"either null or not valid: " + token +
"\nRedirecting for authentication");
}
token = null;
}
} catch (com.iplanet.sso.SSOException e) {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.getSSOToken:SSOException "
+"caught= " + e);
}
token = null;
}
return (token);
}
private void sendAuthnResponse(HttpServletRequest request,
HttpServletResponse response, String authnResponse ) {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnResponse: Called");
}
try{
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnResponse: " +
"AuthnResponse= " + authnResponse);
}
response.setContentType("text/html");
response.setHeader("Pragma", "no-cache");
response.setHeader(RESPONSE_HEADER_ALERT, RESPONSE_HEADER_ALERT_VALUE);
if (authnResponse.contains(FORBIDDEN_STR_MATCH)) {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnResponse: " +
"AuthnResponse showing 403 error page");
}
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
if (authnResponse.contains(SERVER_ERROR_STR_MATCH)) {
if (debug.messageEnabled()) {
debug.error("CDCClientServlet.sendAuthnResponse: " +
"ERROR: An application error has occured.");
}
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
PrintWriter out = response.getWriter();
out.println(authnResponse);
out.close();
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.sendAuthnResponse: "
+ "AuthnResponse sent successfully");
}
return;
} catch(IOException ioe){
debug.error("CDCClientServlet.sendAuthnResponse:" + ioe.getMessage());
}
}
/**
* Return <code>true</code> if the passed URI is valid compared to the valid set loaded during initialization.
*
* @param cdcUri The URI to test.
* @return <code>true</code> if the URI is considered valid, <code>false</code> otherwise.
*/
private boolean isValidCDCURI(String cdcUri) {
int questionMark = cdcUri.indexOf(QUESTION_MARK);
// We are only interested in the URI part up to any parameters that may be included.
if (questionMark != -1) {
cdcUri = cdcUri.substring(0, questionMark);
}
// If there is not an exact match for the passed value then it cannot be considered valid
boolean result = VALID_LOGIN_URIS.contains(cdcUri);
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.isValidCDCURI: checking if " + cdcUri + " is in validLoginURISet: "
+ VALID_LOGIN_URIS + " result:" + result);
}
return result;
}
private static void initConfig() {
adviceParams.add("module");
adviceParams.add("authlevel");
adviceParams.add("role");
adviceParams.add("service");
adviceParams.add("user");
adviceParams.add("realm");
adviceParams.add("org");
adviceParams.add("resource");
adviceParams.add("sunamcompositeadvice");
String invalidStrings = SystemPropertiesManager.get(Constants.INVALID_GOTO_STRINGS);
if (INVALID_SET.isEmpty()) {
debug.message("CDCClientServlet.initConfig: creating invalidSet");
if (invalidStrings == null) {
debug.message("CDCClientServlet.initConfig: invalidStrings is null");
INVALID_SET.add(LEFT_ANGLE);
INVALID_SET.add(RIGHT_ANGLE);
INVALID_SET.add(URLENC_LEFT_ANGLE);
INVALID_SET.add(URLENC_RIGHT_ANGLE);
INVALID_SET.add(JAVASCRIPT);
INVALID_SET.add(URLENC_JAVASCRIPT);
} else {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.initConfig: invalidStrings is: " + invalidStrings);
}
StringTokenizer st = new StringTokenizer(invalidStrings, DELIM);
while (st.hasMoreTokens()) {
INVALID_SET.add(st.nextToken());
}
}
debug.message("CDCClientServlet.initConfig: created invalidSet " + INVALID_SET);
}
String urlFromProps = SystemProperties.get(Constants.CDCSERVLET_LOGIN_URL);
cdcAuthURI = (urlFromProps != null) ? urlFromProps : AUTHURI;
String validLoginURIStrings = SystemPropertiesManager.get(Constants.VALID_LOGIN_URIS);
debug.message("CDCClientServlet.initConfig: creating validLoginURISet");
if (validLoginURIStrings == null) {
debug.message("CDCClientServlet.initConfig: validLoginURIStrings is null, creating default set");
VALID_LOGIN_URIS.add(cdcAuthURI);
} else {
if (debug.messageEnabled()) {
debug.message("CDCClientServlet.initConfig: validLoginURIStrings is: " + validLoginURIStrings);
}
StringTokenizer st = new StringTokenizer(validLoginURIStrings, DELIM);
while (st.hasMoreTokens()) {
VALID_LOGIN_URIS.add(st.nextToken());
}
}
debug.message("CDCClientServlet.initConfig: created validLoginURISet " + VALID_LOGIN_URIS);
}
}