Author: kturner
Date: Tue Sep 4 18:56:36 2012
New Revision: 1380809
URL: http://svn.apache.org/viewvc?rev=1380809&view=rev
Log:
ACCUMULO-241 Added quoting to visibility labels
Modified:
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/ColumnVisibilityTest.java
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
Modified:
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
URL:
http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java?rev=1380809&r1=1380808&r2=1380809&view=diff
==============================================================================
---
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
(original)
+++
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
Tue Sep 4 18:56:36 2012
@@ -75,12 +75,6 @@ public class Authorizations implements I
throw new IllegalArgumentException("Empty authorization");
}
- for (byte b : bs.getBackingArray()) {
- if (!isValidAuthChar(b)) {
- throw new IllegalArgumentException("invalid authorization " +
bs.toString());
- }
- }
-
authsList.add(bs.getBackingArray());
}
}
Modified:
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
URL:
http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java?rev=1380809&r1=1380808&r2=1380809&view=diff
==============================================================================
---
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
(original)
+++
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
Tue Sep 4 18:56:36 2012
@@ -16,12 +16,15 @@
*/
package org.apache.accumulo.core.security;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import org.apache.accumulo.core.data.ArrayByteSequence;
+import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.util.BadArgumentException;
import org.apache.accumulo.core.util.TextUtil;
import org.apache.hadoop.io.Text;
@@ -88,6 +91,20 @@ public class ColumnVisibility {
public int getTermEnd() {
return end;
}
+
+ public ByteSequence getTerm(byte expression[]) {
+ if (type != NodeType.TERM)
+ throw new RuntimeException();
+
+ if (expression[start] == '"') {
+ // its a quoted term
+ int qStart = start + 1;
+ int qEnd = end - 1;
+
+ return new ArrayByteSequence(expression, qStart, qEnd - qStart);
+ }
+ return new ArrayByteSequence(expression, start, end - start);
+ }
}
public static class NodeComparator implements Comparator<Node> {
@@ -181,6 +198,8 @@ public class ColumnVisibility {
Node result = null;
Node expr = null;
int termStart = index;
+ boolean termComplete = false;
+
while (index < expression.length) {
switch (expression[index++]) {
case '&': {
@@ -194,6 +213,7 @@ public class ColumnVisibility {
result.add(expr);
expr = null;
termStart = index;
+ termComplete = false;
break;
}
case '|': {
@@ -207,6 +227,7 @@ public class ColumnVisibility {
result.add(expr);
expr = null;
termStart = index;
+ termComplete = false;
break;
}
case '(': {
@@ -215,6 +236,7 @@ public class ColumnVisibility {
throw new BadArgumentException("expression needs & or |", new
String(expression), index - 1);
expr = parse_(expression);
termStart = index;
+ termComplete = false;
break;
}
case ')': {
@@ -232,7 +254,35 @@ public class ColumnVisibility {
result.end = index - 1;
return result;
}
+ case '"': {
+ if (termStart != index - 1)
+ throw new BadArgumentException("expression needs & or |", new
String(expression), index - 1);
+
+ while (index < expression.length && expression[index] != '"') {
+ if (expression[index] == '\\') {
+ index++;
+ if (expression[index] != '\\' && expression[index] != '"')
+ throw new BadArgumentException("invalid escaping within
quotes", new String(expression), index - 1);
+ }
+ index++;
+ }
+
+ if (index == expression.length)
+ throw new BadArgumentException("unclosed quote", new
String(expression), termStart);
+
+ if (termStart + 1 == index)
+ throw new BadArgumentException("empty term", new
String(expression), termStart);
+
+ index++;
+
+ termComplete = true;
+
+ break;
+ }
default: {
+ if (termComplete)
+ throw new BadArgumentException("expression needs & or |", new
String(expression), index - 1);
+
byte c = expression[index - 1];
if (!Authorizations.isValidAuthChar(c))
throw new BadArgumentException("bad character (" + c + ")", new
String(expression), index - 1);
@@ -295,6 +345,7 @@ public class ColumnVisibility {
*
* </pre>
*
+ * <P>
* The following are not valid expressions for visibility:
*
* <pre>
@@ -306,6 +357,15 @@ public class ColumnVisibility {
* )
* dog|!cat
* </pre>
+ *
+ * <P>
+ * You can use any character you like in your column visibility
expression with quoting. If your quoted term contains '"' or '\' then
escape
+ * them with '\'. The {@link #quote(String)} method will properly
quote and escape terms for you.
+ *
+ * <pre>
+ * "A#C"&B
+ * </pre>
+ *
*/
public ColumnVisibility(byte[] expression) {
validate(expression);
@@ -341,4 +401,48 @@ public class ColumnVisibility {
public Node getParseTree() {
return node;
}
+
+ /**
+ * see {@link #quote(byte[])}
+ *
+ */
+ public static String quote(String term) {
+ try {
+ return new String(quote(term.getBytes("UTF-8")), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Use to properly quote terms in a column visibility expression. If no
quoting is needed, then nothing is done.
+ *
+ * <p>
+ * Examples of using quote :
+ *
+ * <pre>
+ * import static org.apache.accumulo.core.security.ColumnVisibility.quote;
+ * .
+ * .
+ * .
+ * ColumnVisibility cv = new ColumnVisibility(quote("A#C") +
"&" + quote("FOO"));
+ * </pre>
+ *
+ */
+
+ public static byte[] quote(byte[] term) {
+ boolean needsQuote = false;
+
+ for (int i = 0; i < term.length; i++) {
+ if (!Authorizations.isValidAuthChar(term[i])) {
+ needsQuote = true;
+ break;
+ }
+ }
+
+ if (!needsQuote)
+ return term;
+
+ return VisibilityEvaluator.escape(term, true);
+ }
}
Modified:
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
URL:
http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java?rev=1380809&r1=1380808&r2=1380809&view=diff
==============================================================================
---
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
(original)
+++
accumulo/trunk/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
Tue Sep 4 18:56:36 2012
@@ -16,23 +16,58 @@
*/
package org.apache.accumulo.core.security;
+import java.util.ArrayList;
import java.util.Collection;
-import org.apache.accumulo.core.data.ArrayByteSequence;
import org.apache.accumulo.core.security.ColumnVisibility.Node;
public class VisibilityEvaluator {
private Authorizations auths;
+ static Authorizations escape(Authorizations auths) {
+ ArrayList<byte[]> retAuths = new
ArrayList<byte[]>(auths.getAuthorizations().size());
+
+ for (byte[] auth : auths.getAuthorizations())
+ retAuths.add(escape(auth, false));
+
+ return new Authorizations(retAuths);
+ }
+
+ public static byte[] escape(byte[] auth, boolean quote) {
+ int escapeCount = 0;
+
+ for (int i = 0; i < auth.length; i++)
+ if (auth[i] == '"' || auth[i] == '\\')
+ escapeCount++;
+
+ if (escapeCount > 0 || quote) {
+ byte[] escapedAuth = new byte[auth.length + escapeCount + (quote ? 2 :
0)];
+ int index = quote ? 1 : 0;
+ for (int i = 0; i < auth.length; i++) {
+ if (auth[i] == '"' || auth[i] == '\\')
+ escapedAuth[index++] = '\\';
+ escapedAuth[index++] = auth[i];
+ }
+
+ if (quote) {
+ escapedAuth[0] = '"';
+ escapedAuth[escapedAuth.length - 1] = '"';
+ }
+
+ auth = escapedAuth;
+ }
+ return auth;
+ }
+
VisibilityEvaluator(Collection<byte[]> authorizations) {
- this.auths = new Authorizations(authorizations);
+ this(new Authorizations(authorizations));
}
/**
* The VisibilityEvaluator computes a trie from the given Authorizations,
that ColumnVisibility expressions can be evaluated against.
*/
public VisibilityEvaluator(Authorizations authorizations) {
- this.auths = authorizations;
+ this.auths = escape(authorizations);
}
public Authorizations getAuthorizations() {
@@ -46,8 +81,7 @@ public class VisibilityEvaluator {
private final boolean evaluate(final byte[] expression, final Node root)
throws VisibilityParseException {
switch (root.type) {
case TERM:
- int len = root.getTermEnd() - root.getTermStart();
- return auths.contains(new ArrayByteSequence(expression,
root.getTermStart(), len));
+ return auths.contains(root.getTerm(expression));
case AND:
if (root.children == null || root.children.size() < 2)
throw new VisibilityParseException("AND has less than 2 children",
expression, root.start);
Modified:
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/ColumnVisibilityTest.java
URL:
http://svn.apache.org/viewvc/accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/ColumnVisibilityTest.java?rev=1380809&r1=1380808&r2=1380809&view=diff
==============================================================================
---
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/ColumnVisibilityTest.java
(original)
+++
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/ColumnVisibilityTest.java
Tue Sep 4 18:56:36 2012
@@ -108,4 +108,24 @@ public class ColumnVisibilityTest {
shouldThrow("(A&B)|(C&D)&(E)");
shouldThrow("a|b&c", "A&B&C|D", "(A&B)|(C&D)&(E)");
}
+
+ @Test
+ public void testQuotes() {
+ shouldThrow("\"\"");
+ shouldThrow("\"A\"A");
+ shouldThrow("\"A\"\"B\"");
+ shouldThrow("(A)\"B\"");
+ shouldThrow("\"A\"(B)");
+ shouldThrow("\"A");
+ shouldThrow("\"");
+ shouldThrow("\"B");
+ shouldThrow("A&\"B");
+ shouldThrow("A&\"B\\'");
+
+ shouldNotThrow("\"A\"");
+ shouldNotThrow("(\"A\")");
+ shouldNotThrow("A&\"B.D\"");
+ shouldNotThrow("A&\"B\\\\D\"");
+ shouldNotThrow("A&\"B\\\"D\"");
+ }
}
Modified:
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
URL:
http://svn.apache.org/viewvc/accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java?rev=1380809&r1=1380808&r2=1380809&view=diff
==============================================================================
---
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
(original)
+++
accumulo/trunk/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
Tue Sep 4 18:56:36 2012
@@ -16,10 +16,13 @@
*/
package org.apache.accumulo.core.security;
+import static org.apache.accumulo.core.security.ColumnVisibility.quote;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import org.apache.accumulo.core.util.BadArgumentException;
import org.apache.accumulo.core.util.ByteArraySet;
import org.junit.Test;
@@ -54,7 +57,7 @@ public class VisibilityEvaluatorTest {
try {
ct.evaluate(new ColumnVisibility(marking));
fail(marking + " failed to throw");
- } catch (Throwable e) {
+ } catch (BadArgumentException e) {
// all is good
}
}
@@ -64,7 +67,7 @@ public class VisibilityEvaluatorTest {
try {
ct.evaluate(new ColumnVisibility(marking));
fail(marking + " failed to throw");
- } catch (Throwable e) {
+ } catch (BadArgumentException e) {
// all is good
}
}
@@ -74,9 +77,33 @@ public class VisibilityEvaluatorTest {
try {
ct.evaluate(new ColumnVisibility(marking));
fail(marking + " failed to throw");
- } catch (Throwable e) {
+ } catch (BadArgumentException e) {
// all is good
}
}
}
+
+ @Test
+ public void testQuotedExpressions() throws VisibilityParseException {
+ VisibilityEvaluator ct = new
VisibilityEvaluator(ByteArraySet.fromStrings("A#C", "A\"C", "A\\C", "AC"));
+
+ assertTrue(ct.evaluate(new ColumnVisibility(quote("A#C") + "|" +
quote("A?C"))));
+ assertTrue(ct.evaluate(new ColumnVisibility(new
ColumnVisibility(quote("A#C") + "|" + quote("A?C")).flatten())));
+ assertTrue(ct.evaluate(new ColumnVisibility(quote("A\"C") + "&" +
quote("A\\C"))));
+ assertTrue(ct.evaluate(new ColumnVisibility(new
ColumnVisibility(quote("A\"C") + "&" + quote("A\\C")).flatten())));
+ assertTrue(ct.evaluate(new ColumnVisibility("(" + quote("A\"C") + "|B)&("
+ quote("A#C") + "|D)")));
+
+ assertFalse(ct.evaluate(new ColumnVisibility(quote("A#C") + "&B")));
+
+ assertTrue(ct.evaluate(new ColumnVisibility(quote("A#C"))));
+ assertTrue(ct.evaluate(new ColumnVisibility("(" + quote("A#C") + ")")));
+ }
+
+ @Test
+ public void testQuote() {
+ assertEquals("\"A#C\"", quote("A#C"));
+ assertEquals("\"A\\\"C\"", quote("A\"C"));
+ assertEquals("\"A\\\"\\\\C\"", quote("A\"\\C"));
+ assertEquals("ACS", quote("ACS"));
+ }
}