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

any does not behave like a typical or #3002

Closed
shas-apple opened this issue Dec 11, 2020 · 5 comments
Closed

any does not behave like a typical or #3002

shas-apple opened this issue Dec 11, 2020 · 5 comments

Comments

@shas-apple
Copy link

shas-apple commented Dec 11, 2020

any does not behave like a typical or when we have undefined terms or objects.

Behavior

any should behave exactly like when it gets written as a or the rule body.

  • When encountering undefined, any returns undefined. It should evaluate undefined to false.
  • When encountering objects, it should return true as existence of the object.

Steps to Reproduce the Problem

https://play.openpolicyagent.org/p/96S1COuUol
Here or_seperate, or_any1, or_any2, or_any3 should all be true.

Adding the data.json here

{
    "users": {
        "alice": {
            "groups": {
                "groupA": {},
                "groupB": {},
                "groupC": {}
            }
        },
        "bob": {
            "groups": {
                "groupB": {}
            }
        },
        "charlie": {
            "groups": {
                "groupA": {},
                "groupC": {},
                "groupD": {}
            }
        }
    }
}

REPL results

>   any([
|     data.users["charlie"].groups["groupA"],
|     data.users["charlie"].groups["groupB"],
|     data.users["charlie"].groups["groupZ"],
|   ])
| 
undefined
>   any([
|     data.users["charlie"].groups["groupA"],
|     data.users["charlie"].groups["groupC"],
|   ])
| 
false
>   any([
|     data.users["charlie"].groups["groupA"] == {},
|     data.users["charlie"].groups["groupC"] == {},
|   ])
| 
true

Additional Info

@shas-apple shas-apple changed the title any does not behave like a typical or when we have undefined terms. any does not behave like a typical or Dec 11, 2020
@srenatus
Copy link
Contributor

srenatus commented Dec 14, 2020

Thanks for your detailled report! 👍

Please bear in mind that any takes a set or an array, and returns if any of the elements is true.

When you call

any([
     data.users["charlie"].groups["groupA"],
     data.users["charlie"].groups["groupB"],
     data.users["charlie"].groups["groupZ"],
   ])

the evaluation will not even invoke any, since the array that's built there is undefined -- since one of its elements is.

In your example, I'd introduce a partial rule the builds the array of the user's groups:

user_groups[g] {
	data.users[input.subject].groups[g]
}

☝️ This will behave like you'd want it to, I think: It will be a set containing all the groups. It will always be defined. You can query it for a specific one, user_groups["groupZ"] (or even user_groups.groupZ), and you can check if the groups contain the three groups you're interested in using set intersection:

or_any3 {
	count(user_groups & {"groupA", "groupC", "groupZ"}) > 0
}

Does this help? 😃

@tsandall
Copy link
Member

I thought we'd deprecated any() and all() but it turns out we have not! They're still in the docs. I dislike these functions because they seem so unnecessary (they're just alternative ways of expressing AND and OR). I suppose with comprehensions they might be useful but even then you could just use count() or comparisons.

I'm inclined to mark these built-ins as deprecated and put some examples into the docs instead.

@anderseknert
Copy link
Member

There's a ticket for deprecating them here #2437

@shas-apple
Copy link
Author

Would fetching all user groups as a set, affect the performance in any way? Is that better than checking if a particular group exists? That matters for use as in some cases the group list is quite large.

@tsandall
Copy link
Member

Lookups like user_groups["groupX"] have the same performance but operations like user_groups & {"groupX", "groupY"} have to generate the entire set so latency will grow linearly w/ the number of groups under that user.

One option (until #822 is fixed) is to implement (one of) two versions:

# use this version if you require the entire set to be computed at least once
user_groups := {g |
  some g
  data.users[input.user].groups[g]
}

# use this version if you ONLY need to check for existence of specific groups
user_group[g] {
  some g
  data.users[input.user].groups[g]
}

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

No branches or pull requests

4 participants