Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

How do I logout in Sakai so that DistAuth cookies are deleted?

During normal logout in Sakai, only the Sakai session information is cleared. To ensure that the DistAuth cookies are deleted also, one must modify the LoginTool.java file in the sakai-2-0-1-src\login\login\src\java\org\sakaiproject\tool\login directory. An additional parameter can be added to the sakai.properties file so that the logout url is correctly referenced.

Steps involved, for Sakai 2.

...

1.x

#11. modify the complete() method signature, and appropriate calls to include the reply string. See following:

Code Block

package org.sakaiproject.tool.login;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.api.common.authentication.Authentication;
import org.sakaiproject.api.common.authentication.AuthenticationException;
import org.sakaiproject.api.common.authentication.Evidence;
import org.sakaiproject.api.common.authentication.cover.AuthenticationManager;
import org.sakaiproject.api.kernel.session.Session;
import org.sakaiproject.api.kernel.session.cover.SessionManager;
import org.sakaiproject.api.kernel.tool.Tool;
import org.sakaiproject.service.framework.config.cover.ServerConfigurationService;
import org.sakaiproject.util.IdPwEvidence;
import org.sakaiproject.util.LoginUtil;
import org.sakaiproject.util.web.Web;

/**
 * <p>
 * Login tool for Sakai. Works with the ContainerLoginTool servlet to offer container or internal login.
 * </p>
 * <p>
 * This "tool", being login, is not placed, instead each user can interact with only one login at a time. The Sakai Session is used for attributes.
 * </p>
 * 
 * @author University of Michigan, Sakai Software Development Team
 * @version $Revision: 666 $
 */
public class LoginTool extends HttpServlet
{
	/** Our log (commons). */
	private static Log M_log = LogFactory.getLog(LoginTool.class);

	/** Session attribute used to store a message between steps. */
	protected static final String ATTR_MSG = "sakai.login.message";

	/** Session attribute set and shared with ContainerLoginTool: URL for redirecting back here. */
	public static final String ATTR_RETURN_URL = "sakai.login.return.url";

	/** Session attribute set and shared with ContainerLoginTool: if set we have failed container and need to check internal. */
	public static final String ATTR_CONTAINER_CHECKED = "sakai.login.container.checked";

	/**
	 * Access the Servlet's information display.
	 * 
	 * @return servlet information.
	 */
	public String getServletInfo()
	{
		return "Sakai Login";
	}

	/**
	 * Initialize the servlet.
	 * 
	 * @param config
	 *        The servlet config.
	 * @throws ServletException
	 */
	public void init(ServletConfig config) throws ServletException
	{
		super.init(config);

		M_log.info("init()");
	}

	/**
	 * Shutdown the servlet.
	 */
	public void destroy()
	{
		M_log.info("destroy()");

		super.destroy();
	}

	/**
	 * Respond to requests.
	 * 
	 * @param req
	 *        The servlet request.
	 * @param res
	 *        The servlet response.
	 * @throws ServletException.
	 * @throws IOException.
	 */
	protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		// get the session
		Session session = SessionManager.getCurrentSession();

		// get my tool registration
		Tool tool = (Tool) req.getAttribute(Tool.TOOL);

		// recognize what to do from the path
		String option = req.getPathInfo();
		System.out.println(option + "=this logout option*****************");
		// maybe we don't want to do the container this time
		boolean skipContainer = false;

		// if missing, set it to "/login"
		if ((option == null) || ("/".equals(option)))
		{
			option = "/login";
		}
		
		// look for the extreme login (i.e. to skip container checks)
		else if ("/xlogin".equals(option))
		{
			option = "/login";
			skipContainer = true;
		}

		// get the parts (the first will be "", second will be "login" or "logout")
		String[] parts = option.split("/");

		if (parts[1].equals("logout"))
		{
			// get the session info complete needs, since the logout will invalidate and clear the session
			String returnUrl = (String) session.getAttribute(Tool.HELPER_DONE_URL);

			// logout the user
			LoginUtil.logout();

			complete(returnUrl, null, tool, res,"logout");
			return;
		}
		else
		{
			// see if we need to check container
			boolean checkContainer = ServerConfigurationService.getBoolean("container.login", false);
			if (checkContainer && !skipContainer)
			{
				// if we have not checked the container yet, check it now
				if (session.getAttribute(ATTR_CONTAINER_CHECKED) == null)
				{
					// save our return path
					session.setAttribute(ATTR_RETURN_URL, Web.returnUrl(req, null));

					String containerCheckPath = this.getServletConfig().getInitParameter("container");
					String containerCheckUrl = Web.serverUrl(req) + containerCheckPath;

					res.sendRedirect(res.encodeRedirectURL(containerCheckUrl));
					return;
				}
			}

			// send the form
			sendForm(req, res);
		}
	}

	/**
	 * Send the login form
	 * 
	 * @param req
	 *        Servlet request.
	 * @param res
	 *        Servlet response.
	 * @throws IOException
	 */
	protected void sendForm(HttpServletRequest req, HttpServletResponse res) throws IOException
	{
		final String headHtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
				+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"
				+ "  <head>"
				+ "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
				+ "    <link href=\"SKIN_ROOT/tool_base.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />"
				+ "    <link href=\"SKIN_ROOT/DEFAULT_SKIN/tool.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />"
				+ "    <meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />"
				+ "    <title>Sakai</title>"
				+ "    <script type=\"text/javascript\" language=\"JavaScript\" src=\"/library/js/headscripts.js\"></script>"
				+ "  </head>"
				+ "  <body onload=\" setFocus(focus_path);parent.updCourier(doubleDeep, ignoreCourier);\">"
				+ "<script type=\"text/javascript\" language=\"JavaScript\">" + "  focus_path = [\"eid\"];" + "</script>";

		final String tailHtml = "</body></html>";

		final String loginHtml = "<table border=\"3\" cellpadding=\"0\" cellspacing=\"0\" width=\"470\" style =\"margin-right:auto;margin-left:auto;margin-top:5em;background-color:#fff;font-family:helvetica,arial,sans-serif\">"
				+ "	<tr>"
				+ "		<td>"
				+ "			<table bgcolor=\"#FFFFFF\" border=\"0\" cellpadding=\"5\" cellspacing=\"0\">"
				+ "				<tr>"
				+ "					<td colspan=\"2\" bgcolor=\"#11375E\" align=\"left\">"
				+ "						<b style=\"color:#ffffff; font-size: 12pt\">"
				+ "							Login Required"
				+ "						</b>"
				+ "					</td>"
				+ "				</tr>"
				+ "				<tr>"
				+ "					<td>"
				+ "						<img src=\"SKIN_ROOT/DEFAULT_SKIN/images/logo_inst.gif\" border=\"0\" hspace=\"0\" vspace=\"0\" alt=\"\" />"
				+ "					</td>"
				+ "					<td valign=\"top\">"
				+ "						<form method=\"post\" action=\"ACTION\" enctype=\"application/x-www-form-urlencoded\">"
				+ "							<table border=\"0\">"
				+ "								<tr>"
				+ "									<td style=\"font-weight:bold;\">"
				+ "										EID"
				+ "									</td>"
				+ "									<td>"
				+ "										<input name=\"eid\" id=\"eid\"  type=\"text\" style =\"width: 10em\" />"
				+ "									</td>"
				+ "								</tr>"
				+ "								<tr>"
				+ "									<td style=\"font-weight:bold;\">"
				+ "										PW"
				+ "									</td>"
				+ "									<td>"
				+ "										<input name=\"pw\" type=\"password\" style =\"width: 10em\" />"
				+ "									</td>"
				+ "								</tr>"
				+ "								<tr>"
				+ "									<td colspan=\"2\" align=\"right\">"
				+ "										<input name=\"submit\" type=\"submit\" id=\"submit\" value=\"Log in\" style=\"float:right\" />"
				+ "                                        MSG"
				+ "									</td>"
				+ "								</tr>"
				+ "							</table>"
				+ "						</form>"
				+ "					</td>"
				+ "				</tr>"
				+ "			</table>" + "		</td>" + "	</tr>" + "</table>";

		// get the Sakai session
		Session session = SessionManager.getCurrentSession();

		// get my tool registration
		Tool tool = (Tool) req.getAttribute(Tool.TOOL);

		// fragment or not?
		boolean fragment = Boolean.TRUE.toString().equals(req.getAttribute(Tool.FRAGMENT));

		String eidWording = null;
		String pwWording = null;

		// read my configuration
		if (tool != null)
		{
			eidWording = tool.getRegisteredConfig().getProperty("eid");
			pwWording = tool.getRegisteredConfig().getProperty("pw");
		}

		if (eidWording == null) eidWording = "eid";
		if (pwWording == null) pwWording = "pw";

		if (!fragment)
		{
			// set our response type
			res.setContentType("text/html; charset=UTF-8");
		}

		String defaultSkin = ServerConfigurationService.getString("skin.default");
		String skinRoot = ServerConfigurationService.getString("skin.repo");

		// get our response writer
		PrintWriter out = res.getWriter();

		if (!fragment)
		{
			// start our complete document
			String head = headHtml.replaceAll("DEFAULT_SKIN", defaultSkin);
			head = head.replaceAll("SKIN_ROOT", skinRoot);
			out.println(head);
		}

		// if we are in helper mode, there might be a helper message
		if (session.getAttribute(Tool.HELPER_MESSAGE) != null)
		{
			out.println("<p>" + session.getAttribute(Tool.HELPER_MESSAGE) + "</p>");
		}

		// add our return URL
		String returnUrl = res.encodeURL(Web.returnUrl(req, null));
		String html = loginHtml.replaceAll("ACTION", res.encodeURL(returnUrl));

		// add our wording
		html = html.replaceAll("EID", eidWording);
		html = html.replaceAll("PW", pwWording);

		// add the default skin
		html = html.replaceAll("DEFAULT_SKIN", defaultSkin);
		html = html.replaceAll("SKIN_ROOT", skinRoot);

		// write a message if present
		String msg = (String) session.getAttribute(ATTR_MSG);
		if (msg != null)
		{
			html = html.replaceAll("MSG", "<div class=\"chefAlertBox\">Alert: " + msg + "</div>");
			session.removeAttribute(ATTR_MSG);
		}
		else
		{
			html = html.replaceAll("MSG", "");
		}

		// write the login screen
		out.println(html);

		if (!fragment)
		{
			// close the complete document
			out.println(tailHtml);
		}
	}

	/**
	 * Respond to data posting requests.
	 * 
	 * @param req
	 *        The servlet request.
	 * @param res
	 *        The servlet response.
	 * @throws ServletException.
	 * @throws IOException.
	 */
	protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		// get the Sakai session
		Session session = SessionManager.getCurrentSession();

		// get my tool registration
		Tool tool = (Tool) req.getAttribute(Tool.TOOL);

		// here comes the data back from the form... these fields will be present, blank if not filled in
		String eid = req.getParameter("eid").trim();
		String pw = req.getParameter("pw").trim();

		// one of these will be there, one null, depending on how the submit was done
		String submit = req.getParameter("submit");
		String cancel = req.getParameter("cancel");

		// cancel
		if (cancel != null)
		{
			session.setAttribute(ATTR_MSG, "login canceled");

			// get the session info complete needs, since the logout will invalidate and clear the session
			String returnUrl = (String) session.getAttribute(Tool.HELPER_DONE_URL);

			// TODO: send to the cancel URL, cleanup session
			complete(returnUrl, session, tool, res,"cancel");
		}

		// submit
		else
		{
			Evidence e = new IdPwEvidence(eid, pw);

			// authenticate
			try
			{
				if ((eid.length() == 0) || (pw.length() == 0))
				{
					throw new AuthenticationException("missing required fields");
				}

				Authentication a = AuthenticationManager.authenticate(e);

				// login the user
				LoginUtil.login(a, req);

				// get the session info complete needs, since the logout will invalidate and clear the session
				String returnUrl = (String) session.getAttribute(Tool.HELPER_DONE_URL);
				
				complete(returnUrl, session, tool, res,"auth");
			}
			catch (AuthenticationException ex)
			{
				session.setAttribute(ATTR_MSG, "invalid login");

				// respond with a redirect back here
				res.sendRedirect(res.encodeRedirectURL(Web.returnUrl(req, null)));
			}
		}
	}


	/**
	 * Cleanup and redirect when we have a successful login / logout
	 * 
	 * @param session
	 * @param tool
	 * @param res
	 * @throws IOException
	 */
	
	protected void complete(String returnUrl, Session session, Tool tool, HttpServletResponse res, String reply) throws IOException
	{
		//added info by Scott Amerson to include logout url for secureweb
				
		
		// cleanup session
		if (session != null)
		{
			session.removeAttribute(Tool.HELPER_MESSAGE);
			session.removeAttribute(Tool.HELPER_DONE_URL);
			session.removeAttribute(ATTR_MSG);
			session.removeAttribute(ATTR_RETURN_URL);
			session.removeAttribute(ATTR_CONTAINER_CHECKED);
		}

		// redirect to the done URL
		if (reply.equals("logout"))
		{
		String LogOutURL = ServerConfigurationService.getString("secureweb.logoutURL", "");
		res.sendRedirect(LogOutURL + res.encodeRedirectURL(returnUrl));	
		}
		else
		{	
		res.sendRedirect(res.encodeRedirectURL(returnUrl));
		}
	}

	

}


#2. Add the following secureweb logout url value to the Tomcat/sakai/sakai.properties file:

secureweb.logoutURLApply the BasicConfigurationService patch that Jon G made, located at: https://mware.ucdavis.edu/svn/ucdsakai/branches/archive/sakai-2-1-x/legacy.diff
to the root of sakai-src, so that the logout functionality will clear the cookies.

**Here are the contents of that patch

Code Block

Index: legacy/component/src/java/org/sakaiproject/component/framework/config/BasicConfigurationService.java
===================================================================
--- legacy/component/src/java/org/sakaiproject/component/framework/config/BasicConfigurationService.java	(revision 12171)
+++ legacy/component/src/java/org/sakaiproject/component/framework/config/BasicConfigurationService.java	(working copy)
@@ -28,6 +28,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
+import java.text.MessageFormat;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -330,6 +331,9 @@
 		String rv = (String) m_properties.get("loggedOutUrl");
 		if (rv != null)
 		{
+			// Format the server URL into the string at location 0
+			rv = MessageFormat.format(rv, new Object[]{getServerUrl()});
+			
 			// if not a full URL, add the server to the front
 			if (rv.startsWith("/"))
 			{



2. Have a value in your sakai.properties of:

Code Block

loggedOutUrl=https://secureweb.ucdavis.edu/form-auth/logout?{0}/portal

This logout url will direct Sakai to secureweb to logout the cookies properly, and redirect the user to the intended url.