Wiki Markup |
---|
h3. 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 |
...
Steps involved, for Sakai 2.0.x
#1. 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.logoutURL=https://secureweb.ucdavis.edu/form-auth/logout?
...
that the logout url is correctly referenced. h4. Steps involved, for Sakai 2.1.x # Apply 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} 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("/")) { {code} # Have a value in your sakai.properties of: loggedOutUrl=https://secureweb.ucdavis.edu/form-auth/logout?{0}/portal #2. Add the following secureweb logout url value to the Tomcat/sakai/sakai.properties file: This logout url will direct Sakai to secureweb to logout the cookies properly, and redirect the user to the intended url. |