Skip to content

Implement a typechecking function in schema #2374

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

barakmich
Copy link
Contributor

Addressing the implementation part of #2371

Also opened a new issue stemming from the new schema.Graph object not covering all cases.

@barakmich barakmich requested a review from a team as a code owner April 29, 2025 14:53
@github-actions github-actions bot added the area/tooling Affects the dev or user toolchain (e.g. tests, ci, build tools) label Apr 29, 2025
@barakmich barakmich force-pushed the barakmich/typecheck branch from 620c6e6 to 8a35625 Compare April 29, 2025 15:52
definition resource {
relation viewer: organization#member
relation banned: user
permission view = viewer - banned
Copy link
Member

Choose a reason for hiding this comment

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

So this raises an interesting question: the target subject types of view here are technically user and organization#member, since you can issue a check for organization#member here. Question is: do we include that in this set?

Copy link
Member

Choose a reason for hiding this comment

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

I think we do include it and make it required to be specified

Copy link
Contributor Author

Choose a reason for hiding this comment

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

organization#member is also a user though (or, really, the set of things that are on the other end of that relation -- and here we have computed subtypes again). I'm okay with it being specified, though

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, but its valid, so I think we should add it

@barakmich barakmich force-pushed the barakmich/typecheck branch from 8a35625 to 3c06f92 Compare May 1, 2025 19:04
}
if rel.TypeInformation != nil {
return ts.getTypesForInfo(ctx, defName, rel.TypeInformation, seen)
} else if rel.UsersetRewrite != nil {
Copy link
Member

Choose a reason for hiding this comment

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

drop the else here, since all branches return

if err != nil {
return nil, err
}
rel, ok := def.GetRelation(relationName)
Copy link
Member

Choose a reason for hiding this comment

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

newlines after if statements

Copy link
Contributor

@miparnisari miparnisari May 5, 2025

Choose a reason for hiding this comment

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

The wsl linter does this for you 😄 https://github.com/bombsimon/wsl.

We should send a separate PR adding it

if dr.GetRelation() == ellipsesRelation {
out.Add(dr.GetNamespace())
} else if dr.GetRelation() != "" {
rest, err := ts.getTypesForRelationInternal(ctx, dr.GetNamespace(), dr.GetRelation(), seen)
Copy link
Member

Choose a reason for hiding this comment

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

you could pass out to this call and then not need to create a new set every time

`,
expected: map[string][]string{
"organization#member": {"user"},
"resource#viewer": {"user"},
Copy link
Member

Choose a reason for hiding this comment

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

this should include organization#member; please make sure the test captures if it is missing (not sure why it is succeeding right now)

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'm not sure I completely understand why it should.

The type of an organization#member is a user, it's just the set of them; so if I combine direct users and then organization members, it's all users in the end, and the expected type of the viewer permission is always user.

That's what I'd expect, anyway; if there were types annotated after the permission name like:

permission view: user = org->member + viewer

as opposed to

permission view: organization#member + user = org->member + viewer

Copy link
Contributor

@miparnisari miparnisari May 5, 2025

Choose a reason for hiding this comment

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

Sorry, I deleted my previous comment because it was wrong 😄

One use case for this work could be to warn people that their permission doesn't make sense because it will never ever return true. For this use case I believe that you should not include organization#member because it's not a terminal type.

E.g. if your permission is

relation aaa: user
relation bbb: organization#member
permission view = aaa & bbb

view can return true for some users (if the user is member of some organization)

However, if it was

relation aaa: user
relation bbb: apiKey
permission view = aaa & bbb

view will never return true no matter what relationships you write.

The code of CheckPermission API could have something like

if relation is intersection AND len(terminalTypes) != 1 {
    return error("intersection will never lead to true, please rewrite it")
}

Copy link
Member

Choose a reason for hiding this comment

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

It necessary because it is a valid subject type for the check, and for LS. The type annotation is indicating the types of allowed subjects, so it should be present.

Copy link
Contributor

Choose a reason for hiding this comment

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

But we're not gonna use this code to validate the subject types for Check and LS, right?

require.Len(t, types, len(expected), rel)

for _, typ := range types {
require.Contains(t, expected, typ, fmt.Sprintf("expected %v to be in %v", typ, expected))
Copy link
Member

Choose a reason for hiding this comment

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

Better to build a set of expected vs a set of actual and compare, to ensure none are missing either

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/tooling Affects the dev or user toolchain (e.g. tests, ci, build tools)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants