/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/rwiki/trunk/rwiki-access-tool/src/java/uk/ac/cam/caret/sakai/rwiki/access/WikiAccessServlet.java $
* $Id: WikiAccessServlet.java 51318 2008-08-24 05:28:47Z csev@umich.edu $
***********************************************************************************
*
* Copyright (c) 2006, 2008 Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package uk.ac.cam.caret.sakai.rwiki.access;
import java.io.IOException;
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.entity.api.EntityAccessOverloadException;
import org.sakaiproject.entity.api.EntityNotDefinedException;
import org.sakaiproject.entity.api.EntityPermissionException;
import org.sakaiproject.entity.api.EntityProducer;
import org.sakaiproject.entity.api.HttpAccess;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.cover.EntityManager;
import org.sakaiproject.tool.api.ActiveTool;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.Tool;
import org.sakaiproject.tool.api.ToolException;
import org.sakaiproject.tool.cover.ActiveToolManager;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.util.BasicAuth;
import org.sakaiproject.util.Validator;
import org.sakaiproject.util.Web;
/**
* <p>
* Wiki extends access: all references are assumed to be under "/wiki/site"
* </p>
*
* @author Sakai Software Development Team
*/
public class WikiAccessServlet extends HttpServlet
{
/** Our log (commons). */
private static Log M_log = LogFactory.getLog(WikiAccessServlet.class);
protected BasicAuth basicAuth = null;
/**
* initialize the AccessServlet servlet
*
* @param config
* the servlet config parameter
* @exception ServletException
* in case of difficulties
*/
public void init(ServletConfig config) throws ServletException
{
super.init(config);
basicAuth = new BasicAuth();
basicAuth.init();
}
/**
* Set active session according to sessionId parameter
*/
private void setSession( HttpServletRequest req )
{
String sessionId = req.getParameter("session");
if ( sessionId != null)
{
Session session = SessionManager.getSession(sessionId);
if (session != null)
{
session.setActive();
SessionManager.setCurrentSession(session);
}
}
}
/**
* respond to an HTTP GET request
*
* @param req
* HttpServletRequest object with the client request
* @param res
* HttpServletResponse object back to the client
* @exception ServletException
* in case of difficulties
* @exception IOException
* in case of difficulties
*/
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
setSession(req);
dispatch(req, res);
}
/**
* handle get and post communication from the user
*
* @param req
* HttpServletRequest object with the client request
* @param res
* HttpServletResponse object back to the client
*/
public void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException
{
long start = System.currentTimeMillis();
req.setAttribute(Tool.NATIVE_URL, Tool.NATIVE_URL);
// get the path info
String path = req.getPathInfo();
req.setAttribute(Tool.NATIVE_URL, null);
if (path == null) path = "";
// pre-process the path
String origPath = path;
path = preProcessPath(path, req);
// what is being requested?
Reference ref = EntityManager.newReference(path);
// let the entity producer handle it
try
{
// make sure we have a valid reference with an entity producer we can talk to
EntityProducer service = ref.getEntityProducer();
if (service == null) throw new EntityNotDefinedException(ref.getReference());
// get the producer's HttpAccess helper, it might not support one
HttpAccess access = service.getHttpAccess();
if (access == null) throw new EntityNotDefinedException(ref.getReference());
// let the helper do the work
access.handleAccess(req, res, ref, null);
}
catch (EntityNotDefinedException e)
{
// the request was not valid in some way
sendError(res, HttpServletResponse.SC_NOT_FOUND);
return;
}
catch (EntityPermissionException e)
{
// the end user does not have permission - offer a login if there is no user id yet established
// if not permitted, and the user is the anon user, let them login
if (SessionManager.getCurrentSessionUserId() == null)
{
try {
doLogin(req, res, origPath);
} catch ( IOException ioex ) {}
return;
}
// otherwise reject the request
sendError(res, HttpServletResponse.SC_FORBIDDEN);
}
catch (EntityAccessOverloadException e)
{
M_log.info("dispatch(): ref: " + ref.getReference() + e);
sendError(res, HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
catch (Throwable e)
{
M_log.warn("dispatch(): exception: ", e);
sendError(res, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
finally
{
long end = System.currentTimeMillis();
// log
if (M_log.isDebugEnabled())
M_log.debug("from:" + req.getRemoteAddr() + " path:" + origPath + " options: " + req.getQueryString()
+ " time: " + (end-start));
}
}
/**
* respond to an HTTP POST request; only to handle the login process
*
* @param req
* HttpServletRequest object with the client request
* @param res
* HttpServletResponse object back to the client
* @exception ServletException
* in case of difficulties
* @exception IOException
* in case of difficulties
*/
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
req.setAttribute(Tool.NATIVE_URL, Tool.NATIVE_URL);
// catch the login helper posts
String option = req.getPathInfo();
req.setAttribute(Tool.NATIVE_URL, null);
String[] parts = option.split("/");
if ((parts.length == 2) && (("login".equals(parts[1]))))
{
doLogin(req, res, null);
}
else
{
sendError(res, HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Make any changes needed to the path before final "ref" processing.
*
* @param path
* The path from the request.
* @param req
* The request object.
* @return The path to use to make the Reference for further processing.
*/
protected String preProcessPath(String path, HttpServletRequest req)
{
// everything we work with is down the "content" part of the Sakai access URL space
// if path is just "/", we don't really know if the request was to .../SERVLET or .../SERVLET/ - we want to preserve the trailing slash
// the request URI will tell us
if ("/".equals(path) && !(req.getRequestURI().endsWith("/"))) {
return "/wiki";
} else {
// to relax the URL: allow /wiki/site/siteId and also allow /wiki/siteId
if (path.startsWith("/site/")) {
return "/wiki" + path;
} else {
return "/wiki/site" + path;
}
}
}
/**
* Make a redirect to the login url.
*
* @param req
* HttpServletRequest object with the client request.
* @param res
* HttpServletResponse object back to the client.
* @param path
* The current request path, set ONLY if we want this to be where to redirect the user after successfull login
* @throws IOException
*/
protected void doLogin(HttpServletRequest req, HttpServletResponse res, String path) throws ToolException, IOException
{
// if basic auth is valid do that
if ( basicAuth.doAuth(req,res) ) {
//System.err.println("BASIC Auth Request Sent to the Browser ");
return;
}
// get the Sakai session
Session session = SessionManager.getCurrentSession();
// set the return path for after login if needed (Note: in session, not tool session, special for Login helper)
if (path != null)
{
// where to go after
String returnPath = Web.returnUrl(req, Validator.escapeUrl(path));
session.setAttribute(Tool.HELPER_DONE_URL, returnPath );
}
// check that we have a return path set; might have been done earlier
if (session.getAttribute(Tool.HELPER_DONE_URL) == null)
{
M_log.error("doLogin - proceeding with null HELPER_DONE_URL");
}
// map the request to the helper, leaving the path after ".../options" for the helper
ActiveTool tool = ActiveToolManager.getActiveTool("sakai.login");
String context = req.getContextPath() + req.getServletPath() + "/login";
tool.help(req, res, context, "/login");
}
protected void sendError(HttpServletResponse res, int code)
{
try
{
res.sendError(code);
}
catch (Throwable t)
{
M_log.warn("sendError: " + t);
}
}
}