diff --git a/modules/common/src/main/java/org/dcache/auth/attributes/DenyActivityRestriction.java b/modules/common/src/main/java/org/dcache/auth/attributes/DenyActivityRestriction.java index 5f2aea000c5..7c8910c50e2 100644 --- a/modules/common/src/main/java/org/dcache/auth/attributes/DenyActivityRestriction.java +++ b/modules/common/src/main/java/org/dcache/auth/attributes/DenyActivityRestriction.java @@ -60,7 +60,7 @@ public boolean isRestricted(Activity activity, FsPath path) { } @Override - public boolean isRestricted(Activity activity, FsPath directory, String name) { + public boolean isRestricted(Activity activity, FsPath directory, String name, boolean skipSymlinkResolution) { return denied.contains(activity); } diff --git a/modules/common/src/main/java/org/dcache/auth/attributes/MultiTargetedRestriction.java b/modules/common/src/main/java/org/dcache/auth/attributes/MultiTargetedRestriction.java index ec830e1f292..64fe0b02327 100644 --- a/modules/common/src/main/java/org/dcache/auth/attributes/MultiTargetedRestriction.java +++ b/modules/common/src/main/java/org/dcache/auth/attributes/MultiTargetedRestriction.java @@ -138,28 +138,14 @@ public boolean hasUnrestrictedChild(Activity activity, FsPath parent) { @Override public boolean isRestricted(Activity activity, FsPath path) { - for (Authorisation authorisation : authorisations) { - Function resolver = getPathResolver(); - FsPath allowedPath = resolver.apply(authorisation.getPath()); - EnumSet allowedActivity = authorisation.getActivity(); - path = resolver.apply(path); - if (allowedActivity.contains(activity) && path.hasPrefix(allowedPath)) { - return false; - } - - // As a special case, certain activities are always allowed for - // parents of an AllowedPath. - if (ALLOWED_PARENT_ACTIVITIES.contains(activity) && allowedPath.hasPrefix(path)) { - return false; - } - } - - return true; + return isRestricted(activity, path, getPathResolver()); } @Override - public boolean isRestricted(Activity activity, FsPath directory, String child) { - return isRestricted(activity, directory.child(child)); + public boolean isRestricted(Activity activity, FsPath directory, String child, + boolean skipSymlinkResolution) { + return isRestricted(activity, directory.child(child), + skipSymlinkResolution ? getIdentityResolver() : getPathResolver()); } @Override @@ -227,4 +213,23 @@ public String toString() { .map(Object::toString) .collect(Collectors.joining(", ", "MultiTargetedRestriction[", "]")); } + + private boolean isRestricted(Activity activity, FsPath path, Function resolver) { + for (Authorisation authorisation : authorisations) { + FsPath allowedPath = resolver.apply(authorisation.getPath()); + EnumSet allowedActivity = authorisation.getActivity(); + path = resolver.apply(path); + if (allowedActivity.contains(activity) && path.hasPrefix(allowedPath)) { + return false; + } + + // As a special case, certain activities are always allowed for + // parents of an AllowedPath. + if (ALLOWED_PARENT_ACTIVITIES.contains(activity) && allowedPath.hasPrefix(path)) { + return false; + } + } + + return true; + } } diff --git a/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java b/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java index cc5c1565bfc..023c32a8a89 100644 --- a/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java +++ b/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java @@ -46,24 +46,14 @@ public ImmutableSet getPrefixes() { @Override public boolean isRestricted(Activity activity, FsPath path) { - for (FsPath prefix : prefixes) { - Function resolver = getPathResolver(); - prefix = resolver.apply(prefix); - path = resolver.apply(path); - if (path.hasPrefix(prefix)) { - return false; - } - if (prefix.hasPrefix(path) && (activity == Activity.READ_METADATA - || activity == Activity.LIST)) { - return false; - } - } - return true; + return isRestricted(activity, path, getPathResolver()); } @Override - public boolean isRestricted(Activity activity, FsPath directory, String child) { - return isRestricted(activity, directory.child(child)); + public boolean isRestricted(Activity activity, FsPath directory, String child, + boolean skipSymlinkResolution) { + return isRestricted(activity, directory.child(child), + skipSymlinkResolution ? getIdentityResolver() : getPathResolver()); } @Override @@ -154,4 +144,19 @@ public String toString() { } return sb.append(']').toString(); } + + private boolean isRestricted(Activity activity, FsPath path, Function resolver) { + for (FsPath prefix : prefixes) { + prefix = resolver.apply(prefix); + path = resolver.apply(path); + if (path.hasPrefix(prefix)) { + return false; + } + if (prefix.hasPrefix(path) && (activity == Activity.READ_METADATA + || activity == Activity.LIST)) { + return false; + } + } + return true; + } } diff --git a/modules/common/src/main/java/org/dcache/auth/attributes/Restriction.java b/modules/common/src/main/java/org/dcache/auth/attributes/Restriction.java index 48c7135403a..4fabbd02815 100644 --- a/modules/common/src/main/java/org/dcache/auth/attributes/Restriction.java +++ b/modules/common/src/main/java/org/dcache/auth/attributes/Restriction.java @@ -116,7 +116,22 @@ public interface Restriction extends LoginAttribute, Serializable { * @param child The name of the target object within directory. * @return true if the user is not allowed this activity. */ - boolean isRestricted(Activity activity, FsPath directory, String child); + default boolean isRestricted(Activity activity, FsPath directory, String child) { + return isRestricted(activity, directory, child, false); + } + + /** + * An optimised version of isRestricted. A restriction must respond as if {@literal + * isRestricted(activity, new FsPath(directory).add(child));} were called, but the method may be + * able to avoid creating a new FsPath object. + * + * @param activity What the user is attempting. + * @param directory The directory containing the target + * @param child The name of the target object within directory. + * @param skipSymlinkResolution If true, do not resolve symlinks. + * @return true if the user is not allowed this activity. + */ + boolean isRestricted(Activity activity, FsPath directory, String child, boolean skipSymlinkResolution); /** * Return true iff there is a child of the supplied path whether the activity is not @@ -157,6 +172,10 @@ public interface Restriction extends LoginAttribute, Serializable { * @return returns NOP resolver. Should be overridden by implementations. */ default Function getPathResolver() { + return getIdentityResolver(); + } + + default Function getIdentityResolver() { return Function.identity(); } diff --git a/modules/common/src/main/java/org/dcache/auth/attributes/Restrictions.java b/modules/common/src/main/java/org/dcache/auth/attributes/Restrictions.java index 6d972545faf..2731e047c44 100644 --- a/modules/common/src/main/java/org/dcache/auth/attributes/Restrictions.java +++ b/modules/common/src/main/java/org/dcache/auth/attributes/Restrictions.java @@ -165,9 +165,9 @@ public boolean isRestricted(Activity activity, FsPath path) { } @Override - public boolean isRestricted(Activity activity, FsPath directory, String name) { + public boolean isRestricted(Activity activity, FsPath directory, String name, boolean skipSymlink) { for (Restriction r : restrictions) { - r.setPathResolver(getPathResolver()); + r.setPathResolver(skipSymlink? getIdentityResolver() : getPathResolver()); if (r.isRestricted(activity, directory, name)) { return true; } diff --git a/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java b/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java index 6d432b48d35..81de1d8f3f7 100644 --- a/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java +++ b/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java @@ -2223,7 +2223,7 @@ private void sendPartialReply() { @Override public void addEntry(String name, FileAttributes attrs) { if (Subjects.isRoot(_subject) - || !_restriction.isRestricted(READ_METADATA, _directory, name)) { + || !_restriction.isRestricted(READ_METADATA, _directory, name, true)) { long now = System.currentTimeMillis(); _msg.addEntry(name, attrs); if (_msg.getEntries().size() >= _directoryListLimit ||