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.