Good evening,

I still write for the problem I found in the management of the rep:glob restriction set to empty string.
I made more tests and I also used the ReadTest class of OAK.

In effect the restriction rep:glob set to empty string works on nodes.
If I apply the restriction on path//my_folder /by adding the permission of READ, both with the call to Session.getNode ("/ my_folder") that with the call to
Session.getRootNode (). getNodes () the system can access to the path.

The problem is when I run the query "/select F. * from [nt: folder] as F/". The query result is empty. It seems to me weird because if I access to the node I should also have access to the folder that the node represents.

Analyzing the OAK code in version 1.4.0 I have found the GlobPattern class.
In the case of restriction rep:glob set to empty string that class always uses the PathPattern class and always makes the call /path.equals(toMatch)/.
This call works when the path//my_folder/ is used in nodes and it works
even when the path is evaluated in the query "/select * from F. [nt: folder] as F/". The problem is the following: during query execution is also rightly evaluated
the jcr: primaryType node. In this case, the primary type is nt: folder.
It is called again the following method of GlobPattern class:

/*@Override*//*
*//* public boolean matches(@Nonnull Tree tree, @Nullable PropertyState property) {*//* *//* String itemPath = (property == null) ? tree.getPath() : PathUtils.concat(tree.getPath(), property.getName());*//*
*//*        return matches(itemPath);*//*
*//*    }

*/and then the matches method of the class PathPattern:

/*@Override*//*
*//*        boolean matches(@Nonnull String toMatch) {*//*
*//*            if (patternStr.isEmpty()) {*//*
*//*                return path.equals(toMatch);*//*
*//*            } else {*//*
*//* // no wildcard contained in restriction: use path defined*//*
*//*                // by path + restriction to calculate the match*//*
*//* return Text.isDescendantOrEqual(patternStr, toMatch);*//*
*//*            }*//*
*//*        }

*/This call can not works because in the case in which /patternStr.isEmpty()/ is called the method /path.equals(toMatch)/.

But the path class attribute is set to /*/my_folder*/ and the toMatch variable is set to /*/my_folder/jrc: primaryType*/. It 's clear that the call to the equals method will return false.

To solve the problem I changed the class OAK GlobPattern as follows, and now the query "/select * from F. [nt: folder] as F/" correctly returns in its results the folder//my_folder/.

/*
 * 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.jackrabbit.oak.security.authorization.restriction;

import static com.google.common.base.Preconditions.checkNotNull;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.security.AccessControlException;

import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
import org.apache.jackrabbit.util.Text;

import com.google.common.base.Objects;

/**
 * {@code GlobPattern} defines a simplistic pattern matching. It consists
* of a mandatory (leading) path and an optional "glob" that may contain one or
 * more wildcard characters ("{@code *}") according to the glob matching
* defined by {@link javax.jcr.Node#getNodes(String[])}. In contrast to that
 * method the {@code GlobPattern} operates on path (not only names).
 * <p>
 *
 * <p>
 * Please note the following special cases:
 * <pre>
 * NodePath     |   Restriction   |   Matches
* -----------------------------------------------------------------------------
 * /foo         |   null          |   matches /foo and all children of /foo
 * /foo         |   ""            |   matches /foo only
 * </pre>
 * </p>
 *
 * <p>
 * Examples without wildcard char:
 * <pre>
 * NodePath = "/foo"
 * Restriction   |   Matches
* -----------------------------------------------------------------------------
 * /cat          |   the node /foo/cat and all it's children
 * /cat/         |   the descendants of the node /foo/cat
 * cat           |   the node /foocat and all it's children
 * cat/          |   all descendants of the node /foocat
 * </pre>
 * </p>
 *
 * <p>
 * Examples including wildcard char:
 * <pre>
 * NodePath = "/foo"
 * Restriction   |   Matches
* -----------------------------------------------------------------------------
 * &#42;         |   foo, all siblings of foo and their descendants
 * /&#42;cat     |   all children of /foo whose path ends with "cat"
 * /&#42;/cat    |   all non-direct descendants of /foo named "cat"
* /cat&#42; | all descendant path of /foo that have the direct foo-descendant segment starting with "cat" * &#42;cat | all siblings and descendants of foo that have a name ending with cat * &#42;/cat | all descendants of /foo and foo's siblings that have a name segment "cat"
 * cat/&#42;     |   all descendants of '/foocat'
 * /cat/&#42;    |   all descendants of '/foo/cat'
* &#42;cat/&#42; | all siblings and descendants of foo that have an intermediate segment ending with 'cat' * /&#42;cat/&#42; | all descendants of /foo that have an intermediate segment ending with 'cat'
 * </pre>
 * </p>
 */
final class GlobPattern implements RestrictionPattern {

    private static final char WILDCARD_CHAR = '*';
    private static final int MAX_WILDCARD = 20;

    private final String path;
    private final String restriction;

*  private Pattern pattern;*

*private GlobPattern(@Nonnull String path, @Nonnull String restriction)  {**
**        this.path = checkNotNull(path);**
**        this.restriction = restriction;**
**    }*

static GlobPattern create(@Nonnull String nodePath, @Nonnull String restrictions) {
        return new GlobPattern(nodePath, restrictions);
    }

static void validate(@Nonnull String restriction) throws AccessControlException {
        int cnt = 0;
        for (int i = 0; i < restriction.length(); i++) {
            if (WILDCARD_CHAR == restriction.charAt(i)) {
                cnt++;
            }
            if (cnt > MAX_WILDCARD) {
throw new AccessControlException("Number of wildcards in rep:glob exceeds allowed complexity.");
            }
        }
    }

*private Pattern getPattern(@Nullable PropertyState property){**
**        if (!restriction.isEmpty()) {**
**            StringBuilder b = new StringBuilder(path);**
**            b.append(restriction);**
**
**            int lastPos = restriction.lastIndexOf(WILDCARD_CHAR);**
**            if (lastPos >= 0) {**
**                String end;**
**                if (lastPos != restriction.length()-1) {**
**                    end = restriction.substring(lastPos + 1);**
**                } else {**
**                    end = null;**
**                }**
**                pattern = new WildcardPattern(b.toString(), end);**
**            } else {**
**                pattern = new PathPattern(b.toString());**
**            }**
**        } else {**
**            if(property == null){**
**                pattern = new PathPattern(restriction);**
**            }**
**            else{**
** pattern = new PathPropertyPattern(restriction, property.getName());**
**            }**
**        }**
**        return pattern;**
**    }*

//-------------------------------------------------< RestrictionPattern >---
    @Override
public boolean matches(@Nonnull Tree tree, @Nullable PropertyState property) {
*pattern = getPattern(property);*
String itemPath = (property == null) ? tree.getPath() : PathUtils.concat(tree.getPath(), property.getName());
        return matches(itemPath);
    }

    @Override
    public boolean matches(@Nonnull String path) {
        return pattern.matches(path);
    }

    @Override
    public boolean matches() {
        // repository level permissions never match any glob pattern
        return false;
    }

//-------------------------------------------------------------< Object >---
    /**
     * @see Object#hashCode()
     */
    @Override
    public int hashCode() {
        return Objects.hashCode(path, restriction);
    }

    /**
     * @see Object#toString()
     */
    @Override
    public String toString() {
        return path + " : " + restriction;
    }

    /**
     * @see Object#equals(Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof GlobPattern) {
            GlobPattern other = (GlobPattern) obj;
return path.equals(other.path) && restriction.equals(other.restriction);
        }
        return false;
    }

//------------------------------------------------------< inner classes >---
    /**
     * Base for PathPattern and WildcardPattern
     */
    private abstract class Pattern {
        abstract boolean matches(@Nonnull String toMatch);
    }

    /**
* Path pattern: The restriction is missing or doesn't contain any wildcard character.
     */
    private final class PathPattern extends Pattern {

        private final String patternStr;

        private PathPattern(@Nonnull String patternStr) {
            this.patternStr = patternStr;
        }

        @Override
        boolean matches(@Nonnull String toMatch) {
            if (patternStr.isEmpty()) {
                return path.equals(toMatch);
            } else {
                // no wildcard contained in restriction: use path defined
                // by path + restriction to calculate the match
                return Text.isDescendantOrEqual(patternStr, toMatch);
            }
        }
    }

*/****
** * Path pattern: The restriction is missing or doesn't contain any wildcard character and manage PropertyState case.**
**     */**
**    private final class PathPropertyPattern extends Pattern {**
**
**        private final String patternStr;**
****
**        private final String propertyName;**
**
** private PathPropertyPattern(@Nonnull String patternStr, @Nonnull String propertyName) {**
**            this.patternStr = patternStr;**
**            this.propertyName = propertyName;**
**        }**
**
**        @Override**
**        boolean matches(@Nonnull String toMatch) {**
**            if (patternStr.isEmpty()) {**
**                //return path.equals(toMatch);**
**                return evalutatePathAndProperty().equals(toMatch);**
**            } else {**
** // no wildcard contained in restriction: use path defined**
**                // by path + restriction to calculate the match**
**                return Text.isDescendantOrEqual(patternStr, toMatch);**
**            }**
**        }**
****
**        private String evalutatePathAndProperty(){**
**            final StringBuilder builder = new StringBuilder();**
**            builder.append(path);**
**            if(!path.endsWith("/")){**
**                builder.append("/");**
**            }**
**            builder.append(propertyName);**
**            return builder.toString();**
**        }**
**    }*

    /**
* Wildcard pattern: The specified restriction contains one or more wildcard character(s).
     */
    private final class WildcardPattern extends Pattern {

        private final String patternEnd;
        private final char[] patternChars;

private WildcardPattern(@Nonnull String patternStr, @Nullable String patternEnd) {
            patternChars = patternStr.toCharArray();
            this.patternEnd = patternEnd;
        }

        @Override
        boolean matches(@Nonnull String toMatch) {
            if (patternEnd != null && !toMatch.endsWith(patternEnd)) {
// shortcut: verify if end of pattern matches end of toMatch
                return false;
            }
char[] tm = (toMatch.endsWith("/")) ? toMatch.substring(0, toMatch.length()-1).toCharArray() : toMatch.toCharArray(); // shortcut didn't reveal mismatch -> need to process the internal match method.
            return matches(patternChars, 0, tm, 0, MAX_WILDCARD);
        }

        /**
         *
         * @param pattern The pattern
         * @param pOff
         * @param s
         * @param sOff
         * @return {@code true} if matches, {@code false} otherwise
         */
        private boolean matches(char[] pattern, int pOff,
                                char[] s, int sOff, int cnt) {

            if (cnt <= 0) {
throw new IllegalArgumentException("Illegal glob pattern " + GlobPattern.this);
            }

            int pLength = pattern.length;
            int sLength = s.length;

            while (true) {
// end of pattern reached: matches only if sOff points at the end
                // of the string to match.
                if (pOff >= pLength) {
                    return sOff >= sLength;
                }

// the end of the string to match has been reached but pattern
                // doesn't have '*' at patternIndex -> no match
                if (sOff >= sLength && pattern[pOff] != WILDCARD_CHAR) {
                    return false;
                }

                // the next character of the pattern is '*'
// -> recursively test if the rest of the specified string matches
                if (pattern[pOff] == WILDCARD_CHAR) {
                    if (++pOff >= pLength) {
                        return true;
                    }

                    cnt--;
                    while (true) {
                        if (matches(pattern, pOff, s, sOff, cnt)) {
                            return true;
                        }
                        if (sOff >= sLength) {
                            return false;
                        }
                        sOff++;
                    }
                }

// not yet reached end of patter nor string and not wildcard character. // the 2 strings don't match in case the characters at the current
                // position are not the same.
                if (pOff < pLength && sOff < sLength) {
                    if (pattern[pOff] != s[sOff]) {
                        return false;
                    }
                }
                pOff++;
                sOff++;
            }
        }
    }
}*

*My changes make sense in your opinion or behavior of which I spoke about is wanted?

Thanks in advance.

Gianluca Soffredini
Project Manager
Metaframe SPS S.r.l.
Via Toniolo, 13
30030 Vigonovo(VE)
mobile: +39 3342235291
email: [email protected] <mailto:[email protected]>
SKYPE ID: gianlucas72
Logo Metaframe SPS S.r.l.

Reply via email to