Skip to content
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

Merged
merged 9 commits into from
Jun 1, 2024

Conversation

smklein
Copy link
Collaborator

@smklein smklein commented May 31, 2024

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 of nexus/db-queries/src/auth{n,z}, as well as nexus/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 in nexus/auth/storage.
  • nexus-db-fixed-data takes the contents of nexus/db-queries/src/db/fixed-data and separates this logic into a new crate.

@@ -68,57 +62,6 @@ pub enum IdentityProviderType {
Saml(SamlIdentityProvider),
}

impl IdentityProviderType {
Copy link
Collaborator Author

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.

nexus/auth/src/authz/api_resources.rs Show resolved Hide resolved
@@ -51,7 +52,6 @@ impl Authz {
self.oso.is_allowed(actor.clone(), action, resource.clone())
}

#[cfg(test)]
Copy link
Collaborator Author

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>,
Copy link
Collaborator Author

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

Comment on lines 213 to 233
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!();
}
}
Copy link
Collaborator Author

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.

Comment on lines 365 to 385
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!();
}
}
Copy link
Collaborator Author

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

Comment on lines +17 to +18
#[async_trait::async_trait]
pub trait Storage: Send + Sync {
Copy link
Collaborator Author

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";
Copy link
Collaborator Author

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(
Copy link
Collaborator Author

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

Copy link
Contributor

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.

Copy link
Collaborator Author

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?

Copy link
Contributor

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(
Copy link
Collaborator Author

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)

@smklein smklein marked this pull request as ready for review May 31, 2024 23:43
Copy link
Member

@hawkw hawkw left a 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;
Copy link
Member

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly:

#[macro_use]
extern crate slog;
#[macro_use]
extern crate newtype_derive;

nexus/auth/src/authz/api_resources.rs Show resolved Hide resolved
nexus/auth/src/authz/api_resources.rs Outdated Show resolved Hide resolved
nexus/auth/src/authz/context.rs Outdated Show resolved Hide resolved
@@ -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))]
Copy link
Member

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.

nexus/db-fixed-data/Cargo.toml Show resolved Hide resolved
Copy link
Contributor

@andrewjstone andrewjstone left a 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(
Copy link
Contributor

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nifty :)

@smklein smklein enabled auto-merge (squash) June 1, 2024 01:41
@smklein smklein merged commit 450f906 into main Jun 1, 2024
21 checks passed
@smklein smklein deleted the authn-authz-splitup branch June 1, 2024 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants