Skip to content

Commit

Permalink
CADC-12859 update link node handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jburke-cadc committed Feb 2, 2024
1 parent 409e1e9 commit 0075409
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 31 deletions.
2 changes: 1 addition & 1 deletion cadc-vos-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ sourceCompatibility = 1.8

group = 'org.opencadc'

version = '2.0.9'
version = '2.0.10'

description = 'OpenCADC VOSpace server'
def git_url = 'https://github.com/opencadc/vos'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,45 @@ public void setAppName(String appName) {
}
}

/**
*
* @param node
* @param target
* @param relativePath
* @return
*/
public static boolean isTargetInPackage(URI node, URI target, String relativePath) {
// Get the root of the node path
if (relativePath.startsWith("/")) {
String root;
int index = relativePath.indexOf("/", 1);
if (index == -1) {
root = relativePath.substring(1);
} else {
root = relativePath.substring(1, index);
}

if (node.getScheme().equals(target.getScheme()) && node.getAuthority().equals(target.getAuthority()) ) {
String[] nodes = node.getPath().split("/");
String[] targets = target.getPath().split("/");
// length - 1 to ignore the ending link and target path segments
int length = Math.min(nodes.length, targets.length) -1;
// i = 1 to ignore first empty segment in the array
for (int i = 1; i < length; i++) {
log.debug(String.format("node seg: %s target seg: %s root: %s", nodes[i], targets[i], root));
if (nodes[i].equals(targets[i])) {
if (nodes[i].equals(root)) {
return true;
}
} else {
return false;
}
}
}
}
return false;
}

/**
* Get the list of targets for the package and the package name.
*/
Expand Down Expand Up @@ -242,37 +281,40 @@ protected URL getURL(VOSURI nodeURI)
}

// Get a PackageItem for a directory from the given ContainerNode.
protected PackageItem getDirectoryPackageItem(ContainerNode node) {
String nodePath = Utils.getPath(node);
return new PackageItem(nodePath);
protected PackageItem getDirectoryPackageItem(ContainerNode node, String relativePath) {
return new PackageItem(relativePath);
}

// Get a PackageItem for a file from the given DataNode.
protected PackageItem getFilePackageItem(DataNode node)
protected PackageItem getFilePackageItem(DataNode node, String relativePath)
throws NodeNotFoundException, PrivilegedActionException {

String nodeRelativePath = relativePath + "/" + node.getName();
String nodePath = Utils.getPath(node);
VOSURI nodeURI = new VOSURI(resourceID, nodePath);
URL endpointURL = getURL(nodeURI);
return new PackageItem(nodePath, endpointURL);
return new PackageItem(nodeRelativePath, endpointURL);
}

// Get a PackageItem for a symbolic link from the given LinkNode.
protected PackageItem getSymbolicLinkPackageItem(LinkNode node) {
protected PackageItem getSymbolicLinkPackageItem(LinkNode node, String relativePath) {
String nodePath = Utils.getPath(node);
log.debug("LinkNode path: " + nodePath);

// check if target is external to the service
// check that the link target is in the package
// return a null PackageItem if it is out of the package
VOSURI nodeURI = new VOSURI(this.resourceID, nodePath);
URI targetURI = node.getTarget();
if (!targetURI.toASCIIString().startsWith(baseNodeUri)) {
log.debug("external LinkNode: " + targetURI.toASCIIString());
boolean inPackage = isTargetInPackage(nodeURI.getURI(), targetURI, relativePath);
if (!inPackage) {
return null;
}

String linkPath = targetURI.getPath();
VOSURI nodeURI = new VOSURI(this.resourceID, nodePath);
log.debug("LinkNode target path: " + linkPath);

return new PackageItem(nodePath, linkPath);
// get the relative path for the link in the package
Path linkPath = Paths.get(nodePath);
Path targetPath = Paths.get(targetURI.getPath());
Path linkRelativePath = linkPath.relativize(targetPath);
return new PackageItem(nodePath, linkRelativePath.toString());
}

// Build a filename from the provided URI.
Expand All @@ -293,12 +335,13 @@ private String getBaseNodeUri(URI resourceID) {
class PackageItemIterator implements Iterator<PackageItem> {

private final Subject caller;
private final List<ContainerNode> deferredNodes;
private final List<ContainerNode> currentNodes;
private final List<RelativeNode> deferredNodes;
private final List<RelativeNode> currentNodes;
private final Iterator<Node> targetIterator;
private ListIterator<ContainerNode> currentIterator;
private ListIterator<RelativeNode> currentIterator;
private ResourceIterator<Node> childIterator;
private PackageItem next = null;
private String parentPath = "";

public PackageItemIterator(List<URI> targets) {
if (targets == null) {
Expand Down Expand Up @@ -359,7 +402,7 @@ private void advance() {
targetIterator.remove();
if (node != null) {
log.debug("target: " + node.getName());
next = doChildNode(node);
next = doChildNode(node, "");
if (next != null) {
log.debug("return next: " + next);
return;
Expand Down Expand Up @@ -391,17 +434,18 @@ private void advance() {
isCurrentIteratorEmpty(), isChildIteratorEmpty()));
if (isChildIteratorEmpty() && !isCurrentIteratorEmpty()) {
// get the next parent (container) node and it's package-item
ContainerNode node = currentIterator.next();
log.debug("currentIterator next: " + node.getName());
RelativeNode relativeNode = currentIterator.next();
log.debug("currentIterator next: " + relativeNode.node.getName());
currentIterator.remove();
next = getDirectoryPackageItem(node);
parentPath = relativeNode.path;
next = getDirectoryPackageItem(relativeNode.node, relativeNode.path);

// check read access to the parent and if granted refresh the childIterator
boolean canRead = vospaceAuthorizer.hasSingleNodeReadPermission(node, caller);
log.debug(String.format("%s read permission: %s", node.getName(), canRead));
boolean canRead = vospaceAuthorizer.hasSingleNodeReadPermission(relativeNode.node, caller);
log.debug(String.format("%s read permission: %s", relativeNode.node.getName(), canRead));
if (canRead) {
childIterator = nodePersistence.iterator(node, null, null);
log.debug("refreshed childIterator for: " + node.getName());
childIterator = nodePersistence.iterator(relativeNode.node, null, null);
log.debug("refreshed childIterator for: " + relativeNode.node.getName());
}
// return the container PackageItem
log.debug("return next: " + next);
Expand All @@ -418,7 +462,7 @@ private void advance() {
while (childIterator.hasNext()) {
Node child = childIterator.next();
log.debug("childIterator next: " + child.getName());
next = doChildNode(child);
next = doChildNode(child, parentPath);
if (next != null) {
log.debug("return next: " + next);
return;
Expand Down Expand Up @@ -483,21 +527,22 @@ private Iterator<Node> getNodeIterator(List<URI> targets) {
* @param child child Node.
* @return a PackageItem, or null if the node is a ContainerNode, or error creating the PackageItem.
*/
private PackageItem doChildNode(Node child) {
private PackageItem doChildNode(Node child, String nodePath) {
log.debug("doChildNode().start");
PackageItem packageItem = null;
try {
if (child instanceof ContainerNode) {
deferredNodes.add((ContainerNode) child);
String childPath = nodePath + "/" + child.getName();
deferredNodes.add(new RelativeNode((ContainerNode) child, childPath));
log.debug(child.getName() + " added to deferred nodes");
} else {
boolean canRead = vospaceAuthorizer.hasSingleNodeReadPermission(child, caller);
if (!canRead) {
log.debug(child.getName() + " read permission denied");
} else if (child instanceof DataNode) {
packageItem = getFilePackageItem((DataNode) child);
packageItem = getFilePackageItem((DataNode) child, nodePath);
} else if (child instanceof LinkNode) {
packageItem = getSymbolicLinkPackageItem((LinkNode) child);
packageItem = getSymbolicLinkPackageItem((LinkNode) child, nodePath);
} else {
log.info("unknown node type: " + Utils.getPath(child) + CONTINUING_PROCESSING);
}
Expand All @@ -518,4 +563,14 @@ private PackageItem doChildNode(Node child) {

}

public class RelativeNode {
public ContainerNode node;
public String path;

public RelativeNode(ContainerNode node, String path) {
this.node = node;
this.path = path;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
************************************************************************
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
* (c) 2023. (c) 2023.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
* All rights reserved Tous droits réservés
*
* NRC disclaims any warranties, Le CNRC dénie toute garantie
* expressed, implied, or énoncée, implicite ou légale,
* statutory, of any kind with de quelque nature que ce
* respect to the software, soit, concernant le logiciel,
* including without limitation y compris sans restriction
* any warranty of merchantability toute garantie de valeur
* or fitness for a particular marchande ou de pertinence
* purpose. NRC shall not be pour un usage particulier.
* liable in any event for any Le CNRC ne pourra en aucun cas
* damages, whether direct or être tenu responsable de tout
* indirect, special or general, dommage, direct ou indirect,
* consequential or incidental, particulier ou général,
* arising from the use of the accessoire ou fortuit, résultant
* software. Neither the name de l'utilisation du logiciel. Ni
* of the National Research le nom du Conseil National de
* Council of Canada nor the Recherches du Canada ni les noms
* names of its contributors may de ses participants ne peuvent
* be used to endorse or promote être utilisés pour approuver ou
* products derived from this promouvoir les produits dérivés
* software without specific prior de ce logiciel sans autorisation
* written permission. préalable et particulière
* par écrit.
*
* This file is part of the Ce fichier fait partie du projet
* OpenCADC project. OpenCADC.
*
* OpenCADC is free software: OpenCADC est un logiciel libre ;
* you can redistribute it and/or vous pouvez le redistribuer ou le
* modify it under the terms of modifier suivant les termes de
* the GNU Affero General Public la “GNU Affero General Public
* License as published by the License” telle que publiée
* Free Software Foundation, par la Free Software Foundation
* either version 3 of the : soit la version 3 de cette
* License, or (at your option) licence, soit (à votre gré)
* any later version. toute version ultérieure.
*
* OpenCADC is distributed in the OpenCADC est distribué
* hope that it will be useful, dans l’espoir qu’il vous
* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
* without even the implied GARANTIE : sans même la garantie
* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
* General Public License for Générale Publique GNU Affero
* more details. pour plus de détails.
*
* You should have received Vous devriez avoir reçu une
* a copy of the GNU Affero copie de la Licence Générale
* General Public License along Publique GNU Affero avec
* with OpenCADC. If not, see OpenCADC ; si ce n’est
* <http://www.gnu.org/licenses/>. pas le cas, consultez :
* <http://www.gnu.org/licenses/>.
*
* : 5 $
*
************************************************************************
*/

package org.opencadc.vospace.server;

import ca.nrc.cadc.util.Log4jInit;
import java.net.URI;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Test;
import org.opencadc.vospace.server.pkg.VospacePackageRunner;

public class VospacePackageRunnerTest {

private static final Logger log = Logger.getLogger(org.opencadc.vospace.server.UtilsTest.class);

static {
Log4jInit.setLevel("org.opencadc.raven", Level.DEBUG);
}

@Test
public void testIsTargetInPackage() throws Exception {

// should fail
URI node = URI.create("vos://opencadc.org~vault/pkg/foo/link");
URI target = URI.create("http://example.com/target");
String relativePath = "/pkg/foo/";
boolean result = VospacePackageRunner.isTargetInPackage(node, target, relativePath);
Assert.assertFalse(result);

node = URI.create("vos://opencadc.org~vault/pkg/foo/link");
target = URI.create("vos://example.com~vault/pkg/target");
relativePath = "/pkg/foo/";
result = VospacePackageRunner.isTargetInPackage(node, target, relativePath);
Assert.assertFalse(result);

node = URI.create("vos://opencadc.org~vault/pkg/foo/link");
target = URI.create("vos://opencadc.org~cavern/pkg/target");
relativePath = "/pkg/foo/";
result = VospacePackageRunner.isTargetInPackage(node, target, relativePath);
Assert.assertFalse(result);

node = URI.create("vos://opencadc.org~vault/test1/pkg/foo/link");
target = URI.create("vos://opencadc.org~vault/test2/pkg/target");
relativePath = "/pkg/foo/";
result = VospacePackageRunner.isTargetInPackage(node, target, relativePath);
Assert.assertFalse(result);

node = URI.create("vos://opencadc.org~vault/test/foo/bar/link");
target = URI.create("vos://opencadc.org~vault/test/foo/baz/target");
relativePath = "/pkg/foo/bar/";
result = VospacePackageRunner.isTargetInPackage(node, target, relativePath);
Assert.assertFalse(result);

// should pass
node = URI.create("vos://opencadc.org~vault/test/pkg/foo/link");
target = URI.create("vos://opencadc.org~vault/test/pkg/bar/target");
relativePath = "/pkg/foo/";
result = VospacePackageRunner.isTargetInPackage(node, target, relativePath);
Assert.assertTrue(result);
}

}

0 comments on commit 0075409

Please sign in to comment.