While implementing custom metadata based permission provider, you can follow this article to implement it. One issue I faced during this implementation that the version access was not working. To fix this modify below methods:
@Override
public TreePermission getTreePermission(Tree tree, TreePermission parentPermission) {
if (PermissionHelpers.isDamPath(tree) || PermissionHelpers.isDamAncestorPath(tree)) {
if (PermissionHelpers.findAncestorAsset(tree) != null) {
return PermissionHelpers.isAncestorAssetOwner(tree, principalNames) ?
TreePermission.ALL : parentPermission;
} else {
return new EmptyAssetMetadataTreePermission(tree, TreeType.DEFAULT, this);
}
} else if (tree.getPath().startsWith("/" + JcrConstants.JCR_SYSTEM)) {
// This condition added to allow version to path access
// This is just an example code, optimize this condition before you use
return TreePermission.ALL;
}
return TreePermission.NO_RECOURSE;
}
Another method:
@Override
public boolean isGranted(Tree tree, PropertyState property, long permissions) {
TreeType type = treeTypeProvider.getType(tree);
switch (type) {
case HIDDEN:
return true;
case VERSION:
Tree evalTree = getEvaluationTree(tree);
if (evalTree == null) {
return false;
}
if (evalTree.exists()) {
return internalIsGranted(evalTree, property, permissions);
} else {
return false;
}
case INTERNAL:
return false;
default:
return internalIsGranted(tree, property, permissions);
}
}
Core logic to test metadata conditions to meet business requirement is put in the below private method which is called in the above isGranted method.
private boolean internalIsGranted(@NotNull Tree tree, @Nullable PropertyState property,
long permissions) {
boolean answer = false;
if (PermissionHelpers.isAncestorAssetOwner(tree, principalNames)) {
answer = true;
}
if (property != null) {
LOG.debug("isGranted: {}@{} ({}) = {}", tree.getPath(), property.getName(),
permissions, answer);
} else {
LOG.debug("isGranted: {} ({}) = {}", tree.getPath(), permissions, answer);
}
return answer;
}
PermissionHelpers here is a general utility class like below:
package com.myorg.vivek.core.utils; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.stream.Collectors; | |
import java.util.stream.StreamSupport; | |
import org.apache.commons.collections4.CollectionUtils; | |
import org.apache.jackrabbit.oak.api.Tree; | |
import org.apache.jackrabbit.oak.api.Type; | |
import org.apache.jackrabbit.oak.commons.PathUtils; | |
import org.apache.jackrabbit.oak.plugins.tree.TreeLocation; | |
import com.day.cq.commons.jcr.JcrConstants; | |
import com.day.cq.dam.api.DamConstants; | |
import com.myorg.vivek.core.constants.AppConstants; | |
/** | |
* Utility class for permission related operations | |
*/ | |
public class PermissionHelpers { | |
private PermissionHelpers() { | |
// private constructor to hide the implicit public one | |
} | |
public static Tree findAncestorAsset(Tree tree) { | |
if (null == tree) { | |
return null; | |
} | |
if (isAsset(tree)) { | |
return tree; | |
} else if (tree.getPath().contains(JcrConstants.JCR_CONTENT)) { | |
while (!tree.isRoot()) { | |
tree = tree.getParent(); | |
if (isAsset(tree)) { | |
return tree; | |
} | |
} | |
} | |
return null; | |
} | |
public static boolean isAsset(Tree tree) { | |
if (null != tree && tree.hasProperty(JcrConstants.JCR_PRIMARYTYPE)) { | |
return "dam:Asset".equals(tree.getProperty(JcrConstants.JCR_PRIMARYTYPE) | |
.getValue(Type.STRING)); | |
} else { | |
return false; | |
} | |
} | |
public static boolean isDamAncestorPath(final Tree tree) { | |
return tree != null && DamConstants.MOUNTPOINT_ASSETS.startsWith(tree.getPath()); | |
} | |
public static boolean isAncestorAssetOwner(Tree tree, Set<String> principalNames) { | |
if (tree == null) { | |
return false; | |
} | |
Tree asset = PermissionHelpers.findAncestorAsset(tree); | |
if (asset == null) { | |
return false; | |
} | |
String assetPath = asset.getPath(); | |
if (isProtectedPath(assetPath)) { | |
// custom logic to return true based on business logic. | |
String isPrivate = getAssetMetadata(asset, "isPrivate").get(0); | |
if ("true".equals(isPrivate)) { | |
String[] allowedPersonsArray = | |
getAssetMetadata(asset, "allowedPersons").toArray(String[]::new); | |
return (CollectionUtils.containsAny(principalNames, allowedPersonsArray)); | |
} | |
} | |
// no access given for paths that are not meant to be handled by this | |
return false; | |
} | |
private static List<String> getAssetMetadata(Tree asset, String metadataPropertyName) { | |
if (asset.hasChild(JcrConstants.JCR_CONTENT)) { | |
Tree assetContent = asset.getChild(JcrConstants.JCR_CONTENT); | |
if (assetContent.hasChild(DamConstants.METADATA_FOLDER)) { | |
Tree assetMetadata = assetContent.getChild(DamConstants.METADATA_FOLDER); | |
if (assetMetadata.getProperty(metadataPropertyName) != null) { | |
Type type = assetMetadata.getProperty(metadataPropertyName).getType(); | |
if (type.equals(Type.STRINGS)) { | |
return StreamSupport.stream( | |
assetMetadata.getProperty(metadataPropertyName).getValue(Type.STRINGS) | |
.spliterator(), false).map(propValue -> propValue.toLowerCase()) | |
.collect(Collectors.toList()); | |
} else { | |
List<String> result = new ArrayList<>(); | |
result.add(assetMetadata.getProperty(metadataPropertyName) | |
.getValue(Type.STRING).toLowerCase()); | |
return result; | |
} | |
} | |
} | |
} | |
return List.of(""); | |
} | |
private static boolean isProtectedPath(final String assetPath) { | |
// List of assets to be protected by this permission provider | |
return assetPath.startsWith(AppConstants.PRIVATE_FOLDER_ROOT); | |
} | |
public static boolean isDamPath(Tree tree) { | |
return null != tree && tree.getPath().startsWith(DamConstants.MOUNTPOINT_ASSETS); | |
} | |
public static Tree getTreeFromLocation(TreeLocation location) { | |
Tree tree = (location.getProperty() == null) ? location.getTree() | |
: location.getParent().getTree(); | |
while (tree == null && !PathUtils.denotesRoot(location.getPath())) { | |
location = location.getParent(); | |
tree = location.getTree(); | |
} | |
return tree; | |
} | |
} |
No comments:
Post a Comment