Skip to content

Conversation

@muditchaudhary
Copy link
Contributor

Overview

  • Adds Duration extension and Offset functions (RFC 80)
  • Adds missing support for negative decimal and CIDR suffixes '

Changes

  • Implements com.cedarpolicy.value.Duration
    • Validates input against supported format specifications
    • Converts and stores it in millisecond format for semantic comparison
    • Adds JSON Serializer and Deserializer support
    • Adds unit and integration tests
  • Implements com.cedarpolicy.value.functions.Offset
    • Adds JSON Serializer and Deserializer support
    • Does not implement equals or hash as this is supposed to represent a function
    • Adds unit and integration tests
  • Bug Fixes (Cause for previously failing shared integration corpus tests)
    • Adds support negative decimals in com.cedarpolicy.value.Decimal
    • Adds support for CIDR suffix in com.cedarpolicy.value.IpAddress
  • Test logging
    • Adds additional corpus test logging to ease debugging

Example Usage

Duration

Set<Entity> entities = new HashSet<>();
String principalId = "alice";
Map<String, Value> principalAttributes = new HashMap<>();
principalAttributes.put("sessionDuration", new Duration("2h30m"));
Set<EntityUID> principalParents = new HashSet<>();
Entity principal = new Entity(new EntityUID(principalType, principalId), principalAttributes, principalParents);
entities.add(principal);

String actionId = "view";
Map<String, Value> actionAttributes = new HashMap<>();
Set<EntityUID> actionParents = new HashSet<>();
Entity action = new Entity(new EntityUID(actionType, actionId), actionAttributes, actionParents);
entities.add(action);

String resourceId = "photo.jpg";
Map<String, Value> resourceAttributes = new HashMap<>();
Set<EntityUID> resourceParents = new HashSet<>();
var resource = new Entity(new EntityUID(resourceType, resourceId), resourceAttributes, resourceParents);
entities.add(resource);

String p =
        "permit(\n"
                + "principal==" + principal.getEUID().toString() + ",\n"
                + "action==" + action.getEUID().toString() + ",\n"
                + "resource==" + resource.getEUID().toString() + "\n)\n"
                + " when {\n"
                + "principal.sessionDuration > duration(\"1h\")};";
final String policyId = "ID0";
Policy policy = new Policy(p, policyId);
Set<Policy> policies = new HashSet<>();
policies.add(policy);
PolicySet policySet = new PolicySet(policies);
Map<String, Value> currentContext = new HashMap<>();
AuthorizationRequest request =
        new AuthorizationRequest(
                principal, action, resource, currentContext);

Offset

Set<Entity> entities = new HashSet<>();
String principalId = "alice";
Map<String, Value> principalAttributes = new HashMap<>();
DateTime baseDateTime = new DateTime("2023-12-25T10:30:45Z");
Duration offsetDuration = new Duration("2h");
principalAttributes.put("appointmentTime", new Offset(baseDateTime, offsetDuration));
Set<EntityUID> principalParents = new HashSet<>();
Entity principal = new Entity(new EntityUID(principalType, principalId), principalAttributes, principalParents);
entities.add(principal);

String actionId = "view";
Map<String, Value> actionAttributes = new HashMap<>();
Set<EntityUID> actionParents = new HashSet<>();
Entity action = new Entity(new EntityUID(actionType, actionId), actionAttributes, actionParents);
entities.add(action);

String resourceId = "photo.jpg";
Map<String, Value> resourceAttributes = new HashMap<>();
Set<EntityUID> resourceParents = new HashSet<>();
var resource = new Entity(new EntityUID(resourceType, resourceId), resourceAttributes, resourceParents);
entities.add(resource);

String p =
        "permit(\n"
                + "principal=="+ principal.getEUID().toString() + ",\n"
                + "action==" + action.getEUID().toString() + ",\n"
                + "resource==" + resource.getEUID().toString() + "\n)\n"
                + " when {\n"
                + "principal.appointmentTime == datetime(\"2023-12-25T10:30:45Z\").offset(duration(\"2h\"))\n};";
final String policyId = "ID0";
Policy policy = new Policy(p, policyId);
Set<Policy> policies = new HashSet<>();
policies.add(policy);
PolicySet policySet = new PolicySet(policies);
Map<String, Value> currentContext = new HashMap<>();
AuthorizationRequest request =
        new AuthorizationRequest(
                principal, action, resource, currentContext);

Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
@muditchaudhary muditchaudhary marked this pull request as ready for review August 21, 2025 18:00
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
@cdisselkoen cdisselkoen requested a review from adpaco-aws August 25, 2025 18:34
Comment on lines 150 to 152
if (isNegative) {
totalMs = Math.negateExact(totalMs);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code before this is going to overflow before negating the i64::MIN value. In other words, this code should be able to parse duration("-9223372036854775808ms") but it doesn't currently because it'll overflow before getting here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. I'll update it and also add the test cases from lean

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in the revision

"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.)"
+ "{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
+ "{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+ "(?:/(?:[0-9]|[12][0-9]|3[0-2]))?$");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean you couldn't accept ranges before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently yes. We just caught it due to the updated corpus tests

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, this fix probably deserves its own PR. You could probably ship it faster that way, and also include unit tests to make sure we've actually fixed this case. Not a blocker for this PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a couple of tests in a subsequent PR

…ases

Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
@muditchaudhary muditchaudhary removed the request for review from cdisselkoen August 26, 2025 14:59
@muditchaudhary muditchaudhary merged commit 95f00b3 into cedar-policy:main Aug 26, 2025
4 checks passed
muditchaudhary added a commit to muditchaudhary/cedar-java that referenced this pull request Aug 27, 2025
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants