-
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
need to represent role assignments for resources #890
Comments
1 seeems fine to me and easy to generate everywhere else if its a known thing that mostly always exists by the same name |
I don't have much to add (thanks for the great writeup!) but IMO options 1 + 2 both seem reasonable - and option 1 seems the most intuitive, even if it does have the minor "atomic batch updates" issue |
Based on that feedback, I was all set on option 1. But as I go to implement it, I found a critical part that I had overlooked: if there's a separate resource for each role assignment, how are they identified? If you look at the resources:
Internally, role assignments are uniquely identified by Another idea would be to create a whole hierarchy there, so it's:
but that feels unnecessarily bureaucratic (especially if a client wants to fetch all role assignments -- they'd need to make several requests) and would constrain the implementation in annoying ways (like having a way to efficiently list role assignments on a resource for a particular role). This whole thing has me coming around to option 2. If we expose one resource with PUT/GET, this question goes away. One of the downsides we said with option 2 is that it's more annoying for people trying to delete one role (revoke access for one person), but I don't think it actually is any harder for that case. If we used uuids, you'd still have to list the roles to get the uuid to delete. If we used an identifier that encodes the user id and role name, you'd probably have to list all the role assignments to make sure you found all the roles this user had and remove them. The other downsides were around batching and limits. I'd propose that we set a pretty conservative limit (like 32 role assignments per resource) and not bother with pagination internally. This is consistent with GCP, which has a limit of 1500 principals. (1,500 is obviously much larger than 32, but we can always bump this up if folks want it. Then we can evaluate whether we need pagination internally.) |
Sorry for not getting to this sooner — I've had this tab open for weeks. Like with firewall rules, I lean toward a pragmatic approach that combines 1 and 2. Both are useful for all the reasons you give. I agree it's a bummer to create UUIDs for the already-unique
(Do we need an I agree about putting limits on the number of role assignments so we don't have to paginate, but 32 seems a little small. |
My suggestion looks a lot like what GCP does, as far as I can tell. https://cloud.google.com/iam/docs/manage-access-other-resources Add/remove individual role assignmentshttps://cloud.google.com/sdk/gcloud/reference/compute/instances/add-iam-policy-binding GET/PUT entire policyNo pagination. https://cloud.google.com/compute/docs/reference/rest/v1/instances/getIamPolicy |
I think we can do this in addition to Option 2. That is, we can have PUT/GET
Yes, we do.
Fair enough. My thought was to start small because it's much easier to make larger than smaller, and I imagine folks will generally be using groups for this sort of thing anyway. I'm happy to choose a larger number here though. What value do you think makes sense? |
It doesn't fundamentally change things and it's easy to change later, but how about 64 then? That feels a little harder to accidentally run into during RAP. |
Note that those first two links are for command-line tools, I think. It's not clear to me whether the API itself supports add/remove of individual bindings. Here are some random thoughts as I dig deeper into the implementation. I've got an implementation of option 2. The problem is that eventually we're going to want to let you do a conditional PUT so you can avoid clobbering a dueling administrator. How do we implement this? The obvious solution is to hang some property off the resource itself that identifies the version of the role assignments (or policy) that are in-effect right now. This would also make it possible to scale this further if we needed because we could build a new policy in batches, then flip the resource's "policy_id" as the last step. This also feels related to the Zanzibar "new enemy" problem, in that Zanzibar solves it by having consumers update their resource record with the zookie representing the up-to-date Zanzibar state. This starts to feel a lot like option 3 or 4, but maybe it's just an implementation detail. We could implement option 2 for now, but under the hood, we could put a In terms of implementation, we may want to update the |
Interesting point. It hadn't occurred to me that the reason I couldn't find the API endpoint for that is they're faking it client-side. That is very plausible. Is the clobbering problem the same problem we have on all PUTs? I thought we wanted to use ETags for that. On the other hand, you made the good point here that using an ETag (for the whole policy) doesn't make sense on the add/remove single assignment endpoints because the whole point of single add/remove is to avoid having to fetch the whole policy. I don't really see any way around that and it goes some way toward explaining why GCP are handling it on the client. I'm fine saying forget the add/remove for now then. |
Closing this for now because we've got something concrete enough to move forward. I'm sure we'll revisit later. |
Crucible: update rust crate base64 to 0.21.3 (#913) update rust crate slog-async to 2.8 (#915) update rust crate async-recursion to 1.0.5 (#912) Move active jobs into a separate data structure and optimize `ackable_work` (#908) Check repair IDs correctly (#910) update actions/checkout action to v4 (#903) update rust crate tokio to 1.32 (#890) Remove single-item Vecs (#898) Move "extent under repair" into a helper function (#907) offset_mod shouldn't be randomized (#905) Only rehash if a write may have failed (#899) Make negotiation state an enum (#901) Test update for fast write ack and gather errors on test failure (#897) Propolis: Update cpuid-gen util for better coverage Make storage backend config more flexible and consistent Use correct register sizes for PIIX3 PM device Update bitflags dependency fix softnpu port order (#517) Use hex formatting for unhandled MMIO/PIO/MSRs Update deps for new crucible and oximeter Update standalone-with-crucible docs (#514)
Crucible: update rust crate base64 to 0.21.3 (#913) update rust crate slog-async to 2.8 (#915) update rust crate async-recursion to 1.0.5 (#912) Move active jobs into a separate data structure and optimize `ackable_work` (#908) Check repair IDs correctly (#910) update actions/checkout action to v4 (#903) update rust crate tokio to 1.32 (#890) Remove single-item Vecs (#898) Move "extent under repair" into a helper function (#907) offset_mod shouldn't be randomized (#905) Only rehash if a write may have failed (#899) Make negotiation state an enum (#901) Test update for fast write ack and gather errors on test failure (#897) Propolis: Update cpuid-gen util for better coverage Make storage backend config more flexible and consistent Use correct register sizes for PIIX3 PM device Update bitflags dependency fix softnpu port order (#517) Use hex formatting for unhandled MMIO/PIO/MSRs Update deps for new crucible and oximeter Update standalone-with-crucible docs (#514) Co-authored-by: Alan Hanson <[email protected]>
Recall that:
We need a way to grant role assignments on resources. I expect it'll be pretty straightforward to do this on the database side, but I'm not clear yet how to represent this in the API.
RFD 43 touches on this (heavily inspired by GCP). But much of that RFD has been deferred to post-MVP (including custom roles and custom policies) and it also doesn't actually say how policies are managed (i.e., is there a
/policies
endpoint where you create them, get back an id, then set a policy_id field on a resource?)Option 1: separate API endpoints underneath each resource (e.g.,
.../roles
)e.g.,
/organization/:org_name/roles
, with the usual CRUD to create or remove role assignments for that resource. If you granted access to three Groups and two Users, you'd have five HTTP resources under this one. The HTTP resources under this endpoint would have a representation of the actor and role, with the resource being implied by the path. e.g.,GET /organizations/maze-war/roles
would show a list of objects withactor
androle
.Nice:
role_assignments
Not so nice:
Risks:
Option 2: one separate API endpoint underneath each resource (e.g.,
.../policy
)This is the same as option 1, but we put the entire list of roles into one resource called
policy
. So if you have three Groups and two Users with access, you'd have onePolicy
object with an array of five assignments (rather than five separate HTTP resources, as in the previous case).Nice:
Not so nice:
Risks:
Option 3: a top-level "policy" property on every resource with the policy contents
This looks just like option 2, except instead of the
Policy
being a separate resource, it's a new top-level property calledpolicy
on resources that are allowed to have their own policy. That property directly contains the list of role assignments.Nice:
Not so nice:
Option 4: Policies as separate HTTP resources
This seems closest to what RFD 43 envisions, where each resource is associated with a policy that's managed via some other endpoint (e.g.,
/policies
, with associated CRUD)Nice:
Not so nice:
I lean towards option 1 as it seems to provide a clear, simple model for clients and end users and I think it's also the easiest to implement :) though it does mean creating a bunch more API endpoints, with associated boilerplate, tests, etc.
Note that I think there's a somewhat deeper question here, which is about the model we want to expose to end users.
The model of GitHub, Google Drive, etc. is:
This is similar to how things work in our API today, and we've said this is what we're going to do for MVP because it's much simpler to implement. Personally, I find it pretty intuitive as a user.
The model of AWS and GCP seems to be more like: you can assign arbitrarily complex policies to virtually any resource. This is much more flexible. But we've also heard feedback that IAM is nightmarishly complex in AWS and GCP -- is that partly because in order to grant someone access, you need to create a new policy that grants them a specific role and attach that policy to a resource? Feels like a lot of paperwork for the common case.
Whatever we pick here, I don't think we're foreclosing on custom roles and policies, because we can implement compatibility APIs that read or modify the corresponding bits of a resource's policy. On the other hand, if we want the simpler model for the longer term, it doesn't make sense to do option 4 now, and maybe not 2-3 either.
The text was updated successfully, but these errors were encountered: