Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: policy, template, and schema parsing #72

Merged
merged 1 commit into from
Jan 9, 2024

Conversation

Swolebrain
Copy link
Contributor

@Swolebrain Swolebrain commented Dec 15, 2023

Issue #, if available:

n/a

Description of changes:

  • Align behavior of JEntityTypeName with JEntityId. Previously, the FFI code had a difference in behavior between JEntityTypeName and JEntityId. The old code did not allow construction of an invalid id, but it did allow construction of an invalid JEntityTypeName. I changed it so it fails at construction time if it's invalid.
  • Also implemented the same pattern for JEntityUID, and gave it a method get_rust_repr.
  • New schema parse functionality
  • New policy parse functionality
  • New functionality to validate a template link - takes in a template and two EntityUIDs.

@Swolebrain Swolebrain force-pushed the main branch 3 times, most recently from d230441 to c6f2eec Compare December 15, 2023 21:05
@aaronjeline aaronjeline self-requested a review December 15, 2023 22:42
@@ -41,7 +54,8 @@ public Policy(
throw new NullPointerException("Failed to construct policy from null string");
}
if (policyID == null) {
throw new NullPointerException("Failed to construct policy with null ID");
policyID = "policy" + idCounter;
idCounter += 1;

Choose a reason for hiding this comment

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

I think this could cause duplicate IDs if we construct Policy objects from multiple threads. I guess we could:

  • document this and leave it as-is
  • use an AtomicInteger for the counter (presumably the overhead here isn't massive)
  • allow null policy IDs in this class – I think the Rust library's constructors now take Options for policy IDs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AtomicInteger seems like a good choice, will do that

public static boolean validateTemplateLinkedPolicy(Policy p, EntityUID principal, EntityUID resource) {
try {
return validateTemplateLinkedPolicyJni(p.policySrc, principal, resource);
} catch (Exception e) {

Choose a reason for hiding this comment

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

Do we want to swallow every exception here? Internal exceptions might not mean an invalid policy.

Copy link
Contributor Author

@Swolebrain Swolebrain Dec 20, 2023

Choose a reason for hiding this comment

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

What else could they mean? Bear in mind that the principal, resource, and policy have already been parsed by the library so they're valid

I guess they could manually build a Policy object from something invalid. I could consider re-parsing the policy this function takes, or making the rust-side change i mentioned below

Choose a reason for hiding this comment

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

I'm more thinking of exceptions that don't arise from the policy parsing calls specifically. For example, if we have a bug in our JNI code, perhaps that would surface as an exception from the JVM that we would want to bubble up. Possibly worrying too much here, but if we have a base class for our exceptions sitting around (InternalException?) then it might make sense to use that.

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 think im convinced to make it throw

* and EntityIdentifier.parse), then this should only fail because the slots in the template don't match the instantiations
* (barring JNI failures).
* @param p Policy object constructed from a valid template. Best if built from Policy.parsePolicyTemplate
* @param principal EntityUid to put into the principal slot. Leave null if there's no principal slot
Copy link
Contributor

Choose a reason for hiding this comment

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

It's probably not as idiomatic for Java, but I strongly prefer Optional

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Optional is not really a fit though, we need to surface why it failed if it fails. We have two choices that offer library ergonomics that are reasonable to java programmers:

  • Just throw exceptions
  • Use a library that implements an either monad, or implement it ourselves

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I should have been more clear. I'd prefer that the principal and resource be Optional<EntityUID> instead of using null when there's no slot. I agree we should throw an exception on failure, not return an Optional<boolean>.

let basename_str = String::from(jstr_basename);
let mut full_type_name: Vec<String> = jstr_list_to_rust_vec(env, &namespace)?;
full_type_name.push(basename_str);
let fulll_ns_str: String = full_type_name.join("::");
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let fulll_ns_str: String = full_type_name.join("::");
let full_ns_str: String = full_type_name.join("::");

Copy link
Contributor

Choose a reason for hiding this comment

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

We may also want to validate that none of the strings in namespace contain ::.

let basename_str = String::from(jstr_basename);
let mut full_type_name: Vec<String> = jstr_list_to_rust_vec(env, &namespace)?;
full_type_name.push(basename_str);
let fulll_ns_str: String = full_type_name.join("::");
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let fulll_ns_str: String = full_type_name.join("::");
let full_ns_str: String = full_type_name.join("::");

Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment about checking for ::'s

@@ -36,7 +36,8 @@ public class EntityTypeNameTests {
"else",
"in",
"like",
"has"
"has",
"is"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

luckily enough i ran into a test failure from this lol

Copy link
Contributor

Choose a reason for hiding this comment

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

While it is nice that it will "eventually" catch the issue, but I really don't like non-determinism in CI. Created issue here: #73

* and EntityIdentifier.parse), then this should only fail because the slots in the template don't match the instantiations
* (barring JNI failures).
* @param p Policy object constructed from a valid template. Best if built from Policy.parsePolicyTemplate
* @param principal EntityUid to put into the principal slot. Leave null if there's no principal slot
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I should have been more clear. I'd prefer that the principal and resource be Optional<EntityUID> instead of using null when there's no slot. I agree we should throw an exception on failure, not return an Optional<boolean>.

@@ -36,7 +36,8 @@ public class EntityTypeNameTests {
"else",
"in",
"like",
"has"
"has",
"is"
Copy link
Contributor

Choose a reason for hiding this comment

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

While it is nice that it will "eventually" catch the issue, but I really don't like non-determinism in CI. Created issue here: #73

Copy link
Contributor

@khieta khieta left a comment

Choose a reason for hiding this comment

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

Not a Java expert, but the new functionality looks good to me.

cedar-policy = { version = "3.0", path = "../cedar/cedar-policy" } # Need latest version from github
cedar-policy = { version = "3.0", path = "../../cedar/cedar-policy" } # Need latest version from github
Copy link
Contributor

Choose a reason for hiding this comment

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

The README in CedarJavaFFI says that it should be ../cedar, so you should either update this toml file or the README.

Comment on lines +118 to +127
let jstr_basename = env.get_string(&basename)?;
let basename_str = String::from(jstr_basename);
let mut full_type_name: Vec<String> = jstr_list_to_rust_vec(env, &namespace_components)?;
full_type_name.push(basename_str);
let has_namespace_component_with_colon = full_type_name.iter().any(|s| s.contains("::"));
if has_namespace_component_with_colon {
return Err("components of the type name cannot contain colons".into());
}
let full_ns_str: String = full_type_name.join("::");
let type_name: EntityTypeName = full_ns_str.parse()?;
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like the same code as L42-51. Might be nice to centralize this logic in case we end up changing handling in the future.

@andrewmwells-amazon andrewmwells-amazon merged commit 5e2cb3e into cedar-policy:main Jan 9, 2024
1 check passed
ygrignon-sfdc pushed a commit to ygrignon-sfdc/cedar-java that referenced this pull request Jan 16, 2024
ygrignon-sfdc pushed a commit to ygrignon-sfdc/cedar-java that referenced this pull request Jan 16, 2024
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.

4 participants