Skip to content

Commit

Permalink
✨ Add permission predicate bson-request-array-is-subset
Browse files Browse the repository at this point in the history
  • Loading branch information
ujibang committed Sep 14, 2023
1 parent f560d18 commit 6e2636f
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@

import java.util.Map;
import java.util.Set;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.restheart.exchange.BsonRequest;
import org.restheart.exchange.Request;
import org.restheart.utils.BsonUtils;

import io.undertow.attribute.ExchangeAttribute;
import io.undertow.predicate.Predicate;
import io.undertow.predicate.PredicateBuilder;
import io.undertow.server.HttpServerExchange;
Expand All @@ -43,33 +41,42 @@
* a predicate that resolve to true if the request content is bson and
* the property 'key' (can use the dot notation) is an array that contains 'value'
*/
public class BsonRequestPropContainsPredicate implements Predicate {
private static final Logger LOGGER = LoggerFactory.getLogger(BsonRequestPropContainsPredicate.class);
public class BsonRequestArrayContainsPredicate implements Predicate {
private static final Logger LOGGER = LoggerFactory.getLogger(BsonRequestArrayIsSubsetPredicate.class);

private final String key;
private final ExchangeAttribute value;
private final BsonArray values;

public BsonRequestPropContainsPredicate(String key, ExchangeAttribute value) {
if (key == null || value == null) {
throw new IllegalArgumentException("bson-request-prop-contains predicate must specify key and value");
public BsonRequestArrayContainsPredicate(String key, String[] values) {
if (key == null || values == null) {
throw new IllegalArgumentException("bson-request-array-contains predicate must specify key and values");
}

this.key = key;
this.value = value;

this.values = new BsonArray();

for (var value: values) {
try {
this.values.add(BsonUtils.parse(value));
} catch(Throwable t) {
throw new IllegalArgumentException(value + " is not a valid bson value");
}
}
}

@Override
public boolean resolve(HttpServerExchange exchange) {
var _request = Request.of(exchange);

if (_request == null) {
LOGGER.warn("bson-request-prop-contains predicate invkoed on null BsonRequest");
LOGGER.warn("bson-request-array-contains predicate invkoed on null BsonRequest");
return false;
}

if (_request instanceof BsonRequest bsonRequest) {
if (bsonRequest.getContent() == null) {
LOGGER.warn("bson-request-prop-contains predicate invoked on a BsonRequest with null content");
LOGGER.warn("bson-request-array-contains predicate invoked on a BsonRequest with null content");
return false;
}

Expand All @@ -80,42 +87,41 @@ public boolean resolve(HttpServerExchange exchange) {
var value = _value.get();

if (value.isArray()) {
var expected = BsonUtils.parse(this.value.readAttribute(exchange));
return value.asArray().contains(expected);
return value.asArray().contains(values);
} else {
return false;
}
} else {
return false;
}
} else {
LOGGER.warn("bson-request-prop-contains predicate invoked on a BsonRequest with content {}, it requires a BsonDocument", bsonRequest.getContent().getClass().getSimpleName());
LOGGER.warn("bson-request-array-contains predicate invoked on a BsonRequest with content {}, it requires a BsonDocument", bsonRequest.getContent().getClass().getSimpleName());
return false;
}

} else {
LOGGER.warn("bson-request-prop-contains predicate not invoked on a BsonRequest");
LOGGER.warn("bson-request-array-contains predicate not invoked on a BsonRequest");
return false;
}
}

public static class Builder implements PredicateBuilder {
@Override
public String name() {
return "bson-request-prop-contains";
return "bson-request-array-contains";
}

@Override
public Map<String, Class<?>> parameters() {
var params = Maps.<String, Class<?>>newHashMap();
params.put("key", String.class);
params.put("value", ExchangeAttribute.class);
params.put("values", String[].class);
return params;
}

@Override
public Set<String> requiredParameters() {
return Sets.newHashSet("key", "value");
return Sets.newHashSet("key", "values");
}

@Override
Expand All @@ -125,7 +131,7 @@ public String defaultParameter() {

@Override
public Predicate build(Map<String, Object> config) {
return new BsonRequestPropContainsPredicate((String) config.get("key"), (ExchangeAttribute) config.get("value"));
return new BsonRequestArrayIsSubsetPredicate((String) config.get("key"), (String[]) config.get("values"));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*-
* ========================LICENSE_START=================================
* restheart-security
* %%
* Copyright (C) 2018 - 2023 SoftInstigate
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* =========================LICENSE_END==================================
*/
package org.restheart.security.predicates;

import java.util.Map;
import java.util.Set;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.restheart.exchange.BsonRequest;
import org.restheart.exchange.Request;
import org.restheart.utils.BsonUtils;
import io.undertow.predicate.Predicate;
import io.undertow.predicate.PredicateBuilder;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* a predicate that resolve to true if the request content is bson and
* the property 'key' (can use the dot notation) is an array that only contains 'values'
*/
public class BsonRequestArrayIsSubsetPredicate implements Predicate {
private static final Logger LOGGER = LoggerFactory.getLogger(BsonRequestArrayContainsPredicate.class);

private final String key;
private final BsonArray values;

public BsonRequestArrayIsSubsetPredicate(String key, String[] values) {
if (key == null || values == null) {
throw new IllegalArgumentException("bson-request-array-is-subset predicate must specify key and array");
}

this.key = key;
this.values = new BsonArray();

for (var value: values) {
try {
this.values.add(BsonUtils.parse(value));
} catch(Throwable t) {
throw new IllegalArgumentException(value + " is not a valid bson value");
}
}
}

@Override
public boolean resolve(HttpServerExchange exchange) {
var _request = Request.of(exchange);

if (_request == null) {
LOGGER.warn("bson-request-array-is-subset predicate invkoed on null BsonRequest");
return false;
}

if (_request instanceof BsonRequest bsonRequest) {
if (bsonRequest.getContent() == null) {
LOGGER.warn("bson-request-array-is-subset predicate invoked on a BsonRequest with null content");
return false;
}

if (bsonRequest.getContent() instanceof BsonDocument contentDoc) {
var _values = BsonUtils.get(contentDoc, this.key);

if (_values.isPresent()) {
var values = _values.get();

if (values.isArray()) {
return this.values.containsAll(values.asArray());
} else {
return false;
}
} else {
return false;
}
} else {
LOGGER.warn("bson-request-array-is-subset predicate invoked on a BsonRequest with content {}, it requires a BsonDocument", bsonRequest.getContent().getClass().getSimpleName());
return false;
}

} else {
LOGGER.warn("bson-request-array-is-subset predicate not invoked on a BsonRequest");
return false;
}
}

public static class Builder implements PredicateBuilder {
@Override
public String name() {
return "bson-request-array-is-subset";
}

@Override
public Map<String, Class<?>> parameters() {
var params = Maps.<String, Class<?>>newHashMap();
params.put("key", String.class);
params.put("values", String[].class);
return params;
}

@Override
public Set<String> requiredParameters() {
return Sets.newHashSet("key", "values");
}

@Override
public String defaultParameter() {
return "key";
}

@Override
public Predicate build(Map<String, Object> config) {
return new BsonRequestArrayIsSubsetPredicate((String) config.get("key"), (String[]) config.get("values"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ org.restheart.security.predicates.BsonRequestContainsPredicate$Builder
org.restheart.security.predicates.BsonRequestWhitelistPredicate$Builder
org.restheart.security.predicates.BsonRequestBlacklistPredicate$Builder
org.restheart.security.predicates.BsonRequestPropEqualsPredicate$Builder
org.restheart.security.predicates.BsonRequestPropContainsPredicate$Builder
org.restheart.security.predicates.BsonRequestArrayContainsPredicate$Builder
org.restheart.security.predicates.BsonRequestArrayIsSubsetPredicate$Builder
org.restheart.security.predicates.InPredicate$Builder
Loading

0 comments on commit 6e2636f

Please sign in to comment.