add built-in users to the database#486
Conversation
| /// Converts a Diesel pool error to an external error for a case where we do not | ||
| /// expect the query to fail and the error cannot be easily summarized for an | ||
| /// end user | ||
| pub fn public_error_from_diesel_pool_shouldnt_fail( |
There was a problem hiding this comment.
@smklein FYI I refactored the functions here a bit so I could handle things a bit differently during Nexus startup.
There was a problem hiding this comment.
Nice, this eliminates the need for specifying the resource type. I know I'm late here, but I think there are a handful of spots that could use this for internal operations.
There was a problem hiding this comment.
Yes, I think you're right!
| time_modified | ||
| ) VALUES ( | ||
| /* NOTE: this uuid and name are duplicated in nexus::authn. */ | ||
| '001de000-05e4-0000-0000-000000000001', |
There was a problem hiding this comment.
I think this UUID isn't strictly valid, though I don't think it's likely to bite us unless a UUID lib is trying to interpret the value
There was a problem hiding this comment.
Yeah, all of the built-in users have made-up uuids described in the comment in authn.rs. I believe they all parse with uuid::Uuid::from_str, though. When you say it's not strictly valid, did you mean that it's not consistent with any of the v1-v4 specs?
There was a problem hiding this comment.
yeah, and it's in the legacy variant 0 (NCS) space, which has its own subfields
There was a problem hiding this comment.
Yeah, that's true. How about I change all the built-in user uuids to v4 variant 1 (but otherwise keep their structure the same)? Another option would be to stick with variant 0 (which appears obsolete) or maybe variant 2 (which appears irrelevant to us), but I don't think there's any particular reason to do that. When I wrote up this scheme, I was definitely thinking of them as v4 ones.
| name: &Name, | ||
| ) -> LookupResult<UserBuiltin> { | ||
| use db::schema::user_builtin::dsl; | ||
| opctx.authorize(authz::Action::ListChildren, authz::FLEET)?; |
There was a problem hiding this comment.
Should this be Action::Read?
There was a problem hiding this comment.
I don't think so? Check out
omicron/nexus/src/authz/omicron.polar
Lines 41 to 80 in 038dbd0
There was a problem hiding this comment.
From these descriptions it still sounds like Read to me? Unless we consider resolving a single object by name to be a listing operation
There was a problem hiding this comment.
Oh, I see your point.
What resource would the "read" action be applied to? I assumed at first that you meant that we'd create a new Polar resource for UserBuiltin as a child of Fleet. That's totally reasonable. I elected not do that only because it's a bunch more plumbing and it wasn't clear to me that it was useful to be able to assign permissions on builtin users. Admittedly, it's kind of implicit to piggy-back on the fleet-level list-children permission.
The policy currently doesn't say anything about most system-wide resources, including built-in users and all the hardware resources that we'll eventually have. Until we have reason to think that people are going to want to split these up, I'd propose treating these similar to ProjectResource today: we could create one Polar type for SystemResource with no roles of its own, but permissions granted based on one's roles on the Fleet. It's a little hollow because unlike ProjectResource, there's only one possible parent (so there would be no state in a SystemResource struct). But it might be more explicit. What do you think?
Another option is to keep the resource in this check as Fleet, treating the users and other system-wide information as part of the Fleet itself. I think that's not great because I can imagine even pretty unprivileged users getting "read" permission on the Fleet to be able to view system-wide configuration or something, but that doesn't mean they should be able to see all system-wide resources.
There was a problem hiding this comment.
I like the middle ground of creating one SystemResource for all these things to share. I agree with Tess than using a list children permission to read a single thing is weird.
There was a problem hiding this comment.
Done in 9f562e9 -- let me know what you think.
| &*authn::USER_TEST_PRIVILEGED, | ||
| &*authn::USER_TEST_UNPRIVILEGED, |
There was a problem hiding this comment.
Do we want to conditionally include these?
There was a problem hiding this comment.
I think you're right that at some point, we'll want to leave them out of some configurations. But right now, USER_TEST_PRIVILEGED is necessary to do anything useful with oxapi_demo or the console, even if you're not in #[cfg(test)]. Once we add authz to the rest of the endpoints, you really won't be able to do anything useful with Nexus without these. We will need to revisit this as part of initial rack setup, I think.
There was a problem hiding this comment.
On console we use a DB seeding script for local dev and for the QA instance deployed to GCP. Maybe these users could eventually move into something like that rather than a #[cfg(test)]. Or both, actually. #[cfg(test)] for the tests and a script for local dev.
There was a problem hiding this comment.
It might make sense to add a comment/create an appropriately tagged issue to track that?
There was a problem hiding this comment.
@teisenbe Sure thing. I had a TODO-security for this (which I'm hoping we'll audit before we ship...but we don't track that either), but it got moved around and no longer really covers it. Moved and clarified in af85c83.
@david-crespo Longer-term, another option would be to officially support local users and add "create" endpoints for them. Maybe these would be better modeled as service accounts? Then we'd just have the test suite create the ones it wants. For developers playing around, they could create one with oxapi_demo. But...with what creds would they create one? Anyway I'd definitely like to move that discussion to a follow-on PR.
|
Thanks for taking a look @teisenbe! |
| "privileged" => Some(TEST_USER_UUID_PRIVILEGED.parse().unwrap()), | ||
| "unprivileged" => Some(TEST_USER_UUID_UNPRIVILEGED.parse().unwrap()), | ||
| "privileged" => Some(USER_TEST_PRIVILEGED.id), | ||
| "unprivileged" => Some(USER_TEST_UNPRIVILEGED.id), |
See #419 for background. This change takes the predefined users that we use in the test suite and elsewhere and moves them into the database. This doesn't change much practically but it makes the built-in users look more like we expect things to look once we've integrated with some identity provider.