. Final phase of web-client Guest Access

. Fix of UserTransaction handling in AuthenticationHelper - very noticable when Guest access fails on older (pre 1.2) databases
. Refactoring of all servlet authentication patterns to be consistent and use a helper so all support Guest and Ticket arguments
. Fix to external URL generation on Document and Space Details pages

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2214 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2006-01-26 12:38:19 +00:00
parent 43640326e5
commit b36a235769
10 changed files with 474 additions and 388 deletions

View File

@@ -48,26 +48,48 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Helper to authenticate the current user using available Ticket information.
* <p>
* User information is looked up in the Session. If found the ticket is retrieved and validated.
* If the ticket is invalid then a redirect is performed to the login page.
* <p>
* If no User info is found then a search will be made for a previous username stored in a Cookie
* value. If the username if found then a redirect to the Login page will occur. If no username
* is found then Guest access login will be attempted by the system. Guest access can be forced
* with the appropriate method call.
*
* @author Kevin Roast
*/
public final class AuthenticationHelper
{
public static final String FACES_SERVLET = "/faces";
/** session variables */
public static final String AUTHENTICATION_USER = "_alfAuthTicket";
public static final String SESSION_USERNAME = "_alfLastUser";
public static final String SESSION_INVALIDATED = "_alfSessionInvalid";
/** JSF bean IDs */
public static final String LOGIN_BEAN = "LoginBean";
/** public service bean IDs **/
private static final String AUTHENTICATION_SERVICE = "AuthenticationService";
private static final String UNPROTECTED_AUTH_SERVICE = "authenticationService";
private static final String PERSON_SERVICE = "personService";
/** cookie names */
private static final String COOKIE_ALFUSER = "alfUser";
private static Log logger = LogFactory.getLog(AuthenticationHelper.class);
/**
* Helper to authenticate the current user using session based Ticket information.
* <p>
* User information is looked up in the Session. If found the ticket is retrieved and validated.
* If no User info is found or the ticket is invalid then a redirect is performed to the login page.
*
* @param guest True to force a Guest login attempt
*
* @return AuthenticationStatus result.
*/
public static AuthenticationStatus authenticate(
@@ -91,10 +113,14 @@ public final class AuthenticationHelper
// setup the authentication context
WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
AuthenticationService auth = (AuthenticationService)wc.getBean(ServletHelper.AUTHENTICATION_SERVICE);
AuthenticationService auth = (AuthenticationService)wc.getBean(AUTHENTICATION_SERVICE);
if (user == null || guest)
{
// Check for the session invalidated flag - this is set by the Logout action in the LoginBean
// it signals a forced Logout and means we should not immediately attempt a relogin as Guest.
// The attribute is removed from the session by the login.jsp page after the Cookie containing
// the last stored username string is cleared.
if (session.getAttribute(AuthenticationHelper.SESSION_INVALIDATED) == null)
{
Cookie authCookie = getAuthCookie(httpRequest);
@@ -112,7 +138,7 @@ public final class AuthenticationHelper
tx.begin();
NodeService nodeService = services.getNodeService();
PersonService personService = (PersonService)wc.getBean(ServletHelper.PERSON_SERVICE);
PersonService personService = (PersonService)wc.getBean(PERSON_SERVICE);
NodeRef guestRef = personService.getPerson(PermissionService.GUEST);
user = new User(PermissionService.GUEST, auth.getCurrentTicket(), guestRef);
NodeRef guestHomeRef = (NodeRef)nodeService.getProperty(guestRef, ContentModel.PROP_HOMEFOLDER);
@@ -125,6 +151,7 @@ public final class AuthenticationHelper
user.setHomeSpaceId(guestHomeRef.getId());
tx.commit();
tx = null; // clear this so we know not to rollback
// store the User object in the Session - the authentication servlet will then proceed
session.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);
@@ -132,6 +159,9 @@ public final class AuthenticationHelper
// Set the current locale
I18NUtil.setLocale(Application.getLanguage(httpRequest.getSession()));
// remove the session invalidated flag
session.removeAttribute(AuthenticationHelper.SESSION_INVALIDATED);
// it is the responsibilty of the caller to handle the Guest return status
return AuthenticationStatus.Guest;
}
@@ -142,19 +172,26 @@ public final class AuthenticationHelper
catch (AccessDeniedException accessError)
{
// Guest is unable to access either properties on Person
//AuthenticationService smallAuth = (AuthenticationService)wc.getBean(UNPROTECTED_AUTH_SERVICE);
//smallAuth.invalidateTicket(smallAuth.getCurrentTicket());
logger.warn("Unable to login as Guest: " + accessError.getMessage());
}
catch (Throwable e)
{
// Guest access not allowed - some other kind of serious failure to report
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
// Some other kind of serious failure to report
//AuthenticationService smallAuth = (AuthenticationService)wc.getBean(UNPROTECTED_AUTH_SERVICE);
//smallAuth.invalidateTicket(smallAuth.getCurrentTicket());
throw new AlfrescoRuntimeException("Failed to authenticate as Guest user.", e);
}
finally
{
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
}
// no user/ticket found or session invalidated by user (logout) - redirect to login page
httpResponse.sendRedirect(httpRequest.getContextPath() + "/faces" + Application.getLoginPage(context));
httpResponse.sendRedirect(httpRequest.getContextPath() + FACES_SERVLET + Application.getLoginPage(context));
return AuthenticationStatus.Failure;
}
@@ -167,7 +204,7 @@ public final class AuthenticationHelper
catch (AuthenticationException authErr)
{
// expired ticket - redirect to login page
httpResponse.sendRedirect(httpRequest.getContextPath() + "/faces" + Application.getLoginPage(context));
httpResponse.sendRedirect(httpRequest.getContextPath() + FACES_SERVLET + Application.getLoginPage(context));
return AuthenticationStatus.Failure;
}
@@ -195,7 +232,7 @@ public final class AuthenticationHelper
{
// setup the authentication context
WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
AuthenticationService auth = (AuthenticationService)wc.getBean(ServletHelper.AUTHENTICATION_SERVICE);
AuthenticationService auth = (AuthenticationService)wc.getBean(AUTHENTICATION_SERVICE);
try
{
auth.validate(ticket);

View File

@@ -41,7 +41,6 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -66,7 +65,10 @@ import org.apache.commons.logging.LogFactory;
* To retrieve the content of a specific model property, use a 'property' arg, providing the workspace,
* node ID AND the qualified name of the property.
* <p>
* The URL may be followed by a valid ticket argument for authentication: ?ticket=1234567890
* Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication:
* ?ticket=1234567890
* <p>
* And/or also followed by the "?guest=true" argument to force guest access login for the URL.
*
* @author Kevin Roast
*/
@@ -92,93 +94,72 @@ public class DownloadContentServlet extends HttpServlet
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
ServletOutputStream out = res.getOutputStream();
// The URL contains multiple parts
// /alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf
// the protocol, followed by the store, followed by the Id
// the last part is only used for mimetype and browser use
// may be followed by valid ticket for pre-authenticated usage: ?ticket=1234567890
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
AuthenticationStatus status = ServletHelper.servletAuthenticate(req, res, getServletContext());
if (status == AuthenticationStatus.Failure)
{
return;
}
// TODO: add compression here?
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
// only really needed if we don't use the built in compression of the servlet container
StringTokenizer t = new StringTokenizer(uri, "/");
if (t.countTokens() < 7)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
String id = t.nextToken();
String filename = t.nextToken();
// get property qualified name
QName propertyQName = null;
String property = req.getParameter(ARG_PROPERTY);
if (property == null || property.length() == 0)
{
propertyQName = ContentModel.PROP_CONTENT;
}
else
{
propertyQName = QName.createQName(property);
}
NodeRef nodeRef = new NodeRef(storeRef, id);
if (logger.isDebugEnabled())
{
logger.debug("Found NodeRef: " + nodeRef.toString());
logger.debug("Will use filename: " + filename);
logger.debug("For property: " + propertyQName);
logger.debug("With attachment mode: " + attachment);
}
if (attachment == true)
{
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
// this is better than the default response of the browse trying to display the contents!
// TODO: make this configurable - and check it does not prevent streaming of large files
res.setHeader("Content-Disposition", "attachment;filename=\"" + URLDecoder.decode(filename, "UTF-8") + '"');
}
try
{
// The URL contains multiple parts
// /alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf
// the protocol, followed by the store, followed by the Id
// the last part is only used for mimetype and browser use
// may be followed by valid ticket for pre-authenticated usage: ?ticket=1234567890
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
// see if a ticket or guest parameter has been supplied
AuthenticationStatus status;
String ticket = req.getParameter(ServletHelper.ARG_TICKET);
if (ticket != null && ticket.length() != 0)
{
status = AuthenticationHelper.authenticate(getServletContext(), req, res, ticket);
}
else
{
boolean forceGuest = false;
String guest = req.getParameter(ServletHelper.ARG_GUEST);
if (guest != null)
{
forceGuest = Boolean.parseBoolean(guest);
}
status = AuthenticationHelper.authenticate(getServletContext(), req, res, forceGuest);
}
if (status == AuthenticationStatus.Failure)
{
// authentication failed - no point returning the content as we haven't logged in yet
// so end servlet execution and save the URL so the login page knows what to do later
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
return;
}
// TODO: add compression here?
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
// only really needed if we don't use the built in compression of the servlet container
StringTokenizer t = new StringTokenizer(uri, "/");
if (t.countTokens() < 7)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
String id = t.nextToken();
String filename = t.nextToken();
// get property qualified name
QName propertyQName = null;
String property = req.getParameter(ARG_PROPERTY);
if (property == null || property.length() == 0)
{
propertyQName = ContentModel.PROP_CONTENT;
}
else
{
propertyQName = QName.createQName(property);
}
NodeRef nodeRef = new NodeRef(storeRef, id);
if (logger.isDebugEnabled())
{
logger.debug("Found NodeRef: " + nodeRef.toString());
logger.debug("Will use filename: " + filename);
logger.debug("For property: " + propertyQName);
logger.debug("With attachment mode: " + attachment);
}
if (attachment == true)
{
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
// this is better than the default response of the browse trying to display the contents!
// TODO: make this configurable - and check it does not prevent streaming of large files
res.setHeader("Content-Disposition", "attachment;filename=\"" + URLDecoder.decode(filename, "UTF-8") + '"');
}
// get the services we need to retrieve the content
ServiceRegistry serviceRegistry = ServletHelper.getServiceRegistry(getServletContext());
ContentService contentService = serviceRegistry.getContentService();
@@ -234,10 +215,6 @@ public class DownloadContentServlet extends HttpServlet
{
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
}
finally
{
out.close();
}
}
/**

View File

@@ -20,12 +20,18 @@ import java.io.IOException;
import java.text.MessageFormat;
import java.util.StringTokenizer;
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.repo.webdav.WebDAVServlet;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -39,7 +45,11 @@ import org.apache.commons.logging.LogFactory;
* <p>
* <code>http://&lt;server&gt;/alfresco/navigate/&lt;outcome&gt;[/&lt;workspace&gt;/&lt;store&gt;/&lt;nodeId&gt;]</code> or <br/>
* <code>http://&lt;server&gt;/alfresco/navigate/&lt;outcome&gt;[/webdav/&lt;path/to/node&gt;]</code>
* </p>
* <p>
* Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication:
* ?ticket=1234567890
* <p>
* And/or also followed by the "?guest=true" argument to force guest access login for the URL.
*
* @author Kevin Roast
*/
@@ -49,6 +59,11 @@ public class ExternalAccessServlet extends HttpServlet
private static Log logger = LogFactory.getLog(ExternalAccessServlet.class);
private final static String OUTCOME_DOCDETAILS = "showDocDetails";
private final static String OUTCOME_SPACEDETAILS = "showSpaceDetails";
private final static String OUTCOME_BROWSE = "browse";
private static final String ARG_TEMPLATE = "template";
/**
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
@@ -56,20 +71,16 @@ public class ExternalAccessServlet extends HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
boolean forceGuest = false;
String guest = req.getParameter(ServletHelper.ARG_GUEST);
if (guest != null)
{
forceGuest = Boolean.parseBoolean(guest);
}
AuthenticationStatus status = AuthenticationHelper.authenticate(getServletContext(), req, res, forceGuest);
// The URL contains multiple parts
// /alfresco/navigate/<outcome>
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri);
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
AuthenticationStatus status = ServletHelper.servletAuthenticate(req, res, getServletContext());
if (status == AuthenticationStatus.Failure)
{
return;
}
StringTokenizer t = new StringTokenizer(uri, "/");
int count = t.countTokens();
@@ -82,31 +93,101 @@ public class ExternalAccessServlet extends HttpServlet
String outcome = t.nextToken();
// get rest of the tokens
String[] tokens = new String[count - 3];
// get rest of the tokens arguments
String[] args = new String[count - 3];
for (int i=0; i<count - 3; i++)
{
tokens[i] = t.nextToken();
args[i] = t.nextToken();
}
// set the session variable so the login bean knows which outcome to use
req.getSession().setAttribute(LoginBean.LOGIN_OUTCOME_KEY, outcome);
if (logger.isDebugEnabled())
logger.debug("External outcome found: " + outcome);
// set the args if any
req.getSession().setAttribute(LoginBean.LOGIN_OUTCOME_ARGS, tokens);
// we almost always need this bean reference
FacesContext fc = ServletHelper.getFacesContext(req, res, getServletContext());
BrowseBean browseBean = (BrowseBean)ServletHelper.getManagedBean(fc, "BrowseBean");
if (status == AuthenticationStatus.Success || status == AuthenticationStatus.Guest)
// setup is required for certain outcome requests
if (OUTCOME_DOCDETAILS.equals(outcome))
{
// clear the User object from the Session - this will force a relogin
// we do this so the outcome from the login page can then be changed
req.getSession().removeAttribute(AuthenticationHelper.AUTHENTICATION_USER);
NodeRef nodeRef = null;
if (logger.isDebugEnabled())
logger.debug("Removing User session - will redirect via login page...");
if (args[0].equals(WebDAVServlet.WEBDAV_PREFIX))
{
nodeRef = ServletHelper.resolveWebDAVPath(fc, args);
}
else if (args.length == 3)
{
StoreRef storeRef = new StoreRef(args[0], args[1]);
nodeRef = new NodeRef(storeRef, args[2]);
}
// redirect to root URL will force the login page to appear via the Authentication Filter
res.sendRedirect(req.getContextPath());
if (nodeRef != null)
{
// setup the Document on the browse bean
// TODO: the browse bean should accept a full NodeRef - not just an ID
browseBean.setupContentAction(nodeRef.getId(), true);
}
}
else if (OUTCOME_SPACEDETAILS.equals(outcome))
{
NodeRef nodeRef = null;
if (args[0].equals(WebDAVServlet.WEBDAV_PREFIX))
{
nodeRef = ServletHelper.resolveWebDAVPath(fc, args);
}
else if (args.length == 3)
{
StoreRef storeRef = new StoreRef(args[0], args[1]);
nodeRef = new NodeRef(storeRef, args[2]);
}
if (nodeRef != null)
{
// setup the Space on the browse bean
// TODO: the browse bean should accept a full NodeRef - not just an ID
browseBean.setupSpaceAction(nodeRef.getId(), true);
}
}
else if (OUTCOME_BROWSE.equals(outcome))
{
if (args != null)
{
NodeRef nodeRef = null;
int offset = 0;
if (args.length >= 3)
{
offset = args.length - 3;
StoreRef storeRef = new StoreRef(args[0+offset], args[1+offset]);
nodeRef = new NodeRef(storeRef, args[2+offset]);
// setup the ref as current Id in the global navigation bean
NavigationBean navigator = (NavigationBean)ServletHelper.getManagedBean(fc, "NavigationBean");
navigator.setCurrentNodeId(nodeRef.getId());
//
// TODO: handle this code
/*
// check for view mode first argument
if (args[0].equals(ARG_TEMPLATE))
{
browseBean.setDashboardView(true);
// the above call will auto-navigate to the correct outcome - so we don't!
//externalOutcome = null;
}
*/
}
}
}
// perform the appropriate JSF navigation outcome
NavigationHandler navigationHandler = fc.getApplication().getNavigationHandler();
navigationHandler.handleNavigation(fc, null, outcome);
// perform the forward to the page processed by the Faces servlet
String viewId = fc.getViewRoot().getViewId();
getServletContext().getRequestDispatcher(AuthenticationHelper.FACES_SERVLET + viewId).forward(req, res);
}
/**

View File

@@ -16,11 +16,38 @@
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.el.ValueBinding;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.bean.repository.Repository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Useful constant values and common methods for Alfresco servlets.
@@ -30,14 +57,13 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
public final class ServletHelper
{
/** an existing Ticket can be passed to most servlet for non-session based authentication */
public static final String ARG_TICKET = "ticket";
private static final String ARG_TICKET = "ticket";
/** forcing guess access is available on most servlets */
public static final String ARG_GUEST = "guest";
private static final String ARG_GUEST = "guest";
private static Log logger = LogFactory.getLog(ServletHelper.class);
/** public service bean IDs **/
public static final String AUTHENTICATION_SERVICE = "authenticationService";
public static final String PERSON_SERVICE = "personService";
/**
* Return the ServiceRegistry helper instance
@@ -58,4 +84,152 @@ public final class ServletHelper
private ServletHelper()
{
}
/**
* Perform an authentication for the servlet request URI. Processing any "ticket" or
* "guest" URL arguments.
*
* @return AuthenticationStatus
*
* @throws IOException
*/
public static AuthenticationStatus servletAuthenticate(HttpServletRequest req, HttpServletResponse res, ServletContext sc)
throws IOException
{
AuthenticationStatus status;
// see if a ticket or a force Guest parameter has been supplied
String ticket = req.getParameter(ServletHelper.ARG_TICKET);
if (ticket != null && ticket.length() != 0)
{
status = AuthenticationHelper.authenticate(sc, req, res, ticket);
}
else
{
boolean forceGuest = false;
String guest = req.getParameter(ServletHelper.ARG_GUEST);
if (guest != null)
{
forceGuest = Boolean.parseBoolean(guest);
}
status = AuthenticationHelper.authenticate(sc, req, res, forceGuest);
}
if (status == AuthenticationStatus.Failure)
{
// authentication failed - no point returning the content as we haven't logged in yet
// so end servlet execution and save the URL so the login page knows what to do later
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, req.getRequestURI() + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
}
return status;
}
/**
* Return FacesContext made available to servlets and servlet filters.
* {@link http://www.thoughtsabout.net/blog/archives/000033.html}
*
* @return FacesContext
*/
public static FacesContext getFacesContext(ServletRequest req, ServletResponse res, ServletContext sc)
{
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext != null) return facesContext;
FacesContextFactory contextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
// Can get ServletContext here like this:
// ServletContext servletContext = ((HttpServletRequest)request).getSession().getServletContext();
// Doesn't set this instance as the current instance of FacesContext.getCurrentInstance
facesContext = contextFactory.getFacesContext(sc, req, res, lifecycle);
// Set using our inner class
InnerFacesContext.setFacesContextAsCurrent(facesContext);
// set a new viewRoot, otherwise context.getViewRoot returns null
UIViewRoot view = facesContext.getApplication().getViewHandler().createView(facesContext, "/jsp/root");
facesContext.setViewRoot(view);
return facesContext;
}
/**
* We need an inner class to be able to call FacesContext.setCurrentInstance
* since it's a protected method
*/
private abstract static class InnerFacesContext extends FacesContext
{
protected static void setFacesContextAsCurrent(FacesContext facesContext)
{
FacesContext.setCurrentInstance(facesContext);
}
}
/**
* Return a JSF managed bean reference.
*
* @param fc FacesContext
* @param name Name of the managed bean to return
*
* @return the managed bean or null if not found
*/
public static Object getManagedBean(FacesContext fc, String name)
{
ValueBinding vb = fc.getApplication().createValueBinding("#{" + name + "}");
return vb.getValue(fc);
}
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param context Faces context
* @param args The elements of the path to lookup
*/
public static NodeRef resolveWebDAVPath(FacesContext context, String[] args)
{
NodeRef nodeRef = null;
List<String> paths = new ArrayList<String>(args.length-1);
FileInfo file = null;
try
{
// create a list of path elements (decode the URL as we go)
for (int x = 1; x < args.length; x++)
{
paths.add(URLDecoder.decode(args[x], "UTF-8"));
}
if (logger.isDebugEnabled())
logger.debug("Attempting to resolve webdav path to NodeRef: " + paths);
// get the company home node to start the search from
NodeRef companyHome = new NodeRef(Repository.getStoreRef(),
Application.getCompanyRootId());
WebApplicationContext wc = FacesContextUtils.getRequiredWebApplicationContext(context);
//WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
FileFolderService ffs = (FileFolderService)wc.getBean("FileFolderService");
file = ffs.resolveNamePath(companyHome, paths);
nodeRef = file.getNodeRef();
}
catch (UnsupportedEncodingException uee)
{
if (logger.isWarnEnabled())
logger.warn("Failed to resolve webdav path", uee);
nodeRef = null;
}
catch (FileNotFoundException fne)
{
if (logger.isWarnEnabled())
logger.debug("Failed to resolve webdav path", fne);
nodeRef = null;
}
return nodeRef;
}
}

View File

@@ -39,7 +39,6 @@ import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.ui.repo.component.template.DefaultModelHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -57,10 +56,13 @@ import org.apache.commons.logging.LogFactory;
* identify the node to execute the default template for. The second set of elements encode
* the store and node Id of the template to used if a default is not set or not requested.
* <p>
* The URL may be followed by a valid 'ticket' argument for authentication: ?ticket=1234567890
* <br>
* And may be followed by a 'mimetype' argument specifying the mimetype to return the result as
* The URL may be followed by a 'mimetype' argument specifying the mimetype to return the result as
* on the stream. Otherwise it is assumed that HTML is the default response mimetype.
* <p>
* Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication:
* ?ticket=1234567890
* <p>
* And/or also followed by the "?guest=true" argument to force guest access login for the URL.
*
* @author Kevin Roast
*/
@@ -85,67 +87,48 @@ public class TemplateContentServlet extends HttpServlet
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
AuthenticationStatus status = ServletHelper.servletAuthenticate(req, res, getServletContext());
if (status == AuthenticationStatus.Failure)
{
return;
}
StringTokenizer t = new StringTokenizer(uri, "/");
int tokenCount = t.countTokens();
if (tokenCount < 5)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
// get NodeRef to the content
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
NodeRef nodeRef = new NodeRef(storeRef, t.nextToken());
// get NodeRef to the template if supplied
NodeRef templateRef = null;
if (tokenCount >= 8)
{
storeRef = new StoreRef(t.nextToken(), t.nextToken());
templateRef = new NodeRef(storeRef, t.nextToken());
}
String mimetype = MIMETYPE_HTML;
if (req.getParameter(ARG_MIMETYPE) != null)
{
mimetype = req.getParameter(ARG_MIMETYPE);
}
res.setContentType(mimetype);
try
{
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
// see if a ticket has been supplied
AuthenticationStatus status;
String ticket = req.getParameter(ServletHelper.ARG_TICKET);
if (ticket != null && ticket.length() != 0)
{
status = AuthenticationHelper.authenticate(getServletContext(), req, res, ticket);
}
else
{
boolean forceGuest = false;
String guest = req.getParameter(ServletHelper.ARG_GUEST);
if (guest != null)
{
forceGuest = Boolean.parseBoolean(guest);
}
status = AuthenticationHelper.authenticate(getServletContext(), req, res, forceGuest);
}
if (status == AuthenticationStatus.Failure)
{
// authentication failed - no point returning the content as we haven't logged in yet
// so end servlet execution and save the URL so the login page knows what to do later
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
return;
}
StringTokenizer t = new StringTokenizer(uri, "/");
int tokenCount = t.countTokens();
if (tokenCount < 5)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
// get NodeRef to the content
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
NodeRef nodeRef = new NodeRef(storeRef, t.nextToken());
// get NodeRef to the template if supplied
NodeRef templateRef = null;
if (tokenCount >= 8)
{
storeRef = new StoreRef(t.nextToken(), t.nextToken());
templateRef = new NodeRef(storeRef, t.nextToken());
}
String mimetype = MIMETYPE_HTML;
if (req.getParameter(ARG_MIMETYPE) != null)
{
mimetype = req.getParameter(ARG_MIMETYPE);
}
res.setContentType(mimetype);
// get the services we need to retrieve the content
ServiceRegistry serviceRegistry = ServletHelper.getServiceRegistry(getServletContext());
NodeService nodeService = serviceRegistry.getNodeService();

View File

@@ -17,10 +17,7 @@
package org.alfresco.web.bean;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -36,14 +33,9 @@ import javax.servlet.http.HttpServletRequest;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.webdav.WebDAVServlet;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.web.app.Application;
@@ -114,14 +106,6 @@ public class LoginBean
{
this.configService = configService;
}
/**
* @param fileFolderService The FileFolderService to set.
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* @param val Username from login dialog
@@ -276,7 +260,8 @@ public class LoginBean
FacesContext fc = FacesContext.getCurrentInstance();
if (this.username != null && this.password != null)
if (this.username != null && this.username.length() != 0 &&
this.password != null && this.password.length() != 0)
{
try
{
@@ -284,14 +269,18 @@ public class LoginBean
// Authenticate via the authentication service, then save the details of user in an object
// in the session - this is used by the servlet filter etc. on each page to check for login
//this.authenticationService = (AuthenticationService)FacesContextUtils.getRequiredWebApplicationContext(FacesContext.getCurrentInstance()).getBean("authenticationService");
this.authenticationService.authenticate(this.username, this.password.toCharArray());
// remove the session invalidated flag (used to remove last username cookie by AuthenticationFilter)
session.remove(AuthenticationHelper.SESSION_INVALIDATED);
// setup User object and Home space ID
User user = new User(this.authenticationService.getCurrentUserName(), this.authenticationService.getCurrentTicket(),
User user = new User(
this.authenticationService.getCurrentUserName(),
this.authenticationService.getCurrentTicket(),
personService.getPerson(this.username));
NodeRef homeSpaceRef = (NodeRef) this.nodeService.getProperty(personService.getPerson(this.username), ContentModel.PROP_HOMEFOLDER);
// check that the home space node exists - else user cannot login
@@ -305,118 +294,31 @@ public class LoginBean
// the app to continue without redirecting to the login page
session.put(AuthenticationHelper.AUTHENTICATION_USER, user);
// if an external outcome has been provided then use that, else use default
String externalOutcome = (String)fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_KEY);
if (externalOutcome != null)
// if a redirect URL has been provided then use that
// this allows servlets etc. to provide a URL to return too after a successful login
String redirectURL = (String)fc.getExternalContext().getSessionMap().get(LOGIN_REDIRECT_KEY);
if (redirectURL != null)
{
// TODO: This is a quick solution. It would be better to specify the (identifier?)
// of a handler class that would be responsible for processing specific outcome arguments.
if (logger.isDebugEnabled())
logger.debug("External outcome found: " + externalOutcome);
logger.debug("Redirect URL found: " + redirectURL);
// setup is required for certain outcome requests
if (OUTCOME_DOCDETAILS.equals(externalOutcome))
{
NodeRef nodeRef = null;
String[] args = (String[]) fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_ARGS);
if (args[0].equals(WebDAVServlet.WEBDAV_PREFIX))
{
nodeRef = resolveWebDAVPath(fc, args);
}
else if (args.length == 3)
{
StoreRef storeRef = new StoreRef(args[0], args[1]);
nodeRef = new NodeRef(storeRef, args[2]);
}
if (nodeRef != null)
{
// setup the Document on the browse bean
// TODO: the browse bean should accept a full NodeRef - not just an ID
this.browseBean.setupContentAction(nodeRef.getId(), true);
}
}
else if (OUTCOME_SPACEDETAILS.equals(externalOutcome))
{
NodeRef nodeRef = null;
String[] args = (String[]) fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_ARGS);
if (args[0].equals(WebDAVServlet.WEBDAV_PREFIX))
{
nodeRef = resolveWebDAVPath(fc, args);
}
else if (args.length == 3)
{
StoreRef storeRef = new StoreRef(args[0], args[1]);
nodeRef = new NodeRef(storeRef, args[2]);
}
if (nodeRef != null)
{
// setup the Space on the browse bean
// TODO: the browse bean should accept a full NodeRef - not just an ID
this.browseBean.setupSpaceAction(nodeRef.getId(), true);
}
}
else if (OUTCOME_BROWSE.equals(externalOutcome))
{
String[] args = (String[]) fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_ARGS);
if (args != null)
{
NodeRef nodeRef = null;
int offset = 0;
if (args.length >= 3)
{
offset = args.length - 3;
StoreRef storeRef = new StoreRef(args[0+offset], args[1+offset]);
nodeRef = new NodeRef(storeRef, args[2+offset]);
// setup the ref as current Id in the global navigation bean
this.navigator.setCurrentNodeId(nodeRef.getId());
// check for view mode first argument
if (args[0].equals(LOGIN_ARG_TEMPLATE))
{
this.browseBean.setDashboardView(true);
// the above call will auto-navigate to the correct outcome - so we don't!
externalOutcome = null;
}
}
}
}
// remove redirect URL from session
fc.getExternalContext().getSessionMap().remove(LOGIN_REDIRECT_KEY);
fc.getExternalContext().getSessionMap().remove(LOGIN_OUTCOME_KEY);
return externalOutcome;
try
{
fc.getExternalContext().redirect(redirectURL);
fc.responseComplete();
return null;
}
catch (IOException ioErr)
{
logger.warn("Unable to redirect to url: " + redirectURL);
}
}
else
{
// if a redirect URL has been provided then use that
String redirectURL = (String)fc.getExternalContext().getSessionMap().get(LOGIN_REDIRECT_KEY);
if (redirectURL != null)
{
if (logger.isDebugEnabled())
logger.debug("Redirect URL found: " + redirectURL);
// remove URL from session
fc.getExternalContext().getSessionMap().remove(LOGIN_REDIRECT_KEY);
try
{
fc.getExternalContext().redirect(redirectURL);
fc.responseComplete();
return null;
}
catch (IOException ioErr)
{
logger.warn("Unable to redirect to url: " + redirectURL);
}
}
else
{
return "success";
}
return "success";
}
}
catch (AuthenticationException aerr)
@@ -481,59 +383,6 @@ public class LoginBean
return alfrescoAuth ? "logout" : "relogin";
}
// ------------------------------------------------------------------------------
// Private helpers
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param context Faces context
* @param args The elements of the path to lookup
*/
private NodeRef resolveWebDAVPath(FacesContext context, String[] args)
{
NodeRef nodeRef = null;
List<String> paths = new ArrayList<String>(args.length-1);
FileInfo file = null;
try
{
// create a list of path elements (decode the URL as we go)
for (int x = 1; x < args.length; x++)
{
paths.add(URLDecoder.decode(args[x], "UTF-8"));
}
if (logger.isDebugEnabled())
logger.debug("Attempting to resolve webdav path to NodeRef: " + paths);
// get the company home node to start the search from
NodeRef companyHome = new NodeRef(Repository.getStoreRef(),
Application.getCompanyRootId());
file = this.fileFolderService.resolveNamePath(companyHome, paths);
nodeRef = file.getNodeRef();
}
catch (UnsupportedEncodingException uee)
{
if (logger.isWarnEnabled())
logger.warn("Failed to resolve webdav path", uee);
nodeRef = null;
}
catch (FileNotFoundException fne)
{
if (logger.isWarnEnabled())
logger.debug("Failed to resolve webdav path", fne);
nodeRef = null;
}
return nodeRef;
}
// ------------------------------------------------------------------------------
@@ -550,16 +399,8 @@ public class LoginBean
private static final String MSG_PASSWORD_LENGTH = "login_err_password_length";
public static final String LOGIN_REDIRECT_KEY = "_alfRedirect";
public static final String LOGIN_OUTCOME_KEY = "_alfOutcome";
public static final String LOGIN_OUTCOME_ARGS = "_alfOutcomeArgs";
public static final String LOGIN_EXTERNAL_AUTH= "_alfExternalAuth";
public final static String OUTCOME_DOCDETAILS = "showDocDetails";
public final static String OUTCOME_SPACEDETAILS = "showSpaceDetails";
public final static String OUTCOME_BROWSE = "browse";
private static final String LOGIN_ARG_TEMPLATE = "template";
/** user name */
private String username = null;
@@ -586,7 +427,4 @@ public class LoginBean
/** ConfigService bean reference */
private ConfigService configService;
/** FileFolderService bean reference */
private FileFolderService fileFolderService;
}

View File

@@ -27,7 +27,7 @@
</managed-property>
<managed-property>
<property-name>authenticationService</property-name>
<value>#{authenticationService}</value>
<value>#{AuthenticationService}</value>
</managed-property>
<managed-property>
<property-name>personService</property-name>
@@ -45,10 +45,6 @@
<property-name>configService</property-name>
<value>#{configService}</value>
</managed-property>
<managed-property>
<property-name>fileFolderService</property-name>
<value>#{FileFolderService}</value>
</managed-property>
</managed-bean>
<managed-bean>

View File

@@ -250,7 +250,7 @@
<a:actionLink value="#{msg.download_content}" href="#{DocumentDetailsBean.downloadUrl}" target="new" id="link4" />
</td>
<td>
<a href='<a:outputText value="#{DocumentDetailsBean.bookmarkUrl}" id="out1" />' onclick="return false;"><a:outputText value="#{msg.details_page_bookmark}" id="out2" /></a>
<a href='<%=request.getContextPath()%><a:outputText value="#{DocumentDetailsBean.bookmarkUrl}" id="out1" />' onclick="return false;"><a:outputText value="#{msg.details_page_bookmark}" id="out2" /></a>
</td>
<td>
<a href='<a:outputText value="#{DocumentDetailsBean.nodeRefUrl}" id="out3" />' onclick="return false;"><a:outputText value="#{msg.noderef_link}" id="out4" /></a>

View File

@@ -248,7 +248,7 @@
<a:actionLink value="#{msg.view_in_cifs}" href="#{SpaceDetailsBean.cifsPath}" target="new" id="link2" />
</td>
<td>
<a href='<a:outputText value="#{SpaceDetailsBean.bookmarkUrl}" id="out1" />' onclick="return false;"><a:outputText value="#{msg.details_page_bookmark}" id="out2" /></a>
<a href='<%=request.getContextPath()%><a:outputText value="#{SpaceDetailsBean.bookmarkUrl}" id="out1" />' onclick="return false;"><a:outputText value="#{msg.details_page_bookmark}" id="out2" /></a>
</td>
<td>
<a href='<a:outputText value="#{SpaceDetailsBean.nodeRefUrl}" id="out3" />' onclick="return false;"><a:outputText value="#{msg.noderef_link}" id="out4" /></a>

View File

@@ -41,7 +41,7 @@
else
{
// setup value used by JSF bean state ready for login page if we find the cookie
if (authCookie != null)
if (authCookie != null && authCookie.getValue() != null)
{
session.setAttribute(AuthenticationHelper.SESSION_USERNAME, authCookie.getValue());
}