incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cziege...@apache.org
Subject svn commit: r1407568 - in /sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers: JsonObjectCreator.java JsonRendererServlet.java JsonResourceWriter.java ResourceTraversor.java
Date Fri, 09 Nov 2012 18:10:51 GMT
Author: cziegeler
Date: Fri Nov  9 18:10:50 2012
New Revision: 1407568

URL: http://svn.apache.org/viewvc?rev=1407568&view=rev
Log:
SLING-2320 :  Current DOS-prevention for infinity.json can prevent enumeration of children


Added:
    sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
  (with props)
Removed:
    sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonResourceWriter.java
Modified:
    sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererServlet.java
    sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/ResourceTraversor.java

Added: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java?rev=1407568&view=auto
==============================================================================
--- sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
(added)
+++ sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
Fri Nov  9 18:10:50 2012
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.servlets.get.impl.helpers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+
+/**
+ * Creates a JSONObject from a resource
+ *
+ */
+public abstract class JsonObjectCreator {
+
+    /**
+     * Dump given resource in JSON, optionally recursing into its objects
+     * @param tidy if <code>true</code> the json dump is nicely formatted
+     */
+    public static JSONObject create(final Resource resource, final int maxRecursionLevels)
+            throws JSONException {
+        return create(resource, 0, maxRecursionLevels);
+    }
+
+
+    /** Dump given resource in JSON, optionally recursing into its objects */
+    private static JSONObject create(final Resource resource,
+            final int currentRecursionLevel,
+            final int maxRecursionLevels)
+    throws JSONException {
+        final ValueMap valueMap = resource.adaptTo(ValueMap.class);
+
+        @SuppressWarnings("unchecked")
+        final Map propertyMap = (valueMap != null)
+                ? valueMap
+                : resource.adaptTo(Map.class);
+
+        final JSONObject obj = new JSONObject();
+
+        if (propertyMap == null) {
+
+            // no map available, try string
+            final String value = resource.adaptTo(String.class);
+            if (value != null) {
+
+                // single value property or just plain String resource or...
+                obj.put(ResourceUtil.getName(resource), value);
+
+            } else {
+
+                // Try multi-value "property"
+                final String[] values = resource.adaptTo(String[].class);
+                if (values != null) {
+                    obj.put(ResourceUtil.getName(resource), new JSONArray(Arrays.asList(values)));
+                }
+
+            }
+
+        } else {
+
+            @SuppressWarnings("unchecked")
+            final Iterator<Map.Entry> props = propertyMap.entrySet().iterator();
+
+            // the node's actual properties
+            while (props.hasNext()) {
+                @SuppressWarnings("unchecked")
+                final Map.Entry prop = props.next();
+
+                createProperty(obj, valueMap, prop.getKey().toString(),
+                    prop.getValue());
+            }
+        }
+
+        // the child nodes
+        if (recursionLevelActive(currentRecursionLevel, maxRecursionLevels)) {
+            final Iterator<Resource> children = ResourceUtil.listChildren(resource);
+            while (children.hasNext()) {
+                final Resource n = children.next();
+                createSingleResource(n, obj, currentRecursionLevel,
+                    maxRecursionLevels);
+            }
+        }
+
+        return obj;
+    }
+
+    /** Used to format date values */
+    private static final String ECMA_DATE_FORMAT = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z";
+
+    /** Used to format date values */
+    private static final Locale DATE_FORMAT_LOCALE = Locale.US;
+
+    private static final DateFormat CALENDAR_FORMAT = new SimpleDateFormat(ECMA_DATE_FORMAT,
DATE_FORMAT_LOCALE);
+
+    private static synchronized String format(final Calendar date) {
+        return CALENDAR_FORMAT.format(date.getTime());
+    }
+
+    /** Dump only a value in the correct format */
+    private static Object getValue(final Object value) {
+        if ( value instanceof InputStream ) {
+            // input stream is already handled
+            return 0;
+        } else if ( value instanceof Calendar ) {
+            return format((Calendar)value);
+        } else if ( value instanceof Boolean ) {
+            return value;
+        } else if ( value instanceof Long ) {
+            return value;
+        } else if ( value instanceof Integer ) {
+            return value;
+        } else if ( value instanceof Double ) {
+            return value;
+        } else {
+            return value.toString();
+        }
+    }
+
+    /** Dump a single node */
+    private static void createSingleResource(final Resource n, final JSONObject parent,
+            final int currentRecursionLevel, final int maxRecursionLevels)
+            throws JSONException {
+        if (recursionLevelActive(currentRecursionLevel, maxRecursionLevels)) {
+            parent.put(ResourceUtil.getName(n), create(n, currentRecursionLevel + 1, maxRecursionLevels));
+        }
+    }
+
+    /** true if the current recursion level is active */
+    private static boolean recursionLevelActive(final int currentRecursionLevel,
+            final int maxRecursionLevels) {
+        return maxRecursionLevels < 0
+            || currentRecursionLevel < maxRecursionLevels;
+    }
+
+    /**
+     * Write a single property
+     */
+    private static void createProperty(final JSONObject obj,
+                                 final ValueMap valueMap,
+                                 final String key,
+                                 final Object value)
+    throws JSONException {
+        Object[] values = null;
+        if (value.getClass().isArray()) {
+            values = (Object[])value;
+            // write out empty array
+            if ( values.length == 0 ) {
+                obj.put(key, new JSONArray());
+                return;
+            }
+        }
+
+        // special handling for binaries: we dump the length and not the data!
+        if (value instanceof InputStream
+            || (values != null && values[0] instanceof InputStream)) {
+            // TODO for now we mark binary properties with an initial colon in
+            // their name
+            // (colon is not allowed as a JCR property name)
+            // in the name, and the value should be the size of the binary data
+            if (values == null) {
+                obj.put(":" + key, getLength(valueMap, -1, key, (InputStream)value));
+            } else {
+                final JSONArray result = new JSONArray();
+                for (int i = 0; i < values.length; i++) {
+                    result.put(getLength(valueMap, i, key, (InputStream)values[i]));
+                }
+                obj.put(":" + key, result);
+            }
+            return;
+        }
+
+        if (!value.getClass().isArray()) {
+            obj.put(key, getValue(value));
+        } else {
+            final JSONArray result = new JSONArray();
+            for (Object v : values) {
+                result.put(getValue(v));
+            }
+            obj.put(key, result);
+        }
+    }
+
+    private static long getLength(final ValueMap    valueMap,
+                           final int         index,
+                           final String      key,
+                           final InputStream stream) {
+        try {
+            stream.close();
+        } catch (IOException ignore) {}
+        long length = -1;
+        if ( valueMap != null ) {
+            if ( index == -1 ) {
+                length = valueMap.get(key, length);
+            } else {
+                Long[] lengths = valueMap.get(key, Long[].class);
+                if ( lengths != null && lengths.length > index ) {
+                    length = lengths[index];
+                }
+            }
+        }
+        return length;
+    }
+}

Propchange: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonObjectCreator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererServlet.java?rev=1407568&r1=1407567&r2=1407568&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererServlet.java
(original)
+++ sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererServlet.java
Fri Nov  9 18:10:50 2012
@@ -18,14 +18,12 @@ package org.apache.sling.servlets.get.im
 
 import java.io.IOException;
 
-import javax.jcr.RepositoryException;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.sling.api.SlingException;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
-import org.apache.sling.api.request.RecursionTooDeepException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceNotFoundException;
 import org.apache.sling.api.resource.ResourceUtil;
@@ -98,18 +96,16 @@ public class JsonRendererServlet extends
 
         // We check the tree to see if the nr of nodes isn't bigger than the allowed nr.
         boolean allowDump = true;
-        long allowedLevel = 0;
-        boolean tidy = isTidy(req);
+        int allowedLevel = 0;
+        final boolean tidy = isTidy(req);
         ResourceTraversor traversor = null;
         try {
             traversor = new ResourceTraversor(maxRecursionLevels, maximumResults, r, tidy);
-			traversor.collectResources();
-        } catch (RecursionTooDeepException e) {
-            allowDump = false;
-            allowedLevel = Integer.parseInt(e.getMessage()); // this is to avoid depending
on a SNAPSHOT version of the SLing API.
-        } catch (RepositoryException e) {
-            reportException(e);
-        } catch (JSONException e) {
+            allowedLevel = traversor.collectResources();
+            if ( allowedLevel != -1 ) {
+			    allowDump = false;
+            }
+        } catch (final JSONException e) {
             reportException(e);
         }
         try {

Modified: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/ResourceTraversor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/ResourceTraversor.java?rev=1407568&r1=1407567&r2=1407568&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/ResourceTraversor.java
(original)
+++ sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/helpers/ResourceTraversor.java
Fri Nov  9 18:10:50 2012
@@ -17,90 +17,87 @@
 
 package org.apache.sling.servlets.get.impl.helpers;
 
+import java.util.Iterator;
+import java.util.LinkedList;
+
 import org.apache.sling.api.request.RecursionTooDeepException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.commons.json.JSONException;
 import org.apache.sling.commons.json.JSONObject;
 
-import java.io.StringWriter;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-import javax.jcr.RepositoryException;
-
 public class ResourceTraversor {
 
+    public static final class Entry {
+        public final Resource resource;
+        public final JSONObject json;
+
+        public Entry(final Resource r, final JSONObject o) {
+            this.resource = r;
+            this.json = o;
+        }
+    }
+
     private long count;
 
     private long maxResources;
 
-    private int maxRecursionLevels;
+    private final int maxRecursionLevels;
 
-    private JSONObject startObject;
+    private final JSONObject startObject;
 
-    private String startingPath;
+    private LinkedList<Entry> currentQueue;
 
-    private LinkedList<Resource> currentQueue;
+    private LinkedList<Entry> nextQueue;
 
-    private LinkedList<Resource> nextQueue;
+    private final Resource startResource;
 
-    private Resource startResource;
-
-    private JsonResourceWriter jsResourceWriter;
-
-    private boolean tidy;
-
-    public ResourceTraversor(int levels, long maxNodes, Resource resource, boolean tidy)
throws RepositoryException,
-            JSONException {
-        this.setMaxNodes(maxNodes);
+    public ResourceTraversor(final int levels, final long maxResources, final Resource resource,
final boolean tidy)
+    throws JSONException {
+        this.maxResources = maxResources;
         this.maxRecursionLevels = levels;
         this.startResource = resource;
-        this.tidy = tidy;
-        startingPath = resource.getPath();
-        jsResourceWriter = new JsonResourceWriter(null);
-        currentQueue = new LinkedList<Resource>();
-        nextQueue = new LinkedList<Resource>();
-        startObject = adapt(resource);
+        currentQueue = new LinkedList<Entry>();
+        nextQueue = new LinkedList<Entry>();
+        this.startObject = this.adapt(resource);
     }
 
     /**
      * Recursive descent from startResource, collecting JSONObjects into
      * startObject. Throws a RecursionTooDeepException if the maximum number of
-     * nodes is reached on a "deep" traversal (where "deep" === level greateer
+     * nodes is reached on a "deep" traversal (where "deep" === level greater
      * than 1).
      *
-     * @throws RepositoryException
-     * @throws RecursionTooDeepException When the resource has more child nodes
-     *             then allowed.
+     * @return -1 if everything went fine, a positive valuew when the resource
+     *            has more child nodes then allowed.
      * @throws JSONException
      */
-    public void collectResources() throws RepositoryException, RecursionTooDeepException,
JSONException {
-        collectChildren(startResource, 0);
+    public int collectResources() throws RecursionTooDeepException, JSONException {
+        return collectChildren(startResource, this.startObject, 0);
     }
 
     /**
      * @param resource
      * @param currentLevel
-     * @throws RecursionTooDeepException
      * @throws JSONException
-     * @throws RepositoryException
      */
-    private void collectChildren(Resource resource, int currentLevel) throws RecursionTooDeepException,
JSONException,
-            RepositoryException {
+    private int collectChildren(final Resource resource,
+            final JSONObject jsonObj,
+            int currentLevel)
+    throws JSONException {
 
         if (maxRecursionLevels == -1 || currentLevel < maxRecursionLevels) {
             final Iterator<Resource> children = ResourceUtil.listChildren(resource);
             while (children.hasNext()) {
                 count++;
-                Resource res = children.next();
+                final Resource res = children.next();
                 // SLING-2320: always allow enumeration of one's children;
                 // DOS-limitation is for deeper traversals.
                 if (count > maxResources && maxRecursionLevels != 1) {
-                    throw new RecursionTooDeepException(String.valueOf(currentLevel));
+                    return currentLevel;
                 }
-                collectResource(res, currentLevel);
-                nextQueue.addLast(res);
+                final JSONObject json = collectResource(res, jsonObj);
+                nextQueue.addLast(new Entry(res, json));
             }
         }
 
@@ -108,11 +105,15 @@ public class ResourceTraversor {
             if (currentQueue.isEmpty()) {
                 currentLevel++;
                 currentQueue = nextQueue;
-                nextQueue = new LinkedList<Resource>();
+                nextQueue = new LinkedList<Entry>();
+            }
+            final Entry nextResource = currentQueue.removeFirst();
+            final int maxLevel = collectChildren(nextResource.resource, nextResource.json,
currentLevel);
+            if ( maxLevel != -1 ) {
+                return maxLevel;
             }
-            Resource nextResource = currentQueue.removeFirst();
-            collectChildren(nextResource, currentLevel);
         }
+        return -1;
     }
 
     /**
@@ -120,15 +121,13 @@ public class ResourceTraversor {
      *
      * @param resource The resource to add
      * @param level The level where this resource is located.
-     * @throws RepositoryException
      * @throws JSONException
      */
-    protected void collectResource(Resource resource, int level) throws RepositoryException,
JSONException {
-
-        if (!resource.getPath().equals(startingPath)) {
-            JSONObject o = adapt(resource);
-            getParentJSONObject(resource, level).put(ResourceUtil.getName(resource), o);
-        }
+    private JSONObject collectResource(Resource resource, final JSONObject parent)
+    throws JSONException {
+        final JSONObject o = adapt(resource);
+        parent.put(ResourceUtil.getName(resource), o);
+        return o;
     }
 
     /**
@@ -138,66 +137,18 @@ public class ResourceTraversor {
      * @return The JSON representation of the Resource
      * @throws JSONException
      */
-    private JSONObject adapt(Resource resource) throws JSONException {
-        // TODO Find a better way to adapt a Resource to a JSONObject.
-        StringWriter writer = new StringWriter();
-        jsResourceWriter.dump(resource, writer, 0, tidy);
-        return new JSONObject(writer.getBuffer().toString());
+    private JSONObject adapt(final Resource resource) throws JSONException {
+        return JsonObjectCreator.create(resource, 0);
     }
 
     /**
-     * Get the JSON Object where this resource should be added in.
-     *
-     * @param resource
-     * @param level
-     * @return
-     * @throws RepositoryException
-     * @throws JSONException
-     */
-    private JSONObject getParentJSONObject(Resource resource, int level) throws RepositoryException,
JSONException {
-        String path = resource.getPath();
-        // The root node.
-        if (path.equals(startingPath)) {
-            return startObject;
-        }
-
-        // Some level deeper
-        String pathDiff = path.substring(startingPath.length());
-        String[] names = pathDiff.split("/");
-        JSONObject o = startObject;
-        for (String name : names) {
-            try {
-                o = o.getJSONObject(name);
-            } catch (JSONException e) {
-            }
-        }
-        return o;
-
-    }
-
-    /**
-     * @return The number of nodes this visitor found.
+     * @return The number of resources this visitor found.
      */
     public long getCount() {
         return count;
     }
 
-    /**
-     * @param maxNodes the maxNodes to set
-     */
-    public void setMaxNodes(long maxNodes) {
-        this.maxResources = maxNodes;
-    }
-
-    /**
-     * @return the maxNodes
-     */
-    public long getMaxNodes() {
-        return maxResources;
-    }
-
     public JSONObject getJSONObject() {
         return startObject;
     }
-
 }



Mime
View raw message