. Fix to issue in login page redirect code where URL arguments (e.g. for templates and scripts) were removed and not passed through.

- this only affected the template/command servlets that were using parameters and had not already logged in

. Support for 'path' argument on DownloadContentServlet
  for example /alfresco/download/direct?path=/Company%20Home/Kev's%20Home%20Space/myimage.jpg
  - paths are specified in a similar way to webdav path - they are 'cm:name' based URL encoded strings
  - this means we no longer need to have the NodeRef to a document to download it

. Support for 'templatePath' and 'contextPath' URL arguments in TemplateContentServlet
  for example: /alfresco/template?templatePath=/Company%20Home/Data%20Dictionary/Presentation%20Templates/doc_info.ftl&contextPath=/Company%20Home/file.txt
  - the 'templatePath' argument specifies the cm:name based path to the template to execute
  - the 'contextPath' argument specifies the cm:name based path to the context for the template

. Support for 'scriptPath' and 'contextPath' URL arguments in Script CommandProcessor
  for example: /alfresco/command/script/execute?scriptPath=/Company%20Home/Data%20Dictionary/Scripts/append%20copyright.js&contextPath=/Company%20Home/file.txt
  - the 'scriptPath' argument specifies the cm:name based path to the script to execute
  - the 'contextPath' argument specifies the cm:name based path to the context for the script

. Adding debugging output to get timings for executing templates and scripts

. Enhancements and additions to JavaScript API:
  setPermission()
  removePermission()
  get/setInheritsPermissions()

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3297 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2006-07-11 10:13:30 +00:00
parent 8af011cd6d
commit 08f4586da7
8 changed files with 252 additions and 71 deletions

View File

@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
@@ -140,9 +141,10 @@ public abstract class BaseServlet extends HttpServlet
throws IOException
{
// authentication failed - so end servlet execution and redirect to login page
// also save the requested URL so the login page knows where to redirect too later
// also save the full requested URL so the login page knows where to redirect too later
res.sendRedirect(req.getContextPath() + FACES_SERVLET + Application.getLoginPage(sc));
String uri = req.getRequestURI();
String url = uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : "");
if (uri.indexOf(req.getContextPath() + FACES_SERVLET) != -1)
{
// if we find a JSF servlet reference in the URI then we need to check if the rest of the
@@ -150,12 +152,12 @@ public abstract class BaseServlet extends HttpServlet
int jspIndex = uri.indexOf(BaseServlet.FACES_SERVLET) + BaseServlet.FACES_SERVLET.length();
if (uri.length() > jspIndex && BaseServlet.validRedirectJSP(uri.substring(jspIndex)))
{
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, url);
}
}
else
{
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, url);
}
}
@@ -182,6 +184,44 @@ public abstract class BaseServlet extends HttpServlet
* @param args The elements of the path to lookup
*/
public static NodeRef resolveWebDAVPath(FacesContext context, String[] args)
{
WebApplicationContext wc = FacesContextUtils.getRequiredWebApplicationContext(context);
return resolveWebDAVPath(wc, args, true);
}
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param context ServletContext context
* @param args The elements of the path to lookup
*/
public static NodeRef resolveWebDAVPath(ServletContext context, String[] args)
{
WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
return resolveWebDAVPath(wc, args, true);
}
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param context ServletContext context
* @param args The elements of the path to lookup
* @param decode True to decode the arg from UTF-8 format, false for no decoding
*/
public static NodeRef resolveWebDAVPath(ServletContext context, String[] args, boolean decode)
{
WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
return resolveWebDAVPath(wc, args, decode);
}
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param WebApplicationContext Context
* @param args The elements of the path to lookup
* @param decode True to decode the arg from UTF-8 format, false for no decoding
*/
private static NodeRef resolveWebDAVPath(WebApplicationContext wc, String[] args, boolean decode)
{
NodeRef nodeRef = null;
@@ -193,7 +233,7 @@ public abstract class BaseServlet extends HttpServlet
// 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"));
paths.add(decode ? URLDecoder.decode(args[x], "UTF-8") : args[x]);
}
if (logger.isDebugEnabled())
@@ -203,7 +243,6 @@ public abstract class BaseServlet extends HttpServlet
NodeRef companyHome = new NodeRef(Repository.getStoreRef(),
Application.getCompanyRootId());
WebApplicationContext wc = FacesContextUtils.getRequiredWebApplicationContext(context);
FileFolderService ffs = (FileFolderService)wc.getBean("FileFolderService");
file = ffs.resolveNamePath(companyHome, paths);
nodeRef = file.getNodeRef();
@@ -228,4 +267,50 @@ public abstract class BaseServlet extends HttpServlet
return nodeRef;
}
/**
* Resolve a name based into a NodeRef and Filename string
*
* @param sc ServletContext
* @param path 'cm:name' based path using the '/' character as a separator
*
* @return PathRefInfo structure containing the resolved NodeRef and filename
*
* @throws IllegalArgumentException
*/
public final static PathRefInfo resolveNamePath(ServletContext sc, String path)
{
StringTokenizer t = new StringTokenizer(path, "/");
int tokenCount = t.countTokens();
String[] elements = new String[tokenCount];
for (int i=0; i<tokenCount; i++)
{
elements[i] = t.nextToken();
}
// process name based path tokens using the webdav path resolving helper
NodeRef nodeRef = resolveWebDAVPath(sc, elements, false);
if (nodeRef == null)
{
// unable to resolve path - output helpful error to the user
throw new IllegalArgumentException("Unable to resolve item Path: " + path);
}
return new PathRefInfo(nodeRef, elements[tokenCount - 1]);
}
/**
* Simple structure class for returning both a NodeRef and Filename String
* @author Kevin Roast
*/
public static class PathRefInfo
{
PathRefInfo(NodeRef ref, String filename)
{
this.NodeRef = ref;
this.Filename = filename;
}
public NodeRef NodeRef;
public String Filename;
}
}

View File

@@ -18,6 +18,9 @@ package org.alfresco.web.app.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
@@ -101,10 +104,19 @@ public class CommandServlet extends BaseServlet
String command = t.nextToken();
// get any remaining uri elements to pass to the processor
String[] args = new String[tokenCount - 3];
String[] urlElements = new String[tokenCount - 3];
for (int i=0; i<tokenCount-3; i++)
{
args[i] = t.nextToken();
urlElements[i] = t.nextToken();
}
// retrieve the URL arguments to pass to the processor
Map<String, String> args = new HashMap<String, String>(8, 1.0f);
Enumeration names = req.getParameterNames();
while (names.hasMoreElements())
{
String name = (String)names.nextElement();
args.put(name, req.getParameter(name));
}
try
@@ -113,13 +125,13 @@ public class CommandServlet extends BaseServlet
CommandProcessor processor = createCommandProcessor(procName);
// validate that the processor has everything it needs to run the command
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
if (processor.validateArguments(serviceRegistry, command, args) == false)
if (processor.validateArguments(getServletContext(), command, args, urlElements) == false)
{
redirectToLoginPage(req, res, getServletContext());
return;
}
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
UserTransaction txn = null;
try
{

View File

@@ -52,9 +52,11 @@ import org.apache.commons.logging.LogFactory;
* <pre>/alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
* or
* <pre>/alfresco/download/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
* <p>
* The store protocol, followed by the store ID, followed by the content Node Id
* the last element is used for mimetype calculation and browser default filename.
* or
* <pre>/alfresco/download/[direct|attach]?path=/Company%20Home/MyFolder/myfile.pdf</pre>
* The protocol, followed by either the store and Id (NodeRef) or instead specify a name based
* encoded Path to the content, note that the filename element is used for mimetype lookup and
* as the returning filename for the response stream.
* <p>
* The 'attach' or 'direct' element is used to indicate whether to display the stream directly
* in the browser or download it as a file attachment.
@@ -86,6 +88,7 @@ public class DownloadContentServlet extends BaseServlet
private static final String ARG_PROPERTY = "property";
private static final String ARG_ATTACH = "attach";
private static final String ARG_PATH = "path";
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
@@ -93,11 +96,6 @@ public class DownloadContentServlet extends BaseServlet
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
// 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())
@@ -114,35 +112,54 @@ public class DownloadContentServlet extends BaseServlet
// only really needed if we don't use the built in compression of the servlet container
uri = uri.substring(req.getContextPath().length());
StringTokenizer t = new StringTokenizer(uri, "/");
if (t.countTokens() < 6)
int tokenCount = t.countTokens();
t.nextToken(); // skip servlet name
// attachment mode (either 'attach' or 'direct')
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
// get or calculate the noderef and filename to download as
NodeRef nodeRef;
String filename;
// do we have a path parameter instead of a NodeRef?
String path = req.getParameter(ARG_PATH);
if (path != null && path.length() != 0)
{
// process the name based path to resolve the NodeRef and the Filename element
PathRefInfo pathInfo = resolveNamePath(getServletContext(), path);
nodeRef = pathInfo.NodeRef;
filename = pathInfo.Filename;
}
else
{
// a NodeRef must have been specified if no path has been found
if (tokenCount < 6)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip servlet name
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
// assume 'workspace' or other NodeRef based protocol for remaining URL elements
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
String id = t.nextToken();
String filename = t.nextToken();
// build noderef from the appropriate URL elements
nodeRef = new NodeRef(storeRef, id);
// get property qualified name
QName propertyQName = null;
String property = req.getParameter(ARG_PROPERTY);
if (property == null || property.length() == 0)
{
propertyQName = ContentModel.PROP_CONTENT;
// filename is last remaining token
filename = t.nextToken();
}
else
// get qualified of the property to get content from - default to ContentModel.PROP_CONTENT
QName propertyQName = ContentModel.PROP_CONTENT;
String property = req.getParameter(ARG_PROPERTY);
if (property != null && property.length() != 0)
{
propertyQName = QName.createQName(property);
}
// build noderef from the appropriate URL elements
NodeRef nodeRef = new NodeRef(storeRef, id);
if (logger.isDebugEnabled())
{
logger.debug("Found NodeRef: " + nodeRef.toString());
@@ -199,7 +216,9 @@ public class DownloadContentServlet extends BaseServlet
}
}
}
// set mimetype for the content and the character encoding for the stream
res.setContentType(mimetype);
res.setCharacterEncoding(reader.getEncoding());
// get the content and stream directly to the response output stream
// assuming the repo is capable of streaming in chunks, this should allow large files

View File

@@ -27,7 +27,6 @@ import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;
import org.alfresco.error.AlfrescoRuntimeException;
@@ -56,10 +55,15 @@ import org.apache.commons.logging.LogFactory;
* <pre>/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000</pre>
* or
* <pre>/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000/workspace/SpacesStore/0000-0000-0000-0000</pre>
* or
* <pre>/alfresco/template?templatePath=/Company%20Home/Data%20Dictionary/Presentation%20Templates/doc_info.ftl&contextPath=/Company%20Home/mydoc.txt</pre>
* <p>
* The store protocol, followed by the store ID, followed by the content Node Id used to
* 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 store and node Id of the template to used if a default is not set or not requested. Instead
* of using NodeRef references to the template and context, path arguments can be used. The URL args
* of 'templatePath' and 'contextPath' can be used instead to specify name based encoded Paths to the
* template and its context.
* <p>
* 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.
@@ -85,6 +89,8 @@ public class TemplateContentServlet extends BaseServlet
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
private static final String ARG_MIMETYPE = "mimetype";
private static final String ARG_TEMPLATE_PATH = "templatePath";
private static final String ARG_CONTEXT_PATH = "contextPath";
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
@@ -106,25 +112,54 @@ public class TemplateContentServlet extends BaseServlet
uri = uri.substring(req.getContextPath().length());
StringTokenizer t = new StringTokenizer(uri, "/");
int tokenCount = t.countTokens();
if (tokenCount < 4)
{
throw new IllegalArgumentException("Template Servlet URL did not contain all required args: " + uri);
}
t.nextToken(); // skip servlet name
// get NodeRef to the content
NodeRef nodeRef = null;
NodeRef templateRef = null;
String contentPath = req.getParameter(ARG_CONTEXT_PATH);
if (contentPath != null && contentPath.length() != 0)
{
// process the name based path to resolve the NodeRef
PathRefInfo pathInfo = resolveNamePath(getServletContext(), contentPath);
nodeRef = pathInfo.NodeRef;
}
else if (tokenCount > 3)
{
// get NodeRef to the content from the URL elements
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
NodeRef nodeRef = new NodeRef(storeRef, t.nextToken());
nodeRef = new NodeRef(storeRef, t.nextToken());
}
// get NodeRef to the template if supplied
NodeRef templateRef = null;
if (tokenCount >= 7)
String templatePath = req.getParameter(ARG_TEMPLATE_PATH);
if (templatePath != null && templatePath.length() != 0)
{
storeRef = new StoreRef(t.nextToken(), t.nextToken());
// process the name based path to resolve the NodeRef
PathRefInfo pathInfo = resolveNamePath(getServletContext(), templatePath);
templateRef = pathInfo.NodeRef;
}
else if (tokenCount == 7)
{
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
templateRef = new NodeRef(storeRef, t.nextToken());
}
// if no context is specified, use the template itself
// TODO: should this default to something else?
if (nodeRef == null && templateRef != null)
{
nodeRef = templateRef;
}
if (nodeRef == null)
{
throw new TemplateException("Not enough arguments supplied in URL.");
}
// get the services we need to retrieve the content
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
NodeService nodeService = serviceRegistry.getNodeService();

View File

@@ -16,11 +16,15 @@
*/
package org.alfresco.web.app.servlet.command;
import org.alfresco.service.ServiceRegistry;
import java.util.Map;
import javax.servlet.ServletContext;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.web.bean.repository.Repository;
/**
* Initial implementation of a Command Processor that is always passed enough URL elements
@@ -37,21 +41,21 @@ public abstract class BaseNodeCommandProcessor implements CommandProcessor
protected NodeRef targetRef;
/**
* @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(org.alfresco.service.ServiceRegistry, java.lang.String, java.lang.String[])
* @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(javax.servlet.ServletContext, java.lang.String, java.util.Map, java.lang.String[])
*/
public boolean validateArguments(ServiceRegistry serviceRegistry, String command, String[] args)
public boolean validateArguments(ServletContext sc, String command, Map<String, String> args, String[] urlElements)
{
if (args.length < 3)
if (urlElements.length < 3)
{
throw new IllegalArgumentException("Not enough URL arguments passed to command servlet.");
}
// get NodeRef to the node with the workflow attached to it
StoreRef storeRef = new StoreRef(args[0], args[1]);
this.targetRef = new NodeRef(storeRef, args[2]);
StoreRef storeRef = new StoreRef(urlElements[0], urlElements[1]);
this.targetRef = new NodeRef(storeRef, urlElements[2]);
// get the services we need to execute the workflow command
PermissionService permissionService = serviceRegistry.getPermissionService();
PermissionService permissionService = Repository.getServiceRegistry(sc).getPermissionService();
// check that the user has at least READ access on the node - else redirect to the login page
return (permissionService.hasPermission(this.targetRef, PermissionService.READ) == AccessStatus.ALLOWED);

View File

@@ -17,9 +17,10 @@
package org.alfresco.web.app.servlet.command;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.alfresco.service.ServiceRegistry;
@@ -45,13 +46,15 @@ public interface CommandProcessor
* convert the supplied arguments to the objects it expects, and also check any permissions
* that are required by the current user to execute the command.
*
* @param serviceRegistry ServiceRegistry instance
* @param sc ServletContext, can be used to retrieve ServiceRegistry instance
* from the Repository bean.
* @param command Name of the command the arguments are for
* @param args String[] of the remaining URL arguments to the command servlet.
* @param args Map of URL args passed to the command servlet
* @param urlElements String[] of the remaining URL arguments to the command servlet
*
* @return true if the command can be executed by the current user given the supplied args.
*/
public boolean validateArguments(ServiceRegistry serviceRegistry, String command, String[] args);
public boolean validateArguments(ServletContext sc, String command, Map<String, String> args, String[] urlElements);
/**
* Process the supplied command name. It is the responsibility of the Command Processor

View File

@@ -21,6 +21,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.alfresco.error.AlfrescoRuntimeException;
@@ -31,6 +32,8 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.BaseServlet;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
/**
@@ -42,6 +45,9 @@ import org.alfresco.web.bean.repository.User;
*/
public final class ScriptCommandProcessor implements CommandProcessor
{
private static final String ARG_SCRIPT_PATH = "scriptPath";
private static final String ARG_CONTEXT_PATH = "contextPath";
private NodeRef scriptRef;
private NodeRef docRef;
private Object result;
@@ -52,29 +58,47 @@ public final class ScriptCommandProcessor implements CommandProcessor
CommandFactory.getInstance().registerCommand("execute", ExecuteScriptCommand.class);
}
/**
* @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(org.alfresco.service.ServiceRegistry, java.lang.String, java.lang.String[])
* @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(javax.servlet.ServletContext, java.lang.String, java.util.Map, java.lang.String[])
*/
public boolean validateArguments(ServiceRegistry serviceRegistry, String command, String[] args)
public boolean validateArguments(ServletContext sc, String command, Map<String, String> args, String[] urlElements)
{
if (args.length < 3)
boolean allowed = false;
String scriptPath = args.get(ARG_SCRIPT_PATH);
if (scriptPath != null)
{
// resolve path to a node
this.scriptRef = BaseServlet.resolveNamePath(sc, scriptPath).NodeRef;
// same for the document context path if specified
String docPath = args.get(ARG_CONTEXT_PATH);
if (docPath != null)
{
this.docRef = BaseServlet.resolveNamePath(sc, docPath).NodeRef;
}
}
else
{
if (urlElements.length < 3)
{
throw new IllegalArgumentException("Not enough URL arguments passed to command servlet.");
}
// get NodeRef to the node script to execute
StoreRef storeRef = new StoreRef(args[0], args[1]);
this.scriptRef = new NodeRef(storeRef, args[2]);
StoreRef storeRef = new StoreRef(urlElements[0], urlElements[1]);
this.scriptRef = new NodeRef(storeRef, urlElements[2]);
if (args.length >= 6)
if (urlElements.length >= 6)
{
storeRef = new StoreRef(args[3], args[4]);
this.docRef = new NodeRef(storeRef, args[5]);
storeRef = new StoreRef(urlElements[3], urlElements[4]);
this.docRef = new NodeRef(storeRef, urlElements[5]);
}
}
// check we can access the nodes specified
PermissionService ps = serviceRegistry.getPermissionService();
boolean allowed = (ps.hasPermission(this.scriptRef, PermissionService.READ) == AccessStatus.ALLOWED);
PermissionService ps = Repository.getServiceRegistry(sc).getPermissionService();
allowed = (ps.hasPermission(this.scriptRef, PermissionService.READ) == AccessStatus.ALLOWED);
if (this.docRef != null)
{
allowed &= (ps.hasPermission(this.docRef, PermissionService.READ) == AccessStatus.ALLOWED);

View File

@@ -21,7 +21,6 @@ import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.ServiceRegistry;