accumulo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ktur...@apache.org
Subject svn commit: r1380809 - in /accumulo/trunk/core/src: main/java/org/apache/accumulo/core/security/ test/java/org/apache/accumulo/core/security/
Date Tue, 04 Sep 2012 18:56:36 GMT
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 '&quot;' or '\' then escape
+   *          them with '\'. The {@link #quote(String)} method will properly quote and escape
terms for you.
+   * 
+   *          <pre>
+   * &quot;A#C&quot;&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(&quot;A#C&quot;) + &quot;&amp;&quot;
+ quote(&quot;FOO&quot;));
+   * </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"));
+  }
 }



Mime
View raw message