-
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
[nexus] Split authn/authz and db-fixed-data into new crates #5849
Conversation
@@ -68,57 +62,6 @@ pub enum IdentityProviderType { | |||
Saml(SamlIdentityProvider), | |||
} | |||
|
|||
impl IdentityProviderType { |
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.
This method was moved into nexus/db-queries/src/db/datastore/identity_provider.rs
- it doesn't need to be defined here, and is tightly coupled with the datastore, not auth logic.
@@ -51,7 +52,6 @@ impl Authz { | |||
self.oso.is_allowed(actor.clone(), action, resource.clone()) | |||
} | |||
|
|||
#[cfg(test)] |
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.
The policy_test
(look for the files changed in this PR) was previously a "unit test" in db-queries
that used auth and datastore methods extensively.
It has stayed in db-queries
to access the database methods, but still needs to access methods defined in nexus-auth
. As a result, some cfg(test)
methods are exposed as pub
to make this work. This is one such example.
@@ -66,18 +66,22 @@ impl Authz { | |||
pub struct Context { | |||
authn: Arc<authn::Context>, | |||
authz: Arc<Authz>, | |||
datastore: Arc<DataStore>, | |||
datastore: Arc<dyn Storage>, |
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.
This is another common pattern through this PR: Wherever nexus/auth
depends on the DataStore
, I basically use dependency injection via dyn Storage
to not directly depend on the datastore. Then, the implementation of Storage
allows nexus/auth
to access a very limited subset of the datastore.
Turns out, that "limited interface" is only a single method. See: nexus/auth/src/storage.rs
nexus/auth/src/authz/context.rs
Outdated
struct FakeStorage {} | ||
|
||
impl FakeStorage { | ||
fn new() -> Arc<dyn crate::storage::Storage> { | ||
Arc::new(Self {}) | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl crate::storage::Storage for FakeStorage { | ||
async fn role_asgn_list_for( | ||
&self, | ||
_opctx: &OpContext, | ||
_identity_type: IdentityType, | ||
_identity_id: Uuid, | ||
_resource_type: ResourceType, | ||
_resource_id: Uuid, | ||
) -> Result<Vec<RoleAssignment>, Error> { | ||
todo!(); | ||
} | ||
} |
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.
This is an example of a test that actively does not need access to the database, and can be replaced by a stub.
nexus/auth/src/context.rs
Outdated
struct FakeStorage {} | ||
|
||
impl FakeStorage { | ||
fn new() -> Arc<dyn crate::storage::Storage> { | ||
Arc::new(Self {}) | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl crate::storage::Storage for FakeStorage { | ||
async fn role_asgn_list_for( | ||
&self, | ||
_opctx: &OpContext, | ||
_identity_type: IdentityType, | ||
_identity_id: Uuid, | ||
_resource_type: ResourceType, | ||
_resource_id: Uuid, | ||
) -> Result<Vec<RoleAssignment>, Error> { | ||
todo!(); | ||
} | ||
} |
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.
Another example of a test that previously required a whole database, but actually, never queries anything from the database
#[async_trait::async_trait] | ||
pub trait Storage: Send + Sync { |
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'd be interested in figuring out a way to make this not use async-trait.
I basically went down this route because:
- authz::Context contains one of these objects
- If it uses generics, then
OpContext
also needs a generic parameter - That changes a lot of code
- ... so I went down the
dyn Storage
route instead, as the DataStore is already wrapped in an Arc
use nexus_types::external_api::params; | ||
use omicron_common::api::external::IdentityMetadataCreateParams; | ||
use once_cell::sync::Lazy; | ||
|
||
/// The name of the built-in Project and VPC for Oxide services. | ||
pub const SERVICES_DB_NAME: &str = "oxide-services"; |
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.
This is just moved, not new
impl Storage for super::DataStore { | ||
/// Return the built-in roles that the given built-in user has for the given | ||
/// resource | ||
async fn role_asgn_list_for( |
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.
This method was moved exactly from nexus/db-queries/src/db/datastore/role.rs
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.
nit: could spell out assign
, but I also understand not wanting to introduce that to the diff if this is used in a lot of places.
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.
Grepping for role_asgn
, this appears to be a pattern used beyond this method. I agree with you that I'd rather it be fully spelled out, but I'm gonna punt on this to avoid touching several files that aren't currently part of this PR?
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.
That works for me
use omicron_common::api::external::ResourceType; | ||
use ref_cast::RefCast; | ||
|
||
impl DataStore { | ||
pub async fn identity_provider_lookup( |
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.
This method was moved exactly from IdentityProvider::lookup
to here (and DataStore
is now a &self
parameter instead of the first argument)
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.
Overall, this seems good to me, although I had a few small style suggestions --- take them or leave them! And, I've mostly tried to avoid commenting on the code that was moved around but hasn't actually changed.
@@ -9,6 +9,7 @@ use super::SiloAuthnPolicy; | |||
use crate::authn; | |||
use async_trait::async_trait; | |||
use authn::Reason; | |||
use slog::trace; |
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'm assuming this is because there used to be a `#[macro_use] somewhere?
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.
Exactly:
omicron/nexus/db-queries/src/lib.rs
Lines 14 to 17 in b0dfd53
#[macro_use] | |
extern crate slog; | |
#[macro_use] | |
extern crate newtype_derive; |
@@ -172,8 +172,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result<OsoInit, anyhow::Error> { | |||
/// | |||
/// There's currently just one enum of Actions for all of Omicron. We expect | |||
/// most objects to support mostly the same set of actions. | |||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | |||
#[cfg_attr(test, derive(strum::EnumIter))] |
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 could, potentially, make the strum
dependency optional and make this:
#[cfg_attr(feature = "strum", derive(strum::EnumIter)]
and then have policy_test
enable the strum
feature in its dev-dependency on authz
...but i'm not sure if that's really worth the effort, up to you.
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.
Looks great!
impl Storage for super::DataStore { | ||
/// Return the built-in roles that the given built-in user has for the given | ||
/// resource | ||
async fn role_asgn_list_for( |
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.
nit: could spell out assign
, but I also understand not wanting to introduce that to the diff if this is used in a lot of places.
@@ -242,7 +242,39 @@ macro_rules! impl_dyn_authorized_resource_for_global { | |||
}; | |||
} | |||
|
|||
impl_dyn_authorized_resource_for_global!(authz::oso_generic::Database); | |||
impl_dyn_authorized_resource_for_resource!(authz::AddressLot); |
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.
Nifty :)
As a part of the ongoing effort to split Nexus into smaller pieces, this PR splits out two new crates:
nexus-auth
takes the contents ofnexus/db-queries/src/auth{n,z}
, as well asnexus/db-queries/src/context.rs
, and separates this logic into a new bespoke crate. Although this crate does have a dependency on the datastore itself, it only actually invokes a single method, and can be abstracted via a new trait, defined innexus/auth/storage
.nexus-db-fixed-data
takes the contents ofnexus/db-queries/src/db/fixed-data
and separates this logic into a new crate.