-
Notifications
You must be signed in to change notification settings - Fork 40
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
authz: fix several bugs #1110
authz: fix several bugs #1110
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,7 +96,7 @@ impl super::Nexus { | |
) -> ListResultVec<db::model::Project> { | ||
let (.., authz_org) = LookupPath::new(opctx, &self.db_datastore) | ||
.organization_name(organization_name) | ||
.lookup_for(authz::Action::CreateChild) | ||
.lookup_for(authz::Action::ListChildren) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and at L114, a copy/paste mistake meant we were checking whether you can create a Project when all you were trying to do was list them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. I wonder if there's a way to make the type system catch such errors in the future? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a good question. We've talked about it a little bit (well, we've talked about whether there's a way to encode in some type what checks you've done already so that we can have functions that accept arguments that must have already been authorized), but not gotten very far. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, seems like it might be worth some investigation. I might play around with it a bit. |
||
.await?; | ||
self.db_datastore | ||
.projects_list_by_name(opctx, &authz_org, pagparams) | ||
|
@@ -111,7 +111,7 @@ impl super::Nexus { | |
) -> ListResultVec<db::model::Project> { | ||
let (.., authz_org) = LookupPath::new(opctx, &self.db_datastore) | ||
.organization_name(organization_name) | ||
.lookup_for(authz::Action::CreateChild) | ||
.lookup_for(authz::Action::ListChildren) | ||
.await?; | ||
self.db_datastore | ||
.projects_list_by_id(opctx, &authz_org, pagparams) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,23 +137,41 @@ resource Silo { | |
]; | ||
roles = [ "admin", "collaborator", "viewer" ]; | ||
|
||
# permissions granted by this resource's roles | ||
"list_children" if "viewer"; | ||
"read" if "viewer"; | ||
"create_child" if "collaborator"; | ||
plotnick marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"modify" if "admin"; | ||
|
||
# roles implied by other roles | ||
"viewer" if "collaborator"; | ||
"create_child" if "collaborator"; | ||
"collaborator" if "admin"; | ||
"modify" if "admin"; | ||
|
||
# roles implied by relationships with the parent fleet | ||
relations = { parent_fleet: Fleet }; | ||
"admin" if "admin" on "parent_fleet"; | ||
"collaborator" if "collaborator" on "parent_fleet"; | ||
"admin" if "collaborator" on "parent_fleet"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, you got the "admin" role on a Silo if you were an "admin" on the Fleet. That seems a bit too restrictive. If you're "collaborator" on the Fleet, you're supposed to be able to create Silos. Why wouldn't you be able to delete them? I concluded that you should get "admin" on Silos if you already have "collaborator" on the parent Fleet. I think it'd be good to better articulate what's intended by the various roles (and the names that are reused, like "collaborator"). I didn't want to block this on that, but if folks are uncomfortable with this we can defer it. The concrete problem with not doing this is that a Fleet Collaborator can create a Silo that they then cannot delete. (There are other ways to address this too but this seemed cleanest to me.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that this makes sense, but I will note here for the record that I find "collaborator" to be a very unclear role name generally. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not attached to it. Do you think the role makes sense and you'd just pick a different name? Do you have one in mind? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔... maybe Looking at the role itself, I'm not sure that I understand the reason for grouping There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (sorry for the incredible delay on responding here) Your intuition makes sense to me, and I think they match the comment as well? The comment says that "viewer" generally gets "read" and "list_children". "collaborator" gets these plus "create_child". |
||
"viewer" if "viewer" on "parent_fleet"; | ||
} | ||
has_relation(fleet: Fleet, "parent_fleet", silo: Silo) | ||
if silo.fleet = fleet; | ||
has_role(actor: AuthenticatedActor, "viewer", silo: Silo) | ||
|
||
# All authenticated users can read their own Silo. That's not quite the same as | ||
# having the "viewer" role. For example, they cannot list Organizations in the | ||
# Silo. | ||
# | ||
# One reason this is necessary is because if an unprivileged user tries to | ||
# create an Organization using "POST /organizations", they should get back a 403 | ||
# (which implies they're able to see /organizations, which is essentially seeing | ||
# the Silo itself) rather than a 404. This behavior isn't a hard constraint | ||
# (i.e., you could reasonably get a 404 for an API you're not allowed to call). | ||
# Nor is the implementation (i.e., we could special-case this endpoint somehow). | ||
# But granting this permission is the simplest way to keep this endpoint's | ||
# behavior consistent with the rest of the API. | ||
# | ||
# It's unclear what else would break if users couldn't see their own Silo. | ||
has_permission(actor: AuthenticatedActor, "read", silo: Silo) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, every authenticated user got the "viewer" role on their Silo. This meant they could list all Organizations. They really only need the ability to "read" their Silo. So I've narrowed this rule to only provide that. |
||
# TODO-security TODO-coverage We should have a test that exercises this | ||
# case. | ||
# syntax. | ||
if silo in actor.silo; | ||
|
||
resource Organization { | ||
|
@@ -180,7 +198,9 @@ resource Organization { | |
"modify" if "admin"; | ||
|
||
relations = { parent_silo: Silo }; | ||
"admin" if "admin" on "parent_silo"; | ||
"admin" if "collaborator" on "parent_silo"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same argument as I made above -- without this, if you're a Silo Collaborator, you can create Organizations but not delete them. That seemed wrong. |
||
"read" if "viewer" on "parent_silo"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found that Silo Viewers couldn't actually see inside an Organization. We probably want to break down and add an "Organization Viewer" role. It wasn't obviously necessary when I was writing out the initial roles. But at this point, it's weirder not to have it. Still, that's a bigger change so for now I'm just granting these two permissions directly. |
||
"list_children" if "viewer" on "parent_silo"; | ||
} | ||
has_relation(silo: Silo, "parent_silo", organization: Organization) | ||
if organization.silo = silo; | ||
|
@@ -212,7 +232,9 @@ resource Project { | |
"modify" if "admin"; | ||
|
||
relations = { parent_organization: Organization }; | ||
"admin" if "admin" on "parent_organization"; | ||
"admin" if "collaborator" on "parent_organization"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the above two cases: without this, if you're an Organization Collaborator, you could create Projects that you couldn't delete. |
||
|
||
"viewer" if "list_children" on "parent_organization"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the other case above, without this, a Fleet or Silo Viewer couldn't see into the Project. |
||
} | ||
has_relation(organization: Organization, "parent_organization", project: Project) | ||
if project.organization = organization; | ||
|
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.
Fixes #1096. There's a test for this now in tests/integration_tests/silos.rs.