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; + } }
