diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/Authorizations.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/Authorizations.java index 2a07625987b..a82d6d8b3f2 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/Authorizations.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/Authorizations.java @@ -36,22 +36,11 @@ public class Authorizations { public Authorizations(String... labels) { this.labels = new ArrayList(labels.length); for (String label : labels) { - validateLabel(label); this.labels.add(label); } } - private void validateLabel(String label) { - if (!VisibilityLabelsValidator.isValidLabel(label)) { - throw new IllegalArgumentException("Invalid authorization label : " + label - + ". Authorizations cannot contain '(', ')' ,'&' ,'|', '!'" + " and cannot be empty"); - } - } - public Authorizations(List labels) { - for (String label : labels) { - validateLabel(label); - } this.labels = labels; } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/CellVisibility.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/CellVisibility.java index 3bd527b0491..0e71a0a7e78 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/CellVisibility.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/CellVisibility.java @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.security.visibility; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.hbase.util.Bytes; /** * This contains a visibility expression which can be associated with a cell. When it is set with a @@ -46,4 +47,40 @@ public class CellVisibility { public String toString() { return this.expression; } + + /** + * Helps in quoting authentication Strings. Use this if unicode characters to + * be used in expression or special characters like '(', ')', + * '"','\','&','|','!' + */ + public static String quote(String auth) { + return quote(Bytes.toBytes(auth)); + } + + /** + * Helps in quoting authentication Strings. Use this if unicode characters to + * be used in expression or special characters like '(', ')', + * '"','\','&','|','!' + */ + public static String quote(byte[] auth) { + int escapeChars = 0; + + for (int i = 0; i < auth.length; i++) + if (auth[i] == '"' || auth[i] == '\\') + escapeChars++; + + byte[] escapedAuth = new byte[auth.length + escapeChars + 2]; + int index = 1; + for (int i = 0; i < auth.length; i++) { + if (auth[i] == '"' || auth[i] == '\\') { + escapedAuth[index++] = '\\'; + } + escapedAuth[index++] = auth[i]; + } + + escapedAuth[0] = '"'; + escapedAuth[escapedAuth.length - 1] = '"'; + + return Bytes.toString(escapedAuth); + } } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsValidator.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsValidator.java index 9700b5545b7..49b1778dd74 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsValidator.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsValidator.java @@ -17,7 +17,6 @@ */ package org.apache.hadoop.hbase.security.visibility; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hadoop.classification.InterfaceAudience; @@ -60,7 +59,7 @@ public class VisibilityLabelsValidator { return validAuthChars[0xff & b]; } - static final boolean isValidLabel(byte[] label) { + public static final boolean isValidLabel(byte[] label) { for (int i = 0; i < label.length; i++) { if (!isValidAuthChar(label[i])) { return false; @@ -68,9 +67,4 @@ public class VisibilityLabelsValidator { } return true; } - - public static final boolean isValidLabel(String label) { - Matcher matcher = pattern.matcher(label); - return matcher.matches(); - } } diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestScan.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestScan.java index 97c5f0f418f..10da121792f 100644 --- a/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestScan.java +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestScan.java @@ -114,56 +114,21 @@ public class TestScan { @Test public void testSetAuthorizations() { Scan scan = new Scan(); - scan.setAuthorizations(new Authorizations("A", "B", "0123", "A0", "1A1", "_a")); try { + scan.setAuthorizations(new Authorizations("\u002b|\u0029")); + scan.setAuthorizations(new Authorizations("A", "B", "0123", "A0", "1A1", "_a")); scan.setAuthorizations(new Authorizations("A|B")); - fail("Should have failed for A|B."); - } catch (IllegalArgumentException e) { - } - try { scan.setAuthorizations(new Authorizations("A&B")); - fail("Should have failed for A&B."); - } catch (IllegalArgumentException e) { - } - try { scan.setAuthorizations(new Authorizations("!B")); - fail("Should have failed for !B."); - } catch (IllegalArgumentException e) { - } - try { scan.setAuthorizations(new Authorizations("A", "(A)")); - fail("Should have failed for (A)."); - } catch (IllegalArgumentException e) { - } - try { scan.setAuthorizations(new Authorizations("A", "{A")); - fail("Should have failed for {A."); - } catch (IllegalArgumentException e) { - } - try { scan.setAuthorizations(new Authorizations(" ")); - fail("Should have failed for empty"); - } catch (IllegalArgumentException e) { - } - try { scan.setAuthorizations(new Authorizations(":B")); - } catch (IllegalArgumentException e) { - fail("Should not have failed for :B"); - } - try { scan.setAuthorizations(new Authorizations("-B")); - } catch (IllegalArgumentException e) { - fail("Should not have failed for -B"); - } - try { scan.setAuthorizations(new Authorizations(".B")); - } catch (IllegalArgumentException e) { - fail("Should not have failed for .B"); - } - try { scan.setAuthorizations(new Authorizations("/B")); } catch (IllegalArgumentException e) { - fail("Should not have failed for /B"); + fail("should not throw exception"); } } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java index 2fa53f4fa1d..1fc0a5dda35 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java @@ -70,12 +70,11 @@ import org.apache.hadoop.hbase.filter.WhileMatchFilter; import org.apache.hadoop.hbase.rest.ProtobufMessageHandler; import org.apache.hadoop.hbase.rest.protobuf.generated.ScannerMessage.Scanner; import org.apache.hadoop.hbase.security.visibility.Authorizations; -import org.apache.hadoop.hbase.security.visibility.VisibilityLabelsValidator; import org.apache.hadoop.hbase.util.Base64; +import org.apache.hadoop.hbase.util.ByteStringer; import org.apache.hadoop.hbase.util.Bytes; import com.google.protobuf.ByteString; -import org.apache.hadoop.hbase.util.ByteStringer; import com.sun.jersey.api.json.JSONConfiguration; import com.sun.jersey.api.json.JSONJAXBContext; import com.sun.jersey.api.json.JSONMarshaller; @@ -527,11 +526,6 @@ public class ScannerModel implements ProtobufMessageHandler, Serializable { if (authorizations != null) { List labels = authorizations.getLabels(); for (String label : labels) { - if (!VisibilityLabelsValidator.isValidLabel(label)) { - throw new IllegalArgumentException("Invalid authorization label : " + label - + ". Authorizations cannot contain '(', ')' ,'&' ,'|', '!'" + " " + - "and cannot be empty"); - } model.addLabel(label); } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/ExpressionParser.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/ExpressionParser.java index f6ddf75bc98..bb17b482b78 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/ExpressionParser.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/ExpressionParser.java @@ -17,6 +17,8 @@ */ package org.apache.hadoop.hbase.security.visibility; +import java.util.ArrayList; +import java.util.List; import java.util.Stack; import org.apache.hadoop.classification.InterfaceAudience; @@ -35,49 +37,80 @@ public class ExpressionParser { private static final char AND = '&'; private static final char NOT = '!'; private static final char SPACE = ' '; - + private static final char DOUBLE_QUOTES = '"'; public ExpressionNode parse(String expS) throws ParseException { expS = expS.trim(); Stack expStack = new Stack(); int index = 0; - int endPos = expS.length(); byte[] exp = Bytes.toBytes(expS); + int endPos = exp.length; while (index < endPos) { byte b = exp[index]; switch (b) { - case OPEN_PARAN: - processOpenParan(expStack, expS, index); - index = skipSpaces(exp, index); - break; - case CLOSE_PARAN: - processCloseParan(expStack, expS, index); - index = skipSpaces(exp, index); - break; - case AND: - case OR: - processANDorOROp(getOperator(b), expStack, expS, index); - index = skipSpaces(exp, index); - break; - case NOT: - processNOTOp(expStack, expS, index); - break; - default: - int labelOffset = index; - do { - if (!VisibilityLabelsValidator.isValidAuthChar(exp[index])) { - throw new ParseException("Error parsing expression " + expS + " at column : " - + index); + case OPEN_PARAN: + processOpenParan(expStack, expS, index); + index = skipSpaces(exp, index); + break; + case CLOSE_PARAN: + processCloseParan(expStack, expS, index); + index = skipSpaces(exp, index); + break; + case AND: + case OR: + processANDorOROp(getOperator(b), expStack, expS, index); + index = skipSpaces(exp, index); + break; + case NOT: + processNOTOp(expStack, expS, index); + break; + case DOUBLE_QUOTES: + int labelOffset = ++index; + // We have to rewrite the expression within double quotes as incase of expressions + // with escape characters we may have to avoid them as the original expression did + // not have them + List list = new ArrayList(); + while (index < endPos && !endDoubleQuotesFound(exp[index])) { + if (exp[index] == '\\') { + index++; + if (exp[index] != '\\' && exp[index] != '"') + throw new ParseException("invalid escaping with quotes " + expS + " at column : " + + index); + } + list.add(exp[index]); + index++; } - index++; - } while (index < endPos && !isEndOfLabel(exp[index])); - String leafExp = new String(exp, labelOffset, index - labelOffset).trim(); - if (leafExp.isEmpty()) { - throw new ParseException("Error parsing expression " + expS + " at column : " + index); - } - processLabelExpNode(new LeafExpressionNode(leafExp), expStack, expS, index); - // We already crossed the label node index. So need to reduce 1 here. - index--; - index = skipSpaces(exp, index); + // The expression has come to the end. still no double quotes found + if(index == endPos) { + throw new ParseException("No terminating quotes " + expS + " at column : " + index); + } + // This could be costly. but do we have any alternative? + // If we don't do this way then we may have to handle while checking the authorizations. + // Better to do it here. + byte[] array = com.google.common.primitives.Bytes.toArray(list); + String leafExp = new String(array).trim(); + if (leafExp.isEmpty()) { + throw new ParseException("Error parsing expression " + expS + " at column : " + index); + } + processLabelExpNode(new LeafExpressionNode(leafExp), expStack, expS, index); + index = skipSpaces(exp, index); + break; + default: + labelOffset = index; + do { + if (!VisibilityLabelsValidator.isValidAuthChar(exp[index])) { + throw new ParseException("Error parsing expression " + + expS + " at column : " + index); + } + index++; + } while (index < endPos && !isEndOfLabel(exp[index])); + leafExp = new String(exp, labelOffset, index - labelOffset).trim(); + if (leafExp.isEmpty()) { + throw new ParseException("Error parsing expression " + expS + " at column : " + index); + } + processLabelExpNode(new LeafExpressionNode(leafExp), expStack, expS, index); + // We already crossed the label node index. So need to reduce 1 here. + index--; + index = skipSpaces(exp, index); } index++; } @@ -255,8 +288,12 @@ public class ExpressionParser { expStack.push(new NonLeafExpressionNode(Operator.NOT)); } + private static boolean endDoubleQuotesFound(byte b) { + return (b == DOUBLE_QUOTES); + } private static boolean isEndOfLabel(byte b) { - return (b == OPEN_PARAN || b == CLOSE_PARAN || b == OR || b == AND || b == NOT || b == SPACE); + return (b == OPEN_PARAN || b == CLOSE_PARAN || b == OR || b == AND || + b == NOT || b == SPACE); } private static Operator getOperator(byte op) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java index 661d1882643..9439ea50971 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java @@ -53,11 +53,11 @@ import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.KeyValue.Type; import org.apache.hadoop.hbase.KeyValueUtil; -import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Tag; +import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.client.Append; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; @@ -1149,13 +1149,6 @@ public class VisibilityController extends BaseRegionObserver implements MasterOb if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) { return null; } - } else { - for (String label : authorizations.getLabels()) { - if (!VisibilityLabelsValidator.isValidLabel(label)) { - throw new IllegalArgumentException("Invalid authorization label : " + label - + ". Authorizations cannot contain '(', ')' ,'&' ,'|', '!'" + " and cannot be empty"); - } - } } Filter visibilityLabelFilter = null; if (this.scanLabelGenerators != null) { @@ -1310,30 +1303,21 @@ public class VisibilityController extends BaseRegionObserver implements MasterOb for (VisibilityLabel visLabel : labels) { byte[] label = visLabel.getLabel().toByteArray(); String labelStr = Bytes.toString(label); - if (VisibilityLabelsValidator.isValidLabel(label)) { - if (this.visibilityManager.getLabelOrdinal(labelStr) > 0) { - RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); - failureResultBuilder.setException(ResponseConverter - .buildException(new LabelAlreadyExistsException("Label '" + labelStr - + "' already exists"))); - response.addResult(failureResultBuilder.build()); - } else { - Put p = new Put(Bytes.toBytes(ordinalCounter)); - p.addImmutable( - LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS); - if (LOG.isDebugEnabled()) { - LOG.debug("Adding the label "+labelStr); - } - puts.add(p); - ordinalCounter++; - response.addResult(successResult); - } - } else { + if (this.visibilityManager.getLabelOrdinal(labelStr) > 0) { RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException(ResponseConverter - .buildException(new InvalidLabelException("Invalid visibility label '" + labelStr - + "'"))); + .buildException(new LabelAlreadyExistsException("Label '" + labelStr + + "' already exists"))); response.addResult(failureResultBuilder.build()); + } else { + Put p = new Put(Bytes.toBytes(ordinalCounter)); + p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS); + if (LOG.isDebugEnabled()) { + LOG.debug("Adding the label " + labelStr); + } + puts.add(p); + ordinalCounter++; + response.addResult(successResult); } } OperationStatus[] opStatus = this.regionEnv.getRegion().batchMutate( diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestExpressionParser.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestExpressionParser.java index f7a8dfdddc0..ed2a1d511b8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestExpressionParser.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestExpressionParser.java @@ -17,9 +17,9 @@ */ package org.apache.hadoop.hbase.security.visibility; -import static org.junit.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import org.apache.hadoop.hbase.SmallTests; import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode; @@ -308,6 +308,99 @@ public class TestExpressionParser { executeNegativeCase("! a"); } + @Test + public void testNonAsciiCases() throws Exception { + ExpressionNode node = parser.parse(CellVisibility.quote("\u0027") + "&" + + CellVisibility.quote("\u002b") + "|" + CellVisibility.quote("\u002d") + "&" + + CellVisibility.quote("\u003f")); + assertTrue(node instanceof NonLeafExpressionNode); + NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node; + assertEquals(Operator.AND, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u003f", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); + assertEquals(Operator.OR, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u002d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); + assertEquals(Operator.AND, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u002b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertEquals("\u0027", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); + + node = parser.parse(CellVisibility.quote("\u0027") + "&" + CellVisibility.quote("\u002b") + "|" + + CellVisibility.quote("\u002d") + "&" + CellVisibility.quote("\u003f")); + assertTrue(node instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) node; + assertEquals(Operator.AND, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u003f", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); + assertEquals(Operator.OR, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u002d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); + assertEquals(Operator.AND, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u002b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertEquals("\u0027", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); + } + + @Test + public void testCasesSeperatedByDoubleQuotes() throws Exception { + ExpressionNode node = null; + try { + node = parser.parse("\u0027&\"|\u002b&\u003f"); + fail("Excpetion must be thrown as there are special characters without quotes"); + } catch (ParseException e) { + } + node = parser.parse(CellVisibility.quote("\u0027") + "&" + CellVisibility.quote("\"") + "|" + + CellVisibility.quote("\u002b" + "&" + "\u003f")); + assertTrue(node instanceof NonLeafExpressionNode); + NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node; + assertEquals(Operator.OR, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u002b" + "&" + "\u003f", + ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); + assertEquals(Operator.AND, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\"", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertEquals("\u0027", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); + try { + node = parser.parse(CellVisibility.quote("\u0027&\\") + "|" + + CellVisibility.quote("\u002b" + "&" + "\\") + CellVisibility.quote("$$\"")); + fail("Excpetion must be thrown as there is not operator"); + } catch (ParseException e) { + } + node = parser.parse(CellVisibility.quote("\u0027" + "&" + "\\") + "|" + + CellVisibility.quote("\u003f" + "&" + "\\") + "&" + CellVisibility.quote("$$\"")); + assertTrue(node instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) node; + assertEquals(Operator.AND, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("$$\"", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); + nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); + assertEquals(Operator.OR, nlNode.getOperator()); + assertEquals(2, nlNode.getChildExps().size()); + assertEquals("\u0027" + "&" + "\\", + ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); + assertEquals("\u003f" + "&" + "\\", + ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); + try { + node = parser.parse(CellVisibility.quote("\u002b&\\") + "|" + CellVisibility.quote("\u0027&\\") + "&" + + "\"$$"); + fail("Excpetion must be thrown as there is no end quote"); + } catch (ParseException e) { + } + } + private void executeNegativeCase(String exp) { try { parser.parse(exp); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabels.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabels.java index 84ecf6a97e3..654dfd9bc27 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabels.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabels.java @@ -83,6 +83,12 @@ public class TestVisibilityLabels { private static final String PRIVATE = "private"; private static final String CONFIDENTIAL = "confidential"; private static final String SECRET = "secret"; + private static final String COPYRIGHT = "\u00A9ABC"; + private static final String ACCENT = "\u0941"; + private static final String UNICODE_VIS_TAG = COPYRIGHT + "\"" + ACCENT + "\\" + SECRET + "\"" + + "\u0027&\\"; + private static final String UC1 = "\u0027\"\u002b"; + private static final String UC2 = "\u002d\u003f"; public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static final byte[] row1 = Bytes.toBytes("row1"); private static final byte[] row2 = Bytes.toBytes("row2"); @@ -154,6 +160,77 @@ public class TestVisibilityLabels { } } } + + @Test + public void testSimpleVisibilityLabelsWithUniCodeCharacters() throws Exception { + TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); + HTable table = createTableAndWriteDataWithLabels(tableName, + SECRET + "|" + CellVisibility.quote(COPYRIGHT), "(" + CellVisibility.quote(COPYRIGHT) + "&" + + CellVisibility.quote(ACCENT) + ")|" + CONFIDENTIAL, + CellVisibility.quote(UNICODE_VIS_TAG) + "&" + SECRET); + try { + Scan s = new Scan(); + s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL, PRIVATE, COPYRIGHT, ACCENT, + UNICODE_VIS_TAG)); + ResultScanner scanner = table.getScanner(s); + Result[] next = scanner.next(3); + assertTrue(next.length == 3); + CellScanner cellScanner = next[0].cellScanner(); + cellScanner.advance(); + Cell current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row1, 0, row1.length)); + cellScanner = next[1].cellScanner(); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row2, 0, row2.length)); + cellScanner = next[2].cellScanner(); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row3, 0, row3.length)); + } finally { + if (table != null) { + table.close(); + } + } + } + + @Test + public void testAuthorizationsWithSpecialUnicodeCharacters() throws Exception { + TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); + HTable table = createTableAndWriteDataWithLabels(tableName, + CellVisibility.quote(UC1) + "|" + CellVisibility.quote(UC2), CellVisibility.quote(UC1), + CellVisibility.quote(UNICODE_VIS_TAG)); + try { + Scan s = new Scan(); + s.setAuthorizations(new Authorizations(UC1, UC2, ACCENT, + UNICODE_VIS_TAG)); + ResultScanner scanner = table.getScanner(s); + Result[] next = scanner.next(3); + assertTrue(next.length == 3); + CellScanner cellScanner = next[0].cellScanner(); + cellScanner.advance(); + Cell current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row1, 0, row1.length)); + cellScanner = next[1].cellScanner(); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row2, 0, row2.length)); + cellScanner = next[2].cellScanner(); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row3, 0, row3.length)); + } finally { + if (table != null) { + table.close(); + } + } + } @Test public void testVisibilityLabelsWithComplexLabels() throws Exception { @@ -406,7 +483,7 @@ public class TestVisibilityLabels { } } // One label is the "system" label. - Assert.assertEquals("The count should be 8", 8, i); + Assert.assertEquals("The count should be 13", 13, i); } private void waitForLabelsRegionAvailability(HRegionServer regionServer) { @@ -466,8 +543,7 @@ public class TestVisibilityLabels { assertEquals("org.apache.hadoop.hbase.security.visibility.LabelAlreadyExistsException", resultList.get(1).getException().getName()); assertTrue(resultList.get(2).getException().getValue().isEmpty()); - assertEquals("org.apache.hadoop.hbase.security.visibility.InvalidLabelException", - resultList.get(3).getException().getName()); + assertTrue(resultList.get(3).getException().getValue().isEmpty()); assertTrue(resultList.get(4).getException().getValue().isEmpty()); return null; } @@ -929,7 +1005,8 @@ public class TestVisibilityLabels { PrivilegedExceptionAction action = new PrivilegedExceptionAction() { public VisibilityLabelsResponse run() throws Exception { - String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE }; + String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE, COPYRIGHT, ACCENT, + UNICODE_VIS_TAG, UC1, UC2 }; try { VisibilityClient.addLabels(conf, labels); } catch (Throwable t) {