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.0.x
#1. modify the complete() method signature, and appropriate calls to include the reply string. See following:
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.logoutURL=https://secureweb.ucdavis.edu/form-auth/logout?
This logout url will direct Sakai to secureweb to logout the cookies properly, and redirect the user to the intended url.