- Notifications
You must be signed in to change notification settings - Fork923
chore: authz 'any_org' to return if at least 1 org has perms#14009
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Allows checking if a user can do an action in any organization,rather than a specific one. Allows asking general questions on theUI to determine which elements to show.
{ | ||
Name: "CreateTemplateAnyOrg", | ||
Actions: []policy.Action{policy.ActionCreate}, | ||
Resource: rbac.ResourceTemplate.AnyOrganization(), | ||
AuthorizeMap: map[bool][]hasAuthSubjects{ | ||
true: {owner, templateAdmin, orgTemplateAdmin, otherOrgTemplateAdmin, orgAdmin, otherOrgAdmin}, | ||
false: { | ||
userAdmin, memberMe, | ||
orgMemberMe, orgAuditor, orgUserAdmin, | ||
otherOrgMember, otherOrgAuditor, otherOrgUserAdmin, | ||
}, | ||
}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Can you extend these tests to other organization-scoped RBAC objects?
I tried this out for workspaces and ran into behaviour I didn't expect:
{Name: "CreateWorkspaceAnyOrg",Actions: []policy.Action{policy.ActionCreate},Resource: rbac.ResourceWorkspace.AnyOrganization(),AuthorizeMap: map[bool][]hasAuthSubjects{true: {owner, orgAdmin, otherOrgAdmin, orgMemberMe},false: {memberMe, userAdmin, templateAdmin,orgAuditor, orgUserAdmin, orgTemplateAdmin,otherOrgMember, otherOrgAuditor, otherOrgUserAdmin, otherOrgTemplateAdmin,},},},
--- FAIL: TestRolePermissions/CreateWorkspaceAnyOrg (0.01s) roles_test.go:675: Error Trace: /workspaces/coder/coderd/rbac/roles_test.go:675 Error: Received unexpected error: rbac: forbidden: (subject: { f8981f20-e4cc-4be9-a14c-c391200d086e [member organization-member:2c498c97-8c96-4742-9e37-3cd431780fd3] [] all <nil>}), (action: create), (object: { true workspace map[] map[]}), (output: []) Test: TestRolePermissions/CreateWorkspaceAnyOrg Messages: Should pass: CreateWorkspaceAnyOrg as "org_member_me" doing "create" on "workspace"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I can add more tests for sure. The test you wrote is missing withWithOwner
. The test as is, asks the question "Can I create a workspace in any organization that is not owned by me?"
The correct question to see if you can make a workspace belonging to yourself is:
rbac.ResourceWorkspace.AnyOrganization().WithOwner(currentUser.String())
The reason the test without the owner works for some roles, is because owners and org admins are able to create workspaces on behalf of other users
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Ah whoops 👍 I'm still holding the package wrong!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It's not perfect for sure. Maybe there is some api wrapper that could exist that translates everything into some more readable language 🤷♂️.
I agree it's nuanced
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I checked the RBAC benchmarks before/after and didn't see any concerning differences here 👍
coderd/rbac/policy.rego Outdated
count(org_members) > 0 | ||
} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We need to beextremely careful of how we useAnyOrgOwner
. I could see accidental usage of this indbauthz
or similar causing a permissions bug. As this is primarily meant for the UI, it might be good as a follow-up to add a linter for any usage of this outside therbac
package in Go, or in the endpoint for checking permissions.
Good idea with the linter. I agree it should be protected somehow |
3209c86
intomainUh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
What this does
The UI needs to ask the question, "Can this user create a template?". This question is now complicated with
multi-organizations
, since the answer is true if the user can create the template in any org they are a member of.The UI today would have to on every page:
/authcheck
for each org for create templateThis feels like it could add to the number of db queries for a question that doesn't actually require the database. This question can be answered entirely by the policy in memory, since it has the roles and permissions.
So I added a field to an object called
any_org
, that when specified makes the policy return "true" for theorg_allow
section if any organization would return a truthy value.This is a policy change, however we can rely on our extensive rbac testing to avoid regressions.
Closes#14003
Alternative
Rego supports, and we use, partial queries. This is possible with 0 modification of the original policy, meaning we do not need to input this as a first class feature.This was the original plan.
The problem with this plan, is the resulting query to inspect and determine if we should return true/false looks something like:
There is two ways to take this output and make it useful.
Conduct some AST analysis and figure out if there exists and
org_id
in which at least 1 query returns true. Thisfeels trivialas long as our output ASTs remain static. Any policy change could probably break any first draft AST analysis.FRAGILE^--- That would be my choice given unlimited time
The second idea is to execute the queries with various inputs. We can
eval()
each query with a differentinput.object
, and some weak ast analysis could probably pick out someuuids
to throw back in. This would use proper rego evaluation, and bestrong and consistent against changes. This however would result in some loop of execution, becoming a O(n) problem.In the end, if we are going to use rego
eval()
to conclude the result, it makes the most optimization sense just to bake this into the policy itself. Determine the result on the first eval(), rather than trying to rig up something externally.