Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ private static final class AuthorizationRequest extends com.cedarpolicy.model.Au
request.actionEUID,
request.resourceEUID,
request.context,
request.schema);
request.schema,
request.enable_request_validation);
this.slice = slice;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@
* determines if the policies allow for the given principal to perform the given action against the
* given resource.
*
* <p>An optional schema can be provided, but will not be used for validation unless you call
* validate(). The schema is provided to allow parsing Entities from JSON without escape sequences
* (in general, you don't need to worry about this if you construct your entities via the EntityUID
* class).
* <p>If the (optional) schema is provided, this will inform parsing the
* `context` from JSON: for instance, it will allow `__entity` and `__extn`
* escapes to be implicit, and it will error if attributes have the wrong types
* (e.g., string instead of integer).
* If the schema is provided and `enable_request_validation` is true, then the
* schema will also be used for request validation.
*/
public class AuthorizationRequest {
/** EUID of the principal in the request. */
@JsonProperty("principal")
public final Optional<EntityUID> principalEUID;
public final Optional<EntityUID> principalEUID;
/** EUID of the action in the request. */
@JsonProperty("action")
public final EntityUID actionEUID;
Expand All @@ -50,9 +52,17 @@ public class AuthorizationRequest {
/** Key/Value map representing the context of the request. */
public final Optional<Map<String, Value>> context;

/** JSON object representing the Schema. */
/** JSON object representing the Schema. Used for schema-based parsing of
* `context`, and also (if `enable_request_validation` is `true`) for
* request validation. */
public final Optional<Schema> schema;

/** If this is `true` and a schema is provided, perform request validation.
* If this is `false`, the schema will only be used for schema-based parsing
* of `context`, and not for request validation.
* If a schema is not provided, this option has no effect. */
public final boolean enable_request_validation;

/**
* Create an authorization request from the EUIDs and Context.
*
Expand All @@ -61,13 +71,17 @@ public class AuthorizationRequest {
* @param resourceEUID Resource's EUID.
* @param context Key/Value context.
* @param schema Schema (optional).
* @param enable_request_validation Whether to use the schema for just
* schema-based parsing of `context` (false) or also for request validation
* (true). No effect if `schema` is not provided.
*/
public AuthorizationRequest(
Optional<EntityUID> principalEUID,
EntityUID actionEUID,
Optional<EntityUID> resourceEUID,
Optional<Map<String, Value>> context,
Optional<Schema> schema) {
Optional<Schema> schema,
boolean enable_request_validation) {
this.principalEUID = principalEUID;
this.actionEUID = actionEUID;
this.resourceEUID = resourceEUID;
Expand All @@ -77,10 +91,11 @@ public AuthorizationRequest(
this.context = Optional.of(new HashMap<>(context.get()));
}
this.schema = schema;
this.enable_request_validation = enable_request_validation;
}

/**
* Create a request in the empty context.
* Create a request without a schema.
*
* @param principalEUID Principal's EUID.
* @param actionEUID Action's EUID.
Expand All @@ -93,11 +108,12 @@ public AuthorizationRequest(EntityUID principalEUID, EntityUID actionEUID, Entit
actionEUID,
Optional.of(resourceEUID),
Optional.of(context),
Optional.empty());
Optional.empty(),
false);
}

/**
* Create a request without a schema.
* Create a request without a schema, using Entity objects for principal/action/resource.
*
* @param principalEUID Principal's EUID.
* @param actionEUID Action's EUID.
Expand All @@ -106,20 +122,32 @@ public AuthorizationRequest(EntityUID principalEUID, EntityUID actionEUID, Entit
*/
public AuthorizationRequest(Entity principalEUID, Entity actionEUID, Entity resourceEUID, Map<String, Value> context) {
this(
Optional.of(principalEUID.getEUID()),
principalEUID.getEUID(),
actionEUID.getEUID(),
Optional.of(resourceEUID.getEUID()),
Optional.of(context),
Optional.empty());
resourceEUID.getEUID(),
context);
}

public AuthorizationRequest(Optional<Entity> principal, Entity action, Optional<Entity> resource, Optional<Map<String, Value>> context, Optional<Schema> schema) {
/**
* Create a request from Entity objects and Context.
*
* @param principal
* @param action
* @param resource
* @param context
* @param schema
* @param enable_request_validation Whether to use the schema for just
* schema-based parsing of `context` (false) or also for request validation
* (true). No effect if `schema` is not provided.
*/
public AuthorizationRequest(Optional<Entity> principal, Entity action, Optional<Entity> resource, Optional<Map<String, Value>> context, Optional<Schema> schema, boolean enable_request_validation) {
this(
principal.map(e -> e.getEUID()),
action.getEUID(),
resource.map(e -> e.getEUID()),
context,
schema
schema,
enable_request_validation
);
}

Expand Down
5 changes: 3 additions & 2 deletions CedarJava/src/test/java/com/cedarpolicy/JSONTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
Expand Down Expand Up @@ -78,9 +79,9 @@ public void testRequest() {
var moria = new EntityUID(EntityTypeName.parse("Mines").get(), "moria");
AuthorizationRequest q = new AuthorizationRequest(gandalf, opens, moria, new HashMap<String, Value>());
ObjectNode n = JsonNodeFactory.instance.objectNode();
ObjectNode c = JsonNodeFactory.instance.objectNode();
n.set("context", c);
n.set("context", JsonNodeFactory.instance.objectNode());
n.set("schema", JsonNodeFactory.instance.nullNode());
n.set("enable_request_validation", JsonNodeFactory.instance.booleanNode(false));
n.set("principal", buildEuidObject("Wizard", "gandalf"));
n.set("action", buildEuidObject("Action", "opens"));
n.set("resource", buildEuidObject("Mines", "moria"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ private static class JsonRequest {
/** Context map used for the request. */
public Map<String, Value> context;

/** Whether to enable request validation for this request. Default true */
public boolean enable_request_validation = true;

/** The expected decision that should be returned by the authorization engine. */
public AuthorizationResponse.Decision decision;

Expand Down Expand Up @@ -294,7 +297,7 @@ private Set<Policy> loadPolicies(String policiesFile) throws IOException {
String policiesSrc = String.join("\n", Files.readAllLines(resolveIntegrationTestPath(policiesFile)));

// Get a list of the policy sources for the individual policies in the
// file by splitting the full policy source on semicolons. This will
// file by splitting the full policy source on semicolons. This will
// break if a semicolon shows up in a string, eid, or comment.
String[] policyStrings = policiesSrc.split(";");
// Some of the corpus tests contain semicolons in strings and/or eids.
Expand All @@ -305,7 +308,7 @@ private Set<Policy> loadPolicies(String policiesFile) throws IOException {
policyStrings = null;
}
}

Set<Policy> policies = new HashSet<>();
if (policyStrings == null) {
// This case will only be reached for corpus tests.
Expand Down Expand Up @@ -408,10 +411,11 @@ private void executeJsonRequestTest(
request.principal == null ? Optional.empty() : Optional.of(EntityUID.parseFromJson(request.principal).get()),
EntityUID.parseFromJson(request.action).get(),
request.resource == null ? Optional.empty() : Optional.of(EntityUID.parseFromJson(request.resource).get()),
Optional.of(request.context),
Optional.of(schema));
Optional.of(request.context),
Optional.of(schema),
request.enable_request_validation);
Slice slice = new BasicSlice(policies, entities);

try {
AuthorizationResponse response = auth.isAuthorized(authRequest, slice);
System.out.println(response.getErrors());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

/** Integration tests. */
public class IntegrationTests {

final EntityTypeName principalType;
final EntityTypeName actionType;
final EntityTypeName resourceType;
Expand Down Expand Up @@ -368,11 +368,10 @@ public void testUnspecifiedResource() {
Map<String, Value> currentContext = new HashMap<>();
AuthorizationRequest request =
new AuthorizationRequest(
Optional.of(principal),
principal,
action,
Optional.of(resource),
Optional.of(currentContext),
Optional.empty());
resource,
currentContext);
AuthorizationEngine authEngine = new BasicAuthorizationEngine();
AuthorizationResponse response =
Assertions.assertDoesNotThrow(() -> authEngine.isAuthorized(request, slice));
Expand Down Expand Up @@ -599,7 +598,7 @@ public void testSchemaParsingDeny() {
Set<Policy> policies = new HashSet<>();
policies.add(policy);

// Schema says resource.owner is a bool, so we should get a parse failure, which causes
// Schema says resource.owner is a bool, so we should get a parse failure, which causes
// `isAuthorized()` to throw a `BadRequestException`.
Slice slice = new BasicSlice(policies, entities);
Map<String, Value> currentContext = new HashMap<>();
Expand All @@ -609,7 +608,8 @@ public void testSchemaParsingDeny() {
action,
Optional.of(resource),
Optional.of(currentContext),
Optional.of(loadSchemaResource("/schema_parsing_deny_schema.json")));
Optional.of(loadSchemaResource("/schema_parsing_deny_schema.json")),
true);
AuthorizationEngine authEngine = new BasicAuthorizationEngine();
Assertions.assertThrows(BadRequestException.class, () -> authEngine.isAuthorized(request, slice));
}
Expand Down Expand Up @@ -668,7 +668,8 @@ public void testSchemaParsingAllow() {
action,
Optional.of(resource),
Optional.of(currentContext),
Optional.of(loadSchemaResource("/schema_parsing_allow_schema.json")));
Optional.of(loadSchemaResource("/schema_parsing_allow_schema.json")),
true);
AuthorizationEngine authEngine = new BasicAuthorizationEngine();
AuthorizationResponse response =
Assertions.assertDoesNotThrow(() -> authEngine.isAuthorized(request, slice));
Expand Down