Skip to content

Commit

Permalink
More rework
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrian Damian authored and Adrian Damian committed Jan 23, 2024
1 parent 341fea8 commit 809cd1b
Show file tree
Hide file tree
Showing 16 changed files with 101 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

import ca.nrc.cadc.auth.AuthenticationUtil;
import ca.nrc.cadc.util.StringUtil;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
Expand All @@ -87,8 +88,6 @@
* Utility class to follow and resolve the target of link nodes in a local vospace including checking
* read permission along the path.
*
* <p>If the last node on the path is a link node, it will not be resolved.
*
* @author adriand
* @author majorb
*/
Expand All @@ -100,7 +99,6 @@ public class PathResolver {
private static final int VISIT_LIMIT_MAX = 40;
private final NodePersistence nodePersistence;
private final VOSpaceAuthorizer voSpaceAuthorizer;
private final boolean resolveLeafLink;

List<String> visitedPaths = new ArrayList<>();
private int visitLimit = 20;
Expand All @@ -110,90 +108,69 @@ public class PathResolver {
* Ctor
* @param nodePersistence - node persistence to use
* @param voSpaceAuthorizer - vo authorizer to check permissions along the path
* @param resolveLeafLink - If true and the leaf node is a LinkNode it resolves it, otherwise it returns the
* LinkNode after potentially resolving other links in the path.
*/
public PathResolver(NodePersistence nodePersistence, VOSpaceAuthorizer voSpaceAuthorizer, boolean resolveLeafLink) {
public PathResolver(NodePersistence nodePersistence, VOSpaceAuthorizer voSpaceAuthorizer) {
this.nodePersistence = nodePersistence;
this.voSpaceAuthorizer = voSpaceAuthorizer;
this.resolveLeafLink = resolveLeafLink;
}

/**
* Resolves a node URI and returns a Resolved object that contains either the resolved node if the node exists
* or the resolved parent and the name of the node if the node is to be created by the caller.
* @param nodeURI URI of the node to be resolved
* @return Resolved object containing either the child node if it exists already or the parent node and the name
* of the child node to be created otherwise
* Resolves a data node URI and returns a ResolvedNode object that contains details of the data node: the node
* itself (if exists), its parent and the node name.
* @param nodePath path of the data node to be resolved
* @return ResolvedNode object with the actual node (or null if it doesn't exist), its parent and its name.
* @throws Exception
*/
public Resolved resolveNode(VOSURI nodeURI) throws Exception {
log.debug("resolve node: [" + nodeURI + "]");

ContainerNode parent;
try {
parent = (ContainerNode) getNode(nodeURI.getParent());
} catch (ClassCastException ex) {
throw new IllegalArgumentException(nodeURI.getParent() + " not a valid path");
}
if (parent == null) {
throw new IllegalArgumentException(nodeURI.getParent() + " not a valid path");
}
Node node = nodePersistence.get(parent, nodeURI.getName());
Resolved result = new Resolved();
if ((node == null)) {
result.childName = nodeURI.getName(); // default name
}
while (node instanceof LinkNode) {
// resolve it
validateTargetURI((LinkNode) node);
VOSURI targetURI = new VOSURI(((LinkNode)node).getTarget());
log.debug("Target uri " + targetURI);
try {
parent = (ContainerNode) getNode(targetURI.getParent());
log.debug("Parent " + targetURI.getParent() + " is resolved to " + parent);
} catch (ClassCastException ex) {
throw new IllegalArgumentException(nodeURI.getParent() + " in link node not a valid path");
}
node = nodePersistence.get(parent, targetURI.getName());
result.childName = targetURI.getName();
}
if (node == null) {
// new DataNode that needs to be created later.
result.parent = parent;
} else {
// existing DataNode/ContainerNode
result.childName = null;
result.child = node;
}
return result;
public ResolvedNode getTargetDataNode(String nodePath) throws Exception {
return resolveNode(nodePath, true);
}

/**
* Resolves a node URI, follow links, and returns the end node.
*
* @param nodePath
* @param nodePath path of the data node to be resolved
* @param resolveLeafLink - If true and the leaf node is a LinkNode it resolves it, otherwise it returns the
* LinkNode after potentially resolving other links in the path.
* @return the last node in the path or null if not found
* @throws org.opencadc.vospace.LinkingException
*/
public Node getNode(String nodePath) throws Exception {
public Node getNode(String nodePath, boolean resolveLeafLink) throws Exception {
ResolvedNode rn = resolveNode(nodePath, resolveLeafLink);
if ((rn == null) || (rn.node == null)) {
return null;
}
return rn.node;
}

private ResolvedNode resolveNode(String nodePath, boolean resolveLeafLink) throws Exception {
final Subject subject = AuthenticationUtil.getCurrentSubject();
log.debug("get: [" + nodePath + "]");
log.debug("resolve node: [" + nodePath+ "]");
ContainerNode node = nodePersistence.getRootNode();
voSpaceAuthorizer.hasSingleNodeReadPermission(node, subject);

Node ret = node;
ResolvedNode ret = new ResolvedNode();
ret.parent = node;
if (StringUtil.hasLength(nodePath)) {
if (nodePath.charAt(0) == '/') {
nodePath = nodePath.substring(1);
}
Iterator<String> pathIter = Arrays.stream(nodePath.split("/")).iterator();
while (pathIter.hasNext()) {
String name = pathIter.next();
log.debug("get node: '" + name + "' in path '" + nodePath + "'");
log.debug("get node: '" + name + "' in parent '" + Utils.getPath(node) + "'");
Node child = nodePersistence.get(node, name);
if (child == null) {
return null;
if (pathIter.hasNext()) {
log.debug("Could not find child node " + name);
return null;
} else {
// got to a non-existent leaf
ret.parent = node;
ret.name = name;
ret.node = null;
break;
}
}
if (!voSpaceAuthorizer.hasSingleNodeReadPermission(child, subject)) {
LocalServiceURI lsURI = new LocalServiceURI(nodePersistence.getResourceID());
Expand Down Expand Up @@ -223,21 +200,35 @@ public Node getNode(String nodePath) throws Exception {
visitedPaths.add(linkPath);

// recursive follow
log.debug("Resolve: " + linkPath);
child = getNode(linkPath);
log.debug("Resolve: " + targetURI.getPath());
ret = resolveNode(targetURI.getPath(), resolveLeafLink);
if (ret == null) {
log.debug("Could not resolve link " + targetURI.getPath());
return null;
} else {
child = ret.node;
}
}
if (pathIter.hasNext()) {
if (child instanceof ContainerNode) {
node = (ContainerNode) child;
} else {
log.debug("Found non container node in the path " + nodePath);
return null;
}
}
ret = child;
ret.parent = child.parent;
ret.node = child;
ret.name = child.getName();
}
} else {
// root node
ret.node = ret.parent;
ret.parent = ret.node.parent;
ret.name = ret.node.getName();
}

log.debug("return node: " + ((ret != null) ? Utils.getPath(ret) : null));
log.debug("return resolved node: " + ret);
return ret;
}

Expand Down Expand Up @@ -273,16 +264,13 @@ public void setVisitLimit(int visitLimit) {
this.visitLimit = visitLimit;
}

public static class Resolved {
public static class ResolvedNode {
public ContainerNode parent;
public String childName;
public Node child; // possibly null
public String name;
public Node node;

public String toString() {
return "parent " + parent + ", child " + child + ", childName " + childName;
return "parent " + parent + ", node " + node + " name " + name;
}
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ public void doAction() throws Exception {

// get parent container node
// TBD: resolveLinks=true?
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer, true);
Node serverNode = pathResolver.getNode(target.getParentURI().getPath());
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer);
Node serverNode = pathResolver.getNode(target.getParentURI().getPath(), true);
if (!(serverNode instanceof ContainerNode)) {
throw NodeFault.ContainerNotFound.getStatus(clientNodeURI.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ public class DeleteNodeAction extends NodeAction {
@Override
public void doAction() throws Exception {
VOSURI target = getTargetURI();
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer, false);
Node serverNode = pathResolver.getNode(getTargetURI().getPath());
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer);
Node serverNode = pathResolver.getNode(getTargetURI().getPath(), false);

if (serverNode == null) {
throw NodeFault.NodeNotFound.getStatus("Target " + target.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ public void doAction() throws Exception {

// get parent node
// TBD: resolveLinks=true?
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer, false);
Node serverNode = pathResolver.getNode(target.getPath());
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer);
Node serverNode = pathResolver.getNode(target.getPath(), false);
if (serverNode == null) {
throw NodeFault.NodeNotFound.getStatus(target.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ public void doAction() throws Exception {
}

voSpaceAuthorizer.setDisregardLocks(true); // locks don't apply to properties updates
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer, false);
Node serverNode = pathResolver.getNode(target.getPath());
PathResolver pathResolver = new PathResolver(nodePersistence, voSpaceAuthorizer);
Node serverNode = pathResolver.getNode(target.getPath(), false);
if (serverNode == null) {
throw NodeFault.NodeNotFound.getStatus("Target " + clientNodeTarget.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ private void doit() {
}

log.debug("node: " + target);
PathResolver pathResolver = new PathResolver(nodePersistence, authorizer, true);
PathResolver pathResolver = new PathResolver(nodePersistence, authorizer);
String nodePath = target.getPath();
Node serverNode = pathResolver.getNode(nodePath);
Node serverNode = pathResolver.getNode(nodePath, true);
if (serverNode == null) {
Exception ex = NodeFault.NodeNotFound.getStatus(nodePath);
sendError(ex.getMessage());
Expand All @@ -215,7 +215,7 @@ private void doit() {
ep = jobUpdater.setPhase(job.getID(), ExecutionPhase.EXECUTING, endPhase, results, new Date());
}
if (!endPhase.equals(ep)) {
log.warn("Could not change the job phase from " + ExecutionPhase.EXECUTING + " to " + endPhase + " vs " + ep);
log.warn("Could not change the job phase from " + ExecutionPhase.EXECUTING + " to " + endPhase);
}
logInfo.setSuccess(true);
} catch (ResourceNotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public void setAppName(String appName) {
Context ctx = new InitialContext();
this.nodePersistence = (NodePersistence) ctx.lookup(jndiKey);
this.vospaceAuthorizer = new VOSpaceAuthorizer(nodePersistence);
this.pathResolver = new PathResolver(nodePersistence, vospaceAuthorizer, true);
this.pathResolver = new PathResolver(nodePersistence, vospaceAuthorizer);
this.resourceID = nodePersistence.getResourceID();
} catch (NamingException e) {
throw new RuntimeException("BUG: NodePersistence implementation not found with JNDI key " + jndiKey, e);
Expand Down Expand Up @@ -446,7 +446,7 @@ private Iterator<Node> getNodeIterator(List<URI> targets) {
VOSURI vosURI = new VOSURI(target);
String nodePath = vosURI.getPath();
try {
Node node = pathResolver.getNode(nodePath);
Node node = pathResolver.getNode(nodePath, true);
targetNodes.add(node);
log.debug(String.format("target %s -> node %s", target.toASCIIString(), node.getName()));
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ public void doAction() throws Exception {
confirmSingleTarget(transfer);
VOSURI target = new VOSURI(transfer.getTargets().get(0));

PathResolver resolver = new PathResolver(nodePersistence, authorizer, true);
PathResolver resolver = new PathResolver(nodePersistence, authorizer);

Node node = resolver.getNode(target.getPath());
Node node = resolver.getNode(target.getPath(), true);
if (!(node instanceof ContainerNode)) {
throw new TransferException("node is not a container node");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,16 @@ public void doAction() throws Exception {
log.debug("checking move permissions: " + srcURI + " -> " + destURI);

// resolve the links to containers in the path so we get the actual node
PathResolver res = new PathResolver(nodePersistence, authorizer, true);
Node srcNode = res.getNode(srcURI.getPath());
PathResolver res = new PathResolver(nodePersistence, authorizer);
Node srcNode = res.getNode(srcURI.getPath(), true);

log.debug("Resolved src path: " + srcURI + " -> " + srcNode);
LocalServiceURI loc = new LocalServiceURI(nodePersistence.getResourceID());
srcURI = loc.getURI(srcNode);

// resolve destination, parent first
Node destParent = res.getNode(destURI.getParentURI().getPath());
//TODO might need a new path resolver for link cycles
Node destParent = res.getNode(destURI.getParentURI().getPath(), true);
if (!(destParent instanceof ContainerNode)) {
throw new IllegalArgumentException("parent of destination is not a ContainerNode");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ public void doAction() throws Exception {
VOSURI target = new VOSURI(transfer.getTargets().get(0));

// careful to capture a link to data node so we can get the right filename in the transfer
PathResolver resolver1 = new PathResolver(nodePersistence, authorizer, false);
Node apparentNode = resolver1.getNode(target.getPath());
PathResolver resolver1 = new PathResolver(nodePersistence, authorizer);
Node apparentNode = resolver1.getNode(target.getPath(), false);
Node actualNode = apparentNode;
if (apparentNode instanceof LinkNode) {
PathResolver resolver2 = new PathResolver(nodePersistence, authorizer, true);
actualNode = resolver2.getNode(target.getPath());
PathResolver resolver2 = new PathResolver(nodePersistence, authorizer);
actualNode = resolver2.getNode(target.getPath(), true);
}
log.debug("Resolved path: " + target + " -> " + actualNode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import org.apache.log4j.Logger;
import org.opencadc.vospace.ContainerNode;
import org.opencadc.vospace.DataNode;
import org.opencadc.vospace.Node;
import org.opencadc.vospace.VOSURI;
import org.opencadc.vospace.server.LocalServiceURI;
import org.opencadc.vospace.server.NodeFault;
Expand Down Expand Up @@ -112,19 +113,22 @@ public void doAction()

VOSURI target = new VOSURI(transfer.getTargets().get(0));

PathResolver pr = new PathResolver(nodePersistence, authorizer, true);
PathResolver.Resolved resolved = pr.resolveNode(target);
log.debug("Resolved target node: " + resolved);
PathResolver pr = new PathResolver(nodePersistence, authorizer);
PathResolver.ResolvedNode rn = pr.getTargetDataNode(target.getPath());
if (rn == null) {
throw NodeFault.ContainerNotFound.getStatus(target.getPath());
}
log.debug("Target target node: " + rn);

DataNode dn;
Subject caller = AuthenticationUtil.getCurrentSubject();
if (resolved.child == null) {
if (!authorizer.hasSingleNodeWritePermission(resolved.parent, caller)) {
throw NodeFault.PermissionDenied.getStatus(Utils.getPath(resolved.parent));
DataNode dn;
if (rn.node == null) {
if (!authorizer.hasSingleNodeWritePermission(rn.parent, caller)) {
throw NodeFault.PermissionDenied.getStatus(Utils.getPath(rn.parent));
}
// create: this should do the same things that CreateNodeAction does
dn = new DataNode(resolved.childName);
ContainerNode parent = resolved.parent;
dn = new DataNode(rn.name);
ContainerNode parent = rn.parent;
dn.parent = parent;
dn.owner = caller;
if (parent.inheritPermissions != null && parent.inheritPermissions) {
Expand All @@ -133,8 +137,8 @@ public void doAction()
dn.getReadWriteGroup().addAll(parent.getReadWriteGroup());
}
nodePersistence.put(dn);
} else if (resolved.child instanceof DataNode) {
dn = (DataNode) resolved.child;
} else if (rn.node instanceof DataNode) {
dn = (DataNode) rn.node;
if (!authorizer.hasSingleNodeWritePermission(dn, caller)) {
throw NodeFault.PermissionDenied.getStatus(target.getParentURI().getURI().toASCIIString());
}
Expand Down
Loading

0 comments on commit 809cd1b

Please sign in to comment.