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

Constantly running into "unhandled partial" error #1705

Open
IohannesArnold opened this issue May 26, 2023 · 2 comments
Open

Constantly running into "unhandled partial" error #1705

IohannesArnold opened this issue May 26, 2023 · 2 comments

Comments

@IohannesArnold
Copy link

This might be a bug in the oso library or it might be a problem with the way I am writing authorization rules, but if the latter then I claim that this is because the examples in the oso docs are misleading and have led me to this bad habit. I am constantly running into "unhandled partial" errors that are not the result of the body of a rule but the way multiple rules interact. Here's an minimal example:

resource Group {
    permissions = ["query", "update", "delete"];
    roles = ["member", "manager"];
    relations = { managing_group: Group };

    "query" if "member";

    "query" if "manager";
    "update" if "manager";
    "delete" if "manager";

    "manager" if "member" on "managing_group";
}

has_role(user: User, "member", group: Group) if
    member in group.members and
    member matches {id: user.id};

has_relation(parent: Group, "managing_group", child: Group) if
    child.managed_by.id = parent.id;

allow(actor, action, resource) if
    has_permission(actor, action, resource);

I have problems with the following test:

def test_group_manager_role_rule():
    user = User(id=1, ...)
    parent_group = Group(id=2, ...)
    child_group = Group(id=3, ...)
    child_group.managed_by = parent_group
    parent_group.members.append(user)
    assert oso.query_rule_once(
        "has_role", user, "manager", child_group
    )

This yields the following error:

polar.exceptions.PolarRuntimeError: Found an unhandled partial in the query result: _group_74
[...]
The unhandled partial is for variable _group_74.
The expression is: _group_74 matches Group{} and 2 = _group_74.id and _member_76 in _group_74.members and _member_76.id = 1

It took me a while to get a sense of why oso is doing this, but I've realized it has to do with the fact that in the has_relation rule, the first (left-most) term is a free variable, not a bound one. So it resolves out to a query result which I think should be fully determinate, but oso expects me to have provided, somewhere, an existential assertion ("There is a group such that...") which I have not (and don't know where I would).

Is this a bug in oso or my failure to write a proper ruleset? If the latter, I think that the documentation could be clearer, but to prevent this issue from getting too long, I'll not include my argument now.

@samscott89
Copy link
Member

samscott89 commented Jul 6, 2023

Hey @IohannesArnold. Apologies for the slow response here, and for the issues you've encountered.

Is this a bug in oso or my failure to write a proper ruleset? If the latter, I think that the documentation could be clearer

It's kind of a bit of both.

It's currently a pretty big limitation/sharp edge of Polar that we'd like to remove. You're right that Polar is looking for an existential assertion, and currently the problem is the rule:

has_relation(parent: Group, "managing_group", child: Group) if
    child.managed_by.id = parent.id;

Which creates a partially-constrained variable parent which has the properties parent matches Group and parent.id = 2.

Which then goes through to the has_role rule and ends up adding more conditions, which it can't handle.

To address it, you would need to write the rule as:

has_relation(parent: Group, "managing_group", child: Group) if
    child.managed_by = parent;

which should work fine.

We'd like to improve this in future versions

@WaldoJeffers
Copy link
Contributor

WaldoJeffers commented Nov 13, 2023

I'm running into the same issue, and the above solution by @samscott89 only works if both child.managed_by and parent are strictly identical. It's my understanding that at the moment, the Polar compares every property between the two dictionaries.

However, if one of the two dictionaries has a single property with a different value (let's say a metadata field with a timestamp, or anything else outside of the primary key), the two dictionaries won't be considered equal, and the assertion will fail.

This behaviour seems a bit limiting, considering there are many cases where you might have slightly different dictionaries (maybe one of them is a partial copy of the other, maybe it's been slightly altered before an update operation). Also, in most cases, you might not have a managed_by dictionary property but rather a string one, like manager_id, which you would want to compare to parent.id. Does that mean this use case is not supported?

Isn't there a more foolproof way of explaining to Polar how it's supposed to consider that two dictionaries are equal?

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

3 participants