SLING-9812 AccessManager Post servlets should not allow redirects to
other hosts
diff --git a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessPostServlet.java b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessPostServlet.java
index b41171f..2ade35f 100644
--- a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessPostServlet.java
+++ b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessPostServlet.java
@@ -17,6 +17,8 @@
package org.apache.sling.jcr.jackrabbit.accessmanager.post;
import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -54,9 +56,9 @@
* Base class for all the POST servlets for the AccessManager operations
*/
public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
- private static final long serialVersionUID = -5918670409789895333L;
+ private static final long serialVersionUID = -5918670409789895333L;
- /**
+ /**
* default log
*/
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -68,15 +70,15 @@
private PostResponseCreator[] cachedPostResponseCreators = new PostResponseCreator[0];
- /* (non-Javadoc)
- * @see org.apache.sling.api.servlets.SlingAllMethodsServlet#doPost(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.SlingHttpServletResponse)
- */
- @Override
- protected void doPost(SlingHttpServletRequest request,
- SlingHttpServletResponse httpResponse) throws ServletException,
- IOException {
+ /* (non-Javadoc)
+ * @see org.apache.sling.api.servlets.SlingAllMethodsServlet#doPost(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.SlingHttpServletResponse)
+ */
+ @Override
+ protected void doPost(SlingHttpServletRequest request,
+ SlingHttpServletResponse httpResponse) throws ServletException,
+ IOException {
// prepare the response
- PostResponse response = createPostResponse(request);
+ PostResponse response = createPostResponse(request);
response.setReferer(request.getHeader("referer"));
// calculate the paths
@@ -89,7 +91,7 @@
// parent location
path = ResourceUtil.getParent(path);
if (path != null) {
- response.setParentLocation(externalizePath(request, path));
+ response.setParentLocation(externalizePath(request, path));
}
Session session = request.getResourceResolver().adaptTo(Session.class);
@@ -110,8 +112,8 @@
case COPY : response.onCopied(change.getSource(), change.getDestination()); break;
case CREATE : response.onCreated(change.getSource()); break;
case ORDER : response.onChange("ordered", change.getSource(), change.getDestination()); break;
- default:
- break;
+ default:
+ break;
}
}
@@ -148,7 +150,7 @@
// create a html response and send if unsuccessful or no redirect
response.send(httpResponse, isSetStatus(request));
- }
+ }
/**
* Creates an instance of a HtmlResponse.
@@ -160,11 +162,11 @@
* or a {@link org.apache.sling.servlets.post.HtmlResponse} otherwise
* @deprecated use {@link #createPostResponse(SlingHttpServletRequest)} instead
*/
- @Deprecated
+ @Deprecated
protected AbstractPostResponse createHtmlResponse(SlingHttpServletRequest req) {
- return (AbstractPostResponse)createPostResponse(req);
+ return (AbstractPostResponse)createPostResponse(req);
}
-
+
/**
* Creates an instance of a PostResponse.
* @param req The request being serviced
@@ -189,16 +191,16 @@
MediaRangeList mediaRangeList = null;
String queryParam = req.getParameter(MediaRangeList.PARAM_ACCEPT);
if (queryParam == null || queryParam.trim().length() == 0) {
- String headerValue = req.getHeader(MediaRangeList.HEADER_ACCEPT);
- if (headerValue == null || headerValue.trim().length() == 0) {
- //no param or header supplied, so try the response content type
- mediaRangeList = new MediaRangeList(req.getResponseContentType());
- }
+ String headerValue = req.getHeader(MediaRangeList.HEADER_ACCEPT);
+ if (headerValue == null || headerValue.trim().length() == 0) {
+ //no param or header supplied, so try the response content type
+ mediaRangeList = new MediaRangeList(req.getResponseContentType());
+ }
}
// Fall through to default behavior
if (mediaRangeList == null) {
- mediaRangeList = new MediaRangeList(req);
+ mediaRangeList = new MediaRangeList(req);
}
if (JSONResponse.RESPONSE_CONTENT_TYPE.equals(mediaRangeList.prefer("text/html", JSONResponse.RESPONSE_CONTENT_TYPE))) {
return new JSONResponse();
@@ -206,89 +208,104 @@
return new HtmlResponse();
}
}
-
- /**
- * Extending Servlet should implement this operation to do the work
- *
- * @param request the sling http request to process
- * @param response the response
+
+ /**
+ * Extending Servlet should implement this operation to do the work
+ *
+ * @param request the sling http request to process
+ * @param response the response
* @param changes the changes to report
- * @throws RepositoryException if any errors applying the changes
- *
- * @deprecated use {@link #handleOperation(SlingHttpServletRequest, PostResponse, List)} instead
- */
+ * @throws RepositoryException if any errors applying the changes
+ *
+ * @deprecated use {@link #handleOperation(SlingHttpServletRequest, PostResponse, List)} instead
+ */
@Deprecated
- protected void handleOperation(SlingHttpServletRequest request,
- AbstractPostResponse response, List<Modification> changes) throws RepositoryException {
- handleOperation(request, (PostResponse)response, changes);
- }
+ protected void handleOperation(SlingHttpServletRequest request,
+ AbstractPostResponse response, List<Modification> changes) throws RepositoryException {
+ handleOperation(request, (PostResponse)response, changes);
+ }
- /**
- * Extending Servlet should implement this operation to do the work
- *
- * @param request the sling http request to process
- * @param response the response
+ /**
+ * Extending Servlet should implement this operation to do the work
+ *
+ * @param request the sling http request to process
+ * @param response the response
* @param changes the changes to report
- * @throws RepositoryException if any errors applying the changes
- */
- abstract protected void handleOperation(SlingHttpServletRequest request,
- PostResponse response, List<Modification> changes) throws RepositoryException;
+ * @throws RepositoryException if any errors applying the changes
+ */
+ abstract protected void handleOperation(SlingHttpServletRequest request,
+ PostResponse response, List<Modification> changes) throws RepositoryException;
/**
* compute redirect URL (SLING-126)
*
- * @param request the sling http request to process
+ * @param request the sling http request to process
* @param ctx the post processor
* @return the redirect location or <code>null</code>
* @deprecated use {@link #getRedirectUrl(HttpServletRequest, PostResponse)} instead
*/
- @Deprecated
+ @Deprecated
protected String getRedirectUrl(HttpServletRequest request, AbstractPostResponse ctx) {
- return getRedirectUrl(request, (PostResponse)ctx);
+ return getRedirectUrl(request, (PostResponse)ctx);
}
/**
* compute redirect URL (SLING-126)
*
- * @param request the sling http request to process
+ * @param request the sling http request to process
* @param ctx the post processor
* @return the redirect location or <code>null</code>
*/
protected String getRedirectUrl(HttpServletRequest request, PostResponse ctx) {
// redirect param has priority (but see below, magic star)
String result = request.getParameter(SlingPostConstants.RP_REDIRECT_TO);
- if (result != null && ctx.getPath() != null) {
-
- // redirect to created/modified Resource
- int star = result.indexOf('*');
- if (star >= 0) {
- StringBuffer buf = new StringBuffer();
-
- // anything before the star
- if (star > 0) {
- buf.append(result.substring(0, star));
+ if (result != null) {
+ try {
+ URI redirectUri = new URI(result);
+ if (redirectUri.getAuthority() != null) {
+ // if it has a host information
+ log.warn("redirect target ({}) does include host information ({}). This is not allowed for security reasons!", result, redirectUri.getAuthority());
+ return null;
}
-
- // append the name of the manipulated node
- buf.append(ResourceUtil.getName(ctx.getPath()));
-
- // anything after the star
- if (star < result.length() - 1) {
- buf.append(result.substring(star + 1));
- }
-
- // use the created path as the redirect result
- result = buf.toString();
-
- } else if (result.endsWith(SlingPostConstants.DEFAULT_CREATE_SUFFIX)) {
- // if the redirect has a trailing slash, append modified node
- // name
- result = result.concat(ResourceUtil.getName(ctx.getPath()));
+ } catch (URISyntaxException e) {
+ log.warn("given redirect target ({}) is not a valid uri: {}", result, e);
+ return null;
}
- if (log.isDebugEnabled()) {
- log.debug("Will redirect to " + result);
+ log.debug("redirect requested as [{}] for path [{}]", result, ctx.getPath());
+
+ if (ctx.getPath() != null) {
+ // redirect to created/modified Resource
+ int star = result.indexOf('*');
+ if (star >= 0) {
+ StringBuffer buf = new StringBuffer();
+
+ // anything before the star
+ if (star > 0) {
+ buf.append(result.substring(0, star));
+ }
+
+ // append the name of the manipulated node
+ buf.append(ResourceUtil.getName(ctx.getPath()));
+
+ // anything after the star
+ if (star < result.length() - 1) {
+ buf.append(result.substring(star + 1));
+ }
+
+ // use the created path as the redirect result
+ result = buf.toString();
+
+ } else if (result.endsWith(SlingPostConstants.DEFAULT_CREATE_SUFFIX)) {
+ // if the redirect has a trailing slash, append modified node
+ // name
+ result = result.concat(ResourceUtil.getName(ctx.getPath()));
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Will redirect to " + result);
+ }
}
}
return result;
@@ -323,7 +340,7 @@
return true;
}
- // ------ These methods were copied from AbstractSlingPostOperation ------
+ // ------ These methods were copied from AbstractSlingPostOperation ------
/**
* Returns the path of the resource of the request as the item path.
@@ -331,8 +348,8 @@
* This method may be overwritten by extension if the operation has
* different requirements on path processing.
* </p>
- * @param request the sling http request to process
- * @return the resolved path of the found item
+ * @param request the sling http request to process
+ * @return the resolved path of the found item
*/
protected String getItemPath(SlingHttpServletRequest request) {
return request.getResource().getPath();
@@ -342,7 +359,7 @@
* Returns an external form of the given path prepending the context path
* and appending a display extension.
*
- * @param request the sling http request to process
+ * @param request the sling http request to process
* @param path the path to externalize
* @return the url
*/
@@ -421,13 +438,13 @@
* created if the node does not have one yet.
* @return The <code>AccessControlList</code> to modify to control access to
* the node or null if one could not be located or created
- * @throws RepositoryException if any errors reading the information
+ * @throws RepositoryException if any errors reading the information
*/
protected AccessControlList getAccessControlListOrNull(
final AccessControlManager accessControlManager,
final String resourcePath, final boolean mayCreate)
throws RepositoryException {
- AccessControlList acl = null;
+ AccessControlList acl = null;
// check for an existing access control list to edit
AccessControlPolicy[] policies = accessControlManager.getPolicies(resourcePath);
for (AccessControlPolicy policy : policies) {
@@ -457,21 +474,19 @@
* @param creator the response creator service reference
* @param properties the component properties for the service reference
*/
- // NOTE: the @Reference annotation is not inherited, so subclasses will need to override the #bindPostResponseCreator
- // and #unbindPostResponseCreator methods to provide the @Reference annotation.
- //
- // @Reference(service = PostResponseCreator.class,
- // cardinality = ReferenceCardinality.MULTIPLE,
- // policy = ReferencePolicy.DYNAMIC)
+ // NOTE: the @Reference annotation is not inherited, so subclasses will need to override the #bindPostResponseCreator
+ // and #unbindPostResponseCreator methods to provide the @Reference annotation.
+ //
+ // @Reference(service = PostResponseCreator.class,
+ // cardinality = ReferenceCardinality.MULTIPLE,
+ // policy = ReferencePolicy.DYNAMIC)
protected void bindPostResponseCreator(final PostResponseCreator creator, final Map<String, Object> properties) {
- final PostResponseCreatorHolder nngh = new PostResponseCreatorHolder();
- nngh.creator = creator;
- nngh.ranking = getRanking(properties);
+ final PostResponseCreatorHolder nngh = new PostResponseCreatorHolder(creator, getRanking(properties));
synchronized ( this.postResponseCreators ) {
int index = 0;
while ( index < this.postResponseCreators.size() &&
- nngh.ranking < this.postResponseCreators.get(index).ranking ) {
+ nngh.getRanking() < this.postResponseCreators.get(index).getRanking() ) {
index++;
}
if ( index == this.postResponseCreators.size() ) {
@@ -494,7 +509,7 @@
final Iterator<PostResponseCreatorHolder> i = this.postResponseCreators.iterator();
while ( i.hasNext() ) {
final PostResponseCreatorHolder current = i.next();
- if ( current.creator == creator ) {
+ if ( current.getCreator() == creator ) {
i.remove();
}
}
@@ -510,7 +525,7 @@
final PostResponseCreator[] localCache = new PostResponseCreator[this.postResponseCreators.size()];
int index = 0;
for(final PostResponseCreatorHolder current : this.postResponseCreators) {
- localCache[index] = current.creator;
+ localCache[index] = current.getCreator();
index++;
}
this.cachedPostResponseCreators = localCache;
@@ -522,7 +537,21 @@
}
private static final class PostResponseCreatorHolder {
- public PostResponseCreator creator;
- public int ranking;
+ private final PostResponseCreator creator;
+ private final int ranking;
+
+ public PostResponseCreatorHolder(PostResponseCreator creator, int ranking) {
+ this.creator = creator;
+ this.ranking = ranking;
+ }
+
+ public PostResponseCreator getCreator() {
+ return creator;
+ }
+
+ public int getRanking() {
+ return ranking;
+ }
+
}
}