Fixed edge case bug with handling similar content paths one with variables and another without. Upgraded to 0.0.3
This commit is contained in:
parent
464c539924
commit
3d30413841
12 changed files with 235 additions and 38 deletions
7
pom.xml
7
pom.xml
|
@ -3,7 +3,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.redhat.trie</groupId>
|
<groupId>com.redhat.trie</groupId>
|
||||||
<artifactId>PathPacker</artifactId>
|
<artifactId>PathPacker</artifactId>
|
||||||
<version>0.0.2</version>
|
<version>0.0.3</version>
|
||||||
<packaging>bundle</packaging>
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
<prerequisites>
|
<prerequisites>
|
||||||
|
@ -22,6 +22,11 @@
|
||||||
<artifactId>bcprov-jdk16</artifactId>
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
<version>1.46</version>
|
<version>1.46</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.surefire</groupId>
|
||||||
|
<artifactId>surefire-booter</artifactId>
|
||||||
|
<version>2.17</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>log4j</groupId>
|
<groupId>log4j</groupId>
|
||||||
|
|
|
@ -50,5 +50,13 @@ public class NodePair implements Comparable {
|
||||||
public int compareTo(Object other) {
|
public int compareTo(Object other) {
|
||||||
return this.name.compareTo(((NodePair) other).name);
|
return this.name.compareTo(((NodePair) other).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this <tt>NodePair</tt> have any children?
|
||||||
|
* @return <tt>true</tt> if it has at least one connection and <tt>false</tt> otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNoChildren() {
|
||||||
|
return getConnection().getChildren().size() == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.apache.log4j.Logger;
|
||||||
*
|
*
|
||||||
* It holds the relationships to children, as well as all parents that regard it as a child.
|
* It holds the relationships to children, as well as all parents that regard it as a child.
|
||||||
*
|
*
|
||||||
* The Name of a given PathNode, is inferred by the NodePair that regards this PathNode as it's "connection"
|
* The Name of a given PathNode, is inferred by the NodePair that regards this PathNode as its "connection"
|
||||||
*/
|
*/
|
||||||
public class PathNode {
|
public class PathNode {
|
||||||
private static org.apache.log4j.Logger log = Logger.getLogger(PathTree.class);
|
private static org.apache.log4j.Logger log = Logger.getLogger(PathTree.class);
|
||||||
|
|
|
@ -308,40 +308,56 @@ public class PathTree {
|
||||||
/**
|
/**
|
||||||
* Validate whether contentPath is included in this tree.
|
* Validate whether contentPath is included in this tree.
|
||||||
*
|
*
|
||||||
* @param contentPath A String, like "/foo/bar/baz"
|
* @param contentPath A String, like "/foo/bar/baz"
|
||||||
* @return true or false
|
* @return true or false
|
||||||
*/
|
*/
|
||||||
public boolean validate(String contentPath) {
|
public boolean validate(final String contentPath) {
|
||||||
StringTokenizer st = new StringTokenizer(contentPath, "/");
|
PathNode rootPathNode = null;
|
||||||
PathNode root;
|
try {
|
||||||
PathNode pn;
|
rootPathNode = getRootPathNode();
|
||||||
String curTok;
|
} catch(PayloadException pe) {
|
||||||
|
log.error(pe);
|
||||||
try {
|
|
||||||
root = this.getRootPathNode();
|
|
||||||
} catch (PayloadException ex) {
|
|
||||||
log.error(ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pn = root;
|
|
||||||
while (st.hasMoreTokens()) {
|
|
||||||
curTok = st.nextToken();
|
|
||||||
|
|
||||||
for (NodePair np : pn.getChildren()) {
|
|
||||||
if (curTok.equals(np.getName()) || np.getName().startsWith("$")) {
|
|
||||||
//System.out.println("[" + curTok + "] == [" + np.getName() + "]");
|
|
||||||
if (np.getConnection().getChildren().size() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pn = np.getConnection();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
return test(contentPath, rootPathNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Character used to delimit client path request elements */
|
||||||
|
private static String PATH_DELIMITER = "/";
|
||||||
|
/** Character used as a variable name prefix in content path definitions */
|
||||||
|
private static String CONTENT_PATH_VARIABLE_PREFIX = "$";
|
||||||
|
/**
|
||||||
|
* Tests if the given path request is reachable via the current <tt>tree.</tt>.
|
||||||
|
* @param request The request to test.
|
||||||
|
* @param tree The content path tree.
|
||||||
|
* @return <tt>true</tt> if the path is reachable and <tt>false</tt> otherwise.
|
||||||
|
*/
|
||||||
|
private boolean test(final String request, final PathNode tree) {
|
||||||
|
/* Request is of the form "/content/rc/rhel/7/..."
|
||||||
|
* Grab the next element.
|
||||||
|
*/
|
||||||
|
log.debug("test(" + request + ")");
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(request, PATH_DELIMITER);
|
||||||
|
if(tokenizer.countTokens() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String currentToken = tokenizer.nextToken();
|
||||||
|
for(NodePair nodePair: tree.getChildren()) {
|
||||||
|
String nodePairName = nodePair.getName();
|
||||||
|
log.debug("Current token: [" + currentToken + "] =??= NodePair name: [" + nodePairName + "]");
|
||||||
|
if(currentToken.equals(nodePairName) || nodePairName.startsWith(CONTENT_PATH_VARIABLE_PREFIX)) {
|
||||||
|
if(nodePair.hasNoChildren()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
String s = PATH_DELIMITER + currentToken;
|
||||||
|
boolean retval = test(request.substring(currentToken.length()+1), nodePair.getConnection());
|
||||||
|
if(retval) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,11 +16,13 @@ import static org.junit.Assert.fail;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is just to provide helpers for the other tests
|
* This class is just to provide helpers for the other tests
|
||||||
*/
|
*/
|
||||||
public class TestHelpers {
|
public class TestHelpers {
|
||||||
|
private static org.apache.log4j.Logger log = Logger.getLogger(TestHelpers.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* junit requires at least one runnable test
|
* junit requires at least one runnable test
|
||||||
|
@ -33,8 +35,8 @@ public class TestHelpers {
|
||||||
// Helpers
|
// Helpers
|
||||||
//
|
//
|
||||||
public static boolean cmpStrings(List<String> thisList, List<String> thatList) {
|
public static boolean cmpStrings(List<String> thisList, List<String> thatList) {
|
||||||
Collection<String> thisColl = new ArrayList(thisList);
|
Collection<String> thisColl = new ArrayList<String>(thisList);
|
||||||
Collection<String> thatColl = new ArrayList(thatList);
|
Collection<String> thatColl = new ArrayList<String>(thatList);
|
||||||
|
|
||||||
Collection<String> similar = new HashSet<String>( thisColl );
|
Collection<String> similar = new HashSet<String>( thisColl );
|
||||||
Collection<String> different = new HashSet<String>();
|
Collection<String> different = new HashSet<String>();
|
||||||
|
@ -43,7 +45,7 @@ public class TestHelpers {
|
||||||
|
|
||||||
similar.retainAll( thatColl );
|
similar.retainAll( thatColl );
|
||||||
different.removeAll( similar );
|
different.removeAll( similar );
|
||||||
|
|
||||||
if (different.size() > 0) {
|
if (different.size() > 0) {
|
||||||
System.out.printf("Different:%s%n", different);
|
System.out.printf("Different:%s%n", different);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +93,14 @@ public class TestHelpers {
|
||||||
String content;
|
String content;
|
||||||
List<String> contentList = new ArrayList<String>();
|
List<String> contentList = new ArrayList<String>();
|
||||||
InputStream in = resStream(klass, filename);
|
InputStream in = resStream(klass, filename);
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
BufferedReader br = null;
|
||||||
|
try {
|
||||||
|
br = new BufferedReader(new InputStreamReader(in));
|
||||||
|
} catch(NullPointerException npe) {
|
||||||
|
// can happen in the case of an empty content set list
|
||||||
|
log.warn(">>>>>>> Empty content set <<<<<");
|
||||||
|
return contentList;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while ((content = br.readLine()) != null) {
|
while ((content = br.readLine()) != null) {
|
||||||
|
|
106
src/test/java/com/redhat/trie/TestValidater.java
Normal file
106
src/test/java/com/redhat/trie/TestValidater.java
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package com.redhat.trie;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.log4j.Level;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests of whether a URL should be granted access based on a given content set
|
||||||
|
* (as would be specified in the requester's entitlement certificate).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
|
public class TestValidater {
|
||||||
|
private String contentSetFileName;
|
||||||
|
private String requestedPath;
|
||||||
|
private boolean expectedResult;
|
||||||
|
private PathTree pathTree;
|
||||||
|
|
||||||
|
@Parameters
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
/*
|
||||||
|
* Data for each run of the test.
|
||||||
|
* [0] = The name of the file containing content set paths
|
||||||
|
* [1] = The requested resource
|
||||||
|
* [3] = The expected result.
|
||||||
|
*/
|
||||||
|
Object[][] data = new Object[][] {
|
||||||
|
/*
|
||||||
|
* WARNING --
|
||||||
|
* Do not uncomment empty.contents.list cases unless
|
||||||
|
* PathTree has been updated to handle that case.
|
||||||
|
*
|
||||||
|
{ "empty.contents.list", "/content/rc/rhel/7/vt", false},
|
||||||
|
{ "empty.contents.list", "/content/rc/rhel/7", false},
|
||||||
|
*/
|
||||||
|
{ "contents.list", "/content/beta/rhel/server/5/5server/x86_64/sap/os/repomd.xml", true},
|
||||||
|
{ "contents.list", "/fart/face/mcjones", false},
|
||||||
|
{ "contents.list", "/content/dist/rhel/server/6/$releasever/$basearch/vt/os", false},
|
||||||
|
|
||||||
|
{ "variables-last-contents.list", "/content/rc/rhel/server/7/x86_64/debug", true},
|
||||||
|
{ "variables-last-contents.list", "/content/rc/rhel/server/7/x86_64/debug/tools/firefox", true},
|
||||||
|
{ "variables-last-contents.list", "/content/rc/rhel/server/7/x86_64/Debug", false},
|
||||||
|
{ "variables-last-contents.list", "/content/rc/rhel/server/7/Debug", false},
|
||||||
|
{ "variables-last-contents.list", "/content/rc/rhel/server/7/x86_64/os", true},
|
||||||
|
|
||||||
|
{ "variables-first-contents.list", "/content/rc/rhel/server/7/x86_64/debug", true},
|
||||||
|
{ "variables-first-contents.list", "/content/rc/rhel/server/7/x86_64/debug/tools/firefox", true},
|
||||||
|
{ "variables-first-contents.list", "/content/rc/rhel/server/7/x86_64/Debug", false},
|
||||||
|
{ "variables-first-contents.list", "/content/rc/rhel/server/7/Debug", false},
|
||||||
|
{ "variables-first-contents.list", "/content/rc/rhel/server/7/x86_64/os", true},
|
||||||
|
|
||||||
|
{ "variables-mixed-contents.list", "/content/rc/rhel/server/7/x86_64/debug", true},
|
||||||
|
{ "variables-mixed-contents.list", "/content/rc/rhel/server/7/x86_64/debug/tools/firefox", true},
|
||||||
|
{ "variables-mixed-contents.list", "/content/rc/rhel/server/7/x86_64/Debug", false},
|
||||||
|
{ "variables-mixed-contents.list", "/content/rc/rhel/server/7/Debug", false},
|
||||||
|
{ "variables-mixed-contents.list", "/content/rc/rhel/server/7/x86_64/os", true},
|
||||||
|
|
||||||
|
{ "another-contents.list", "/content/rc/rhel/server/7/x86_64/os", true},
|
||||||
|
{ "another-contents.list", "/content/rc/rhel/server/7/x86_64/debug/os", false}
|
||||||
|
};
|
||||||
|
return Arrays.asList(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instatiate a test class with the given parameters.
|
||||||
|
* @param contentSetFileName The name of the file containing the allowed content sets.
|
||||||
|
* @param requestedPath The resource being requested
|
||||||
|
* @param expectedResult The correct response.
|
||||||
|
*/
|
||||||
|
public TestValidater(final String contentSetFileName, final String requestedPath, final boolean expectedResult) {
|
||||||
|
this.contentSetFileName = contentSetFileName;
|
||||||
|
this.requestedPath = requestedPath;
|
||||||
|
this.expectedResult = expectedResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare for a test run.
|
||||||
|
*/
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
List<String> validContentPaths = TestHelpers.loadContents(this, contentSetFileName);
|
||||||
|
pathTree = new PathTree();
|
||||||
|
pathTree.setContentSets(validContentPaths);
|
||||||
|
assertTrue(TestHelpers.cmpStrings(validContentPaths, pathTree.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the test.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void justDoIt() throws Exception {
|
||||||
|
assertEquals("[" + requestedPath + "] " + (expectedResult?"allowed ":"not allowed ") + "using " + contentSetFileName,
|
||||||
|
expectedResult, pathTree.validate(requestedPath));
|
||||||
|
}
|
||||||
|
}
|
1
src/test/resources/another-contents.list
Normal file
1
src/test/resources/another-contents.list
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/content/rc/rhel/server/$releasever/$basearch/os
|
0
src/test/resources/emtpy.contents.list
Normal file
0
src/test/resources/emtpy.contents.list
Normal file
4
src/test/resources/log4j.properties
Normal file
4
src/test/resources/log4j.properties
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
log4j.rootLogger=DEBUG, R
|
||||||
|
log4j.appender.R=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.R.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.R.layout.ConversionPattern=[%-5p] %c{1} - %m%n
|
16
src/test/resources/variables-first-contents.list
Normal file
16
src/test/resources/variables-first-contents.list
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/os
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/os
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/x86_64/debug
|
||||||
|
/content/rc/rhel/server/7/x86_64/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/kickstart
|
||||||
|
/content/rc/rhel/server/7/x86_64/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/source/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/debug
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/source/SRPMS
|
16
src/test/resources/variables-last-contents.list
Normal file
16
src/test/resources/variables-last-contents.list
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/content/rc/rhel/server/7/x86_64/debug
|
||||||
|
/content/rc/rhel/server/7/x86_64/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/kickstart
|
||||||
|
/content/rc/rhel/server/7/x86_64/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/source/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/debug
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/os
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/os
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/source/SRPMS
|
16
src/test/resources/variables-mixed-contents.list
Normal file
16
src/test/resources/variables-mixed-contents.list
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/x86_64/debug
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/os
|
||||||
|
/content/rc/rhel/server/7/$basearch/highavailability/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/x86_64/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/kickstart
|
||||||
|
/content/rc/rhel/server/7/x86_64/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/source/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/source/SRPMS
|
||||||
|
/content/rc/rhel/server/7/$basearch/resilientstorage/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/debug
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/iso
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/os
|
||||||
|
/content/rc/rhel/server/7/x86_64/supplementary/source/SRPMS
|
Loading…
Reference in a new issue