From b36a2357699a22dc4de38a65a9e941dd376607d8 Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Thu, 26 Jan 2006 12:38:19 +0000 Subject: [PATCH] . 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 --- .../web/app/servlet/AuthenticationHelper.java | 51 ++++- .../app/servlet/DownloadContentServlet.java | 157 ++++++------- .../app/servlet/ExternalAccessServlet.java | 137 ++++++++--- .../web/app/servlet/ServletHelper.java | 184 ++++++++++++++- .../app/servlet/TemplateContentServlet.java | 109 ++++----- .../java/org/alfresco/web/bean/LoginBean.java | 212 +++--------------- source/web/WEB-INF/faces-config.xml | 6 +- source/web/jsp/dialog/document-details.jsp | 2 +- source/web/jsp/dialog/space-details.jsp | 2 +- source/web/jsp/login.jsp | 2 +- 10 files changed, 474 insertions(+), 388 deletions(-) diff --git a/source/java/org/alfresco/web/app/servlet/AuthenticationHelper.java b/source/java/org/alfresco/web/app/servlet/AuthenticationHelper.java index d930b176b3..0b3c526d65 100644 --- a/source/java/org/alfresco/web/app/servlet/AuthenticationHelper.java +++ b/source/java/org/alfresco/web/app/servlet/AuthenticationHelper.java @@ -48,26 +48,48 @@ import org.springframework.web.context.support.WebApplicationContextUtils; /** * Helper to authenticate the current user using available Ticket information. + *

+ * 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. + *

+ * 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. *

* 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); diff --git a/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java b/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java index 0bc2a4d4c7..18c8cf16cc 100644 --- a/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java @@ -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. *

- * 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 + *

+ * 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(); - } } /** diff --git a/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java b/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java index 5d6c8ce005..79b13cfb53 100644 --- a/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java +++ b/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java @@ -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; *

* http://<server>/alfresco/navigate/<outcome>[/<workspace>/<store>/<nodeId>] or
* http://<server>/alfresco/navigate/<outcome>[/webdav/<path/to/node>] - *

+ *

+ * Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication: + * ?ticket=1234567890 + *

+ * 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/ 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= 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); } /** diff --git a/source/java/org/alfresco/web/app/servlet/ServletHelper.java b/source/java/org/alfresco/web/app/servlet/ServletHelper.java index c9829ccc35..1d3cc27b44 100644 --- a/source/java/org/alfresco/web/app/servlet/ServletHelper.java +++ b/source/java/org/alfresco/web/app/servlet/ServletHelper.java @@ -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 paths = new ArrayList(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; + } } diff --git a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java index b8a594400d..024b576a9e 100644 --- a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java @@ -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. *

- * The URL may be followed by a valid 'ticket' argument for authentication: ?ticket=1234567890 - *
- * 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. + *

+ * Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication: + * ?ticket=1234567890 + *

+ * 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(); diff --git a/source/java/org/alfresco/web/bean/LoginBean.java b/source/java/org/alfresco/web/bean/LoginBean.java index 56c488ef71..8ac8d62899 100644 --- a/source/java/org/alfresco/web/bean/LoginBean.java +++ b/source/java/org/alfresco/web/bean/LoginBean.java @@ -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 paths = new ArrayList(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; } diff --git a/source/web/WEB-INF/faces-config.xml b/source/web/WEB-INF/faces-config.xml index 7773b0bddb..a8d556ce18 100644 --- a/source/web/WEB-INF/faces-config.xml +++ b/source/web/WEB-INF/faces-config.xml @@ -27,7 +27,7 @@ authenticationService - #{authenticationService} + #{AuthenticationService} personService @@ -45,10 +45,6 @@ configService #{configService} - - fileFolderService - #{FileFolderService} - diff --git a/source/web/jsp/dialog/document-details.jsp b/source/web/jsp/dialog/document-details.jsp index 013a58dbf7..6c8e15c628 100644 --- a/source/web/jsp/dialog/document-details.jsp +++ b/source/web/jsp/dialog/document-details.jsp @@ -250,7 +250,7 @@ - + diff --git a/source/web/jsp/dialog/space-details.jsp b/source/web/jsp/dialog/space-details.jsp index 84edcab29b..83c449ed44 100644 --- a/source/web/jsp/dialog/space-details.jsp +++ b/source/web/jsp/dialog/space-details.jsp @@ -248,7 +248,7 @@ - + diff --git a/source/web/jsp/login.jsp b/source/web/jsp/login.jsp index d863aa3882..7166e88ca3 100644 --- a/source/web/jsp/login.jsp +++ b/source/web/jsp/login.jsp @@ -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()); }