### Eclipse Workspace Patch 1.0 #P oak-core Index: src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java (revision 1478666) +++ src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java (working copy) @@ -22,11 +22,13 @@ import java.security.Principal; import java.security.acl.Group; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -69,6 +71,9 @@ private Map repoEntries; private Map userEntries; private Map groupEntries; + + private boolean readStatusCalculated = false; + private Map readStatuses; CompiledPermissionImpl(@Nonnull Set principals, @Nonnull ImmutableTree permissionsTree, @@ -111,28 +116,31 @@ if (refresh) { buildEntries(permissionsTree); + readStatusCalculated = false; } } //------------------------------------------------< CompiledPermissions >--- - @Override - public ReadStatus getReadStatus(@Nonnull Tree tree, @Nullable PropertyState property) { - // TODO merge with readstatus - if (isReadablePath(tree, null)) { - return ReadStatus.ALLOW_ALL_REGULAR; - } - long permission = (property == null) ? Permissions.READ_NODE : Permissions.READ_PROPERTY; - Iterator it = getEntryIterator(tree, property); - while (it.hasNext()) { - PermissionEntry entry = it.next(); - if (entry.readStatus != null) { - return entry.readStatus; - } else if (entry.privilegeBits.includesRead(permission)) { - return (entry.isAllow) ? ReadStatus.ALLOW_THIS : ReadStatus.DENY_THIS; - } - } - return ReadStatus.DENY_THIS; - } + @Override + public ReadStatus getReadStatus(@Nonnull Tree tree, + @Nullable PropertyState property) { + // TODO merge with readstatus + if (isReadablePath(tree, null)) { + return ReadStatus.ALLOW_ALL_REGULAR; + } + if (!readStatusCalculated) { + buildReadStatus(Iterables. concat( + userEntries.values(), groupEntries.values())); + } + if (readStatuses != null) { + for (String path : readStatuses.keySet()) { + if (Text.isDescendantOrEqual(path, tree.getPath())) { + return readStatuses.get(path); + } + } + } + return ReadStatus.DENY_THIS; + } @Override public boolean isGranted(long permissions) { @@ -182,13 +190,201 @@ repoEntries = builder.getRepoEntries(); userEntries = builder.getUserEntries(); groupEntries = builder.getGroupEntries(); - buildReadStatus(Iterables.concat(userEntries.values(), groupEntries.values())); } } - private static void buildReadStatus(Iterable permissionEntries) { - // TODO - } + private void buildReadStatus(Iterable permissionEntries) { + this.readStatusCalculated = true; + this.readStatuses = new TreeMap( + new Comparator() { + @Override + public int compare(String o1, String o2) { + int depth1 = PathUtils.getDepth(o1); + int depth2 = PathUtils.getDepth(o2); + if (depth1 == depth2) { + return o1.compareTo(o2); + } else { + return (depth1 < depth2) ? 1 : -1; + } + } + }); + + for (PermissionEntry permissionEntry : permissionEntries) { + // if the privilegeBits doesn't contain read permission skip this + // permission entry + if (!permissionEntry.privilegeBits + .includesRead(Permissions.READ_NODE) + && !permissionEntry.privilegeBits + .includesRead(Permissions.READ_PROPERTY)) { + continue; + } + // if this path has already been calculated skip this permission + // entry + if (readStatuses.get(permissionEntry.path) != null) { + continue; + } + readStatuses.put(permissionEntry.path, + getReadStatus(permissionEntry, permissionEntries)); + } + + } + + private ReadStatus getReadStatus(PermissionEntry permissionEntry, + Iterable permissionEntries) { + ReadStatus readStatus = ReadStatus.DENY_THIS; + if (permissionEntry.privilegeBits.includesRead(Permissions.READ)) { + if (permissionEntry.isAllow) { + readStatus = getReadStatusAllowAllRegular(permissionEntry, + permissionEntries); + } else { + readStatus = getReadStatusDenyAllRegular(permissionEntry, + permissionEntries); + } + } else if (permissionEntry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + if (permissionEntry.isAllow) { + readStatus = getReadStatusAllowNodes(permissionEntry, + permissionEntries); + } else { + readStatus = ReadStatus.DENY_NODES; + } + } else if (permissionEntry.privilegeBits + .includesRead(Permissions.READ_PROPERTY)) { + if (permissionEntry.isAllow) { + readStatus = getReadStatusAllowProperties(permissionEntry, + permissionEntries); + } else { + readStatus = getReadStatusDenyProperties(permissionEntry, + permissionEntries); + } + } + + return readStatus; + } + + private ReadStatus getReadStatusAllowAllRegular( + PermissionEntry permissionEntry, + Iterable permissionEntries) { + ReadStatus readStatus = ReadStatus.ALLOW_ALL_REGULAR; + Iterator it = new ReadStatusEntryIterator( + permissionEntries, permissionEntry, readStatus); + while (it.hasNext()) { + PermissionEntry entry = it.next(); + if (!entry.isAllow) { + readStatus = ReadStatus.ALLOW_THIS; + break; + } + } + return readStatus; + } + + private ReadStatus getReadStatusDenyAllRegular( + PermissionEntry permissionEntry, + Iterable permissionEntries) { + ReadStatus readStatus = ReadStatus.DENY_ALL_REGULAR; + Iterator it = new ReadStatusEntryIterator( + permissionEntries, permissionEntry, readStatus); + while (it.hasNext()) { + PermissionEntry entry = it.next(); + if (entry.isAllow) { + readStatus = ReadStatus.DENY_THIS; + break; + } + } + return readStatus; + } + + private ReadStatus getReadStatusAllowNodes(PermissionEntry permissionEntry, + Iterable permissionEntries) { + ReadStatus readStatus = ReadStatus.ALLOW_NODES; + Iterator it = new ReadStatusEntryIterator( + permissionEntries, permissionEntry, readStatus); + while (it.hasNext()) { + PermissionEntry entry = it.next(); + if (PathUtils.getDepth(entry.path) > PathUtils + .getDepth(permissionEntry.path)) { + if (!entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + readStatus = ReadStatus.ALLOW_THIS; + break; + } + } else { + if (!entry.isAllow) { + break; + } + if (entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_PROPERTY)) { + readStatus = ReadStatus.ALLOW_ALL_REGULAR; + break; + } + } + } + return readStatus; + } + + private ReadStatus getReadStatusAllowProperties( + PermissionEntry permissionEntry, + Iterable permissionEntries) { + ReadStatus readStatus = ReadStatus.ALLOW_PROPERTIES; + Iterator it = new ReadStatusEntryIterator( + permissionEntries, permissionEntry, readStatus); + while (it.hasNext()) { + PermissionEntry entry = it.next(); + if (PathUtils.getDepth(entry.path) > PathUtils + .getDepth(permissionEntry.path)) { + if (!entry.isAllow) { + break; + } + } else { + if (!entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + break; + } + if (entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + readStatus = ReadStatus.ALLOW_ALL_REGULAR; + break; + } + } + } + return readStatus; + } + + private ReadStatus getReadStatusDenyProperties( + PermissionEntry permissionEntry, + Iterable permissionEntries) { + ReadStatus readStatus = ReadStatus.DENY_THIS; + Iterator it = new ReadStatusEntryIterator( + permissionEntries, permissionEntry, readStatus); + while (it.hasNext()) { + PermissionEntry entry = it.next(); + if (PathUtils.getDepth(entry.path) > PathUtils + .getDepth(permissionEntry.path)) { + if (!entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + break; + } + } else { + if (!entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + break; + } + if (entry.isAllow + && entry.privilegeBits + .includesRead(Permissions.READ_NODE)) { + readStatus = ReadStatus.ALLOW_NODES; + break; + } + } + } + return readStatus; + } private boolean hasPermissions(@Nonnull Iterator entries, long permissions, @Nullable Tree tree, @Nullable String path) { @@ -370,7 +566,6 @@ private final String path; private final RestrictionPattern restriction; - private ReadStatus readStatus; private PermissionEntry next; private PermissionEntry(String accessControlledPath, Tree entryTree, RestrictionProvider restrictionsProvider) { @@ -547,4 +742,71 @@ return input.getValue(); } } + + private static class ReadStatusEntryIterator implements + Iterator { + private final Iterator it; + + private ReadStatusEntryIterator( + @Nonnull Iterable permissionEntries, + PermissionEntry permissionEntry, ReadStatus readStatus) { + it = Iterators.filter(permissionEntries.iterator(), + new ReadStatusEntryPredicate(permissionEntry, readStatus)); + } + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public PermissionEntry next() { + return it.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private static class ReadStatusEntryPredicate implements + Predicate { + + private final PermissionEntry permissionEntry; + private final ReadStatus readStatus; + + private ReadStatusEntryPredicate( + @Nonnull PermissionEntry permissionEntry, ReadStatus readStatus) { + this.permissionEntry = permissionEntry; + this.readStatus = readStatus; + } + + @Override + public boolean apply(PermissionEntry entry) { + + if (entry == null) { + return false; + } + + if (permissionEntry.path.equals(entry.path)) { + return false; + } + + if (readStatus.equals(ReadStatus.ALLOW_ALL_REGULAR) + || readStatus.equals(ReadStatus.DENY_ALL_REGULAR)) { + if (Text.isDescendant(permissionEntry.path, entry.path)) { + return entry.restriction.matches(permissionEntry.path); + } + } else { + if (Text.isDescendant(permissionEntry.path, entry.path) + || Text.isDescendant(entry.path, permissionEntry.path)) { + return entry.restriction.matches(permissionEntry.path); + } + } + return false; + } + + } + }