Skip to content

Commit

Permalink
fix TODOs, fix path of simple fields in queries, add tests
Browse files Browse the repository at this point in the history
Signed-off-by: Dominik Guggemos <dominik.guggemos@bosch.io>
  • Loading branch information
dguggemos committed Apr 12, 2022
1 parent 5cbc648 commit 415c503
Show file tree
Hide file tree
Showing 6 changed files with 378 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,19 @@ Optional<Bson> getAuthorizationBson(final JsonPointer pointer) {
}

Optional<Bson> getFeatureWildcardAuthorizationBson(final JsonPointer pointer) {
final Stream<JsonPointer> fixedPaths = Stream.of(
final Stream<CharSequence> fixedPaths = Stream.of(
JsonPointer.empty(), // root grants
JsonPointer.of("/features"), // features grants
JsonPointer.of("/id")); // feature grants

return Optional.ofNullable(authorizationSubjectIds).map(nonNullSubjectsIds ->
Stream.concat(fixedPaths, collectPaths(pointer)).reduce(null,
(child, p) -> getAuthFilter(nonNullSubjectsIds, p, child),
(b1, b2) -> b1) // TODO
);

final Stream<CharSequence> collectedPaths = collectPaths(pointer);
final List<CharSequence> allPaths = Stream.concat(fixedPaths, collectedPaths).toList();
return Optional.ofNullable(authorizationSubjectIds).map(nonNullSubjectsIds -> {
Bson child = null;
for (final CharSequence path : allPaths) {
child = getAuthFilter(nonNullSubjectsIds, path, child);
}
return child;
});
}

public final Bson visitSimple(final String fieldName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public Bson visitAttribute(final String key) {
@Override
public Bson visitFeature(final String featureId) {
if (FEATURE_ID_WILDCARD.equals(featureId)) {
// TODO any feature exists?
return new Document();
// any feature exists
return Filters.gt(FIELD_F_ARRAY, 0);
} else {
return matchKey(FIELD_FEATURES_PATH + featureId);
}
Expand Down Expand Up @@ -123,7 +123,7 @@ Bson visitPointer(final String key) {

@Override
Bson visitRootLevelField(final String fieldName) {
return Filters.exists(fieldName);
return Filters.exists(String.join(DOT, FIELD_THING, fieldName));
}

private Bson matchKey(final CharSequence key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Bson visitPointer(final String pointer) {

@Override
Bson visitRootLevelField(final String fieldName) {
return predicateFunction.apply(fieldName);
return predicateFunction.apply(String.join(DOT, FIELD_THING, fieldName));
}

private Bson matchValue(final CharSequence key) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.thingsearch.service.persistence.read.expression.visitors;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.conversions.Bson;

import com.mongodb.reactivestreams.client.MongoClients;

final class FilterAssertions {

public static final String AND = "$and";
public static final String OR = "$or";
public static final String ELEM_MATCH = "$elemMatch";

private FilterAssertions() {}

static void assertAuthFilter(final BsonValue authFilter, final List<String> subjects, final String... paths) {
assertThat(authFilter.isDocument()).isTrue();
final BsonDocument document = authFilter.asDocument();
assertThat(document).hasSize(1);
if (document.get(AND) != null) {
final BsonArray next = document.get(AND).asArray();
final BsonDocument revoke = next.get(0).asDocument();
assertThat(revoke).hasSize(1);
assertThat(revoke.entrySet()).containsExactly(Map.entry(revokePath(paths[0]), nin(subjects)));
assertAuthFilter(next.get(1), subjects, paths);
} else if (document.get(OR) != null) {
final BsonArray next = document.get(OR).asArray();
final BsonDocument grant = next.get(0).asDocument();
assertThat(grant).hasSize(1);
assertThat(grant.entrySet()).containsExactly(Map.entry(grantPath(paths[0]), in(subjects)));
if (next.size() > 1) {
assertAuthFilter(next.get(1), subjects, Arrays.copyOfRange(paths, 1, paths.length));
} else if (paths.length > 1) {
fail("The following paths are missing in the auth filter: %s", Arrays.asList(paths));
}
} else {
fail("Must be either " + AND + " or " + OR);
}
}

private static String revokePath(final String path) {
return String.join(".", "p", path, "r");
}

private static String grantPath(final String path) {
return String.join(".", "p", path, "g");
}


private static BsonDocument nin(final Collection<String> subjects) {
return new BsonDocument("$nin",
new BsonArray(subjects.stream()
.map(BsonString::new).collect(Collectors.toList())));
}

private static BsonDocument in(final Collection<String> subjects) {
return new BsonDocument("$in",
new BsonArray(subjects.stream().map(BsonString::new).collect(Collectors.toList())));
}

static BsonDocument toBsonDocument(final Bson bson) {
return bson.toBsonDocument(BsonDocument.class, MongoClients.getDefaultCodecRegistry());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.thingsearch.service.persistence.read.expression.visitors;

import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.ditto.thingsearch.service.persistence.read.expression.visitors.FilterAssertions.AND;
import static org.eclipse.ditto.thingsearch.service.persistence.read.expression.visitors.FilterAssertions.ELEM_MATCH;
import static org.eclipse.ditto.thingsearch.service.persistence.read.expression.visitors.FilterAssertions.assertAuthFilter;
import static org.eclipse.ditto.thingsearch.service.persistence.read.expression.visitors.FilterAssertions.toBsonDocument;

import java.util.List;

import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
import org.bson.BsonValue;
import org.bson.conversions.Bson;
import org.eclipse.ditto.rql.query.expression.visitors.ExistsFieldExpressionVisitor;
import org.junit.Before;
import org.junit.Test;
import org.mongodb.scala.model.Filters;

public class GetExistsBsonVisitorTest {

public static final BsonDocument EXISTS = new BsonDocument("$exists",
new BsonBoolean(true));
private ExistsFieldExpressionVisitor<Bson> underTest;

@Before
public void setUp() throws Exception {
underTest = new GetExistsBsonVisitor(List.of("subject1", "subject2"));
}

@Test
public void testAttribute() {
final Bson filterBson = underTest.visitAttribute("a1");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t.attributes.a1", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"), "/attributes/a1", "/attributes", "/");
}

@Test
public void testFeature() {
final Bson filterBson = underTest.visitFeature("f1");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t.features.f1", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"), "/features/f1", "/features", "/");
}

@Test
public void testFeatureProperties() {
final Bson filterBson = underTest.visitFeatureProperties("f1");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t.features.f1.properties", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"), "/features/f1/properties","/features/f1",
"/features", "/");
}

@Test
public void testFeatureDesiredProperties() {
final Bson filterBson = underTest.visitFeatureDesiredProperties("f1");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t.features.f1.desiredProperties", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"), "/features/f1/desiredProperties","/features/f1",
"/features", "/");
}

@Test
public void testFeatureProperty() {
final Bson filterBson = underTest.visitFeatureIdProperty("f1", "temperature");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t.features.f1.properties.temperature", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"),
"/features/f1/properties/temperature",
"/features/f1/properties",
"/features/f1",
"/features",
"/");
}

@Test
public void testFeatureDesiredProperty() {
final Bson filterBson = underTest.visitFeatureIdDesiredProperty("f1", "targetTemperature");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t.features.f1.desiredProperties.targetTemperature", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"),
"/features/f1/desiredProperties/targetTemperature",
"/features/f1/desiredProperties",
"/features/f1",
"/features",
"/");
}
@Test
public void testWildcardFeatureProperty() {
final Bson filterBson = underTest.visitFeatureIdProperty("*", "temperature");
final BsonDocument document = toBsonDocument(filterBson);
final BsonArray elemMatchAnd = document.get("f").asDocument().get(ELEM_MATCH).asDocument().get(AND).asArray();
final BsonValue existsFilter = elemMatchAnd.get(0);
final BsonValue authFilter = elemMatchAnd.get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("properties.temperature", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"),
"/properties/temperature",
"/properties",
"/id",
"/features",
"/");
}

@Test
public void testSimplePropertyWithLeadingSlash() {
final Bson filterBson = underTest.visitSimple("/_modified");
final BsonDocument document = toBsonDocument(filterBson);
final BsonValue existsFilter = document.getArray(AND).get(0);
final BsonValue authFilter = document.getArray(AND).get(1);
assertThat(existsFilter).isEqualTo(new BsonDocument("t._modified", EXISTS));
assertAuthFilter(authFilter, List.of("subject1", "subject2"),
"/_modified",
"/");
}
@Test
public void testSimplePropertyNoLoadingSlash() {
final Bson filterBson = underTest.visitSimple("_modified");
final BsonDocument document = toBsonDocument(filterBson);
assertThat(document).isEqualTo(new BsonDocument("t._modified", EXISTS));
}

}
Loading

0 comments on commit 415c503

Please sign in to comment.