diff --git a/config/alfresco/web-scripts-application-context-test.xml b/config/alfresco/web-scripts-application-context-test.xml index fe7be45526..c44792d9a8 100644 --- a/config/alfresco/web-scripts-application-context-test.xml +++ b/config/alfresco/web-scripts-application-context-test.xml @@ -10,6 +10,7 @@ + diff --git a/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java b/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java index 789946b184..08d953d978 100644 --- a/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java +++ b/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java @@ -33,6 +33,7 @@ import java.util.Map; import javax.servlet.http.HttpServletResponse; +import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.jscript.Node; import org.alfresco.repo.jscript.ScriptableHashMap; import org.alfresco.repo.template.TemplateNode; @@ -132,12 +133,34 @@ public class DeclarativeWebScript extends AbstractWebScript { res.setStatus(statusCode); } - res.setContentType(mimetype + ";charset=UTF-8"); - if (logger.isDebugEnabled()) - logger.debug("Rendering response: content type=" + mimetype + ", status=" + statusCode); + String callback = req.getJSONCallback(); + if (format.equals(WebScriptResponse.JSON_FORMAT) && callback != null) + { + + if (logger.isDebugEnabled()) + logger.debug("Rendering JSON callback response: content type=" + MimetypeMap.MIMETYPE_TEXT_JAVASCRIPT + ", status=" + statusCode + ", callback=" + callback); + + // NOTE: special case for wrapping JSON results in a javascript function callback + res.setContentType(MimetypeMap.MIMETYPE_TEXT_JAVASCRIPT + ";charset=UTF-8"); + res.getOutputStream().write((callback + "(").getBytes()); + } + else + { + if (logger.isDebugEnabled()) + logger.debug("Rendering response: content type=" + mimetype + ", status=" + statusCode); + + res.setContentType(mimetype + ";charset=UTF-8"); + } + // render response according to requested format renderFormatTemplate(format, templateModel, res.getWriter()); + + if (format.equals(WebScriptResponse.JSON_FORMAT) && callback != null) + { + // NOTE: special case for wrapping JSON results in a javascript function callback + res.getOutputStream().write(")".getBytes()); + } } } catch(Throwable e) diff --git a/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java b/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java index afe4ec2cc9..c21520dff6 100644 --- a/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java +++ b/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java @@ -259,6 +259,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean { // establish static part of url template boolean wildcard = false; + boolean extension = true; String uriTemplate = uri.getURI(); int queryArgIdx = uriTemplate.indexOf('?'); if (queryArgIdx != -1) @@ -278,6 +279,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean { uriTemplate = uriTemplate.substring(0, extIdx); } + extension = false; } // index service by static part of url (ensuring no other service has already claimed the url) @@ -294,7 +296,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean } else { - URLIndex urlIndex = new URLIndex(uriTemplate, wildcard, serviceImpl); + URLIndex urlIndex = new URLIndex(uriTemplate, wildcard, extension, serviceImpl); webscriptsByURL.put(uriIdx, urlIndex); if (logger.isDebugEnabled()) @@ -580,13 +582,15 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean String matchedPath = null; DeclarativeWebScriptMatch apiServiceMatch = null; String match = method.toString().toUpperCase() + ":" + uri; + String matchNoExt = method.toString().toUpperCase() + ":" + ((uri.indexOf('.') != -1) ? uri.substring(0, uri.indexOf('.')) : uri); // locate full match - on URI and METHOD for (Map.Entry entry : webscriptsByURL.entrySet()) { URLIndex urlIndex = entry.getValue(); String index = entry.getKey(); - if ((urlIndex.wildcardPath && match.startsWith(index)) || (!urlIndex.wildcardPath && match.equals(index))) + String test = urlIndex.includeExtension ? match : matchNoExt; + if ((urlIndex.wildcardPath && test.startsWith(index)) || (!urlIndex.wildcardPath && test.equals(index))) { apiServiceMatch = new DeclarativeWebScriptMatch(urlIndex.path, urlIndex.script); break; @@ -715,15 +719,17 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean */ private static class URLIndex { - private URLIndex(String path, boolean wildcardPath, WebScript script) + private URLIndex(String path, boolean wildcardPath, boolean includeExtension, WebScript script) { this.path = path; this.wildcardPath = wildcardPath; + this.includeExtension = includeExtension; this.script = script; } private String path; private boolean wildcardPath; + private boolean includeExtension; private WebScript script; } diff --git a/source/java/org/alfresco/web/scripts/TestWebScriptServer.java b/source/java/org/alfresco/web/scripts/TestWebScriptServer.java index aaa657c998..6084744686 100644 --- a/source/java/org/alfresco/web/scripts/TestWebScriptServer.java +++ b/source/java/org/alfresco/web/scripts/TestWebScriptServer.java @@ -42,6 +42,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.config.ServerConfigElement; import org.springframework.context.ApplicationContext; @@ -62,6 +63,7 @@ public class TestWebScriptServer // dependencies protected AuthenticationService authenticationService; protected TransactionService transactionService; + protected AuthorityService authorityService; protected DeclarativeWebScriptRegistry registry; protected ConfigService configService; @@ -118,7 +120,15 @@ public class TestWebScriptServer { this.authenticationService = authenticationService; } - + + /** + * @param authorityService + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + /** * Sets the Messages resource bundle * @@ -200,7 +210,7 @@ public class TestWebScriptServer MockHttpServletRequest req = createRequest(method, uri); MockHttpServletResponse res = new MockHttpServletResponse(); - WebScriptRuntime runtime = new WebScriptServletRuntime(registry, transactionService, null, req, res, serverConfig); + WebScriptRuntime runtime = new WebScriptServletRuntime(registry, transactionService, authorityService, null, req, res, serverConfig); runtime.executeScript(); return res; @@ -225,7 +235,7 @@ public class TestWebScriptServer } MockHttpServletResponse res = new MockHttpServletResponse(); - WebScriptRuntime runtime = new WebScriptServletRuntime(registry, transactionService, null, req, res, serverConfig); + WebScriptRuntime runtime = new WebScriptServletRuntime(registry, transactionService, authorityService, null, req, res, serverConfig); runtime.executeScript(); return res; diff --git a/source/java/org/alfresco/web/scripts/WebScriptDescription.java b/source/java/org/alfresco/web/scripts/WebScriptDescription.java index 28730df775..f276f6666d 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptDescription.java +++ b/source/java/org/alfresco/web/scripts/WebScriptDescription.java @@ -42,7 +42,8 @@ public interface WebScriptDescription { none, guest, - user + user, + admin } /** diff --git a/source/java/org/alfresco/web/scripts/WebScriptRequest.java b/source/java/org/alfresco/web/scripts/WebScriptRequest.java index 04c2543835..e2a3e51ec0 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptRequest.java +++ b/source/java/org/alfresco/web/scripts/WebScriptRequest.java @@ -140,6 +140,13 @@ public interface WebScriptRequest * @return format style (excludes any) */ public FormatStyle getFormatStyle(); + + /** + * Get the JSON callback method + * + * @return method (or null, if not specified) + */ + public String getJSONCallback(); /** * Get User Agent diff --git a/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java b/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java index c5c724a4ba..5412a41cfb 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java +++ b/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java @@ -138,5 +138,13 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest } } } - + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptRequest#getJSONCallback() + */ + public String getJSONCallback() + { + return getParameter("alf_callback"); + } + } diff --git a/source/java/org/alfresco/web/scripts/WebScriptRuntime.java b/source/java/org/alfresco/web/scripts/WebScriptRuntime.java index 1c969ff752..ad8818bb58 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptRuntime.java +++ b/source/java/org/alfresco/web/scripts/WebScriptRuntime.java @@ -28,13 +28,13 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication; import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction; @@ -62,6 +62,7 @@ public abstract class WebScriptRuntime /** Component Dependencies */ private WebScriptRegistry registry; private TransactionService transactionService; + private AuthorityService authorityService; /** * Construct @@ -69,10 +70,11 @@ public abstract class WebScriptRuntime * @param registry web script registry * @param transactionService transaction service */ - public WebScriptRuntime(WebScriptRegistry registry, TransactionService transactionService) + public WebScriptRuntime(WebScriptRegistry registry, TransactionService transactionService, AuthorityService authorityService) { this.registry = registry; this.transactionService = transactionService; + this.authorityService = authorityService; } /** @@ -265,7 +267,7 @@ public abstract class WebScriptRuntime { wrappedExecute(scriptReq, scriptRes); } - else if (required == RequiredAuthentication.user && isGuest) + else if ((required == RequiredAuthentication.user || required == RequiredAuthentication.admin) && isGuest) { throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access."); } @@ -291,6 +293,11 @@ public abstract class WebScriptRuntime // if (authenticate(required, isGuest)) { + if (required == RequiredAuthentication.admin && !authorityService.hasAdminAuthority()) + { + throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires admin authentication; however, a non-admin has attempted access."); + } + // Execute Web Script wrappedExecute(scriptReq, scriptRes); } diff --git a/source/java/org/alfresco/web/scripts/WebScriptServlet.java b/source/java/org/alfresco/web/scripts/WebScriptServlet.java index afd64dc835..db63f06993 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptServlet.java +++ b/source/java/org/alfresco/web/scripts/WebScriptServlet.java @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletResponse; import org.alfresco.config.Config; import org.alfresco.config.ConfigService; +import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.config.ServerConfigElement; import org.apache.commons.logging.Log; @@ -56,6 +57,7 @@ public class WebScriptServlet extends HttpServlet // Component Dependencies private DeclarativeWebScriptRegistry registry; private TransactionService transactionService; + private AuthorityService authorityService; private WebScriptServletAuthenticator authenticator; protected ConfigService configService; @@ -70,6 +72,7 @@ public class WebScriptServlet extends HttpServlet ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); registry = (DeclarativeWebScriptRegistry)context.getBean("webscripts.registry"); transactionService = (TransactionService)context.getBean("transactionComponent"); + authorityService = (AuthorityService)context.getBean("authorityServce"); configService = (ConfigService)context.getBean("webClientConfigService"); // retrieve authenticator via servlet initialisation parameter @@ -105,7 +108,7 @@ public class WebScriptServlet extends HttpServlet res.setHeader("Cache-Control", "no-cache"); res.setHeader("Pragma", "no-cache"); - WebScriptRuntime runtime = new WebScriptServletRuntime(registry, transactionService, authenticator, req, res, serverConfig); + WebScriptRuntime runtime = new WebScriptServletRuntime(registry, transactionService, authorityService, authenticator, req, res, serverConfig); runtime.executeScript(); } diff --git a/source/java/org/alfresco/web/scripts/WebScriptServletRuntime.java b/source/java/org/alfresco/web/scripts/WebScriptServletRuntime.java index 88f2905234..033333b69d 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptServletRuntime.java +++ b/source/java/org/alfresco/web/scripts/WebScriptServletRuntime.java @@ -27,6 +27,7 @@ package org.alfresco.web.scripts; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.config.ServerConfigElement; import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication; @@ -54,10 +55,11 @@ public class WebScriptServletRuntime extends WebScriptRuntime * @param req * @param res */ - public WebScriptServletRuntime(WebScriptRegistry registry, TransactionService transactionService, WebScriptServletAuthenticator authenticator, + public WebScriptServletRuntime(WebScriptRegistry registry, TransactionService transactionService, + AuthorityService authorityService, WebScriptServletAuthenticator authenticator, HttpServletRequest req, HttpServletResponse res, ServerConfigElement serverConfig) { - super(registry, transactionService); + super(registry, transactionService, authorityService); this.req = req; this.res = res; this.authenticator = authenticator; @@ -78,13 +80,13 @@ public class WebScriptServletRuntime extends WebScriptRuntime String overload = req.getHeader("X-HTTP-Method-Override"); if (overload == null || overload.length() == 0) { - overload = req.getParameter("alf:method"); + overload = req.getParameter("alf_method"); overloadParam = true; } if (overload != null && overload.length() > 0) { if (logger.isDebugEnabled()) - logger.debug("POST is tunnelling method '" + overload + "' as specified by " + (overloadParam ? "alf:method parameter" : "X-HTTP-Method-Override header")); + logger.debug("POST is tunnelling method '" + overload + "' as specified by " + (overloadParam ? "alf_method parameter" : "X-HTTP-Method-Override header")); method = overload; } diff --git a/source/java/org/alfresco/web/scripts/jsf/UIWebScript.java b/source/java/org/alfresco/web/scripts/jsf/UIWebScript.java index 312d3914af..06d8e7ac16 100644 --- a/source/java/org/alfresco/web/scripts/jsf/UIWebScript.java +++ b/source/java/org/alfresco/web/scripts/jsf/UIWebScript.java @@ -37,6 +37,7 @@ import javax.faces.event.ActionEvent; import javax.faces.event.FacesEvent; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.scripts.DeclarativeWebScriptRegistry; import org.alfresco.web.scripts.WebScriptMatch; @@ -70,6 +71,7 @@ public class UIWebScript extends SelfRenderingComponent private WebScriptRegistry registry; private TransactionService txnService; + private AuthorityService authorityService; /** * Default constructor @@ -80,6 +82,7 @@ public class UIWebScript extends SelfRenderingComponent FacesContext.getCurrentInstance()); this.registry = (DeclarativeWebScriptRegistry)ctx.getBean("webscripts.registry"); this.txnService = (TransactionService)ctx.getBean("transactionComponent"); + this.authorityService = (AuthorityService)ctx.getBean("authorityService"); } /** @@ -225,7 +228,7 @@ public class UIWebScript extends SelfRenderingComponent WebScriptJSFRuntime(FacesContext fc, String scriptUrl) { - super(registry, txnService); + super(registry, txnService, authorityService); this.fc = fc; this.scriptUrl = scriptUrl; this.script = WebScriptURLRequest.splitURL(scriptUrl)[2]; diff --git a/source/java/org/alfresco/web/scripts/portlet/WebScriptPortlet.java b/source/java/org/alfresco/web/scripts/portlet/WebScriptPortlet.java index 9fffb53a72..8bba515194 100644 --- a/source/java/org/alfresco/web/scripts/portlet/WebScriptPortlet.java +++ b/source/java/org/alfresco/web/scripts/portlet/WebScriptPortlet.java @@ -39,6 +39,7 @@ import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.WindowState; +import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.scripts.DeclarativeWebScriptRegistry; import org.alfresco.web.scripts.WebScript; @@ -73,6 +74,7 @@ public class WebScriptPortlet implements Portlet // Component Dependencies protected DeclarativeWebScriptRegistry registry; protected TransactionService transactionService; + protected AuthorityService authorityService; protected WebScriptPortletAuthenticator authenticator; @@ -86,6 +88,7 @@ public class WebScriptPortlet implements Portlet WebApplicationContext ctx = (WebApplicationContext)portletCtx.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); registry = (DeclarativeWebScriptRegistry)ctx.getBean("webscripts.registry"); transactionService = (TransactionService)ctx.getBean("transactionComponent"); + authorityService = (AuthorityService)ctx.getBean("authorityService"); authenticator = (WebScriptPortletAuthenticator)ctx.getBean("webscripts.authenticator.jsr168"); } @@ -209,7 +212,7 @@ public class WebScriptPortlet implements Portlet */ public WebScriptPortalRuntime(RenderRequest req, RenderResponse res, String requestUrl) { - super(registry, transactionService); + super(registry, transactionService, authorityService); this.req = req; this.res = res; this.requestUrlParts = WebScriptURLRequest.splitURL(requestUrl);