You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by ca...@apache.org on 2017/06/01 01:22:22 UTC
[2/3] incubator-rya git commit: RYA-119 Added MongoDB Column
Visibility (called Document Visibility). This adds a new field to each
document called documentVisibility which uses a boolean expression to
determine if the user can access the document. The b
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DisjunctiveNormalFormConverter.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DisjunctiveNormalFormConverter.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DisjunctiveNormalFormConverter.java
new file mode 100644
index 0000000..43d3e13
--- /dev/null
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DisjunctiveNormalFormConverter.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rya.mongodb.document.util;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.ColumnVisibility.Node;
+import org.apache.accumulo.core.security.ColumnVisibility.NodeType;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.rya.mongodb.document.visibility.DocumentVisibility;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+
+/**
+ * Utility for converting document visibility boolean expressions into
+ * Disjunctive Normal Form.
+ */
+public final class DisjunctiveNormalFormConverter {
+ private static final Logger log = Logger.getLogger(DisjunctiveNormalFormConverter.class);
+
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private DisjunctiveNormalFormConverter() {
+ }
+
+ /**
+ * Creates a new document visibility based on the boolean expression string
+ * that is converted into disjunctive normal form.
+ * @param expression the boolean string expression.
+ * @return the {@link DocumentVisibility} in DNF.
+ */
+ public static DocumentVisibility createDnfDocumentVisibility(final String expression) {
+ return createDnfDocumentVisibility(expression.getBytes(UTF_8));
+ }
+
+ /**
+ * Creates a new document visibility based on the boolean expression that is
+ * converted into disjunctive normal form.
+ * @param expression the boolean expression bytes.
+ * @return the {@link DocumentVisibility} in DNF.
+ */
+ public static DocumentVisibility createDnfDocumentVisibility(final byte[] expression) {
+ final DocumentVisibility documentVisibility = new DocumentVisibility(expression);
+ final DocumentVisibility dnfDv = convertToDisjunctiveNormalForm(documentVisibility);
+ return dnfDv;
+ }
+
+ /**
+ * Creates a document visibility boolean expression string into Disjunctive
+ * Normal Form (DNF). Expressions use this format in DNF:<pre>
+ * (P1 & P2 & P3 ... Pn) | (Q1 & Q2 ... Qm) ...
+ * </pre>
+ * @param documentVisibility the {@link DocumentVisibility}.
+ * @return a new {@link DocumentVisibility} with its expression in DNF.
+ */
+ public static DocumentVisibility convertToDisjunctiveNormalForm(final DocumentVisibility documentVisibility) {
+ // Find all the terms used in the expression
+ final List<String> terms = findNodeTerms(documentVisibility.getParseTree(), documentVisibility.getExpression());
+ // Create an appropriately sized truth table that has the correct 0's
+ // and 1's in place based on the number of terms.
+ // This size should be [numberOfTerms][2 ^ numberOfTerms].
+ final byte[][] truthTable = createTruthTableInputs(terms);
+
+ // Go through each row in the truth table.
+ // If the row has a 1 for the term then create an Authorization for it
+ // and test if it works.
+ // If the row passes then that means all the terms that were a 1 and
+ // were used can be AND'ed together to pass the expression.
+ // All the rows that pass can be OR'd together.
+ // Disjunction Normal Form: (P1 & P2 & P3 ... Pn) | (Q1 & Q2 ... Qm) ...
+ final List<List<String>> termRowsThatPass = new ArrayList<>();
+ for (final byte[] row : truthTable) {
+ final List<String> termRowToCheck = new ArrayList<>();
+ // If the truth table input is a 1 then include the corresponding
+ // term that it matches.
+ for (int i = 0; i < row.length; i++) {
+ final byte entry = row[i];
+ if (entry == 1) {
+ termRowToCheck.add(terms.get(i));
+ }
+ }
+
+ final List<String> authList = new ArrayList<>();
+ for (final String auth : termRowToCheck) {
+ String formattedAuth = auth;
+ formattedAuth = StringUtils.removeStart(formattedAuth, "\"");
+ formattedAuth = StringUtils.removeEnd(formattedAuth, "\"");
+ authList.add(formattedAuth);
+ }
+ final Authorizations auths = new Authorizations(authList.toArray(new String[0]));
+ final boolean hasAccess = DocumentVisibilityUtil.doesUserHaveDocumentAccess(auths, documentVisibility, false);
+ if (hasAccess) {
+ boolean alreadyCoveredBySimplerTerms = false;
+ // If one 'AND' group is (A&C) and another is (A&B&C) then we
+ // can drop (A&B&C) since it is already covered by simpler terms
+ // (it's a subset)
+ for (final List<String> existingTermRowThatPassed : termRowsThatPass) {
+ alreadyCoveredBySimplerTerms = termRowToCheck.containsAll(existingTermRowThatPassed);
+ if (alreadyCoveredBySimplerTerms) {
+ break;
+ }
+ }
+ if (!alreadyCoveredBySimplerTerms) {
+ termRowsThatPass.add(termRowToCheck);
+ }
+ }
+ }
+
+ // Rebuild the term rows that passed as a document visibility boolean
+ // expression string.
+ final StringBuilder sb = new StringBuilder();
+ boolean isFirst = true;
+ final boolean hasMultipleGroups = termRowsThatPass.size() > 1;
+ for (final List<String> termRowThatPassed : termRowsThatPass) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ sb.append("|");
+ }
+ if (hasMultipleGroups && termRowThatPassed.size() > 1) {
+ sb.append("(");
+ }
+ sb.append(Joiner.on("&").join(termRowThatPassed));
+ if (hasMultipleGroups && termRowThatPassed.size() > 1) {
+ sb.append(")");
+ }
+ }
+
+ log.trace(sb.toString());
+ final DocumentVisibility dnfDv = new DocumentVisibility(sb.toString());
+ return dnfDv;
+ }
+
+ /**
+ * Searches a node for all unique terms in its expression and returns them.
+ * Duplicates are not included.
+ * @param node the {@link Node}.
+ * @return an unmodifiable {@link List} of string terms without duplicates.
+ */
+ public static List<String> findNodeTerms(final Node node, final byte[] expression) {
+ final Set<String> terms = new LinkedHashSet<>();
+ if (node.getType() == NodeType.TERM) {
+ final String data = DocumentVisibilityUtil.getTermNodeData(node, expression);
+ terms.add(data);
+ }
+ for (final Node child : node.getChildren()) {
+ switch (node.getType()) {
+ case AND:
+ case OR:
+ terms.addAll(findNodeTerms(child, expression));
+ break;
+ default:
+ break;
+ }
+ }
+ return Collections.unmodifiableList(Lists.newArrayList(terms));
+ }
+
+ /**
+ * Creates the inputs needed to populate a truth table based on the provided
+ * number of terms that the expression uses. So, a node that only has 3
+ * terms will create a 3 x 8 size table:
+ * <pre>
+ * 0 0 0
+ * 0 0 1
+ * 0 1 0
+ * 0 1 1
+ * 1 0 0
+ * 1 0 1
+ * 1 1 0
+ * 1 1 1
+ * </pre>
+ * @param node the {@link Node}.
+ * @return a two-dimensional array of bytes representing the truth table
+ * inputs. The table will be of size: [termNumber] x [2 ^ termNumber]
+ */
+ public static byte[][] createTruthTableInputs(final Node node, final byte[] expression) {
+ final List<String> terms = findNodeTerms(node, expression);
+ return createTruthTableInputs(terms);
+ }
+
+ /**
+ * Creates the inputs needed to populate a truth table based on the provided
+ * number of terms that the expression uses. So, if there are 3 terms then
+ * it will create a 3 x 8 size table:
+ * <pre>
+ * 0 0 0
+ * 0 0 1
+ * 0 1 0
+ * 0 1 1
+ * 1 0 0
+ * 1 0 1
+ * 1 1 0
+ * 1 1 1
+ * </pre>
+ * @param terms the {@link List} of term strings.
+ * @return a two-dimensional array of bytes representing the truth table
+ * inputs. The table will be of size: [termNumber] x [2 ^ termNumber]
+ */
+ public static byte[][] createTruthTableInputs(final List<String> terms) {
+ return createTruthTableInputs(terms.size());
+ }
+
+ /**
+ * Creates the inputs needed to populate a truth table based on the provided
+ * number of terms that the expression uses. So, entering 3 for the number
+ * of terms will create a 3 x 8 size table:
+ * <pre>
+ * 0 0 0
+ * 0 0 1
+ * 0 1 0
+ * 0 1 1
+ * 1 0 0
+ * 1 0 1
+ * 1 1 0
+ * 1 1 1
+ * </pre>
+ * @param termNumber the number of terms.
+ * @return a two-dimensional array of bytes representing the truth table
+ * inputs. The table will be of size: [termNumber] x [2 ^ termNumber]
+ */
+ public static byte[][] createTruthTableInputs(final int termNumber) {
+ final int numColumns = termNumber;
+ final int numRows = (int) Math.pow(2, numColumns);
+ final byte[][] truthTable = new byte[numRows][numColumns];
+
+ for (int row = 0; row < numRows; row++) {
+ for (int col = 0; col < numColumns; col++) {
+ // We're starting from the top-left and going right then down to
+ // the next row. The left-side is of a higher order than the
+ // right-side so adjust accordingly.
+ final int digitOrderPosition = numColumns - 1 - col;
+ final int power = (int) Math.pow(2, digitOrderPosition);
+ final int toggle = (row / power) % 2;
+ truthTable[row][col] = (byte) toggle;
+ }
+ }
+
+ log.trace("Truth table inputs: " + Arrays.deepToString(truthTable));
+
+ return truthTable;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityConversionException.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityConversionException.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityConversionException.java
new file mode 100644
index 0000000..18111ee
--- /dev/null
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityConversionException.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rya.mongodb.document.util;
+
+/**
+ * Exception thrown when document visibility conversion encounters a problem.
+ */
+public class DocumentVisibilityConversionException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance of {@link DocumentVisibilityConversionException} with no
+ * detail message.
+ */
+ public DocumentVisibilityConversionException() {
+ super();
+ }
+
+ /**
+ * Creates a new instance of {@link DocumentVisibilityConversionException} with the
+ * specified detail message.
+ * @param message the detail message.
+ */
+ public DocumentVisibilityConversionException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new instance of {@link DocumentVisibilityConversionException} with the
+ * specified detail message and cause.
+ * @param message the detail message.
+ * @param cause the {@link Throwable} cause.
+ */
+ public DocumentVisibilityConversionException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Creates a new instance of {@link DocumentVisibilityConversionException} with the
+ * specified cause.
+ * @param cause the {@link Throwable} cause.
+ */
+ public DocumentVisibilityConversionException(final Throwable cause) {
+ super(cause);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityUtil.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityUtil.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityUtil.java
new file mode 100644
index 0000000..8044dfd
--- /dev/null
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/util/DocumentVisibilityUtil.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rya.mongodb.document.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.accumulo.core.data.ByteSequence;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.ColumnVisibility.Node;
+import org.apache.accumulo.core.security.ColumnVisibility.NodeType;
+import org.apache.accumulo.core.security.VisibilityEvaluator;
+import org.apache.accumulo.core.security.VisibilityParseException;
+import org.apache.log4j.Logger;
+import org.apache.rya.mongodb.MongoDbRdfConstants;
+import org.apache.rya.mongodb.document.visibility.DocumentVisibility;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.mongodb.BasicDBList;
+
+/**
+ * Utility methods for converting boolean expressions between a string
+ * representation to a MongoDB-friendly multidimensional array form that can be
+ * used in MongoDB's aggregation set operations.
+ */
+public final class DocumentVisibilityUtil {
+ private static final Logger log = Logger.getLogger(DocumentVisibilityUtil.class);
+
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private DocumentVisibilityUtil() {
+ }
+
+ /**
+ * Converts a boolean string expression into a multidimensional
+ * array representation of the boolean expression.
+ * @param booleanString the boolean string expression.
+ * @return the multidimensional array representation of the boolean
+ * expression.
+ * @throws DocumentVisibilityConversionException
+ */
+ public static Object[] toMultidimensionalArray(final String booleanString) throws DocumentVisibilityConversionException {
+ final DocumentVisibility dv = new DocumentVisibility(booleanString);
+ return toMultidimensionalArray(dv);
+ }
+
+ /**
+ * Converts a {@link DocumentVisibility} object into a multidimensional
+ * array representation of the boolean expression.
+ * @param dv the {@link DocumentVisibility}. (not {@code null})
+ * @return the multidimensional array representation of the boolean
+ * expression.
+ * @throws DocumentVisibilityConversionException
+ */
+ public static Object[] toMultidimensionalArray(final DocumentVisibility dv) throws DocumentVisibilityConversionException {
+ checkNotNull(dv);
+ final byte[] expression = dv.flatten();
+ final DocumentVisibility flattenedDv = DisjunctiveNormalFormConverter.createDnfDocumentVisibility(expression);
+ final Object[] result = toMultidimensionalArray(flattenedDv.getParseTree(), flattenedDv.getExpression());
+ // If there's only one group then make sure it's wrapped as an array.
+ // (i.e. "A" should be ["A"])
+ if (result.length > 0 && result[0] instanceof String) {
+ final List<Object[]> formattedResult = new ArrayList<>();
+ formattedResult.add(result);
+ return formattedResult.toArray(new Object[0]);
+ }
+ return result;
+ }
+
+ /**
+ * Converts a {@link Node} and its corresponding expression into a
+ * multidimensional array representation of the boolean expression.
+ * @param node the {@link Node}. (not {@code null})
+ * @param expression the expression byte array.
+ * @return the multidimensional array representation of the boolean
+ * expression.
+ * @throws DocumentVisibilityConversionException
+ */
+ public static Object[] toMultidimensionalArray(final Node node, final byte[] expression) throws DocumentVisibilityConversionException {
+ checkNotNull(node);
+ final List<Object> array = new ArrayList<>();
+
+ if (node.getChildren().isEmpty() && node.getType() == NodeType.TERM) {
+ final String data = getTermNodeData(node, expression);
+ array.add(data);
+ }
+
+ log.trace("Children size: " + node.getChildren().size() + " Type: " + node.getType());
+ for (final Node child : node.getChildren()) {
+ switch (child.getType()) {
+ case EMPTY:
+ case TERM:
+ String data;
+ if (child.getType() == NodeType.TERM) {
+ data = getTermNodeData(child, expression);
+ } else {
+ data = "";
+ }
+ if (node.getType() == NodeType.OR) {
+ array.add(Lists.newArrayList(data).toArray(new Object[0]));
+ } else {
+ array.add(data);
+ }
+ break;
+ case OR:
+ case AND:
+ array.add(toMultidimensionalArray(child, expression));
+ break;
+ default:
+ throw new DocumentVisibilityConversionException("Unknown type: " + child.getType());
+ }
+ }
+
+ return array.toArray(new Object[0]);
+ }
+
+ public static String nodeToBooleanString(final Node node, final byte[] expression) throws DocumentVisibilityConversionException {
+ boolean isFirst = true;
+ final StringBuilder sb = new StringBuilder();
+ if (node.getType() == NodeType.TERM) {
+ final String data = getTermNodeData(node, expression);
+ sb.append(data);
+ }
+ if (node.getType() == NodeType.AND) {
+ sb.append("(");
+ }
+ for (final Node child : node.getChildren()) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ if (node.getType() == NodeType.OR) {
+ sb.append("|");
+ } else if (node.getType() == NodeType.AND) {
+ sb.append("&");
+ }
+ }
+ switch (child.getType()) {
+ case EMPTY:
+ sb.append("");
+ break;
+ case TERM:
+ final String data = getTermNodeData(child, expression);
+ sb.append(data);
+ break;
+ case OR:
+ sb.append("(");
+ sb.append(nodeToBooleanString(child, expression));
+ sb.append(")");
+ break;
+ case AND:
+ sb.append(nodeToBooleanString(child, expression));
+ break;
+ default:
+ throw new DocumentVisibilityConversionException("Unknown type: " + child.getType());
+ }
+ }
+ if (node.getType() == NodeType.AND) {
+ sb.append(")");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Converts a multidimensional array object representation of the document
+ * visibility boolean expression into a string.
+ * @param object the multidimensional array object representing the
+ * document visibility boolean expression.
+ * @return the boolean string expression.
+ */
+ public static String multidimensionalArrayToBooleanString(final Object[] object) {
+ final String booleanString = multidimensionalArrayToBooleanStringInternal(object);
+
+ // Simplify and clean up the formatting.
+ final DocumentVisibility dv = DisjunctiveNormalFormConverter.createDnfDocumentVisibility(booleanString);
+ final byte[] bytes = dv.flatten();
+ final String result = new String(bytes, Charsets.UTF_8);
+
+ return result;
+ }
+
+ private static String multidimensionalArrayToBooleanStringInternal(final Object[] object) {
+ final StringBuilder sb = new StringBuilder();
+
+ int count = 0;
+ boolean isAnd = false;
+ for (final Object child : object) {
+ if (child instanceof String) {
+ isAnd = true;
+ if (count > 0) {
+ sb.append("&");
+ }
+ sb.append(child);
+ } else if (child instanceof Object[]) {
+ if (count > 0 && isAnd) {
+ sb.append("&");
+ }
+ final Object[] obj = (Object[]) child;
+ sb.append("(");
+ sb.append(multidimensionalArrayToBooleanStringInternal(obj));
+ sb.append(")");
+ }
+
+ if (object.length > 1 && count + 1 < object.length && !isAnd) {
+ sb.append("|");
+ }
+ count++;
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Conditionally adds quotes around a string.
+ * @param data the string to add quotes to.
+ * @param addQuotes {@code true} to add quotes. {@code false} to leave the
+ * string as is.
+ * @return the quoted string if {@code addQuotes} is {@code true}.
+ * Otherwise, returns the string as is.
+ */
+ public static String addQuotes(final String data, final boolean addQuotes) {
+ if (addQuotes) {
+ return "\"" + data + "\"";
+ } else {
+ return data;
+ }
+ }
+
+ /**
+ * Returns the term node's data.
+ * @param node the {@link Node}.
+ * @return the term node's data.
+ */
+ public static String getTermNodeData(final Node node, final byte[] expression) {
+ final boolean isQuotedTerm = expression[node.getTermStart()] == '"';
+ final ByteSequence bs = node.getTerm(expression);
+ final String data = addQuotes(new String(bs.toArray(), Charsets.UTF_8), isQuotedTerm);
+ return data;
+ }
+
+ /**
+ * Checks if the user's authorizations allows them to have access to the
+ * provided document based on its document visibility.
+ * @param authorizations the {@link Authorizations}.
+ * @param documentVisibility the document visibility byte expression.
+ * @return {@code true} if the user has access to the document.
+ * {@code false} otherwise.
+ */
+ public static boolean doesUserHaveDocumentAccess(final Authorizations authorizations, final byte[] documentVisibilityExpression) {
+ final byte[] expression = documentVisibilityExpression != null ? documentVisibilityExpression : MongoDbRdfConstants.EMPTY_DV.getExpression();
+ final DocumentVisibility documentVisibility = new DocumentVisibility(expression);
+ return doesUserHaveDocumentAccess(authorizations, documentVisibility);
+ }
+
+ /**
+ * Checks if the user's authorizations allows them to have access to the
+ * provided document based on its document visibility.
+ * @param authorizations the {@link Authorizations}.
+ * @param documentVisibility the {@link DocumentVisibility}.
+ * @return {@code true} if the user has access to the document.
+ * {@code false} otherwise.
+ */
+ public static boolean doesUserHaveDocumentAccess(final Authorizations authorizations, final DocumentVisibility documentVisibility) {
+ return doesUserHaveDocumentAccess(authorizations, documentVisibility, true);
+ }
+
+ /**
+ * Checks if the user's authorizations allows them to have access to the
+ * provided document based on its document visibility.
+ * @param authorizations the {@link Authorizations}.
+ * @param documentVisibility the {@link DocumentVisibility}.
+ * @param doesEmptyAccessPass {@code true} if an empty authorization pass
+ * allows access to everything. {@code false} otherwise.
+ * @return {@code true} if the user has access to the document.
+ * {@code false} otherwise.
+ */
+ public static boolean doesUserHaveDocumentAccess(final Authorizations authorizations, final DocumentVisibility documentVisibility, final boolean doesEmptyAccessPass) {
+ final Authorizations userAuths = authorizations != null ? authorizations : MongoDbRdfConstants.ALL_AUTHORIZATIONS;
+ final VisibilityEvaluator visibilityEvaluator = new VisibilityEvaluator(userAuths);
+ boolean accept = false;
+ if (doesEmptyAccessPass && MongoDbRdfConstants.ALL_AUTHORIZATIONS.equals(userAuths)) {
+ accept = true;
+ } else {
+ try {
+ accept = visibilityEvaluator.evaluate(documentVisibility);
+ } catch (final VisibilityParseException e) {
+ log.error("Could not parse document visibility.");
+ }
+ }
+
+ return accept;
+ }
+
+ /**
+ * Converts a {@link BasicDBList} into an array of {@link Object}s.
+ * @param basicDbList the {@link BasicDBList} to convert.
+ * @return the array of {@link Object}s.
+ */
+ public static Object[] convertBasicDBListToObjectArray(final BasicDBList basicDbList) {
+ final List<Object> list = new ArrayList<>();
+ final Object[] array = basicDbList.toArray();
+ for (final Object child : array) {
+ if (child instanceof BasicDBList) {
+ list.add(convertBasicDBListToObjectArray((BasicDBList)child));
+ } else {
+ list.add(child);
+ }
+ }
+ return list.toArray(new Object[0]);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibility.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibility.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibility.java
new file mode 100644
index 0000000..7f30e7f
--- /dev/null
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibility.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rya.mongodb.document.visibility;
+
+import org.apache.accumulo.core.security.ColumnVisibility;
+import org.apache.hadoop.io.Text;
+
+/**
+ * Validate the document visibility is a valid expression and set the visibility for a Mutation. See {@link DocumentVisibility#DocumentVisibility(byte[])} for the
+ * definition of an expression.
+ *
+ * <p>
+ * The expression is a sequence of characters from the set [A-Za-z0-9_-.] along with the binary operators "&" and "|" indicating that both operands are
+ * necessary, or the either is necessary. The following are valid expressions for visibility:
+ *
+ * <pre>
+ * A
+ * A|B
+ * (A|B)&(C|D)
+ * orange|(red&yellow)
+ * </pre>
+ *
+ * <p>
+ * The following are not valid expressions for visibility:
+ *
+ * <pre>
+ * A|B&C
+ * A=B
+ * A|B|
+ * A&|B
+ * ()
+ * )
+ * dog|!cat
+ * </pre>
+ *
+ * <p>
+ * In addition to the base set of visibilities, any character can be used in the expression if it is quoted. If the quoted term contains '"' or '\', then
+ * escape the character with '\'. The {@link #quote(String)} method can be used to properly quote and escape terms automatically. The following is an example of
+ * a quoted term:
+ *
+ * <pre>
+ * "A#C" & B
+ * </pre>
+ */
+public class DocumentVisibility extends ColumnVisibility {
+ /**
+ * Creates an empty visibility. Normally, elements with empty visibility can be seen by everyone. Though, one could change this behavior with filters.
+ *
+ * @see #DocumentVisibility(String)
+ */
+ public DocumentVisibility() {
+ super();
+ }
+
+ /**
+ * Creates a document visibility for a Mutation.
+ *
+ * @param expression
+ * An expression of the rights needed to see this mutation. The expression syntax is defined at the class-level documentation
+ */
+ public DocumentVisibility(final String expression) {
+ super(expression);
+ }
+
+ /**
+ * Creates a document visibility for a Mutation.
+ *
+ * @param expression
+ * visibility expression
+ * @see #DocumentVisibility(String)
+ */
+ public DocumentVisibility(final Text expression) {
+ super(expression);
+ }
+
+ /**
+ * Creates a document visibility for a Mutation from a string already encoded in UTF-8 bytes.
+ *
+ * @param expression
+ * visibility expression, encoded as UTF-8 bytes
+ * @see #DocumentVisibility(String)
+ */
+ public DocumentVisibility(final byte[] expression) {
+ super(expression);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibilityAdapter.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibilityAdapter.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibilityAdapter.java
new file mode 100644
index 0000000..50dc311
--- /dev/null
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/document/visibility/DocumentVisibilityAdapter.java
@@ -0,0 +1,143 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rya.mongodb.document.visibility;
+
+import org.apache.log4j.Logger;
+import org.apache.rya.mongodb.MongoDbRdfConstants;
+import org.apache.rya.mongodb.dao.SimpleMongoDBStorageStrategy;
+import org.apache.rya.mongodb.document.util.DocumentVisibilityConversionException;
+import org.apache.rya.mongodb.document.util.DocumentVisibilityUtil;
+
+import com.mongodb.BasicDBList;
+import com.mongodb.BasicDBObject;
+import com.mongodb.BasicDBObjectBuilder;
+import com.mongodb.DBObject;
+
+import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * Serializes the document visibility field of a Rya Statement for use in
+ * MongoDB.
+ * The {@link DBObject} will look like:
+ * <pre>
+ * {@code
+ * {
+ * "documentVisibility": <array>,
+ * }
+ * </pre>
+ */
+@DefaultAnnotation(NonNull.class)
+public final class DocumentVisibilityAdapter {
+ private static final Logger log = Logger.getLogger(DocumentVisibilityAdapter.class);
+
+ public static final String DOCUMENT_VISIBILITY_KEY = SimpleMongoDBStorageStrategy.DOCUMENT_VISIBILITY;
+
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private DocumentVisibilityAdapter() {
+ }
+
+ /**
+ * Serializes a document visibility expression byte array to a MongoDB
+ * {@link DBObject}.
+ * @param expression the document visibility expression byte array to be
+ * serialized.
+ * @return The MongoDB {@link DBObject}.
+ */
+ public static BasicDBObject toDBObject(final byte[] expression) {
+ DocumentVisibility dv;
+ if (expression == null) {
+ dv = MongoDbRdfConstants.EMPTY_DV;
+ } else {
+ dv = new DocumentVisibility(expression);
+ }
+ return toDBObject(dv);
+ }
+
+ /**
+ * Serializes a {@link DocumentVisibility} to a MongoDB {@link DBObject}.
+ * @param documentVisibility the {@link DocumentVisibility} to be
+ * serialized.
+ * @return The MongoDB {@link DBObject}.
+ */
+ public static BasicDBObject toDBObject(final DocumentVisibility documentVisibility) {
+ DocumentVisibility dv;
+ if (documentVisibility == null) {
+ dv = MongoDbRdfConstants.EMPTY_DV;
+ } else {
+ dv = documentVisibility;
+ }
+ Object[] dvArray = null;
+ try {
+ dvArray = DocumentVisibilityUtil.toMultidimensionalArray(dv);
+ } catch (final DocumentVisibilityConversionException e) {
+ log.error("Unable to convert document visibility");
+ }
+
+ final BasicDBObjectBuilder builder = BasicDBObjectBuilder.start();
+ builder.add(DOCUMENT_VISIBILITY_KEY, dvArray);
+ return (BasicDBObject) builder.get();
+ }
+
+ /**
+ * Deserializes a MongoDB {@link DBObject} to a {@link DocumentVisibility}.
+ * @param mongoObj the {@link DBObject} to be deserialized.
+ * @return the {@link DocumentVisibility} object.
+ * @throws MalformedDocumentVisibilityException
+ */
+ public static DocumentVisibility toDocumentVisibility(final DBObject mongoObj) throws MalformedDocumentVisibilityException {
+ try {
+ final BasicDBObject basicObj = (BasicDBObject) mongoObj;
+
+ final Object documentVisibilityObject = basicObj.get(DOCUMENT_VISIBILITY_KEY);
+ Object[] documentVisibilityArray = null;
+ if (documentVisibilityObject instanceof Object[]) {
+ documentVisibilityArray = (Object[]) documentVisibilityObject;
+ } else if (documentVisibilityObject instanceof BasicDBList) {
+ documentVisibilityArray = DocumentVisibilityUtil.convertBasicDBListToObjectArray((BasicDBList) documentVisibilityObject);
+ }
+
+ final String documentVisibilityString = DocumentVisibilityUtil.multidimensionalArrayToBooleanString(documentVisibilityArray);
+ final DocumentVisibility dv = documentVisibilityString == null ? MongoDbRdfConstants.EMPTY_DV : new DocumentVisibility(documentVisibilityString);
+
+ return dv;
+ } catch(final Exception e) {
+ throw new MalformedDocumentVisibilityException("Failed to make Document Visibility from Mongo Object, it is malformed.", e);
+ }
+ }
+
+ /**
+ * Exception thrown when a MongoDB {@link DBObject} is malformed when
+ * attempting to adapt it into a {@link DocumentVisibility}.
+ */
+ public static class MalformedDocumentVisibilityException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new {@link MalformedDocumentVisibilityException}
+ * @param message - The message to be displayed by the exception.
+ * @param e - The source cause of the exception.
+ */
+ public MalformedDocumentVisibilityException(final String message, final Throwable e) {
+ super(message, e);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementBindingSetCursorIterator.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementBindingSetCursorIterator.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementBindingSetCursorIterator.java
index ebe5c3f..4807c4f 100644
--- a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementBindingSetCursorIterator.java
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementBindingSetCursorIterator.java
@@ -1,5 +1,3 @@
-package org.apache.rya.mongodb.iter;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -8,9 +6,9 @@ package org.apache.rya.mongodb.iter;
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,108 +16,125 @@ package org.apache.rya.mongodb.iter;
* specific language governing permissions and limitations
* under the License.
*/
+package org.apache.rya.mongodb.iter;
-
-import info.aduna.iteration.CloseableIteration;
-
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
import java.util.Map.Entry;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.log4j.Logger;
import org.apache.rya.api.RdfCloudTripleStoreUtils;
import org.apache.rya.api.domain.RyaStatement;
import org.apache.rya.api.persist.RyaDAOException;
import org.apache.rya.mongodb.dao.MongoDBStorageStrategy;
-
+import org.apache.rya.mongodb.document.operators.aggregation.AggregationUtil;
import org.openrdf.query.BindingSet;
import com.google.common.collect.Multimap;
+import com.mongodb.AggregationOutput;
+import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
import com.mongodb.DBObject;
-public class RyaStatementBindingSetCursorIterator implements CloseableIteration<Entry<RyaStatement, BindingSet>, RyaDAOException> {
+import info.aduna.iteration.CloseableIteration;
- private DBCollection coll;
- private Multimap<DBObject, BindingSet> rangeMap;
- private Iterator<DBObject> queryIterator;
- private Long maxResults;
- private DBCursor resultCursor;
- private RyaStatement currentStatement;
- private Collection<BindingSet> currentBindingSetCollection;
- private Iterator<BindingSet> currentBindingSetIterator;
- private MongoDBStorageStrategy strategy;
-
- public RyaStatementBindingSetCursorIterator(DBCollection coll,
- Multimap<DBObject, BindingSet> rangeMap, MongoDBStorageStrategy strategy) {
- this.coll = coll;
- this.rangeMap = rangeMap;
- this.queryIterator = rangeMap.keySet().iterator();
- this.strategy = strategy;
- }
-
- @Override
- public boolean hasNext() {
- if (!currentBindingSetIteratorIsValid()) {
- findNextResult();
- }
- return currentBindingSetIteratorIsValid();
- }
-
- @Override
- public Entry<RyaStatement, BindingSet> next() {
- if (!currentBindingSetIteratorIsValid()) {
- findNextResult();
- }
- if (currentBindingSetIteratorIsValid()) {
- BindingSet currentBindingSet = currentBindingSetIterator.next();
- return new RdfCloudTripleStoreUtils.CustomEntry<RyaStatement, BindingSet>(currentStatement, currentBindingSet);
- }
- return null;
- }
-
- private boolean currentBindingSetIteratorIsValid() {
- return (currentBindingSetIterator != null) && currentBindingSetIterator.hasNext();
- }
-
- private void findNextResult() {
- if (!currentResultCursorIsValid()) {
- findNextValidResultCursor();
- }
- if (currentResultCursorIsValid()) {
- // convert to Rya Statement
- DBObject queryResult = resultCursor.next();
- currentStatement = strategy.deserializeDBObject(queryResult);
- currentBindingSetIterator = currentBindingSetCollection.iterator();
- }
- }
-
- private void findNextValidResultCursor() {
- while (queryIterator.hasNext()){
- DBObject currentQuery = queryIterator.next();
- resultCursor = coll.find(currentQuery);
- currentBindingSetCollection = rangeMap.get(currentQuery);
- if (resultCursor.hasNext()) return;
- }
- }
-
- private boolean currentResultCursorIsValid() {
- return (resultCursor != null) && resultCursor.hasNext();
- }
-
-
- public void setMaxResults(Long maxResults) {
- this.maxResults = maxResults;
- }
-
- @Override
- public void close() throws RyaDAOException {
- // TODO don't know what to do here
- }
-
- @Override
- public void remove() throws RyaDAOException {
- next();
- }
+public class RyaStatementBindingSetCursorIterator implements CloseableIteration<Entry<RyaStatement, BindingSet>, RyaDAOException> {
+ private static final Logger log = Logger.getLogger(RyaStatementBindingSetCursorIterator.class);
+
+ private final DBCollection coll;
+ private final Multimap<DBObject, BindingSet> rangeMap;
+ private final Iterator<DBObject> queryIterator;
+ private Long maxResults;
+ private Iterator<DBObject> resultsIterator;
+ private RyaStatement currentStatement;
+ private Collection<BindingSet> currentBindingSetCollection;
+ private Iterator<BindingSet> currentBindingSetIterator;
+ private final MongoDBStorageStrategy<RyaStatement> strategy;
+ private final Authorizations auths;
+
+ public RyaStatementBindingSetCursorIterator(final DBCollection coll,
+ final Multimap<DBObject, BindingSet> rangeMap, final MongoDBStorageStrategy<RyaStatement> strategy, final Authorizations auths) {
+ this.coll = coll;
+ this.rangeMap = rangeMap;
+ this.queryIterator = rangeMap.keySet().iterator();
+ this.strategy = strategy;
+ this.auths = auths;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!currentBindingSetIteratorIsValid()) {
+ findNextResult();
+ }
+ return currentBindingSetIteratorIsValid();
+ }
+
+ @Override
+ public Entry<RyaStatement, BindingSet> next() {
+ if (!currentBindingSetIteratorIsValid()) {
+ findNextResult();
+ }
+ if (currentBindingSetIteratorIsValid()) {
+ final BindingSet currentBindingSet = currentBindingSetIterator.next();
+ return new RdfCloudTripleStoreUtils.CustomEntry<RyaStatement, BindingSet>(currentStatement, currentBindingSet);
+ }
+ return null;
+ }
+
+ private boolean currentBindingSetIteratorIsValid() {
+ return (currentBindingSetIterator != null) && currentBindingSetIterator.hasNext();
+ }
+
+ private void findNextResult() {
+ if (!currentResultCursorIsValid()) {
+ findNextValidResultCursor();
+ }
+ if (currentResultCursorIsValid()) {
+ // convert to Rya Statement
+ final DBObject queryResult = resultsIterator.next();
+ currentStatement = strategy.deserializeDBObject(queryResult);
+ currentBindingSetIterator = currentBindingSetCollection.iterator();
+ }
+ }
+
+ private void findNextValidResultCursor() {
+ while (queryIterator.hasNext()){
+ final DBObject currentQuery = queryIterator.next();
+ currentBindingSetCollection = rangeMap.get(currentQuery);
+ // Executing redact aggregation to only return documents the user
+ // has access to.
+ final List<DBObject> pipeline = new ArrayList<>();
+ pipeline.add(new BasicDBObject("$match", currentQuery));
+ pipeline.addAll(AggregationUtil.createRedactPipeline(auths));
+ log.debug(pipeline);
+ final AggregationOutput output = coll.aggregate(pipeline);
+ resultsIterator = output.results().iterator();
+ if (resultsIterator.hasNext()) {
+ break;
+ }
+ }
+ }
+
+ private boolean currentResultCursorIsValid() {
+ return (resultsIterator != null) && resultsIterator.hasNext();
+ }
+
+
+ public void setMaxResults(final Long maxResults) {
+ this.maxResults = maxResults;
+ }
+
+ @Override
+ public void close() throws RyaDAOException {
+ // TODO don't know what to do here
+ }
+
+ @Override
+ public void remove() throws RyaDAOException {
+ next();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementCursorIterator.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementCursorIterator.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementCursorIterator.java
index 9bb5d38..2f6fd44 100644
--- a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementCursorIterator.java
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/iter/RyaStatementCursorIterator.java
@@ -1,5 +1,3 @@
-package org.apache.rya.mongodb.iter;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -8,9 +6,9 @@ package org.apache.rya.mongodb.iter;
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,87 +16,100 @@ package org.apache.rya.mongodb.iter;
* specific language governing permissions and limitations
* under the License.
*/
+package org.apache.rya.mongodb.iter;
-
-import info.aduna.iteration.CloseableIteration;
-
+import java.util.ArrayList;
import java.util.Iterator;
-import java.util.Map.Entry;
+import java.util.List;
import java.util.Set;
-import org.apache.rya.api.RdfCloudTripleStoreUtils;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.log4j.Logger;
import org.apache.rya.api.domain.RyaStatement;
import org.apache.rya.api.persist.RyaDAOException;
import org.apache.rya.mongodb.dao.MongoDBStorageStrategy;
+import org.apache.rya.mongodb.document.operators.aggregation.AggregationUtil;
-import org.calrissian.mango.collect.CloseableIterable;
-import org.openrdf.query.BindingSet;
-
+import com.mongodb.AggregationOutput;
+import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
import com.mongodb.DBObject;
-public class RyaStatementCursorIterator implements CloseableIteration<RyaStatement, RyaDAOException> {
-
- private DBCollection coll;
- private Iterator<DBObject> queryIterator;
- private DBCursor currentCursor;
- private MongoDBStorageStrategy strategy;
- private Long maxResults;
-
- public RyaStatementCursorIterator(DBCollection coll, Set<DBObject> queries, MongoDBStorageStrategy strategy) {
- this.coll = coll;
- this.queryIterator = queries.iterator();
- this.strategy = strategy;
- }
-
- @Override
- public boolean hasNext() {
- if (!currentCursorIsValid()) {
- findNextValidCursor();
- }
- return currentCursorIsValid();
- }
-
- @Override
- public RyaStatement next() {
- if (!currentCursorIsValid()) {
- findNextValidCursor();
- }
- if (currentCursorIsValid()) {
- // convert to Rya Statement
- DBObject queryResult = currentCursor.next();
- RyaStatement statement = strategy.deserializeDBObject(queryResult);
- return statement;
- }
- return null;
- }
-
- private void findNextValidCursor() {
- while (queryIterator.hasNext()){
- DBObject currentQuery = queryIterator.next();
- currentCursor = coll.find(currentQuery);
- if (currentCursor.hasNext()) break;
- }
- }
-
- private boolean currentCursorIsValid() {
- return (currentCursor != null) && currentCursor.hasNext();
- }
-
-
- public void setMaxResults(Long maxResults) {
- this.maxResults = maxResults;
- }
-
- @Override
- public void close() throws RyaDAOException {
- // TODO don't know what to do here
- }
-
- @Override
- public void remove() throws RyaDAOException {
- next();
- }
+import info.aduna.iteration.CloseableIteration;
+public class RyaStatementCursorIterator implements CloseableIteration<RyaStatement, RyaDAOException> {
+ private static final Logger log = Logger.getLogger(RyaStatementCursorIterator.class);
+
+ private final DBCollection coll;
+ private final Iterator<DBObject> queryIterator;
+ private Iterator<DBObject> resultsIterator;
+ private final MongoDBStorageStrategy<RyaStatement> strategy;
+ private Long maxResults;
+ private final Authorizations auths;
+
+ public RyaStatementCursorIterator(final DBCollection coll, final Set<DBObject> queries, final MongoDBStorageStrategy<RyaStatement> strategy, final Authorizations auths) {
+ this.coll = coll;
+ this.queryIterator = queries.iterator();
+ this.strategy = strategy;
+ this.auths = auths;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!currentCursorIsValid()) {
+ findNextValidCursor();
+ }
+ return currentCursorIsValid();
+ }
+
+ @Override
+ public RyaStatement next() {
+ if (!currentCursorIsValid()) {
+ findNextValidCursor();
+ }
+ if (currentCursorIsValid()) {
+ // convert to Rya Statement
+ final DBObject queryResult = resultsIterator.next();
+ final RyaStatement statement = strategy.deserializeDBObject(queryResult);
+ return statement;
+ }
+ return null;
+ }
+
+ private void findNextValidCursor() {
+ while (queryIterator.hasNext()){
+ final DBObject currentQuery = queryIterator.next();
+
+ // Executing redact aggregation to only return documents the user
+ // has access to.
+ final List<DBObject> pipeline = new ArrayList<>();
+ pipeline.add(new BasicDBObject("$match", currentQuery));
+ pipeline.addAll(AggregationUtil.createRedactPipeline(auths));
+ log.debug(pipeline);
+ final AggregationOutput output = coll.aggregate(pipeline);
+ resultsIterator = output.results().iterator();
+ if (resultsIterator.hasNext()) {
+ break;
+ }
+ }
+ }
+
+ private boolean currentCursorIsValid() {
+ return (resultsIterator != null) && resultsIterator.hasNext();
+ }
+
+
+ public void setMaxResults(final Long maxResults) {
+ this.maxResults = maxResults;
+ }
+
+ @Override
+ public void close() throws RyaDAOException {
+ // TODO don't know what to do here
+ }
+
+ @Override
+ public void remove() throws RyaDAOException {
+ next();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/cc1cc712/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/MongoDBRyaDAOIT.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/MongoDBRyaDAOIT.java b/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/MongoDBRyaDAOIT.java
index b4f7819..c862815 100644
--- a/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/MongoDBRyaDAOIT.java
+++ b/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/MongoDBRyaDAOIT.java
@@ -18,25 +18,32 @@ package org.apache.rya.mongodb;
* under the License.
*/
+import static org.apache.rya.mongodb.dao.SimpleMongoDBStorageStrategy.DOCUMENT_VISIBILITY;
import static org.apache.rya.mongodb.dao.SimpleMongoDBStorageStrategy.TIMESTAMP;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import org.apache.accumulo.core.security.Authorizations;
import org.apache.hadoop.conf.Configuration;
import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
import org.apache.rya.api.domain.RyaStatement;
import org.apache.rya.api.domain.RyaStatement.RyaStatementBuilder;
import org.apache.rya.api.domain.RyaURI;
import org.apache.rya.api.persist.RyaDAOException;
+import org.apache.rya.api.persist.query.RyaQuery;
+import org.apache.rya.mongodb.document.util.AuthorizationsUtil;
+import org.apache.rya.mongodb.document.visibility.DocumentVisibility;
+import org.bson.Document;
+import org.calrissian.mango.collect.CloseableIterable;
import org.junit.Before;
import org.junit.Test;
-import com.mongodb.DB;
-import com.mongodb.DBCollection;
-import com.mongodb.DBObject;
import com.mongodb.MongoException;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
public class MongoDBRyaDAOIT extends MongoRyaTestBase {
@@ -45,40 +52,43 @@ public class MongoDBRyaDAOIT extends MongoRyaTestBase {
@Before
public void setUp() throws IOException, RyaDAOException{
- final Configuration conf = new Configuration();
- conf.set(MongoDBRdfConfiguration.MONGO_DB_NAME, "test");
- conf.set(MongoDBRdfConfiguration.MONGO_COLLECTION_PREFIX, "rya_");
- conf.set(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX, "rya_");
- configuration = new MongoDBRdfConfiguration(conf);
- final int port = mongoClient.getServerAddressList().get(0).getPort();
- configuration.set(MongoDBRdfConfiguration.MONGO_INSTANCE_PORT, Integer.toString(port));
- dao = new MongoDBRyaDAO(configuration, mongoClient);
+ final Configuration conf = new Configuration();
+ conf.set(MongoDBRdfConfiguration.MONGO_DB_NAME, "test");
+ conf.set(MongoDBRdfConfiguration.MONGO_COLLECTION_PREFIX, "rya_");
+ conf.set(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX, "rya_");
+ configuration = new MongoDBRdfConfiguration(conf);
+ configuration.setAuths("A", "B", "C");
+ final int port = mongoClient.getServerAddressList().get(0).getPort();
+ configuration.set(MongoDBRdfConfiguration.MONGO_INSTANCE_PORT, Integer.toString(port));
+ dao = new MongoDBRyaDAO(configuration, mongoClient);
}
@Test
public void testDeleteWildcard() throws RyaDAOException {
final RyaStatementBuilder builder = new RyaStatementBuilder();
builder.setPredicate(new RyaURI("http://temp.com"));
+ builder.setColumnVisibility(new DocumentVisibility("A").flatten());
dao.delete(builder.build(), configuration);
}
-
@Test
public void testAdd() throws RyaDAOException, MongoException, IOException {
final RyaStatementBuilder builder = new RyaStatementBuilder();
builder.setPredicate(new RyaURI("http://temp.com"));
builder.setSubject(new RyaURI("http://subject.com"));
builder.setObject(new RyaURI("http://object.com"));
+ builder.setColumnVisibility(new DocumentVisibility("B").flatten());
- final DB db = mongoClient.getDB(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
- final DBCollection coll = db.getCollection(configuration.getTriplesCollectionName());
+ final MongoDatabase db = mongoClient.getDatabase(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
+ final MongoCollection<Document> coll = db.getCollection(configuration.getTriplesCollectionName());
dao.add(builder.build());
assertEquals(coll.count(),1);
- final DBObject dbo = coll.findOne();
- assertTrue(dbo.containsField(TIMESTAMP));
+ final Document dbo = coll.find().first();
+ assertTrue(dbo.containsKey(DOCUMENT_VISIBILITY));
+ assertTrue(dbo.containsKey(TIMESTAMP));
}
@Test
@@ -87,9 +97,10 @@ public class MongoDBRyaDAOIT extends MongoRyaTestBase {
builder.setPredicate(new RyaURI("http://temp.com"));
builder.setSubject(new RyaURI("http://subject.com"));
builder.setObject(new RyaURI("http://object.com"));
+ builder.setColumnVisibility(new DocumentVisibility("C").flatten());
final RyaStatement statement = builder.build();
- final DB db = mongoClient.getDB(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
- final DBCollection coll = db.getCollection(configuration.getTriplesCollectionName());
+ final MongoDatabase db = mongoClient.getDatabase(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
+ final MongoCollection<Document> coll = db.getCollection(configuration.getTriplesCollectionName());
dao.add(statement);
@@ -108,10 +119,11 @@ public class MongoDBRyaDAOIT extends MongoRyaTestBase {
builder.setSubject(new RyaURI("http://subject.com"));
builder.setObject(new RyaURI("http://object.com"));
builder.setContext(new RyaURI("http://context.com"));
+ builder.setColumnVisibility(new DocumentVisibility("A&B&C").flatten());
final RyaStatement statement = builder.build();
- final DB db = mongoClient.getDB(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
- final DBCollection coll = db.getCollection(configuration.getTriplesCollectionName());
+ final MongoDatabase db = mongoClient.getDatabase(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
+ final MongoCollection<Document> coll = db.getCollection(configuration.getTriplesCollectionName());
dao.add(statement);
@@ -127,4 +139,406 @@ public class MongoDBRyaDAOIT extends MongoRyaTestBase {
assertEquals(coll.count(),1);
}
+
+ @Test
+ public void testVisibility() throws RyaDAOException, MongoException, IOException {
+ // Doc requires "A" and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A", new Authorizations("B")));
+
+ // Doc requires "A" and user has "A" = User can view
+ assertTrue(testVisibilityStatement("A", new Authorizations("A")));
+
+ // Doc requires "A" and "B" and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("A&B", new Authorizations("A", "B")));
+
+ // Doc requires "A" or "B" and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("A|B", new Authorizations("A", "B")));
+
+ // Doc requires "A" and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("A", new Authorizations("A", "B")));
+
+ // Doc requires "A" and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("A", new Authorizations("A", "B", "C")));
+
+ // Doc requires "A" and "B" and user has "A" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B", new Authorizations("A")));
+
+ // Doc requires "A" and "B" and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B", new Authorizations("B")));
+
+ // Doc requires "A" and "B" and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B", new Authorizations("C")));
+
+ // Doc requires "A" and "B" and "C" and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("A&B&C", new Authorizations("A", "B", "C")));
+
+ // Doc requires "A" and "B" and "C" and user has "A" and "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B&C", new Authorizations("A", "B")));
+
+ // Doc requires "A" and "B" and "C" and user has "A" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B&C", new Authorizations("A")));
+
+ // Doc requires "A" and "B" and "C" and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B&C", new Authorizations("B")));
+
+ // Doc requires "A" and "B" and "C" and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&B&C", new Authorizations("C")));
+
+ // Doc requires "A" and "B" and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("A&B", new Authorizations("A", "B", "C")));
+
+ // Doc requires "A" or "B" and user has "A" = User can view
+ assertTrue(testVisibilityStatement("A|B", new Authorizations("A")));
+
+ // Doc requires "A" or "B" and user has "B" = User can view
+ assertTrue(testVisibilityStatement("A|B", new Authorizations("B")));
+
+ // Doc requires "A" or "B" and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|B", new Authorizations("C")));
+
+ // Doc requires "A" or "B" or "C" and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("A|B|C", new Authorizations("A", "B", "C")));
+
+ // Doc requires "A" or "B" or "C" and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("A|B|C", new Authorizations("A", "B")));
+
+ // Doc requires "A" or "B" or "C" and user has "A" = User can view
+ assertTrue(testVisibilityStatement("A|B|C", new Authorizations("A")));
+
+ // Doc requires "A" or "B" or "C" and user has "B" = User can view
+ assertTrue(testVisibilityStatement("A|B|C", new Authorizations("B")));
+
+ // Doc requires "A" or "B" or "C" and user has "C" = User can view
+ assertTrue(testVisibilityStatement("A|B|C", new Authorizations("C")));
+
+ // Doc requires "A" or "B" and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("A|B", new Authorizations("A", "B", "C")));
+
+ // Doc requires "A" and user has ALL_AUTHORIZATIONS = User can view
+ assertTrue(testVisibilityStatement("A", MongoDbRdfConstants.ALL_AUTHORIZATIONS));
+
+ // Doc requires "A" and "B" and user has ALL_AUTHORIZATIONS = User can view
+ assertTrue(testVisibilityStatement("A&B", MongoDbRdfConstants.ALL_AUTHORIZATIONS));
+
+ // Doc requires "A" or "B" and user has ALL_AUTHORIZATIONS = User can view
+ assertTrue(testVisibilityStatement("A|B", MongoDbRdfConstants.ALL_AUTHORIZATIONS));
+
+ // Doc has no requirement and user has ALL_AUTHORIZATIONS = User can view
+ assertTrue(testVisibilityStatement("", MongoDbRdfConstants.ALL_AUTHORIZATIONS));
+
+ // Doc has no requirement and user has "A" = User can view
+ assertTrue(testVisibilityStatement("", new Authorizations("A")));
+
+ // Doc has no requirement and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("", new Authorizations("A", "B")));
+
+ // Doc requires "A" or ("B" and "C") and user has "A" = User can view
+ assertTrue(testVisibilityStatement("A|(B&C)", new Authorizations("A")));
+
+ // Doc requires "A" or ("B" and "C") and user has "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("A|(B&C)", new Authorizations("B", "C")));
+
+ // Doc requires "A" or ("B" and "C") and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C)", new Authorizations("B")));
+
+ // Doc requires "A" or ("B" and "C") and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C)", new Authorizations("C")));
+
+ // Doc requires "A" and ("B" or "C") and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("A&(B|C)", new Authorizations("A", "B")));
+
+ // Doc requires "A" and ("B" or "C") and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("A&(B|C)", new Authorizations("A", "C")));
+
+ // Doc requires "A" and ("B" or "C") and user has "B" and "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&(B|C)", new Authorizations("B", "C")));
+
+ // Doc requires "A" and ("B" or "C") and user has "A" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&(B|C)", new Authorizations("A")));
+
+ // Doc requires "A" and ("B" or "C") and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A&(B|C)", new Authorizations("B")));
+
+ // Doc requires "A" and ("B" or "C") and user has "C" = User can view
+ assertFalse(testVisibilityStatement("A&(B|C)", new Authorizations("C")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("B")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("C")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("E")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "B")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("C", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "B" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "B", "E")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "C" and "D" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("C", "D", "E")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "C")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "B" and "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("B", "C")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "B" and "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("B", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "B", "C")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "B", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "C", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "B" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("B", "C", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "B" and "C" and "D" and "E"= User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("B", "C", "D", "E")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "B" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "B", "C", "D")));
+
+ // Doc requires ("A" and "B") or ("C" and "D") and user has "A" and "B" and "C" and "D" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A&B)|(C&D)", new Authorizations("A", "B", "C", "D", "E")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("B")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("C")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("E")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "B")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "C" and "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("C", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "B" and "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "B", "E")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "C" and "D" and "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("C", "D", "E")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "C")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("B", "C")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "B" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("B", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "B" and "D" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("B", "D", "E")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "B", "C")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "B", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "C", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "B" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("B", "C", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "B" and "C" and "D" and "E"= User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("B", "C", "D", "E")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "B" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "B", "C", "D")));
+
+ // Doc requires ("A" or "B") and ("C" or "D") and user has "A" and "B" and "C" and "D" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|D)", new Authorizations("A", "B", "C", "D", "E")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "A" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("A", "C")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "B" and "C" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("B", "C")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "A" and "D" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("A", "D", "E")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "B" and "D" and "E" = User can view
+ assertTrue(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("B", "D", "E")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "A" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("A")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("B")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("C")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("D")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("E")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "D" and "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("D", "E")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "A" and "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("A", "D")));
+
+ // Doc requires "(A|B)&(C|(D&E))" and user has "B" and "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("(A|B)&(C|(D&E))", new Authorizations("B", "E")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "A" = User can view
+ assertTrue(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("A")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "B" and "C" and "D" = User can view
+ assertTrue(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("B", "C", "D")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "B" and "C" and "E" = User can view
+ assertTrue(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("B", "C", "E")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "B" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("B")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("C")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "D" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("D")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("E")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "B" and "C" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("B", "C")));
+
+ // Doc requires "A|(B&C&(D|E))" and user has "D" and "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|(B&C&(D|E))", new Authorizations("D", "E")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "A" = User can view
+ assertTrue(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("A")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "E" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("E")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "E" and "F" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("E", "F")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "I" = User CANNOT view
+ assertFalse(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("I")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "A" and "I" = User can view
+ assertTrue(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("A", "I")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "E" and "F" and "G" and "H" = User can view
+ assertTrue(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("E", "F", "G", "H")));
+
+ // Doc requires "A|B|C|D|(E&F&G&H)" and user has "E" and "F" and "G" and "H" and "I" = User can view
+ assertTrue(testVisibilityStatement("A|B|C|D|(E&F&G&H)", new Authorizations("E", "F", "G", "H", "I")));
+
+ // Doc has no requirement and user has ALL_AUTHORIZATIONS = User can view
+ assertTrue(testVisibilityStatement(null, MongoDbRdfConstants.ALL_AUTHORIZATIONS));
+
+ // Doc has no requirement and user has "A" = User can view
+ assertTrue(testVisibilityStatement(null, new Authorizations("A")));
+
+ // Doc has no requirement and user has "A" and "B" = User can view
+ assertTrue(testVisibilityStatement(null, new Authorizations("A", "B")));
+
+ // Doc has no requirement and user has no authorizations = User can view
+ assertTrue(testVisibilityStatement(null, null));
+
+ // Doc has no requirement and user has no authorizations = User can view
+ assertTrue(testVisibilityStatement("", null));
+
+ // Doc requires "A" and user has no authorizations = User can view
+ assertTrue(testVisibilityStatement("A", null));
+
+ // Doc requires "A" and "B" and user has no authorizations = User can view
+ assertTrue(testVisibilityStatement("A&B", null));
+
+ // Doc requires "A" or "B" and user has no authorizations = User can view
+ assertTrue(testVisibilityStatement("A|B", null));
+ }
+
+ /**
+ * Generates a test statement with the provided document visibility to
+ * determine if the specified user authorization can view the statement.
+ * @param documentVisibility the document visibility boolean expression
+ * string.
+ * @param userAuthorizations the user authorization strings.
+ * @return {@code true} if provided authorization could access the document
+ * in the collection. {@code false} otherwise.
+ * @throws RyaDAOException
+ */
+ private boolean testVisibilityStatement(final String documentVisibility, final Authorizations userAuthorizations) throws RyaDAOException {
+ final MongoDatabase db = mongoClient.getDatabase(configuration.get(MongoDBRdfConfiguration.MONGO_DB_NAME));
+ final MongoCollection<Document> coll = db.getCollection(configuration.getTriplesCollectionName());
+
+ final RyaStatement statement = buildVisibilityTestRyaStatement(documentVisibility);
+
+ dao.getConf().setAuths(AuthorizationsUtil.getAuthorizationsStringArray(Authorizations.EMPTY));
+ dao.add(statement);
+ dao.getConf().setAuths(AuthorizationsUtil.getAuthorizationsStringArray(userAuthorizations != null ? userAuthorizations : Authorizations.EMPTY));
+
+ assertEquals(coll.count(), 1);
+
+ final MongoDBQueryEngine queryEngine = (MongoDBQueryEngine) dao.getQueryEngine();
+ queryEngine.setConf(configuration);
+ final CloseableIterable<RyaStatement> iter = queryEngine.query(new RyaQuery(statement));
+
+ // Check if user has authorization to view document based on its visibility
+ final boolean hasNext = iter.iterator().hasNext();
+
+ // Reset
+ dao.delete(statement, configuration);
+ assertEquals(coll.count(), 0);
+ dao.getConf().setAuths(AuthorizationsUtil.getAuthorizationsStringArray(Authorizations.EMPTY));
+
+ return hasNext;
+ }
+
+ private static RyaStatement buildVisibilityTestRyaStatement(final String documentVisibility) {
+ final RyaStatementBuilder builder = new RyaStatementBuilder();
+ builder.setPredicate(new RyaURI("http://temp.com"));
+ builder.setSubject(new RyaURI("http://subject.com"));
+ builder.setObject(new RyaURI("http://object.com"));
+ builder.setContext(new RyaURI("http://context.com"));
+ builder.setColumnVisibility(documentVisibility != null ? documentVisibility.getBytes() : null);
+ final RyaStatement statement = builder.build();
+ return statement;
+ }
}