From b76bacf3601b9797d1d8a5ff6923bf6e0e0c4933 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 12:44:43 -0700 Subject: [PATCH 1/9] Split authn and authz out of db-queries (still need to do tests) --- Cargo.lock | 175 ++++++++++++++ Cargo.toml | 6 + nexus/Cargo.toml | 2 + nexus/auth/Cargo.toml | 103 ++++++++ nexus/auth/build.rs | 10 + .../src/authn/external/cookies.rs | 2 + .../src/authn/external/mod.rs | 1 + .../src/authn/external/session_cookie.rs | 1 + .../src/authn/external/spoof.rs | 1 + .../src/authn/external/token.rs | 0 nexus/{db-queries => auth}/src/authn/mod.rs | 35 +-- nexus/{db-queries => auth}/src/authn/saga.rs | 0 nexus/{db-queries => auth}/src/authn/silos.rs | 61 +---- nexus/{db-queries => auth}/src/authz/actor.rs | 0 .../src/authz/api_resources.rs | 220 ++++++++---------- .../{db-queries => auth}/src/authz/context.rs | 51 ++-- nexus/{db-queries => auth}/src/authz/mod.rs | 0 .../src/authz/omicron.polar | 0 .../src/authz/oso_generic.rs | 20 +- .../src/authz/policy_test/coverage.rs | 0 .../src/authz/policy_test/mod.rs | 0 .../src/authz/policy_test/resource_builder.rs | 0 .../src/authz/policy_test/resources.rs | 0 nexus/{db-queries => auth}/src/authz/roles.rs | 11 +- nexus/{db-queries => auth}/src/context.rs | 19 +- nexus/auth/src/lib.rs | 7 + nexus/auth/src/storage.rs | 27 +++ nexus/db-fixed-data/Cargo.toml | 102 ++++++++ nexus/db-fixed-data/build.rs | 10 + .../src}/allow_list.rs | 0 .../mod.rs => db-fixed-data/src/lib.rs} | 0 .../src}/project.rs | 10 +- .../src}/role_assignment.rs | 4 +- .../src}/role_builtin.rs | 0 .../fixed_data => db-fixed-data/src}/silo.rs | 10 +- .../src}/silo_user.rs | 41 ++-- .../src}/user_builtin.rs | 0 .../fixed_data => db-fixed-data/src}/vpc.rs | 8 +- .../src}/vpc_firewall_rule.rs | 0 .../src}/vpc_subnet.rs | 2 +- nexus/db-queries/Cargo.toml | 2 + .../db-queries/src/db/datastore/allow_list.rs | 2 +- nexus/db-queries/src/db/datastore/auth.rs | 81 +++++++ .../src/db/datastore/identity_provider.rs | 48 ++++ nexus/db-queries/src/db/datastore/instance.rs | 2 +- nexus/db-queries/src/db/datastore/mod.rs | 8 +- .../src/db/datastore/network_interface.rs | 2 +- nexus/db-queries/src/db/datastore/project.rs | 4 +- .../src/db/datastore/pub_test_utils.rs | 8 +- nexus/db-queries/src/db/datastore/rack.rs | 8 +- nexus/db-queries/src/db/datastore/role.rs | 65 +----- nexus/db-queries/src/db/datastore/silo.rs | 2 +- .../db-queries/src/db/datastore/silo_user.rs | 4 +- .../virtual_provisioning_collection.rs | 4 +- nexus/db-queries/src/db/datastore/vpc.rs | 20 +- nexus/db-queries/src/db/mod.rs | 2 +- .../virtual_provisioning_collection_update.rs | 2 +- nexus/db-queries/src/lib.rs | 9 +- nexus/src/app/mod.rs | 18 +- nexus/src/app/test_interfaces.rs | 12 +- nexus/src/external_api/console_api.rs | 21 +- nexus/tests/integration_tests/saml.rs | 25 +- nexus/tests/integration_tests/silos.rs | 23 +- 63 files changed, 882 insertions(+), 429 deletions(-) create mode 100644 nexus/auth/Cargo.toml create mode 100644 nexus/auth/build.rs rename nexus/{db-queries => auth}/src/authn/external/cookies.rs (98%) rename nexus/{db-queries => auth}/src/authn/external/mod.rs (99%) rename nexus/{db-queries => auth}/src/authn/external/session_cookie.rs (99%) rename nexus/{db-queries => auth}/src/authn/external/spoof.rs (99%) rename nexus/{db-queries => auth}/src/authn/external/token.rs (100%) rename nexus/{db-queries => auth}/src/authn/mod.rs (95%) rename nexus/{db-queries => auth}/src/authn/saga.rs (100%) rename nexus/{db-queries => auth}/src/authn/silos.rs (86%) rename nexus/{db-queries => auth}/src/authz/actor.rs (100%) rename nexus/{db-queries => auth}/src/authz/api_resources.rs (86%) rename nexus/{db-queries => auth}/src/authz/context.rs (91%) rename nexus/{db-queries => auth}/src/authz/mod.rs (100%) rename nexus/{db-queries => auth}/src/authz/omicron.polar (100%) rename nexus/{db-queries => auth}/src/authz/oso_generic.rs (97%) rename nexus/{db-queries => auth}/src/authz/policy_test/coverage.rs (100%) rename nexus/{db-queries => auth}/src/authz/policy_test/mod.rs (100%) rename nexus/{db-queries => auth}/src/authz/policy_test/resource_builder.rs (100%) rename nexus/{db-queries => auth}/src/authz/policy_test/resources.rs (100%) rename nexus/{db-queries => auth}/src/authz/roles.rs (96%) rename nexus/{db-queries => auth}/src/context.rs (97%) create mode 100644 nexus/auth/src/lib.rs create mode 100644 nexus/auth/src/storage.rs create mode 100644 nexus/db-fixed-data/Cargo.toml create mode 100644 nexus/db-fixed-data/build.rs rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/allow_list.rs (100%) rename nexus/{db-queries/src/db/fixed_data/mod.rs => db-fixed-data/src/lib.rs} (100%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/project.rs (79%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/role_assignment.rs (97%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/role_builtin.rs (100%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/silo.rs (91%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/silo_user.rs (67%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/user_builtin.rs (100%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/vpc.rs (91%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/vpc_firewall_rule.rs (100%) rename nexus/{db-queries/src/db/fixed_data => db-fixed-data/src}/vpc_subnet.rs (98%) create mode 100644 nexus/db-queries/src/db/datastore/auth.rs diff --git a/Cargo.lock b/Cargo.lock index 7b8326fb8d..cd28f853e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4484,6 +4484,92 @@ dependencies = [ "rustc_version 0.1.7", ] +[[package]] +name = "nexus-auth" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_matches", + "async-bb8-diesel", + "async-trait", + "authz-macros", + "base64 0.22.1", + "bb8", + "camino", + "camino-tempfile", + "chrono", + "const_format", + "cookie 0.18.1", + "db-macros", + "diesel", + "diesel-dtrace", + "dropshot", + "expectorate", + "futures", + "gateway-client", + "headers", + "http 0.2.12", + "hyper 0.14.28", + "hyper-rustls 0.26.0", + "illumos-utils", + "internal-dns", + "ipnetwork", + "itertools 0.12.1", + "macaddr", + "newtype_derive", + "nexus-config", + "nexus-db-fixed-data", + "nexus-db-model", + "nexus-inventory", + "nexus-reconfigurator-planning", + "nexus-test-utils", + "nexus-types", + "omicron-common", + "omicron-passwords", + "omicron-rpaths", + "omicron-sled-agent", + "omicron-test-utils", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "once_cell", + "openapiv3", + "openssl", + "oso", + "oximeter", + "oxnet", + "paste", + "pem", + "petgraph", + "pq-sys", + "predicates", + "pretty_assertions", + "rand 0.8.5", + "rcgen", + "ref-cast", + "regex", + "rustls 0.22.4", + "samael", + "schemars", + "semver 1.0.23", + "serde", + "serde_json", + "serde_urlencoded", + "serde_with", + "sled-agent-client", + "slog", + "slog-error-chain", + "static_assertions", + "steno", + "strum", + "subprocess", + "swrite", + "term", + "thiserror", + "tokio", + "usdt 0.5.0", + "uuid", +] + [[package]] name = "nexus-client" version = "0.1.0" @@ -4526,6 +4612,91 @@ dependencies = [ "uuid", ] +[[package]] +name = "nexus-db-fixed-data" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_matches", + "async-bb8-diesel", + "async-trait", + "authz-macros", + "base64 0.22.1", + "bb8", + "camino", + "camino-tempfile", + "chrono", + "const_format", + "cookie 0.18.1", + "db-macros", + "diesel", + "diesel-dtrace", + "dropshot", + "expectorate", + "futures", + "gateway-client", + "headers", + "http 0.2.12", + "hyper 0.14.28", + "hyper-rustls 0.26.0", + "illumos-utils", + "internal-dns", + "ipnetwork", + "itertools 0.12.1", + "macaddr", + "newtype_derive", + "nexus-config", + "nexus-db-model", + "nexus-inventory", + "nexus-reconfigurator-planning", + "nexus-test-utils", + "nexus-types", + "omicron-common", + "omicron-passwords", + "omicron-rpaths", + "omicron-sled-agent", + "omicron-test-utils", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "once_cell", + "openapiv3", + "openssl", + "oso", + "oximeter", + "oxnet", + "paste", + "pem", + "petgraph", + "pq-sys", + "predicates", + "pretty_assertions", + "rand 0.8.5", + "rcgen", + "ref-cast", + "regex", + "rustls 0.22.4", + "samael", + "schemars", + "semver 1.0.23", + "serde", + "serde_json", + "serde_urlencoded", + "serde_with", + "sled-agent-client", + "slog", + "slog-error-chain", + "static_assertions", + "steno", + "strum", + "subprocess", + "swrite", + "term", + "thiserror", + "tokio", + "usdt 0.5.0", + "uuid", +] + [[package]] name = "nexus-db-model" version = "0.1.0" @@ -4604,7 +4775,9 @@ dependencies = [ "itertools 0.12.1", "macaddr", "newtype_derive", + "nexus-auth", "nexus-config", + "nexus-db-fixed-data", "nexus-db-model", "nexus-inventory", "nexus-reconfigurator-planning", @@ -5448,8 +5621,10 @@ dependencies = [ "itertools 0.12.1", "macaddr", "mg-admin-client", + "nexus-auth", "nexus-client", "nexus-config", + "nexus-db-fixed-data", "nexus-db-model", "nexus-db-queries", "nexus-defaults", diff --git a/Cargo.toml b/Cargo.toml index e6b0ffb099..1a013c38a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ members = [ "nexus", "nexus-config", "nexus/authz-macros", + "nexus/auth", + "nexus/db-fixed-data", "nexus/db-macros", "nexus/db-model", "nexus/db-queries", @@ -123,9 +125,11 @@ default-members = [ "nexus", "nexus-config", "nexus/authz-macros", + "nexus/auth", "nexus/macros-common", "nexus/metrics-producer-gc", "nexus/networking", + "nexus/db-fixed-data", "nexus/db-macros", "nexus/db-model", "nexus/db-queries", @@ -317,8 +321,10 @@ newtype_derive = "0.1.6" mg-admin-client = { git = "https://github.com/oxidecomputer/maghemite", rev = "025389ff39d594bf2b815377e2c1dc4dd23b1f96" } ddm-admin-client = { git = "https://github.com/oxidecomputer/maghemite", rev = "025389ff39d594bf2b815377e2c1dc4dd23b1f96" } multimap = "0.10.0" +nexus-auth = { path = "nexus/auth" } nexus-client = { path = "clients/nexus-client" } nexus-config = { path = "nexus-config" } +nexus-db-fixed-data = { path = "nexus/db-fixed-data" } nexus-db-model = { path = "nexus/db-model" } nexus-db-queries = { path = "nexus/db-queries" } nexus-defaults = { path = "nexus/defaults" } diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index 0b0bd097bc..a9a89720ce 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -86,7 +86,9 @@ tough.workspace = true trust-dns-resolver.workspace = true uuid.workspace = true +nexus-auth.workspace = true nexus-defaults.workspace = true +nexus-db-fixed-data.workspace = true nexus-db-model.workspace = true nexus-db-queries.workspace = true nexus-inventory.workspace = true diff --git a/nexus/auth/Cargo.toml b/nexus/auth/Cargo.toml new file mode 100644 index 0000000000..cfef322b4a --- /dev/null +++ b/nexus/auth/Cargo.toml @@ -0,0 +1,103 @@ +[package] +name = "nexus-auth" +version = "0.1.0" +edition = "2021" +license = "MPL-2.0" + +[lints] +workspace = true + +[build-dependencies] +omicron-rpaths.workspace = true + +[dependencies] +anyhow.workspace = true +async-bb8-diesel.workspace = true +async-trait.workspace = true +base64.workspace = true +bb8.workspace = true +camino.workspace = true +chrono.workspace = true +const_format.workspace = true +cookie.workspace = true +diesel.workspace = true +diesel-dtrace.workspace = true +dropshot.workspace = true +futures.workspace = true +headers.workspace = true +http.workspace = true +hyper.workspace = true +ipnetwork.workspace = true +macaddr.workspace = true +newtype_derive.workspace = true +once_cell.workspace = true +openssl.workspace = true +oso.workspace = true +oxnet.workspace = true +paste.workspace = true +# See omicron-rpaths for more about the "pq-sys" dependency. +pq-sys = "*" +rand.workspace = true +ref-cast.workspace = true +samael.workspace = true +schemars.workspace = true +semver.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_urlencoded.workspace = true +serde_with.workspace = true +sled-agent-client.workspace = true +slog.workspace = true +slog-error-chain.workspace = true +static_assertions.workspace = true +steno.workspace = true +strum.workspace = true +swrite.workspace = true +thiserror.workspace = true +tokio = { workspace = true, features = ["full"] } +uuid.workspace = true +usdt.workspace = true + +authz-macros.workspace = true +db-macros.workspace = true +nexus-config.workspace = true +nexus-db-fixed-data.workspace = true +nexus-db-model.workspace = true +nexus-types.workspace = true +omicron-common.workspace = true +omicron-passwords.workspace = true +omicron-uuid-kinds.workspace = true +oximeter.workspace = true +omicron-workspace-hack.workspace = true + +# only enabled during tests or via the `testing` feature +omicron-test-utils = { workspace = true, optional = true } + +[features] +# Enable to export `datastore_test` +testing = ["omicron-test-utils"] + +[dev-dependencies] +assert_matches.workspace = true +camino-tempfile.workspace = true +expectorate.workspace = true +hyper-rustls.workspace = true +gateway-client.workspace = true +illumos-utils.workspace = true +internal-dns.workspace = true +itertools.workspace = true +nexus-inventory.workspace = true +nexus-reconfigurator-planning.workspace = true +nexus-test-utils.workspace = true +omicron-sled-agent.workspace = true +omicron-test-utils.workspace = true +openapiv3.workspace = true +pem.workspace = true +petgraph.workspace = true +predicates.workspace = true +pretty_assertions.workspace = true +rcgen.workspace = true +regex.workspace = true +rustls.workspace = true +subprocess.workspace = true +term.workspace = true diff --git a/nexus/auth/build.rs b/nexus/auth/build.rs new file mode 100644 index 0000000000..1ba9acd41c --- /dev/null +++ b/nexus/auth/build.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// See omicron-rpaths for documentation. +// NOTE: This file MUST be kept in sync with the other build.rs files in this +// repository. +fn main() { + omicron_rpaths::configure_default_omicron_rpaths(); +} diff --git a/nexus/db-queries/src/authn/external/cookies.rs b/nexus/auth/src/authn/external/cookies.rs similarity index 98% rename from nexus/db-queries/src/authn/external/cookies.rs rename to nexus/auth/src/authn/external/cookies.rs index e3ad2e3264..35e697475b 100644 --- a/nexus/db-queries/src/authn/external/cookies.rs +++ b/nexus/auth/src/authn/external/cookies.rs @@ -9,6 +9,8 @@ use dropshot::{ ApiEndpointBodyContentType, ExtensionMode, ExtractorMetadata, HttpError, RequestContext, ServerContext, SharedExtractor, }; +use newtype_derive::NewtypeDeref; +use newtype_derive::NewtypeFrom; pub fn parse_cookies( headers: &http::HeaderMap, diff --git a/nexus/db-queries/src/authn/external/mod.rs b/nexus/auth/src/authn/external/mod.rs similarity index 99% rename from nexus/db-queries/src/authn/external/mod.rs rename to nexus/auth/src/authn/external/mod.rs index 623544d38c..ccb7218285 100644 --- a/nexus/db-queries/src/authn/external/mod.rs +++ b/nexus/auth/src/authn/external/mod.rs @@ -9,6 +9,7 @@ use super::SiloAuthnPolicy; use crate::authn; use async_trait::async_trait; use authn::Reason; +use slog::trace; use std::borrow::Borrow; use uuid::Uuid; diff --git a/nexus/db-queries/src/authn/external/session_cookie.rs b/nexus/auth/src/authn/external/session_cookie.rs similarity index 99% rename from nexus/db-queries/src/authn/external/session_cookie.rs rename to nexus/auth/src/authn/external/session_cookie.rs index 74faafef9b..7811bf2826 100644 --- a/nexus/db-queries/src/authn/external/session_cookie.rs +++ b/nexus/auth/src/authn/external/session_cookie.rs @@ -13,6 +13,7 @@ use async_trait::async_trait; use chrono::{DateTime, Duration, Utc}; use dropshot::HttpError; use http::HeaderValue; +use slog::debug; use uuid::Uuid; // many parts of the implementation will reference this OWASP guide diff --git a/nexus/db-queries/src/authn/external/spoof.rs b/nexus/auth/src/authn/external/spoof.rs similarity index 99% rename from nexus/db-queries/src/authn/external/spoof.rs rename to nexus/auth/src/authn/external/spoof.rs index 9b5ed94bde..326d529431 100644 --- a/nexus/db-queries/src/authn/external/spoof.rs +++ b/nexus/auth/src/authn/external/spoof.rs @@ -17,6 +17,7 @@ use async_trait::async_trait; use headers::authorization::{Authorization, Bearer}; use headers::HeaderMapExt; use once_cell::sync::Lazy; +use slog::debug; use uuid::Uuid; // This scheme is intended for demos, development, and testing until we have a diff --git a/nexus/db-queries/src/authn/external/token.rs b/nexus/auth/src/authn/external/token.rs similarity index 100% rename from nexus/db-queries/src/authn/external/token.rs rename to nexus/auth/src/authn/external/token.rs diff --git a/nexus/db-queries/src/authn/mod.rs b/nexus/auth/src/authn/mod.rs similarity index 95% rename from nexus/db-queries/src/authn/mod.rs rename to nexus/auth/src/authn/mod.rs index 305c359820..85c4929cf6 100644 --- a/nexus/db-queries/src/authn/mod.rs +++ b/nexus/auth/src/authn/mod.rs @@ -28,22 +28,21 @@ pub mod external; pub mod saga; pub mod silos; -pub use crate::db::fixed_data::silo_user::USER_TEST_PRIVILEGED; -pub use crate::db::fixed_data::silo_user::USER_TEST_UNPRIVILEGED; -pub use crate::db::fixed_data::user_builtin::USER_DB_INIT; -pub use crate::db::fixed_data::user_builtin::USER_EXTERNAL_AUTHN; -pub use crate::db::fixed_data::user_builtin::USER_INTERNAL_API; -pub use crate::db::fixed_data::user_builtin::USER_INTERNAL_READ; -pub use crate::db::fixed_data::user_builtin::USER_SAGA_RECOVERY; -pub use crate::db::fixed_data::user_builtin::USER_SERVICE_BALANCER; -use crate::db::model::ConsoleSession; +pub use nexus_db_fixed_data::silo_user::USER_TEST_PRIVILEGED; +pub use nexus_db_fixed_data::silo_user::USER_TEST_UNPRIVILEGED; +pub use nexus_db_fixed_data::user_builtin::USER_DB_INIT; +pub use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_AUTHN; +pub use nexus_db_fixed_data::user_builtin::USER_INTERNAL_API; +pub use nexus_db_fixed_data::user_builtin::USER_INTERNAL_READ; +pub use nexus_db_fixed_data::user_builtin::USER_SAGA_RECOVERY; +pub use nexus_db_fixed_data::user_builtin::USER_SERVICE_BALANCER; use crate::authz; -use crate::db; -use crate::db::fixed_data::silo::DEFAULT_SILO; -use crate::db::identity::Asset; +use newtype_derive::NewtypeDisplay; +use nexus_db_fixed_data::silo::DEFAULT_SILO; use nexus_types::external_api::shared::FleetRole; use nexus_types::external_api::shared::SiloRole; +use nexus_types::identity::Asset; use omicron_common::api::external::LookupType; use serde::Deserialize; use serde::Serialize; @@ -386,11 +385,13 @@ impl Actor { } } -impl From<&Actor> for db::model::IdentityType { - fn from(actor: &Actor) -> db::model::IdentityType { +impl From<&Actor> for nexus_db_model::IdentityType { + fn from(actor: &Actor) -> nexus_db_model::IdentityType { match actor { - Actor::UserBuiltin { .. } => db::model::IdentityType::UserBuiltin, - Actor::SiloUser { .. } => db::model::IdentityType::SiloUser, + Actor::UserBuiltin { .. } => { + nexus_db_model::IdentityType::UserBuiltin + } + Actor::SiloUser { .. } => nexus_db_model::IdentityType::SiloUser, } } } @@ -421,7 +422,7 @@ impl std::fmt::Debug for Actor { /// A console session with the silo id of the authenticated user #[derive(Clone, Debug)] pub struct ConsoleSessionWithSiloId { - pub console_session: ConsoleSession, + pub console_session: nexus_db_model::ConsoleSession, pub silo_id: Uuid, } diff --git a/nexus/db-queries/src/authn/saga.rs b/nexus/auth/src/authn/saga.rs similarity index 100% rename from nexus/db-queries/src/authn/saga.rs rename to nexus/auth/src/authn/saga.rs diff --git a/nexus/db-queries/src/authn/silos.rs b/nexus/auth/src/authn/silos.rs similarity index 86% rename from nexus/db-queries/src/authn/silos.rs rename to nexus/auth/src/authn/silos.rs index fc5068fc3c..40b6346fa0 100644 --- a/nexus/db-queries/src/authn/silos.rs +++ b/nexus/auth/src/authn/silos.rs @@ -4,12 +4,6 @@ //! Silo related authentication types and functions -use crate::authz; -use crate::context::OpContext; -use crate::db::lookup::LookupPath; -use crate::db::{model, DataStore}; -use omicron_common::api::external::LookupResult; - use anyhow::{anyhow, Result}; use base64::Engine; use dropshot::HttpError; @@ -36,10 +30,10 @@ pub struct SamlIdentityProvider { pub group_attribute_name: Option, } -impl TryFrom for SamlIdentityProvider { +impl TryFrom for SamlIdentityProvider { type Error = anyhow::Error; fn try_from( - model: model::SamlIdentityProvider, + model: nexus_db_model::SamlIdentityProvider, ) -> Result { let provider = SamlIdentityProvider { idp_metadata_document_string: model.idp_metadata_document_string, @@ -68,57 +62,6 @@ pub enum IdentityProviderType { Saml(SamlIdentityProvider), } -impl IdentityProviderType { - /// First, look up the provider type, then look in for the specific - /// provider details. - pub async fn lookup( - datastore: &DataStore, - opctx: &OpContext, - silo_name: &model::Name, - provider_name: &model::Name, - ) -> LookupResult<(authz::Silo, model::Silo, Self)> { - let (authz_silo, db_silo) = LookupPath::new(opctx, datastore) - .silo_name(silo_name) - .fetch() - .await?; - - let (.., identity_provider) = LookupPath::new(opctx, datastore) - .silo_name(silo_name) - .identity_provider_name(provider_name) - .fetch() - .await?; - - match identity_provider.provider_type { - model::IdentityProviderType::Saml => { - let (.., saml_identity_provider) = - LookupPath::new(opctx, datastore) - .silo_name(silo_name) - .saml_identity_provider_name(provider_name) - .fetch() - .await?; - - let saml_identity_provider = IdentityProviderType::Saml( - saml_identity_provider.try_into() - .map_err(|e: anyhow::Error| - // If an error is encountered converting from the - // model to the authn type here, this is a server - // error: it was validated before it went into the - // DB. - omicron_common::api::external::Error::internal_error( - &format!( - "saml_identity_provider.try_into() failed! {}", - &e.to_string() - ) - ) - )? - ); - - Ok((authz_silo, db_silo, saml_identity_provider)) - } - } - } -} - impl SamlIdentityProvider { pub fn sign_in_url(&self, relay_state: Option) -> Result { let idp_metadata: EntityDescriptor = diff --git a/nexus/db-queries/src/authz/actor.rs b/nexus/auth/src/authz/actor.rs similarity index 100% rename from nexus/db-queries/src/authz/actor.rs rename to nexus/auth/src/authz/actor.rs diff --git a/nexus/db-queries/src/authz/api_resources.rs b/nexus/auth/src/authz/api_resources.rs similarity index 86% rename from nexus/db-queries/src/authz/api_resources.rs rename to nexus/auth/src/authz/api_resources.rs index 69b883a8cf..e2cbddc034 100644 --- a/nexus/db-queries/src/authz/api_resources.rs +++ b/nexus/auth/src/authz/api_resources.rs @@ -34,13 +34,11 @@ use super::Action; use super::{actor::AuthenticatedActor, Authz}; use crate::authn; use crate::context::OpContext; -use crate::db; -use crate::db::fixed_data::FLEET_ID; -use crate::db::model::{ArtifactId, SemverVersion}; -use crate::db::DataStore; use authz_macros::authz_resource; use futures::future::BoxFuture; use futures::FutureExt; +use nexus_db_fixed_data::FLEET_ID; +use nexus_db_model::{ArtifactId, SemverVersion}; use nexus_types::external_api::shared::{FleetRole, ProjectRole, SiloRole}; use omicron_common::api::external::{Error, LookupType, ResourceType}; use once_cell::sync::Lazy; @@ -103,27 +101,27 @@ pub trait ApiResourceWithRoles: ApiResource { pub trait ApiResourceWithRolesType: ApiResourceWithRoles { type AllowedRoles: serde::Serialize + serde::de::DeserializeOwned - + db::model::DatabaseString + + nexus_db_model::DatabaseString + Clone; } -impl AuthorizedResource for T { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( +impl AuthorizedResource for T +where + T: ApiResource + oso::PolarClass + Clone, +{ + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { - load_roles_for_resource_tree(self, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(self, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -263,26 +261,23 @@ impl oso::PolarClass for BlueprintConfig { } impl AuthorizedResource for BlueprintConfig { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // There are no roles on the BlueprintConfig, only permissions. But we // still need to load the Fleet-related roles to verify that the actor // has the "admin" role on the Fleet (possibly conferred from a Silo // role). - load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -323,22 +318,19 @@ impl oso::PolarClass for ConsoleSessionList { } impl AuthorizedResource for ConsoleSessionList { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { - load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -379,22 +371,19 @@ impl oso::PolarClass for DnsConfig { } impl AuthorizedResource for DnsConfig { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { - load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -435,25 +424,22 @@ impl oso::PolarClass for IpPoolList { } impl AuthorizedResource for IpPoolList { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // There are no roles on the IpPoolList, only permissions. But we still // need to load the Fleet-related roles to verify that the actor has the // "admin" role on the Fleet (possibly conferred from a Silo role). - load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -486,25 +472,22 @@ impl oso::PolarClass for DeviceAuthRequestList { } impl AuthorizedResource for DeviceAuthRequestList { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // There are no roles on the DeviceAuthRequestList, only permissions. But we // still need to load the Fleet-related roles to verify that the actor has the // "admin" role on the Fleet. - load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -544,22 +527,19 @@ impl oso::PolarClass for Inventory { } impl AuthorizedResource for Inventory { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { - load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset) - .boxed() + load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } fn on_unauthorized( @@ -603,23 +583,21 @@ impl oso::PolarClass for SiloCertificateList { } impl AuthorizedResource for SiloCertificateList { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // There are no roles on this resource, but we still need to load the // Silo-related roles. - self.silo().load_roles(opctx, datastore, authn, roleset) + self.silo().load_roles(opctx, authn, roleset) } fn on_unauthorized( @@ -663,23 +641,21 @@ impl oso::PolarClass for SiloIdentityProviderList { } impl AuthorizedResource for SiloIdentityProviderList { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // There are no roles on this resource, but we still need to load the // Silo-related roles. - self.silo().load_roles(opctx, datastore, authn, roleset) + self.silo().load_roles(opctx, authn, roleset) } fn on_unauthorized( @@ -720,23 +696,21 @@ impl oso::PolarClass for SiloUserList { } impl AuthorizedResource for SiloUserList { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // There are no roles on this resource, but we still need to load the // Silo-related roles. - self.silo().load_roles(opctx, datastore, authn, roleset) + self.silo().load_roles(opctx, authn, roleset) } fn on_unauthorized( diff --git a/nexus/db-queries/src/authz/context.rs b/nexus/auth/src/authz/context.rs similarity index 91% rename from nexus/db-queries/src/authz/context.rs rename to nexus/auth/src/authz/context.rs index 0d6f2a73ac..4eed7ca799 100644 --- a/nexus/db-queries/src/authz/context.rs +++ b/nexus/auth/src/authz/context.rs @@ -10,12 +10,13 @@ use crate::authn; use crate::authz::oso_generic; use crate::authz::Action; use crate::context::OpContext; -use crate::db::DataStore; +use crate::storage::Storage; use futures::future::BoxFuture; use omicron_common::api::external::Error; use omicron_common::bail_unless; use oso::Oso; use oso::OsoError; +use slog::debug; use std::collections::BTreeSet; use std::sync::Arc; @@ -66,18 +67,22 @@ impl Authz { pub struct Context { authn: Arc, authz: Arc, - datastore: Arc, + datastore: Arc, } impl Context { pub fn new( authn: Arc, authz: Arc, - datastore: Arc, + datastore: Arc, ) -> Context { Context { authn, authz, datastore } } + pub fn datastore(&self) -> &Arc { + &self.datastore + } + /// Check whether the actor performing this request is authorized for /// `action` on `resource`. pub async fn authorize( @@ -111,9 +116,7 @@ impl Context { ); let mut roles = RoleSet::new(); - resource - .load_roles(opctx, &self.datastore, &self.authn, &mut roles) - .await?; + resource.load_roles(opctx, &self.authn, &mut roles).await?; debug!(opctx.log, "roles"; "roles" => ?roles); let actor = AnyActor::new(&self.authn, roles); let is_authn = self.authn.actor().is_some(); @@ -162,19 +165,17 @@ pub trait AuthorizedResource: oso::ToPolar + Send + Sync + 'static { /// That's how this works for most resources. There are other kinds of /// resources (like the Database itself) that aren't stored in the database /// and for which a different mechanism might be used. - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, opctx: &'b OpContext, - datastore: &'c DataStore, - authn: &'d authn::Context, - roleset: &'e mut RoleSet, - ) -> BoxFuture<'f, Result<(), Error>> + authn: &'c authn::Context, + roleset: &'d mut RoleSet, + ) -> BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f; + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e; /// Invoked on authz failure to determine the final authz result /// @@ -230,19 +231,17 @@ mod test { #[derive(Clone, PolarClass)] struct UnregisteredResource; impl AuthorizedResource for UnregisteredResource { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, _: &'b OpContext, - _: &'c DataStore, - _: &'d authn::Context, - _: &'e mut RoleSet, - ) -> futures::future::BoxFuture<'f, Result<(), Error>> + _: &'c authn::Context, + _: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // authorize() shouldn't get far enough to call this. unimplemented!(); diff --git a/nexus/db-queries/src/authz/mod.rs b/nexus/auth/src/authz/mod.rs similarity index 100% rename from nexus/db-queries/src/authz/mod.rs rename to nexus/auth/src/authz/mod.rs diff --git a/nexus/db-queries/src/authz/omicron.polar b/nexus/auth/src/authz/omicron.polar similarity index 100% rename from nexus/db-queries/src/authz/omicron.polar rename to nexus/auth/src/authz/omicron.polar diff --git a/nexus/db-queries/src/authz/oso_generic.rs b/nexus/auth/src/authz/oso_generic.rs similarity index 97% rename from nexus/db-queries/src/authz/oso_generic.rs rename to nexus/auth/src/authz/oso_generic.rs index dd646a1c98..6816d54724 100644 --- a/nexus/db-queries/src/authz/oso_generic.rs +++ b/nexus/auth/src/authz/oso_generic.rs @@ -12,7 +12,6 @@ use super::roles::RoleSet; use super::Authz; use crate::authn; use crate::context::OpContext; -use crate::db::DataStore; use anyhow::ensure; use anyhow::Context; use futures::future::BoxFuture; @@ -20,6 +19,7 @@ use futures::FutureExt; use omicron_common::api::external::Error; use oso::Oso; use oso::PolarClass; +use slog::info; use std::collections::BTreeSet; use std::fmt; @@ -267,19 +267,17 @@ impl oso::PolarClass for Database { } impl AuthorizedResource for Database { - fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>( + fn load_roles<'a, 'b, 'c, 'd, 'e>( &'a self, _: &'b OpContext, - _: &'c DataStore, - _: &'d authn::Context, - _: &'e mut RoleSet, - ) -> BoxFuture<'f, Result<(), Error>> + _: &'c authn::Context, + _: &'d mut RoleSet, + ) -> BoxFuture<'e, Result<(), Error>> where - 'a: 'f, - 'b: 'f, - 'c: 'f, - 'd: 'f, - 'e: 'f, + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, { // We don't use (database) roles to grant access to the database. The // role assignment is hardcoded for all authenticated users. See the diff --git a/nexus/db-queries/src/authz/policy_test/coverage.rs b/nexus/auth/src/authz/policy_test/coverage.rs similarity index 100% rename from nexus/db-queries/src/authz/policy_test/coverage.rs rename to nexus/auth/src/authz/policy_test/coverage.rs diff --git a/nexus/db-queries/src/authz/policy_test/mod.rs b/nexus/auth/src/authz/policy_test/mod.rs similarity index 100% rename from nexus/db-queries/src/authz/policy_test/mod.rs rename to nexus/auth/src/authz/policy_test/mod.rs diff --git a/nexus/db-queries/src/authz/policy_test/resource_builder.rs b/nexus/auth/src/authz/policy_test/resource_builder.rs similarity index 100% rename from nexus/db-queries/src/authz/policy_test/resource_builder.rs rename to nexus/auth/src/authz/policy_test/resource_builder.rs diff --git a/nexus/db-queries/src/authz/policy_test/resources.rs b/nexus/auth/src/authz/policy_test/resources.rs similarity index 100% rename from nexus/db-queries/src/authz/policy_test/resources.rs rename to nexus/auth/src/authz/policy_test/resources.rs diff --git a/nexus/db-queries/src/authz/roles.rs b/nexus/auth/src/authz/roles.rs similarity index 96% rename from nexus/db-queries/src/authz/roles.rs rename to nexus/auth/src/authz/roles.rs index 11b3d482d1..0716e05bc7 100644 --- a/nexus/db-queries/src/authz/roles.rs +++ b/nexus/auth/src/authz/roles.rs @@ -37,9 +37,9 @@ use super::api_resources::ApiResource; use crate::authn; use crate::context::OpContext; -use crate::db::DataStore; use omicron_common::api::external::Error; use omicron_common::api::external::ResourceType; +use slog::trace; use std::collections::BTreeSet; use uuid::Uuid; @@ -87,7 +87,6 @@ impl RoleSet { pub async fn load_roles_for_resource_tree( resource: &R, opctx: &OpContext, - datastore: &DataStore, authn: &authn::Context, roleset: &mut RoleSet, ) -> Result<(), Error> @@ -100,7 +99,6 @@ where let resource_id = with_roles.resource_id(); load_directly_attached_roles( opctx, - datastore, authn, resource_type, resource_id, @@ -115,7 +113,6 @@ where { load_directly_attached_roles( opctx, - datastore, authn, resource_type, resource_id, @@ -135,7 +132,7 @@ where // it's clearer to just call this "parent" than // "related_resources_whose_roles_might_grant_access_to_this".) if let Some(parent) = resource.parent() { - parent.load_roles(opctx, datastore, authn, roleset).await?; + parent.load_roles(opctx, authn, roleset).await?; } Ok(()) @@ -143,7 +140,6 @@ where async fn load_directly_attached_roles( opctx: &OpContext, - datastore: &DataStore, authn: &authn::Context, resource_type: ResourceType, resource_id: Uuid, @@ -159,7 +155,8 @@ async fn load_directly_attached_roles( "resource_id" => resource_id.to_string(), ); - let roles = datastore + let roles = opctx + .datastore() .role_asgn_list_for( opctx, actor.into(), diff --git a/nexus/db-queries/src/context.rs b/nexus/auth/src/context.rs similarity index 97% rename from nexus/db-queries/src/context.rs rename to nexus/auth/src/context.rs index dfd1fe4322..9d32c333fd 100644 --- a/nexus/db-queries/src/context.rs +++ b/nexus/auth/src/context.rs @@ -8,9 +8,12 @@ use super::authz; use crate::authn::external::session_cookie::Session; use crate::authn::ConsoleSessionWithSiloId; use crate::authz::AuthorizedResource; -use crate::db::DataStore; +use crate::storage::Storage; use chrono::{DateTime, Utc}; use omicron_common::api::external::Error; +use slog::debug; +use slog::o; +use slog::trace; use std::collections::BTreeMap; use std::fmt::Debug; use std::sync::Arc; @@ -111,6 +114,10 @@ impl OpContext { }) } + pub(crate) fn datastore(&self) -> &Arc { + self.authz.datastore() + } + fn log_and_metadata_for_authn( log: &slog::Logger, authn: &authn::Context, @@ -135,8 +142,8 @@ impl OpContext { (log, metadata) } - pub fn load_request_metadata( - rqctx: &dropshot::RequestContext, + pub fn load_request_metadata( + rqctx: &dropshot::RequestContext, metadata: &mut BTreeMap, ) { let request = &rqctx.request; @@ -151,7 +158,7 @@ impl OpContext { log: slog::Logger, authz: Arc, authn: authn::Context, - datastore: Arc, + datastore: Arc, ) -> OpContext { let created_instant = Instant::now(); let created_walltime = SystemTime::now(); @@ -180,7 +187,7 @@ impl OpContext { // outside public interfaces. pub fn for_tests( log: slog::Logger, - datastore: Arc, + datastore: Arc, ) -> OpContext { let created_instant = Instant::now(); let created_walltime = SystemTime::now(); @@ -207,7 +214,7 @@ impl OpContext { /// functionally the same as one that you already have, but where you want /// to provide extra debugging information (in the form of key-value pairs) /// in both the OpContext itself and its logger. - pub fn child(&self, new_metadata: BTreeMap) -> OpContext { + pub fn child(&self, new_metadata: BTreeMap) -> Self { let created_instant = Instant::now(); let created_walltime = SystemTime::now(); let mut metadata = self.metadata.clone(); diff --git a/nexus/auth/src/lib.rs b/nexus/auth/src/lib.rs new file mode 100644 index 0000000000..f825049113 --- /dev/null +++ b/nexus/auth/src/lib.rs @@ -0,0 +1,7 @@ +pub mod authn; +pub mod authz; +pub mod context; +pub mod storage; + +#[macro_use] +extern crate newtype_derive; diff --git a/nexus/auth/src/storage.rs b/nexus/auth/src/storage.rs new file mode 100644 index 0000000000..9708f9a2ec --- /dev/null +++ b/nexus/auth/src/storage.rs @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Describes the dependency from the auth system on the datastore. +//! +//! Auth and storage are intertwined, but by isolating the interface from +//! auth on the database, we can avoid a circular dependency. + +use crate::context::OpContext; +use nexus_db_model::IdentityType; +use nexus_db_model::RoleAssignment; +use omicron_common::api::external::Error; +use omicron_common::api::external::ResourceType; +use uuid::Uuid; + +#[async_trait::async_trait] +pub trait Storage: Send + Sync { + async fn role_asgn_list_for( + &self, + _opctx: &OpContext, + _identity_type: IdentityType, + _identity_id: Uuid, + _resource_type: ResourceType, + _resource_id: Uuid, + ) -> Result, Error>; +} diff --git a/nexus/db-fixed-data/Cargo.toml b/nexus/db-fixed-data/Cargo.toml new file mode 100644 index 0000000000..c697ea0c1e --- /dev/null +++ b/nexus/db-fixed-data/Cargo.toml @@ -0,0 +1,102 @@ +[package] +name = "nexus-db-fixed-data" +version = "0.1.0" +edition = "2021" +license = "MPL-2.0" + +[lints] +workspace = true + +[build-dependencies] +omicron-rpaths.workspace = true + +[dependencies] +anyhow.workspace = true +async-bb8-diesel.workspace = true +async-trait.workspace = true +base64.workspace = true +bb8.workspace = true +camino.workspace = true +chrono.workspace = true +const_format.workspace = true +cookie.workspace = true +diesel.workspace = true +diesel-dtrace.workspace = true +dropshot.workspace = true +futures.workspace = true +headers.workspace = true +http.workspace = true +hyper.workspace = true +ipnetwork.workspace = true +macaddr.workspace = true +newtype_derive.workspace = true +once_cell.workspace = true +openssl.workspace = true +oso.workspace = true +oxnet.workspace = true +paste.workspace = true +# See omicron-rpaths for more about the "pq-sys" dependency. +pq-sys = "*" +rand.workspace = true +ref-cast.workspace = true +samael.workspace = true +schemars.workspace = true +semver.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_urlencoded.workspace = true +serde_with.workspace = true +sled-agent-client.workspace = true +slog.workspace = true +slog-error-chain.workspace = true +static_assertions.workspace = true +steno.workspace = true +strum.workspace = true +swrite.workspace = true +thiserror.workspace = true +tokio = { workspace = true, features = ["full"] } +uuid.workspace = true +usdt.workspace = true + +authz-macros.workspace = true +db-macros.workspace = true +nexus-config.workspace = true +nexus-db-model.workspace = true +nexus-types.workspace = true +omicron-common.workspace = true +omicron-passwords.workspace = true +omicron-uuid-kinds.workspace = true +oximeter.workspace = true +omicron-workspace-hack.workspace = true + +# only enabled during tests or via the `testing` feature +omicron-test-utils = { workspace = true, optional = true } + +[features] +# Enable to export `datastore_test` +testing = ["omicron-test-utils"] + +[dev-dependencies] +assert_matches.workspace = true +camino-tempfile.workspace = true +expectorate.workspace = true +hyper-rustls.workspace = true +gateway-client.workspace = true +illumos-utils.workspace = true +internal-dns.workspace = true +itertools.workspace = true +nexus-inventory.workspace = true +nexus-reconfigurator-planning.workspace = true +nexus-test-utils.workspace = true +omicron-sled-agent.workspace = true +omicron-test-utils.workspace = true +openapiv3.workspace = true +pem.workspace = true +petgraph.workspace = true +predicates.workspace = true +pretty_assertions.workspace = true +rcgen.workspace = true +regex.workspace = true +rustls.workspace = true +subprocess.workspace = true +term.workspace = true diff --git a/nexus/db-fixed-data/build.rs b/nexus/db-fixed-data/build.rs new file mode 100644 index 0000000000..1ba9acd41c --- /dev/null +++ b/nexus/db-fixed-data/build.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// See omicron-rpaths for documentation. +// NOTE: This file MUST be kept in sync with the other build.rs files in this +// repository. +fn main() { + omicron_rpaths::configure_default_omicron_rpaths(); +} diff --git a/nexus/db-queries/src/db/fixed_data/allow_list.rs b/nexus/db-fixed-data/src/allow_list.rs similarity index 100% rename from nexus/db-queries/src/db/fixed_data/allow_list.rs rename to nexus/db-fixed-data/src/allow_list.rs diff --git a/nexus/db-queries/src/db/fixed_data/mod.rs b/nexus/db-fixed-data/src/lib.rs similarity index 100% rename from nexus/db-queries/src/db/fixed_data/mod.rs rename to nexus/db-fixed-data/src/lib.rs diff --git a/nexus/db-queries/src/db/fixed_data/project.rs b/nexus/db-fixed-data/src/project.rs similarity index 79% rename from nexus/db-queries/src/db/fixed_data/project.rs rename to nexus/db-fixed-data/src/project.rs index e240900e0c..6b9f005916 100644 --- a/nexus/db-queries/src/db/fixed_data/project.rs +++ b/nexus/db-fixed-data/src/project.rs @@ -2,12 +2,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::db; -use crate::db::datastore::SERVICES_DB_NAME; +use nexus_db_model as model; 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"; + /// UUID of built-in project for internal services on the rack. pub static SERVICES_PROJECT_ID: Lazy = Lazy::new(|| { "001de000-4401-4000-8000-000000000000" @@ -16,8 +18,8 @@ pub static SERVICES_PROJECT_ID: Lazy = Lazy::new(|| { }); /// Built-in Project for internal services on the rack. -pub static SERVICES_PROJECT: Lazy = Lazy::new(|| { - db::model::Project::new_with_id( +pub static SERVICES_PROJECT: Lazy = Lazy::new(|| { + model::Project::new_with_id( *SERVICES_PROJECT_ID, *super::silo::INTERNAL_SILO_ID, params::ProjectCreate { diff --git a/nexus/db-queries/src/db/fixed_data/role_assignment.rs b/nexus/db-fixed-data/src/role_assignment.rs similarity index 97% rename from nexus/db-queries/src/db/fixed_data/role_assignment.rs rename to nexus/db-fixed-data/src/role_assignment.rs index d6c95d47b6..25b26786f8 100644 --- a/nexus/db-queries/src/db/fixed_data/role_assignment.rs +++ b/nexus/db-fixed-data/src/role_assignment.rs @@ -6,8 +6,8 @@ use super::role_builtin; use super::user_builtin; use super::FLEET_ID; -use crate::db::model::IdentityType; -use crate::db::model::RoleAssignment; +use nexus_db_model::IdentityType; +use nexus_db_model::RoleAssignment; use once_cell::sync::Lazy; pub static BUILTIN_ROLE_ASSIGNMENTS: Lazy> = diff --git a/nexus/db-queries/src/db/fixed_data/role_builtin.rs b/nexus/db-fixed-data/src/role_builtin.rs similarity index 100% rename from nexus/db-queries/src/db/fixed_data/role_builtin.rs rename to nexus/db-fixed-data/src/role_builtin.rs diff --git a/nexus/db-queries/src/db/fixed_data/silo.rs b/nexus/db-fixed-data/src/silo.rs similarity index 91% rename from nexus/db-queries/src/db/fixed_data/silo.rs rename to nexus/db-fixed-data/src/silo.rs index dc5f19fc2f..ebc6776923 100644 --- a/nexus/db-queries/src/db/fixed_data/silo.rs +++ b/nexus/db-fixed-data/src/silo.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::db; +use nexus_db_model as model; use nexus_types::external_api::{params, shared}; use omicron_common::api::external::IdentityMetadataCreateParams; use once_cell::sync::Lazy; @@ -17,8 +17,8 @@ pub static DEFAULT_SILO_ID: Lazy = Lazy::new(|| { /// /// This was historically used for demos and the unit tests. The plan is to /// remove it per omicron#2305. -pub static DEFAULT_SILO: Lazy = Lazy::new(|| { - db::model::Silo::new_with_id( +pub static DEFAULT_SILO: Lazy = Lazy::new(|| { + model::Silo::new_with_id( *DEFAULT_SILO_ID, params::SiloCreate { identity: IdentityMetadataCreateParams { @@ -47,8 +47,8 @@ pub static INTERNAL_SILO_ID: Lazy = Lazy::new(|| { /// Built-in Silo to house internal resources. It contains no users and /// can't be logged into. -pub static INTERNAL_SILO: Lazy = Lazy::new(|| { - db::model::Silo::new_with_id( +pub static INTERNAL_SILO: Lazy = Lazy::new(|| { + model::Silo::new_with_id( *INTERNAL_SILO_ID, params::SiloCreate { identity: IdentityMetadataCreateParams { diff --git a/nexus/db-queries/src/db/fixed_data/silo_user.rs b/nexus/db-fixed-data/src/silo_user.rs similarity index 67% rename from nexus/db-queries/src/db/fixed_data/silo_user.rs rename to nexus/db-fixed-data/src/silo_user.rs index eb49093152..3f9a2787a8 100644 --- a/nexus/db-queries/src/db/fixed_data/silo_user.rs +++ b/nexus/db-fixed-data/src/silo_user.rs @@ -4,8 +4,8 @@ //! Built-in Silo Users use super::role_builtin; -use crate::db; -use crate::db::identity::Asset; +use nexus_db_model as model; +use nexus_types::identity::Asset; use once_cell::sync::Lazy; /// Test user that's granted all privileges, used for automated testing and @@ -13,9 +13,9 @@ use once_cell::sync::Lazy; // TODO-security Once we have a way to bootstrap the initial Silo with the // initial privileged user, this user should be created in the test suite, // not automatically at Nexus startup. See omicron#2305. -pub static USER_TEST_PRIVILEGED: Lazy = Lazy::new(|| { - db::model::SiloUser::new( - *db::fixed_data::silo::DEFAULT_SILO_ID, +pub static USER_TEST_PRIVILEGED: Lazy = Lazy::new(|| { + model::SiloUser::new( + *crate::silo::DEFAULT_SILO_ID, // "4007" looks a bit like "root". "001de000-05e4-4000-8000-000000004007".parse().unwrap(), "privileged".into(), @@ -23,23 +23,23 @@ pub static USER_TEST_PRIVILEGED: Lazy = Lazy::new(|| { }); /// Role assignments needed for the privileged user -pub static ROLE_ASSIGNMENTS_PRIVILEGED: Lazy> = +pub static ROLE_ASSIGNMENTS_PRIVILEGED: Lazy> = Lazy::new(|| { vec![ // The "test-privileged" user gets the "admin" role on the sole // Fleet as well as the default Silo. - db::model::RoleAssignment::new( - db::model::IdentityType::SiloUser, + model::RoleAssignment::new( + model::IdentityType::SiloUser, USER_TEST_PRIVILEGED.id(), role_builtin::FLEET_ADMIN.resource_type, - *db::fixed_data::FLEET_ID, + *crate::FLEET_ID, role_builtin::FLEET_ADMIN.role_name, ), - db::model::RoleAssignment::new( - db::model::IdentityType::SiloUser, + model::RoleAssignment::new( + model::IdentityType::SiloUser, USER_TEST_PRIVILEGED.id(), role_builtin::SILO_ADMIN.resource_type, - *db::fixed_data::silo::DEFAULT_SILO_ID, + *crate::silo::DEFAULT_SILO_ID, role_builtin::SILO_ADMIN.role_name, ), ] @@ -49,15 +49,14 @@ pub static ROLE_ASSIGNMENTS_PRIVILEGED: Lazy> = // TODO-security Once we have a way to bootstrap the initial Silo with the // initial privileged user, this user should be created in the test suite, // not automatically at Nexus startup. See omicron#2305. -pub static USER_TEST_UNPRIVILEGED: Lazy = - Lazy::new(|| { - db::model::SiloUser::new( - *db::fixed_data::silo::DEFAULT_SILO_ID, - // 60001 is the decimal uid for "nobody" on Helios. - "001de000-05e4-4000-8000-000000060001".parse().unwrap(), - "unprivileged".into(), - ) - }); +pub static USER_TEST_UNPRIVILEGED: Lazy = Lazy::new(|| { + model::SiloUser::new( + *crate::silo::DEFAULT_SILO_ID, + // 60001 is the decimal uid for "nobody" on Helios. + "001de000-05e4-4000-8000-000000060001".parse().unwrap(), + "unprivileged".into(), + ) +}); #[cfg(test)] mod test { diff --git a/nexus/db-queries/src/db/fixed_data/user_builtin.rs b/nexus/db-fixed-data/src/user_builtin.rs similarity index 100% rename from nexus/db-queries/src/db/fixed_data/user_builtin.rs rename to nexus/db-fixed-data/src/user_builtin.rs diff --git a/nexus/db-queries/src/db/fixed_data/vpc.rs b/nexus/db-fixed-data/src/vpc.rs similarity index 91% rename from nexus/db-queries/src/db/fixed_data/vpc.rs rename to nexus/db-fixed-data/src/vpc.rs index c71b655ddc..25628a83b5 100644 --- a/nexus/db-queries/src/db/fixed_data/vpc.rs +++ b/nexus/db-fixed-data/src/vpc.rs @@ -2,8 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::db; -use crate::db::datastore::SERVICES_DB_NAME; +use crate::project::SERVICES_DB_NAME; +use nexus_db_model as model; use nexus_types::external_api::params; use omicron_common::address::SERVICE_VPC_IPV6_PREFIX; use omicron_common::api::external::IdentityMetadataCreateParams; @@ -31,8 +31,8 @@ pub static SERVICES_VPC_DEFAULT_ROUTE_ID: Lazy = Lazy::new(|| { }); /// Built-in VPC for internal services on the rack. -pub static SERVICES_VPC: Lazy = Lazy::new(|| { - db::model::IncompleteVpc::new( +pub static SERVICES_VPC: Lazy = Lazy::new(|| { + model::IncompleteVpc::new( *SERVICES_VPC_ID, *super::project::SERVICES_PROJECT_ID, *SERVICES_VPC_ROUTER_ID, diff --git a/nexus/db-queries/src/db/fixed_data/vpc_firewall_rule.rs b/nexus/db-fixed-data/src/vpc_firewall_rule.rs similarity index 100% rename from nexus/db-queries/src/db/fixed_data/vpc_firewall_rule.rs rename to nexus/db-fixed-data/src/vpc_firewall_rule.rs diff --git a/nexus/db-queries/src/db/fixed_data/vpc_subnet.rs b/nexus/db-fixed-data/src/vpc_subnet.rs similarity index 98% rename from nexus/db-queries/src/db/fixed_data/vpc_subnet.rs rename to nexus/db-fixed-data/src/vpc_subnet.rs index c42d4121c9..622799b000 100644 --- a/nexus/db-queries/src/db/fixed_data/vpc_subnet.rs +++ b/nexus/db-fixed-data/src/vpc_subnet.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::db::model::VpcSubnet; +use nexus_db_model::VpcSubnet; use omicron_common::address::{ DNS_OPTE_IPV4_SUBNET, DNS_OPTE_IPV6_SUBNET, NEXUS_OPTE_IPV4_SUBNET, NEXUS_OPTE_IPV6_SUBNET, NTP_OPTE_IPV4_SUBNET, NTP_OPTE_IPV6_SUBNET, diff --git a/nexus/db-queries/Cargo.toml b/nexus/db-queries/Cargo.toml index 135f2fcdf7..f5b46edb75 100644 --- a/nexus/db-queries/Cargo.toml +++ b/nexus/db-queries/Cargo.toml @@ -60,7 +60,9 @@ usdt.workspace = true authz-macros.workspace = true db-macros.workspace = true +nexus-auth.workspace = true nexus-config.workspace = true +nexus-db-fixed-data.workspace = true nexus-db-model.workspace = true nexus-types.workspace = true omicron-common.workspace = true diff --git a/nexus/db-queries/src/db/datastore/allow_list.rs b/nexus/db-queries/src/db/datastore/allow_list.rs index 111ccad08f..7c1643451f 100644 --- a/nexus/db-queries/src/db/datastore/allow_list.rs +++ b/nexus/db-queries/src/db/datastore/allow_list.rs @@ -8,12 +8,12 @@ use crate::authz; use crate::context::OpContext; use crate::db::error::public_error_from_diesel; use crate::db::error::ErrorHandler; -use crate::db::fixed_data::allow_list::USER_FACING_SERVICES_ALLOW_LIST_ID; use crate::db::DbConnection; use async_bb8_diesel::AsyncRunQueryDsl; use diesel::ExpressionMethods; use diesel::QueryDsl; use diesel::SelectableHelper; +use nexus_db_fixed_data::allow_list::USER_FACING_SERVICES_ALLOW_LIST_ID; use nexus_db_model::schema::allow_list; use nexus_db_model::AllowList; use omicron_common::api::external::AllowedSourceIps; diff --git a/nexus/db-queries/src/db/datastore/auth.rs b/nexus/db-queries/src/db/datastore/auth.rs new file mode 100644 index 0000000000..3b1d1d18e3 --- /dev/null +++ b/nexus/db-queries/src/db/datastore/auth.rs @@ -0,0 +1,81 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Implements the [Storage] interface for [nexus_auth] integration. + +use crate::db; +use crate::db::error::public_error_from_diesel; +use crate::db::error::ErrorHandler; + +use async_bb8_diesel::AsyncRunQueryDsl; +use diesel::prelude::*; +use nexus_auth::context::OpContext; +use nexus_auth::storage::Storage; +use nexus_db_model::IdentityType; +use nexus_db_model::RoleAssignment; +use omicron_common::api::external::Error; +use omicron_common::api::external::ResourceType; +use uuid::Uuid; + +#[async_trait::async_trait] +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( + &self, + opctx: &OpContext, + identity_type: IdentityType, + identity_id: Uuid, + resource_type: ResourceType, + resource_id: Uuid, + ) -> Result, Error> { + use db::schema::role_assignment::dsl as role_dsl; + use db::schema::silo_group_membership::dsl as group_dsl; + + // There is no resource-specific authorization check because all + // authenticated users need to be able to list their own roles -- + // otherwise we can't do any authorization checks. + // TODO-security rethink this -- how do we know the user is looking up + // their own roles? Maybe this should use an internal authz context. + + // TODO-scalability TODO-security This needs to be paginated. It's not + // exposed via an external API right now but someone could still put us + // into some hurt by assigning loads of roles to someone and having that + // person attempt to access anything. + + let direct_roles_query = role_dsl::role_assignment + .filter(role_dsl::identity_type.eq(identity_type.clone())) + .filter(role_dsl::identity_id.eq(identity_id)) + .filter(role_dsl::resource_type.eq(resource_type.to_string())) + .filter(role_dsl::resource_id.eq(resource_id)) + .select(RoleAssignment::as_select()); + + let roles_from_groups_query = role_dsl::role_assignment + .filter(role_dsl::identity_type.eq(IdentityType::SiloGroup)) + .filter( + role_dsl::identity_id.eq_any( + group_dsl::silo_group_membership + .filter(group_dsl::silo_user_id.eq(identity_id)) + .select(group_dsl::silo_group_id), + ), + ) + .filter(role_dsl::resource_type.eq(resource_type.to_string())) + .filter(role_dsl::resource_id.eq(resource_id)) + .select(RoleAssignment::as_select()); + + let conn = self.pool_connection_authorized(opctx).await?; + if identity_type == IdentityType::SiloUser { + direct_roles_query + .union(roles_from_groups_query) + .load_async::(&*conn) + .await + .map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) + } else { + direct_roles_query + .load_async::(&*conn) + .await + .map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) + } + } +} diff --git a/nexus/db-queries/src/db/datastore/identity_provider.rs b/nexus/db-queries/src/db/datastore/identity_provider.rs index cee577acd6..e7ab9bde16 100644 --- a/nexus/db-queries/src/db/datastore/identity_provider.rs +++ b/nexus/db-queries/src/db/datastore/identity_provider.rs @@ -11,18 +11,66 @@ use crate::db; use crate::db::error::public_error_from_diesel; use crate::db::error::ErrorHandler; use crate::db::identity::Resource; +use crate::db::lookup::LookupPath; +use crate::db::model; use crate::db::model::IdentityProvider; use crate::db::model::Name; use crate::db::pagination::paginated; use async_bb8_diesel::AsyncRunQueryDsl; use diesel::prelude::*; +use nexus_auth::authn::silos::IdentityProviderType; use omicron_common::api::external::http_pagination::PaginatedBy; use omicron_common::api::external::CreateResult; use omicron_common::api::external::ListResultVec; +use omicron_common::api::external::LookupResult; use omicron_common::api::external::ResourceType; use ref_cast::RefCast; impl DataStore { + pub async fn identity_provider_lookup( + &self, + opctx: &OpContext, + silo_name: &model::Name, + provider_name: &model::Name, + ) -> LookupResult<(authz::Silo, model::Silo, IdentityProviderType)> { + let (authz_silo, db_silo) = + LookupPath::new(opctx, self).silo_name(silo_name).fetch().await?; + + let (.., identity_provider) = LookupPath::new(opctx, self) + .silo_name(silo_name) + .identity_provider_name(provider_name) + .fetch() + .await?; + + match identity_provider.provider_type { + model::IdentityProviderType::Saml => { + let (.., saml_identity_provider) = LookupPath::new(opctx, self) + .silo_name(silo_name) + .saml_identity_provider_name(provider_name) + .fetch() + .await?; + + let saml_identity_provider = IdentityProviderType::Saml( + saml_identity_provider.try_into() + .map_err(|e: anyhow::Error| + // If an error is encountered converting from the + // model to the authn type here, this is a server + // error: it was validated before it went into the + // DB. + omicron_common::api::external::Error::internal_error( + &format!( + "saml_identity_provider.try_into() failed! {}", + &e.to_string() + ) + ) + )? + ); + + Ok((authz_silo, db_silo, saml_identity_provider)) + } + } + } + pub async fn identity_provider_list( &self, opctx: &OpContext, diff --git a/nexus/db-queries/src/db/datastore/instance.rs b/nexus/db-queries/src/db/datastore/instance.rs index 60fd5c9dc3..c7daab9243 100644 --- a/nexus/db-queries/src/db/datastore/instance.rs +++ b/nexus/db-queries/src/db/datastore/instance.rs @@ -783,8 +783,8 @@ impl DataStore { mod tests { use super::*; use crate::db::datastore::test_utils::datastore_test; - use crate::db::fixed_data; use crate::db::lookup::LookupPath; + use nexus_db_fixed_data; use nexus_db_model::Project; use nexus_test_utils::db::test_setup_database; use nexus_types::external_api::params; diff --git a/nexus/db-queries/src/db/datastore/mod.rs b/nexus/db-queries/src/db/datastore/mod.rs index b90f81affb..f54ef0583d 100644 --- a/nexus/db-queries/src/db/datastore/mod.rs +++ b/nexus/db-queries/src/db/datastore/mod.rs @@ -49,6 +49,7 @@ use uuid::Uuid; mod address_lot; mod allow_list; +mod auth; mod bfd; mod bgp; mod bootstore; @@ -130,9 +131,6 @@ pub const REGION_REDUNDANCY_THRESHOLD: usize = 3; /// The name of the built-in IP pool for Oxide services. pub const SERVICE_IP_POOL_NAME: &str = "oxide-service-pool"; -/// The name of the built-in Project and VPC for Oxide services. -pub const SERVICES_DB_NAME: &str = "oxide-services"; - /// "limit" to be used in SQL queries that paginate through large result sets /// /// This value is chosen to be small enough to avoid any queries being too @@ -385,8 +383,6 @@ mod test { IneligibleSledKind, IneligibleSleds, }; use crate::db::explain::ExplainableAsync; - use crate::db::fixed_data::silo::DEFAULT_SILO; - use crate::db::fixed_data::silo::DEFAULT_SILO_ID; use crate::db::identity::Asset; use crate::db::lookup::LookupPath; use crate::db::model::{ @@ -400,6 +396,8 @@ mod test { use futures::stream; use futures::StreamExt; use nexus_config::RegionAllocationStrategy; + use nexus_db_fixed_data::silo::DEFAULT_SILO; + use nexus_db_fixed_data::silo::DEFAULT_SILO_ID; use nexus_db_model::IpAttachState; use nexus_db_model::{to_db_typed_uuid, Generation}; use nexus_test_utils::db::test_setup_database; diff --git a/nexus/db-queries/src/db/datastore/network_interface.rs b/nexus/db-queries/src/db/datastore/network_interface.rs index af3f832e35..3ea2945b2f 100644 --- a/nexus/db-queries/src/db/datastore/network_interface.rs +++ b/nexus/db-queries/src/db/datastore/network_interface.rs @@ -854,8 +854,8 @@ impl DataStore { mod tests { use super::*; use crate::db::datastore::test_utils::datastore_test; - use crate::db::fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; use nexus_config::NUM_INITIAL_RESERVED_IP_ADDRESSES; + use nexus_db_fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; use nexus_test_utils::db::test_setup_database; use omicron_common::address::NEXUS_OPTE_IPV4_SUBNET; use omicron_test_utils::dev; diff --git a/nexus/db-queries/src/db/datastore/project.rs b/nexus/db-queries/src/db/datastore/project.rs index 08647b421e..42ccca4ed6 100644 --- a/nexus/db-queries/src/db/datastore/project.rs +++ b/nexus/db-queries/src/db/datastore/project.rs @@ -13,8 +13,6 @@ use crate::db::collection_insert::AsyncInsertError; use crate::db::collection_insert::DatastoreCollection; use crate::db::error::public_error_from_diesel; use crate::db::error::ErrorHandler; -use crate::db::fixed_data::project::SERVICES_PROJECT; -use crate::db::fixed_data::silo::INTERNAL_SILO_ID; use crate::db::identity::Resource; use crate::db::model::CollectionTypeProvisioned; use crate::db::model::Name; @@ -27,6 +25,8 @@ use crate::transaction_retry::OptionalError; use async_bb8_diesel::AsyncRunQueryDsl; use chrono::Utc; use diesel::prelude::*; +use nexus_db_fixed_data::project::SERVICES_PROJECT; +use nexus_db_fixed_data::silo::INTERNAL_SILO_ID; use omicron_common::api::external::http_pagination::PaginatedBy; use omicron_common::api::external::CreateResult; use omicron_common::api::external::DeleteResult; diff --git a/nexus/db-queries/src/db/datastore/pub_test_utils.rs b/nexus/db-queries/src/db/datastore/pub_test_utils.rs index 5259a03656..93a172bd15 100644 --- a/nexus/db-queries/src/db/datastore/pub_test_utils.rs +++ b/nexus/db-queries/src/db/datastore/pub_test_utils.rs @@ -39,7 +39,7 @@ pub async fn datastore_test( logctx.log.new(o!()), Arc::new(authz::Authz::new(&logctx.log)), authn::Context::internal_db_init(), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); // TODO: Can we just call "Populate" instead of doing this? @@ -59,8 +59,10 @@ pub async fn datastore_test( // Create an OpContext with the credentials of "test-privileged" for general // testing. - let opctx = - OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore)); + let opctx = OpContext::for_tests( + logctx.log.new(o!()), + Arc::clone(&datastore) as Arc, + ); (opctx, datastore) } diff --git a/nexus/db-queries/src/db/datastore/rack.rs b/nexus/db-queries/src/db/datastore/rack.rs index d836185d87..4af6bf7263 100644 --- a/nexus/db-queries/src/db/datastore/rack.rs +++ b/nexus/db-queries/src/db/datastore/rack.rs @@ -16,10 +16,6 @@ use crate::db::error::public_error_from_diesel; use crate::db::error::retryable; use crate::db::error::ErrorHandler; use crate::db::error::MaybeRetryable::*; -use crate::db::fixed_data::silo::INTERNAL_SILO_ID; -use crate::db::fixed_data::vpc_subnet::DNS_VPC_SUBNET; -use crate::db::fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; -use crate::db::fixed_data::vpc_subnet::NTP_VPC_SUBNET; use crate::db::identity::Asset; use crate::db::lookup::LookupPath; use crate::db::model::Dataset; @@ -37,6 +33,10 @@ use diesel::prelude::*; use diesel::result::Error as DieselError; use diesel::upsert::excluded; use ipnetwork::IpNetwork; +use nexus_db_fixed_data::silo::INTERNAL_SILO_ID; +use nexus_db_fixed_data::vpc_subnet::DNS_VPC_SUBNET; +use nexus_db_fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; +use nexus_db_fixed_data::vpc_subnet::NTP_VPC_SUBNET; use nexus_db_model::IncompleteNetworkInterface; use nexus_db_model::InitialDnsGroup; use nexus_db_model::PasswordHashString; diff --git a/nexus/db-queries/src/db/datastore/role.rs b/nexus/db-queries/src/db/datastore/role.rs index 3a57ffc44c..b91597ad1d 100644 --- a/nexus/db-queries/src/db/datastore/role.rs +++ b/nexus/db-queries/src/db/datastore/role.rs @@ -14,8 +14,6 @@ use crate::db::datastore::RunnableQueryNoReturn; use crate::db::error::public_error_from_diesel; use crate::db::error::ErrorHandler; use crate::db::error::TransactionError; -use crate::db::fixed_data::role_assignment::BUILTIN_ROLE_ASSIGNMENTS; -use crate::db::fixed_data::role_builtin::BUILTIN_ROLES; use crate::db::model::DatabaseString; use crate::db::model::IdentityType; use crate::db::model::RoleAssignment; @@ -25,13 +23,13 @@ use crate::db::pool::DbConnection; use async_bb8_diesel::AsyncConnection; use async_bb8_diesel::AsyncRunQueryDsl; use diesel::prelude::*; +use nexus_db_fixed_data::role_assignment::BUILTIN_ROLE_ASSIGNMENTS; +use nexus_db_fixed_data::role_builtin::BUILTIN_ROLES; use nexus_types::external_api::shared; use omicron_common::api::external::DataPageParams; use omicron_common::api::external::Error; use omicron_common::api::external::ListResultVec; -use omicron_common::api::external::ResourceType; use omicron_common::bail_unless; -use uuid::Uuid; impl DataStore { /// List built-in roles @@ -117,65 +115,6 @@ impl DataStore { Ok(()) } - /// Return the built-in roles that the given built-in user has for the given - /// resource - pub async fn role_asgn_list_for( - &self, - opctx: &OpContext, - identity_type: IdentityType, - identity_id: Uuid, - resource_type: ResourceType, - resource_id: Uuid, - ) -> Result, Error> { - use db::schema::role_assignment::dsl as role_dsl; - use db::schema::silo_group_membership::dsl as group_dsl; - - // There is no resource-specific authorization check because all - // authenticated users need to be able to list their own roles -- - // otherwise we can't do any authorization checks. - // TODO-security rethink this -- how do we know the user is looking up - // their own roles? Maybe this should use an internal authz context. - - // TODO-scalability TODO-security This needs to be paginated. It's not - // exposed via an external API right now but someone could still put us - // into some hurt by assigning loads of roles to someone and having that - // person attempt to access anything. - - let direct_roles_query = role_dsl::role_assignment - .filter(role_dsl::identity_type.eq(identity_type.clone())) - .filter(role_dsl::identity_id.eq(identity_id)) - .filter(role_dsl::resource_type.eq(resource_type.to_string())) - .filter(role_dsl::resource_id.eq(resource_id)) - .select(RoleAssignment::as_select()); - - let roles_from_groups_query = role_dsl::role_assignment - .filter(role_dsl::identity_type.eq(IdentityType::SiloGroup)) - .filter( - role_dsl::identity_id.eq_any( - group_dsl::silo_group_membership - .filter(group_dsl::silo_user_id.eq(identity_id)) - .select(group_dsl::silo_group_id), - ), - ) - .filter(role_dsl::resource_type.eq(resource_type.to_string())) - .filter(role_dsl::resource_id.eq(resource_id)) - .select(RoleAssignment::as_select()); - - let conn = self.pool_connection_authorized(opctx).await?; - if identity_type == IdentityType::SiloUser { - direct_roles_query - .union(roles_from_groups_query) - .load_async::(&*conn) - .await - .map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) - } else { - direct_roles_query - .load_async::(&*conn) - .await - .map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) - } - } - /// Fetches all of the externally-visible role assignments for the specified /// resource /// diff --git a/nexus/db-queries/src/db/datastore/silo.rs b/nexus/db-queries/src/db/datastore/silo.rs index 0fd858b900..2b7afa3270 100644 --- a/nexus/db-queries/src/db/datastore/silo.rs +++ b/nexus/db-queries/src/db/datastore/silo.rs @@ -15,7 +15,6 @@ use crate::db::error::public_error_from_diesel; use crate::db::error::retryable; use crate::db::error::ErrorHandler; use crate::db::error::TransactionError; -use crate::db::fixed_data::silo::{DEFAULT_SILO, INTERNAL_SILO}; use crate::db::identity::Resource; use crate::db::model::CollectionTypeProvisioned; use crate::db::model::IpPoolResourceType; @@ -29,6 +28,7 @@ use async_bb8_diesel::AsyncConnection; use async_bb8_diesel::AsyncRunQueryDsl; use chrono::Utc; use diesel::prelude::*; +use nexus_db_fixed_data::silo::{DEFAULT_SILO, INTERNAL_SILO}; use nexus_db_model::Certificate; use nexus_db_model::ServiceKind; use nexus_db_model::SiloQuotas; diff --git a/nexus/db-queries/src/db/datastore/silo_user.rs b/nexus/db-queries/src/db/datastore/silo_user.rs index 59cb19a609..2825e2a310 100644 --- a/nexus/db-queries/src/db/datastore/silo_user.rs +++ b/nexus/db-queries/src/db/datastore/silo_user.rs @@ -429,7 +429,9 @@ impl DataStore { use db::schema::role_assignment::dsl; debug!(opctx.log, "attempting to create silo user role assignments"); let count = diesel::insert_into(dsl::role_assignment) - .values(&*db::fixed_data::silo_user::ROLE_ASSIGNMENTS_PRIVILEGED) + .values( + &*nexus_db_fixed_data::silo_user::ROLE_ASSIGNMENTS_PRIVILEGED, + ) .on_conflict(( dsl::identity_type, dsl::identity_id, diff --git a/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs b/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs index 3630231b63..cc00bd3753 100644 --- a/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs +++ b/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs @@ -312,7 +312,7 @@ impl DataStore { &self, opctx: &OpContext, ) -> Result<(), Error> { - let id = *db::fixed_data::FLEET_ID; + let id = *nexus_db_fixed_data::FLEET_ID; self.virtual_provisioning_collection_create( opctx, db::model::VirtualProvisioningCollection::new( @@ -331,8 +331,8 @@ mod test { use super::*; use crate::db::datastore::test_utils::datastore_test; - use crate::db::fixed_data; use crate::db::lookup::LookupPath; + use nexus_db_fixed_data; use nexus_db_model::Instance; use nexus_db_model::Project; use nexus_db_model::SiloQuotasUpdate; diff --git a/nexus/db-queries/src/db/datastore/vpc.rs b/nexus/db-queries/src/db/datastore/vpc.rs index 98af47f0e2..5322e20dbf 100644 --- a/nexus/db-queries/src/db/datastore/vpc.rs +++ b/nexus/db-queries/src/db/datastore/vpc.rs @@ -12,7 +12,6 @@ use crate::db::collection_insert::AsyncInsertError; use crate::db::collection_insert::DatastoreCollection; use crate::db::error::public_error_from_diesel; use crate::db::error::ErrorHandler; -use crate::db::fixed_data::vpc::SERVICES_VPC_ID; use crate::db::identity::Resource; use crate::db::model::ApplyBlueprintZoneFilterExt; use crate::db::model::ApplySledFilterExt; @@ -45,6 +44,7 @@ use diesel::prelude::*; use diesel::result::DatabaseErrorKind; use diesel::result::Error as DieselError; use ipnetwork::IpNetwork; +use nexus_db_fixed_data::vpc::SERVICES_VPC_ID; use nexus_types::deployment::BlueprintZoneFilter; use nexus_types::deployment::SledFilter; use omicron_common::api::external::http_pagination::PaginatedBy; @@ -72,9 +72,9 @@ impl DataStore { &self, opctx: &OpContext, ) -> Result<(), Error> { - use crate::db::fixed_data::project::SERVICES_PROJECT_ID; - use crate::db::fixed_data::vpc::SERVICES_VPC; - use crate::db::fixed_data::vpc::SERVICES_VPC_DEFAULT_ROUTE_ID; + use nexus_db_fixed_data::project::SERVICES_PROJECT_ID; + use nexus_db_fixed_data::vpc::SERVICES_VPC; + use nexus_db_fixed_data::vpc::SERVICES_VPC_DEFAULT_ROUTE_ID; opctx.authorize(authz::Action::Modify, &authz::DATABASE).await?; @@ -175,8 +175,8 @@ impl DataStore { &self, opctx: &OpContext, ) -> Result<(), Error> { - use db::fixed_data::vpc_firewall_rule::DNS_VPC_FW_RULE; - use db::fixed_data::vpc_firewall_rule::NEXUS_VPC_FW_RULE; + use nexus_db_fixed_data::vpc_firewall_rule::DNS_VPC_FW_RULE; + use nexus_db_fixed_data::vpc_firewall_rule::NEXUS_VPC_FW_RULE; debug!(opctx.log, "attempting to create built-in VPC firewall rules"); @@ -229,9 +229,9 @@ impl DataStore { &self, opctx: &OpContext, ) -> Result<(), Error> { - use crate::db::fixed_data::vpc_subnet::DNS_VPC_SUBNET; - use crate::db::fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; - use crate::db::fixed_data::vpc_subnet::NTP_VPC_SUBNET; + use nexus_db_fixed_data::vpc_subnet::DNS_VPC_SUBNET; + use nexus_db_fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; + use nexus_db_fixed_data::vpc_subnet::NTP_VPC_SUBNET; debug!(opctx.log, "attempting to create built-in VPC Subnets"); @@ -1230,9 +1230,9 @@ mod tests { use crate::db::datastore::test::sled_system_hardware_for_test; use crate::db::datastore::test_utils::datastore_test; use crate::db::datastore::test_utils::IneligibleSleds; - use crate::db::fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; use crate::db::model::Project; use crate::db::queries::vpc::MAX_VNI_SEARCH_RANGE_SIZE; + use nexus_db_fixed_data::vpc_subnet::NEXUS_VPC_SUBNET; use nexus_db_model::IncompleteNetworkInterface; use nexus_db_model::SledUpdate; use nexus_reconfigurator_planning::blueprint_builder::BlueprintBuilder; diff --git a/nexus/db-queries/src/db/mod.rs b/nexus/db-queries/src/db/mod.rs index 7ce6890a4d..7bd1bbec61 100644 --- a/nexus/db-queries/src/db/mod.rs +++ b/nexus/db-queries/src/db/mod.rs @@ -17,7 +17,6 @@ mod cte_utils; pub mod datastore; pub(crate) mod error; mod explain; -pub mod fixed_data; pub mod lookup; mod on_conflict_ext; // Public for doctests. @@ -42,6 +41,7 @@ pub use pool_connection::DISALLOW_FULL_TABLE_SCAN_SQL; #[cfg(test)] mod test_utils; +pub use nexus_db_fixed_data as fixed_data; pub use nexus_db_model as model; use nexus_db_model::saga_types; pub use nexus_db_model::schema; diff --git a/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs b/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs index 895fee2092..b3c1a569b0 100644 --- a/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs +++ b/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs @@ -122,7 +122,7 @@ WITH UNION (SELECT ").param().sql(" AS id) ),") .bind::(project_id) - .bind::(*crate::db::fixed_data::FLEET_ID) + .bind::(*nexus_db_fixed_data::FLEET_ID) .sql(" quotas AS ( diff --git a/nexus/db-queries/src/lib.rs b/nexus/db-queries/src/lib.rs index 60177990e8..5780bd1c41 100644 --- a/nexus/db-queries/src/lib.rs +++ b/nexus/db-queries/src/lib.rs @@ -4,17 +4,16 @@ //! Facilities for working with the Omicron database -pub mod authn; -pub mod authz; -pub mod context; +pub use nexus_auth::authn; +pub use nexus_auth::authz; +pub use nexus_auth::context; + pub mod db; pub mod provisioning; pub mod transaction_retry; #[macro_use] extern crate slog; -#[macro_use] -extern crate newtype_derive; #[cfg(test)] #[macro_use] extern crate diesel; diff --git a/nexus/src/app/mod.rs b/nexus/src/app/mod.rs index 263ab24c70..f9bcc2cf80 100644 --- a/nexus/src/app/mod.rs +++ b/nexus/src/app/mod.rs @@ -377,7 +377,7 @@ impl Nexus { log.new(o!("component" => "DataLoader")), Arc::clone(&authz), authn::Context::internal_db_init(), - Arc::clone(&db_datastore), + Arc::clone(&db_datastore) as Arc, ); let populate_args = PopulateArgs::new(rack_id); @@ -391,7 +391,7 @@ impl Nexus { log.new(o!("component" => "BackgroundTasks")), Arc::clone(&authz), authn::Context::internal_api(), - Arc::clone(&db_datastore), + Arc::clone(&db_datastore) as Arc, ); let v2p_watcher_channel = tokio::sync::watch::channel(()); @@ -440,13 +440,15 @@ impl Nexus { log.new(o!("component" => "InstanceAllocator")), Arc::clone(&authz), authn::Context::internal_read(), - Arc::clone(&db_datastore), + Arc::clone(&db_datastore) + as Arc, ), opctx_external_authn: OpContext::for_background( log.new(o!("component" => "ExternalAuthn")), Arc::clone(&authz), authn::Context::external_authn(), - Arc::clone(&db_datastore), + Arc::clone(&db_datastore) + as Arc, ), samael_max_issue_delay: std::sync::Mutex::new(None), internal_resolver: resolver, @@ -469,7 +471,7 @@ impl Nexus { log.new(o!("component" => "SagaRecoverer")), Arc::clone(&authz), authn::Context::internal_saga_recovery(), - Arc::clone(&db_datastore), + Arc::clone(&db_datastore) as Arc, ); let saga_logger = nexus.log.new(o!("saga_type" => "recovery")); let recovery_task = db::recover( @@ -701,7 +703,8 @@ impl Nexus { self.log.new(o!("component" => "ServiceBalancer")), Arc::clone(&self.authz), authn::Context::internal_service_balancer(), - Arc::clone(&self.db_datastore), + Arc::clone(&self.db_datastore) + as Arc, ) } @@ -711,7 +714,8 @@ impl Nexus { self.log.new(o!("component" => "InternalApi")), Arc::clone(&self.authz), authn::Context::internal_api(), - Arc::clone(&self.db_datastore), + Arc::clone(&self.db_datastore) + as Arc, ) } diff --git a/nexus/src/app/test_interfaces.rs b/nexus/src/app/test_interfaces.rs index 581b9a89bb..9e7bd1582f 100644 --- a/nexus/src/app/test_interfaces.rs +++ b/nexus/src/app/test_interfaces.rs @@ -73,7 +73,8 @@ impl TestInterfaces for super::Nexus { ) -> Result>, Error> { let opctx = OpContext::for_tests( self.log.new(o!()), - Arc::clone(&self.db_datastore), + Arc::clone(&self.db_datastore) + as Arc, ); self.instance_sled_by_id_with_opctx(id, &opctx).await @@ -98,7 +99,8 @@ impl TestInterfaces for super::Nexus { ) -> Result>, Error> { let opctx = OpContext::for_tests( self.log.new(o!()), - Arc::clone(&self.db_datastore), + Arc::clone(&self.db_datastore) + as Arc, ); let (.., db_disk) = LookupPath::new(&opctx, &self.db_datastore) .disk_id(*id) @@ -112,7 +114,8 @@ impl TestInterfaces for super::Nexus { async fn instance_sled_id(&self, id: &Uuid) -> Result, Error> { let opctx = OpContext::for_tests( self.log.new(o!()), - Arc::clone(&self.db_datastore), + Arc::clone(&self.db_datastore) + as Arc, ); self.instance_sled_id_with_opctx(id, &opctx).await @@ -138,7 +141,8 @@ impl TestInterfaces for super::Nexus { async fn set_disk_as_faulted(&self, disk_id: &Uuid) -> Result { let opctx = OpContext::for_tests( self.log.new(o!()), - Arc::clone(&self.db_datastore), + Arc::clone(&self.db_datastore) + as Arc, ); let (.., authz_disk, db_disk) = diff --git a/nexus/src/external_api/console_api.rs b/nexus/src/external_api/console_api.rs index caff195047..fb0a47bbea 100644 --- a/nexus/src/external_api/console_api.rs +++ b/nexus/src/external_api/console_api.rs @@ -270,13 +270,14 @@ pub(crate) async fn login_saml_redirect( // unauthenticated. let opctx = nexus.opctx_external_authn(); - let (.., identity_provider) = IdentityProviderType::lookup( - &nexus.datastore(), - &opctx, - &path_params.silo_name, - &path_params.provider_name, - ) - .await?; + let (.., identity_provider) = nexus + .datastore() + .identity_provider_lookup( + &opctx, + &path_params.silo_name, + &path_params.provider_name, + ) + .await?; match identity_provider { IdentityProviderType::Saml(saml_identity_provider) => { @@ -330,9 +331,9 @@ pub(crate) async fn login_saml( // keep specifically for this purpose. let opctx = nexus.opctx_external_authn(); - let (authz_silo, db_silo, identity_provider) = - IdentityProviderType::lookup( - &nexus.datastore(), + let (authz_silo, db_silo, identity_provider) = nexus + .datastore() + .identity_provider_lookup( &opctx, &path_params.silo_name, &path_params.provider_name, diff --git a/nexus/tests/integration_tests/saml.rs b/nexus/tests/integration_tests/saml.rs index 80816f2ea2..e075f3e4da 100644 --- a/nexus/tests/integration_tests/saml.rs +++ b/nexus/tests/integration_tests/saml.rs @@ -106,20 +106,23 @@ async fn test_create_a_saml_idp(cptestctx: &ControlPlaneTestContext) { .await .unwrap(); - let (.., retrieved_silo_idp_from_nexus) = IdentityProviderType::lookup( - &nexus.datastore(), - &nexus.opctx_external_authn(), - &omicron_common::api::external::Name::try_from(SILO_NAME.to_string()) + let (.., retrieved_silo_idp_from_nexus) = nexus + .datastore() + .identity_provider_lookup( + &nexus.opctx_external_authn(), + &omicron_common::api::external::Name::try_from( + SILO_NAME.to_string(), + ) + .unwrap() + .into(), + &omicron_common::api::external::Name::try_from( + "some-totally-real-saml-provider".to_string(), + ) .unwrap() .into(), - &omicron_common::api::external::Name::try_from( - "some-totally-real-saml-provider".to_string(), ) - .unwrap() - .into(), - ) - .await - .unwrap(); + .await + .unwrap(); match retrieved_silo_idp_from_nexus { IdentityProviderType::Saml(_) => { diff --git a/nexus/tests/integration_tests/silos.rs b/nexus/tests/integration_tests/silos.rs index e95b2870ca..e240edac47 100644 --- a/nexus/tests/integration_tests/silos.rs +++ b/nexus/tests/integration_tests/silos.rs @@ -525,19 +525,22 @@ async fn test_deleting_a_silo_deletes_the_idp( // Expect that the silo is gone let nexus = &cptestctx.server.server_context().nexus; - let response = IdentityProviderType::lookup( - &nexus.datastore(), - &nexus.opctx_external_authn(), - &omicron_common::api::external::Name::try_from(SILO_NAME.to_string()) + let response = nexus + .datastore() + .identity_provider_lookup( + &nexus.opctx_external_authn(), + &omicron_common::api::external::Name::try_from( + SILO_NAME.to_string(), + ) + .unwrap() + .into(), + &omicron_common::api::external::Name::try_from( + "some-totally-real-saml-provider".to_string(), + ) .unwrap() .into(), - &omicron_common::api::external::Name::try_from( - "some-totally-real-saml-provider".to_string(), ) - .unwrap() - .into(), - ) - .await; + .await; assert!(response.is_err()); match response.err().unwrap() { From e7264c44f23611c5ba9ad9975ef5f6bbfa744bc0 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 15:46:24 -0700 Subject: [PATCH 2/9] working through tests --- nexus/auth/Cargo.toml | 8 -- nexus/auth/src/authn/mod.rs | 5 +- nexus/auth/src/authz/context.rs | 93 ------------------ nexus/auth/src/authz/mod.rs | 7 +- nexus/auth/src/authz/oso_generic.rs | 3 +- nexus/auth/src/context.rs | 2 + nexus/auth/src/lib.rs | 4 + nexus/auth/src/storage.rs | 10 +- nexus/db-fixed-data/Cargo.toml | 8 -- nexus/db-fixed-data/src/role_builtin.rs | 2 +- nexus/db-fixed-data/src/silo_user.rs | 2 +- nexus/db-queries/src/db/datastore/auth.rs | 92 ++++++++++++++++++ .../src/db/datastore/cockroachdb_settings.rs | 2 +- nexus/db-queries/src/db/datastore/instance.rs | 3 +- nexus/db-queries/src/db/datastore/mod.rs | 10 +- .../virtual_provisioning_collection.rs | 5 +- nexus/db-queries/src/db/lookup.rs | 2 +- nexus/db-queries/src/db/saga_recovery.rs | 4 +- nexus/db-queries/src/lib.rs | 3 + .../src}/policy_test/coverage.rs | 5 +- .../src}/policy_test/mod.rs | 17 ++-- .../src}/policy_test/resource_builder.rs | 96 ++++++++++++------- .../src}/policy_test/resources.rs | 6 +- nexus/db-queries/tests/output/authz-roles.out | 2 +- 24 files changed, 208 insertions(+), 183 deletions(-) rename nexus/{auth/src/authz => db-queries/src}/policy_test/coverage.rs (97%) rename nexus/{auth/src/authz => db-queries/src}/policy_test/mod.rs (97%) rename nexus/{auth/src/authz => db-queries/src}/policy_test/resource_builder.rs (74%) rename nexus/{auth/src/authz => db-queries/src}/policy_test/resources.rs (99%) diff --git a/nexus/auth/Cargo.toml b/nexus/auth/Cargo.toml index cfef322b4a..62140377d0 100644 --- a/nexus/auth/Cargo.toml +++ b/nexus/auth/Cargo.toml @@ -70,13 +70,6 @@ omicron-uuid-kinds.workspace = true oximeter.workspace = true omicron-workspace-hack.workspace = true -# only enabled during tests or via the `testing` feature -omicron-test-utils = { workspace = true, optional = true } - -[features] -# Enable to export `datastore_test` -testing = ["omicron-test-utils"] - [dev-dependencies] assert_matches.workspace = true camino-tempfile.workspace = true @@ -89,7 +82,6 @@ itertools.workspace = true nexus-inventory.workspace = true nexus-reconfigurator-planning.workspace = true nexus-test-utils.workspace = true -omicron-sled-agent.workspace = true omicron-test-utils.workspace = true openapiv3.workspace = true pem.workspace = true diff --git a/nexus/auth/src/authn/mod.rs b/nexus/auth/src/authn/mod.rs index 85c4929cf6..08b27b9773 100644 --- a/nexus/auth/src/authn/mod.rs +++ b/nexus/auth/src/authn/mod.rs @@ -253,7 +253,6 @@ pub struct SiloAuthnPolicy { } impl SiloAuthnPolicy { - #[cfg(test)] pub fn new( mapped_fleet_roles: BTreeMap>, ) -> SiloAuthnPolicy { @@ -289,8 +288,8 @@ mod test { use super::USER_SERVICE_BALANCER; use super::USER_TEST_PRIVILEGED; use super::USER_TEST_UNPRIVILEGED; - use crate::db::fixed_data::user_builtin::USER_EXTERNAL_AUTHN; - use crate::db::identity::Asset; + use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_AUTHN; + use nexus_types::identity::Asset; #[test] fn test_internal_users() { diff --git a/nexus/auth/src/authz/context.rs b/nexus/auth/src/authz/context.rs index 4eed7ca799..76f0a1f1af 100644 --- a/nexus/auth/src/authz/context.rs +++ b/nexus/auth/src/authz/context.rs @@ -52,7 +52,6 @@ impl Authz { self.oso.is_allowed(actor.clone(), action, resource.clone()) } - #[cfg(test)] pub fn into_class_names(self) -> BTreeSet { self.class_names } @@ -192,95 +191,3 @@ pub trait AuthorizedResource: oso::ToPolar + Send + Sync + 'static { /// Returns the Polar class that implements this resource fn polar_class(&self) -> oso::Class; } - -#[cfg(test)] -mod test { - use crate::authn; - use crate::authz::Action; - use crate::authz::Authz; - use crate::authz::Context; - use crate::db::DataStore; - use nexus_test_utils::db::test_setup_database; - use omicron_test_utils::dev; - use std::sync::Arc; - - fn authz_context_for_actor( - log: &slog::Logger, - authn: authn::Context, - datastore: Arc, - ) -> Context { - let authz = Authz::new(log); - Context::new(Arc::new(authn), Arc::new(authz), datastore) - } - - #[tokio::test] - async fn test_unregistered_resource() { - let logctx = dev::test_setup_log("test_unregistered_resource"); - let mut db = test_setup_database(&logctx.log).await; - let (opctx, datastore) = - crate::db::datastore::test_utils::datastore_test(&logctx, &db) - .await; - - // Define a resource that we "forget" to register with Oso. - use super::AuthorizedResource; - use crate::authz::actor::AnyActor; - use crate::authz::roles::RoleSet; - use crate::context::OpContext; - use omicron_common::api::external::Error; - use oso::PolarClass; - #[derive(Clone, PolarClass)] - struct UnregisteredResource; - impl AuthorizedResource for UnregisteredResource { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - _: &'b OpContext, - _: &'c authn::Context, - _: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { - // authorize() shouldn't get far enough to call this. - unimplemented!(); - } - - fn on_unauthorized( - &self, - _: &Authz, - _: Error, - _: AnyActor, - _: Action, - ) -> Error { - // authorize() shouldn't get far enough to call this. - unimplemented!(); - } - - fn polar_class(&self) -> oso::Class { - Self::get_polar_class() - } - } - - // Make sure an authz check with this resource fails with a clear - // message. - let unregistered_resource = UnregisteredResource {}; - let authz_privileged = authz_context_for_actor( - &logctx.log, - authn::Context::privileged_test_user(), - Arc::clone(&datastore), - ); - let error = authz_privileged - .authorize(&opctx, Action::Read, unregistered_resource) - .await; - println!("{:?}", error); - assert!(matches!(error, Err(Error::InternalError { - internal_message - }) if internal_message == "attempted authz check \ - on unregistered resource: \"UnregisteredResource\"")); - - db.cleanup().await.unwrap(); - logctx.cleanup_successful(); - } -} diff --git a/nexus/auth/src/authz/mod.rs b/nexus/auth/src/authz/mod.rs index 6b7dab7208..1c666d2296 100644 --- a/nexus/auth/src/authz/mod.rs +++ b/nexus/auth/src/authz/mod.rs @@ -168,6 +168,8 @@ //! allowed. Otherwise, it's not. mod actor; +pub use actor::AnyActor; +pub use actor::AuthenticatedActor; mod api_resources; pub use api_resources::*; @@ -179,9 +181,8 @@ pub use context::Context; mod oso_generic; pub use oso_generic::Action; +pub use oso_generic::Database; pub use oso_generic::DATABASE; mod roles; - -#[cfg(test)] -mod policy_test; +pub use roles::RoleSet; diff --git a/nexus/auth/src/authz/oso_generic.rs b/nexus/auth/src/authz/oso_generic.rs index 6816d54724..7ad48c1dec 100644 --- a/nexus/auth/src/authz/oso_generic.rs +++ b/nexus/auth/src/authz/oso_generic.rs @@ -172,8 +172,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result { /// /// 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))] +#[derive(Clone, Copy, Debug, Eq, PartialEq, strum::EnumIter)] pub enum Action { Query, // only used for `Database` Read, diff --git a/nexus/auth/src/context.rs b/nexus/auth/src/context.rs index 9d32c333fd..0a11a0cff6 100644 --- a/nexus/auth/src/context.rs +++ b/nexus/auth/src/context.rs @@ -347,6 +347,7 @@ impl Session for ConsoleSessionWithSiloId { } } +/* #[cfg(test)] mod test { use super::OpContext; @@ -462,3 +463,4 @@ mod test { logctx.cleanup_successful(); } } +*/ diff --git a/nexus/auth/src/lib.rs b/nexus/auth/src/lib.rs index f825049113..0f0b9064b2 100644 --- a/nexus/auth/src/lib.rs +++ b/nexus/auth/src/lib.rs @@ -5,3 +5,7 @@ pub mod storage; #[macro_use] extern crate newtype_derive; + +#[allow(unused_imports)] +#[macro_use] +extern crate slog; diff --git a/nexus/auth/src/storage.rs b/nexus/auth/src/storage.rs index 9708f9a2ec..c1d2fcedd8 100644 --- a/nexus/auth/src/storage.rs +++ b/nexus/auth/src/storage.rs @@ -18,10 +18,10 @@ use uuid::Uuid; pub trait Storage: Send + Sync { async fn role_asgn_list_for( &self, - _opctx: &OpContext, - _identity_type: IdentityType, - _identity_id: Uuid, - _resource_type: ResourceType, - _resource_id: Uuid, + opctx: &OpContext, + identity_type: IdentityType, + identity_id: Uuid, + resource_type: ResourceType, + resource_id: Uuid, ) -> Result, Error>; } diff --git a/nexus/db-fixed-data/Cargo.toml b/nexus/db-fixed-data/Cargo.toml index c697ea0c1e..d60d3b6015 100644 --- a/nexus/db-fixed-data/Cargo.toml +++ b/nexus/db-fixed-data/Cargo.toml @@ -69,13 +69,6 @@ omicron-uuid-kinds.workspace = true oximeter.workspace = true omicron-workspace-hack.workspace = true -# only enabled during tests or via the `testing` feature -omicron-test-utils = { workspace = true, optional = true } - -[features] -# Enable to export `datastore_test` -testing = ["omicron-test-utils"] - [dev-dependencies] assert_matches.workspace = true camino-tempfile.workspace = true @@ -89,7 +82,6 @@ nexus-inventory.workspace = true nexus-reconfigurator-planning.workspace = true nexus-test-utils.workspace = true omicron-sled-agent.workspace = true -omicron-test-utils.workspace = true openapiv3.workspace = true pem.workspace = true petgraph.workspace = true diff --git a/nexus/db-fixed-data/src/role_builtin.rs b/nexus/db-fixed-data/src/role_builtin.rs index f58077fc3f..c617874e98 100644 --- a/nexus/db-fixed-data/src/role_builtin.rs +++ b/nexus/db-fixed-data/src/role_builtin.rs @@ -83,7 +83,7 @@ pub static BUILTIN_ROLES: Lazy> = Lazy::new(|| { #[cfg(test)] mod test { use super::BUILTIN_ROLES; - use crate::db::model::DatabaseString; + use nexus_db_model::DatabaseString; use nexus_types::external_api::shared::{FleetRole, ProjectRole, SiloRole}; use omicron_common::api::external::ResourceType; use strum::IntoEnumIterator; diff --git a/nexus/db-fixed-data/src/silo_user.rs b/nexus/db-fixed-data/src/silo_user.rs index 3f9a2787a8..defaa9bd52 100644 --- a/nexus/db-fixed-data/src/silo_user.rs +++ b/nexus/db-fixed-data/src/silo_user.rs @@ -63,7 +63,7 @@ mod test { use super::super::assert_valid_uuid; use super::USER_TEST_PRIVILEGED; use super::USER_TEST_UNPRIVILEGED; - use crate::db::identity::Asset; + use nexus_types::identity::Asset; #[test] fn test_silo_user_ids_are_valid() { diff --git a/nexus/db-queries/src/db/datastore/auth.rs b/nexus/db-queries/src/db/datastore/auth.rs index 3b1d1d18e3..0a41b05887 100644 --- a/nexus/db-queries/src/db/datastore/auth.rs +++ b/nexus/db-queries/src/db/datastore/auth.rs @@ -79,3 +79,95 @@ impl Storage for super::DataStore { } } } + +#[cfg(test)] +mod test { + use crate::db::DataStore; + use nexus_auth::authn; + use nexus_auth::authz::Action; + use nexus_auth::authz::AnyActor; + use nexus_auth::authz::Authz; + use nexus_auth::authz::Context; + use nexus_auth::authz::RoleSet; + use nexus_test_utils::db::test_setup_database; + use omicron_test_utils::dev; + use std::sync::Arc; + + fn authz_context_for_actor( + log: &slog::Logger, + authn: authn::Context, + datastore: Arc, + ) -> Context { + let authz = Authz::new(log); + Context::new(Arc::new(authn), Arc::new(authz), datastore) + } + + #[tokio::test] + async fn test_unregistered_resource() { + let logctx = dev::test_setup_log("test_unregistered_resource"); + let mut db = test_setup_database(&logctx.log).await; + let (opctx, datastore) = + crate::db::datastore::test_utils::datastore_test(&logctx, &db) + .await; + + // Define a resource that we "forget" to register with Oso. + use nexus_auth::authz::AuthorizedResource; + use crate::context::OpContext; + use omicron_common::api::external::Error; + use oso::PolarClass; + #[derive(Clone, PolarClass)] + struct UnregisteredResource; + impl AuthorizedResource for UnregisteredResource { + fn load_roles<'a, 'b, 'c, 'd, 'e>( + &'a self, + _: &'b OpContext, + _: &'c authn::Context, + _: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> + where + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, + { + // authorize() shouldn't get far enough to call this. + unimplemented!(); + } + + fn on_unauthorized( + &self, + _: &Authz, + _: Error, + _: AnyActor, + _: Action, + ) -> Error { + // authorize() shouldn't get far enough to call this. + unimplemented!(); + } + + fn polar_class(&self) -> oso::Class { + Self::get_polar_class() + } + } + + // Make sure an authz check with this resource fails with a clear + // message. + let unregistered_resource = UnregisteredResource {}; + let authz_privileged = authz_context_for_actor( + &logctx.log, + authn::Context::privileged_test_user(), + Arc::clone(&datastore), + ); + let error = authz_privileged + .authorize(&opctx, Action::Read, unregistered_resource) + .await; + println!("{:?}", error); + assert!(matches!(error, Err(Error::InternalError { + internal_message + }) if internal_message == "attempted authz check \ + on unregistered resource: \"UnregisteredResource\"")); + + db.cleanup().await.unwrap(); + logctx.cleanup_successful(); + } +} diff --git a/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs b/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs index 177cf673e7..39ecc58765 100644 --- a/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs +++ b/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs @@ -148,7 +148,7 @@ mod test { crate::db::datastore::test_utils::datastore_test(&logctx, &db) .await; let opctx = - OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore)); + OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore) as Arc); let settings = datastore.cockroachdb_settings(&opctx).await.unwrap(); // With a fresh cluster, this is the expected state diff --git a/nexus/db-queries/src/db/datastore/instance.rs b/nexus/db-queries/src/db/datastore/instance.rs index c7daab9243..3b655e5bb9 100644 --- a/nexus/db-queries/src/db/datastore/instance.rs +++ b/nexus/db-queries/src/db/datastore/instance.rs @@ -784,7 +784,6 @@ mod tests { use super::*; use crate::db::datastore::test_utils::datastore_test; use crate::db::lookup::LookupPath; - use nexus_db_fixed_data; use nexus_db_model::Project; use nexus_test_utils::db::test_setup_database; use nexus_types::external_api::params; @@ -796,7 +795,7 @@ mod tests { datastore: &DataStore, opctx: &OpContext, ) -> authz::Instance { - let silo_id = *fixed_data::silo::DEFAULT_SILO_ID; + let silo_id = *nexus_db_fixed_data::silo::DEFAULT_SILO_ID; let project_id = Uuid::new_v4(); let instance_id = Uuid::new_v4(); diff --git a/nexus/db-queries/src/db/datastore/mod.rs b/nexus/db-queries/src/db/datastore/mod.rs index f54ef0583d..9ec3575860 100644 --- a/nexus/db-queries/src/db/datastore/mod.rs +++ b/nexus/db-queries/src/db/datastore/mod.rs @@ -483,7 +483,7 @@ mod test { logctx.log.new(o!("component" => "TestExternalAuthn")), Arc::new(authz::Authz::new(&logctx.log)), authn::Context::external_authn(), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); let token = "a_token".to_string(); @@ -585,7 +585,7 @@ mod test { *DEFAULT_SILO_ID, SiloAuthnPolicy::try_from(&*DEFAULT_SILO).unwrap(), ), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); let delete = datastore .session_hard_delete(&silo_user_opctx, &authz_session) @@ -1622,8 +1622,10 @@ mod test { let pool = Arc::new(db::Pool::new(&logctx.log, &cfg)); let datastore = Arc::new(DataStore::new(&logctx.log, pool, None).await.unwrap()); - let opctx = - OpContext::for_tests(logctx.log.new(o!()), datastore.clone()); + let opctx = OpContext::for_tests( + logctx.log.new(o!()), + Arc::clone(&datastore) as Arc, + ); let rack_id = Uuid::new_v4(); let addr1 = "[fd00:1de::1]:12345".parse().unwrap(); diff --git a/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs b/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs index cc00bd3753..9738f05ff6 100644 --- a/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs +++ b/nexus/db-queries/src/db/datastore/virtual_provisioning_collection.rs @@ -332,7 +332,6 @@ mod test { use crate::db::datastore::test_utils::datastore_test; use crate::db::lookup::LookupPath; - use nexus_db_fixed_data; use nexus_db_model::Instance; use nexus_db_model::Project; use nexus_db_model::SiloQuotasUpdate; @@ -384,8 +383,8 @@ mod test { datastore: &DataStore, opctx: &OpContext, ) -> TestData { - let fleet_id = *fixed_data::FLEET_ID; - let silo_id = *fixed_data::silo::DEFAULT_SILO_ID; + let fleet_id = *nexus_db_fixed_data::FLEET_ID; + let silo_id = *nexus_db_fixed_data::silo::DEFAULT_SILO_ID; let project_id = Uuid::new_v4(); let (authz_project, _project) = datastore diff --git a/nexus/db-queries/src/db/lookup.rs b/nexus/db-queries/src/db/lookup.rs index 487a68b517..65616fa39d 100644 --- a/nexus/db-queries/src/db/lookup.rs +++ b/nexus/db-queries/src/db/lookup.rs @@ -925,7 +925,7 @@ mod test { crate::db::datastore::test_utils::datastore_test(&logctx, &db) .await; let opctx = - OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore)); + OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore) as Arc); let project_name: Name = Name("my-project".parse().unwrap()); let instance_name: Name = Name("my-instance".parse().unwrap()); diff --git a/nexus/db-queries/src/db/saga_recovery.rs b/nexus/db-queries/src/db/saga_recovery.rs index 55cda03c3c..610c8c76fb 100644 --- a/nexus/db-queries/src/db/saga_recovery.rs +++ b/nexus/db-queries/src/db/saga_recovery.rs @@ -447,7 +447,7 @@ mod test { let (storage, sec_client, uctx) = create_storage_sec_and_context(&log, db_datastore.clone(), sec_id); let sec_log = log.new(o!("component" => "SEC")); - let opctx = OpContext::for_tests(log, Arc::clone(&db_datastore)); + let opctx = OpContext::for_tests(log, Arc::clone(&db_datastore) as Arc); // Create and start a saga. // @@ -520,7 +520,7 @@ mod test { let (storage, sec_client, uctx) = create_storage_sec_and_context(&log, db_datastore.clone(), sec_id); let sec_log = log.new(o!("component" => "SEC")); - let opctx = OpContext::for_tests(log, Arc::clone(&db_datastore)); + let opctx = OpContext::for_tests(log, Arc::clone(&db_datastore) as Arc); // Create and start a saga, which we expect to complete successfully. let saga_id = SagaId(Uuid::new_v4()); diff --git a/nexus/db-queries/src/lib.rs b/nexus/db-queries/src/lib.rs index 5780bd1c41..003310f920 100644 --- a/nexus/db-queries/src/lib.rs +++ b/nexus/db-queries/src/lib.rs @@ -12,6 +12,9 @@ pub mod db; pub mod provisioning; pub mod transaction_retry; +#[cfg(test)] +mod policy_test; + #[macro_use] extern crate slog; #[cfg(test)] diff --git a/nexus/auth/src/authz/policy_test/coverage.rs b/nexus/db-queries/src/policy_test/coverage.rs similarity index 97% rename from nexus/auth/src/authz/policy_test/coverage.rs rename to nexus/db-queries/src/policy_test/coverage.rs index 021c9ef119..08235332ff 100644 --- a/nexus/auth/src/authz/policy_test/coverage.rs +++ b/nexus/db-queries/src/policy_test/coverage.rs @@ -2,8 +2,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::authz; -use crate::authz::AuthorizedResource; +use nexus_auth::authz; +use nexus_auth::authz::AuthorizedResource; +use slog::{debug, error, o, warn}; use std::collections::BTreeSet; /// Helper for identifying authz resources not covered by the IAM role policy diff --git a/nexus/auth/src/authz/policy_test/mod.rs b/nexus/db-queries/src/policy_test/mod.rs similarity index 97% rename from nexus/auth/src/authz/policy_test/mod.rs rename to nexus/db-queries/src/policy_test/mod.rs index b6961bcc30..42c1131759 100644 --- a/nexus/auth/src/authz/policy_test/mod.rs +++ b/nexus/db-queries/src/policy_test/mod.rs @@ -14,12 +14,12 @@ mod coverage; mod resource_builder; mod resources; -use crate::authn; -use crate::authn::SiloAuthnPolicy; -use crate::authz; -use crate::context::OpContext; +use nexus_auth::authn; +use nexus_auth::authn::SiloAuthnPolicy; +use nexus_auth::authn::USER_TEST_PRIVILEGED; +use nexus_auth::authz; +use nexus_auth::context::OpContext; use crate::db; -use authn::USER_TEST_PRIVILEGED; use coverage::Coverage; use futures::StreamExt; use nexus_test_utils::db::test_setup_database; @@ -33,6 +33,7 @@ use omicron_test_utils::dev; use resource_builder::DynAuthorizedResource; use resource_builder::ResourceBuilder; use resource_builder::ResourceSet; +use slog::{o, trace}; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::io::Cursor; @@ -117,7 +118,7 @@ async fn test_iam_roles_behavior() { main_silo_id, SiloAuthnPolicy::default(), ), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); Arc::new((username.clone(), opctx)) @@ -140,7 +141,7 @@ async fn test_iam_roles_behavior() { user_log, Arc::clone(&authz), authn::Context::internal_unauthenticated(), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ), ))); @@ -439,7 +440,7 @@ async fn test_conferred_roles() { main_silo_id, policy.clone(), ), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); Arc::new((username.clone(), opctx)) }) diff --git a/nexus/auth/src/authz/policy_test/resource_builder.rs b/nexus/db-queries/src/policy_test/resource_builder.rs similarity index 74% rename from nexus/auth/src/authz/policy_test/resource_builder.rs rename to nexus/db-queries/src/policy_test/resource_builder.rs index 59cb283a95..55e37603c3 100644 --- a/nexus/auth/src/authz/policy_test/resource_builder.rs +++ b/nexus/db-queries/src/policy_test/resource_builder.rs @@ -6,10 +6,10 @@ //! IAM policy test use super::coverage::Coverage; -use crate::authz; -use crate::authz::ApiResourceWithRolesType; -use crate::authz::AuthorizedResource; -use crate::context::OpContext; +use nexus_auth::authz; +use nexus_auth::authz::ApiResourceWithRolesType; +use nexus_auth::authz::AuthorizedResource; +use nexus_auth::context::OpContext; use crate::db; use authz::ApiResource; use futures::future::BoxFuture; @@ -192,40 +192,40 @@ pub trait DynAuthorizedResource: AuthorizedResource + std::fmt::Debug { fn resource_name(&self) -> String; } -impl DynAuthorizedResource for T -where - T: ApiResource + AuthorizedResource + oso::PolarClass + Clone, -{ - fn do_authorize<'a, 'b>( - &'a self, - opctx: &'b OpContext, - action: authz::Action, - ) -> BoxFuture<'a, Result<(), Error>> - where - 'b: 'a, - { - opctx.authorize(action, self).boxed() - } - - fn resource_name(&self) -> String { - let my_ident = match self.lookup_type() { - LookupType::ByName(name) => format!("{:?}", name), - LookupType::ById(id) => format!("id {:?}", id.to_string()), - LookupType::ByCompositeId(id) => format!("id {:?}", id), - LookupType::ByOther(_) => { - unimplemented!() +macro_rules! impl_dyn_authorized_resource_for_global { + ($t:ty) => { + impl DynAuthorizedResource for $t { + fn resource_name(&self) -> String { + String::from(stringify!($t)) } - }; - format!("{:?} {}", self.resource_type(), my_ident) - } + fn do_authorize<'a, 'b>( + &'a self, + opctx: &'b OpContext, + action: authz::Action, + ) -> BoxFuture<'a, Result<(), Error>> + where + 'b: 'a, + { + opctx.authorize(action, self).boxed() + } + } + }; } -macro_rules! impl_dyn_authorized_resource_for_global { +macro_rules! impl_dyn_authorized_resource_for_resource { ($t:ty) => { impl DynAuthorizedResource for $t { fn resource_name(&self) -> String { - String::from(stringify!($t)) + let my_ident = match self.lookup_type() { + LookupType::ByName(name) => format!("{:?}", name), + LookupType::ById(id) => format!("id {:?}", id.to_string()), + LookupType::ByCompositeId(id) => format!("id {:?}", id), + LookupType::ByOther(_) => { + unimplemented!() + } + }; + format!("{:?} {}", self.resource_type(), my_ident) } fn do_authorize<'a, 'b>( @@ -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); +impl_dyn_authorized_resource_for_resource!(authz::Blueprint); +impl_dyn_authorized_resource_for_resource!(authz::Certificate); +impl_dyn_authorized_resource_for_resource!(authz::DeviceAccessToken); +impl_dyn_authorized_resource_for_resource!(authz::DeviceAuthRequest); +impl_dyn_authorized_resource_for_resource!(authz::Disk); +impl_dyn_authorized_resource_for_resource!(authz::Fleet); +impl_dyn_authorized_resource_for_resource!(authz::FloatingIp); +impl_dyn_authorized_resource_for_resource!(authz::IdentityProvider); +impl_dyn_authorized_resource_for_resource!(authz::Image); +impl_dyn_authorized_resource_for_resource!(authz::Instance); +impl_dyn_authorized_resource_for_resource!(authz::InstanceNetworkInterface); +impl_dyn_authorized_resource_for_resource!(authz::LoopbackAddress); +impl_dyn_authorized_resource_for_resource!(authz::Rack); +impl_dyn_authorized_resource_for_resource!(authz::PhysicalDisk); +impl_dyn_authorized_resource_for_resource!(authz::Project); +impl_dyn_authorized_resource_for_resource!(authz::ProjectImage); +impl_dyn_authorized_resource_for_resource!(authz::SamlIdentityProvider); +impl_dyn_authorized_resource_for_resource!(authz::Service); +impl_dyn_authorized_resource_for_resource!(authz::Silo); +impl_dyn_authorized_resource_for_resource!(authz::SiloGroup); +impl_dyn_authorized_resource_for_resource!(authz::SiloImage); +impl_dyn_authorized_resource_for_resource!(authz::SiloUser); +impl_dyn_authorized_resource_for_resource!(authz::Sled); +impl_dyn_authorized_resource_for_resource!(authz::Snapshot); +impl_dyn_authorized_resource_for_resource!(authz::SshKey); +impl_dyn_authorized_resource_for_resource!(authz::TufArtifact); +impl_dyn_authorized_resource_for_resource!(authz::TufRepo); +impl_dyn_authorized_resource_for_resource!(authz::Vpc); +impl_dyn_authorized_resource_for_resource!(authz::VpcSubnet); +impl_dyn_authorized_resource_for_resource!(authz::Zpool); + +impl_dyn_authorized_resource_for_global!(authz::Database); impl_dyn_authorized_resource_for_global!(authz::BlueprintConfig); impl_dyn_authorized_resource_for_global!(authz::ConsoleSessionList); impl_dyn_authorized_resource_for_global!(authz::DeviceAuthRequestList); diff --git a/nexus/auth/src/authz/policy_test/resources.rs b/nexus/db-queries/src/policy_test/resources.rs similarity index 99% rename from nexus/auth/src/authz/policy_test/resources.rs rename to nexus/db-queries/src/policy_test/resources.rs index bc30e77fac..fddedae854 100644 --- a/nexus/auth/src/authz/policy_test/resources.rs +++ b/nexus/db-queries/src/policy_test/resources.rs @@ -6,7 +6,7 @@ use super::resource_builder::ResourceBuilder; use super::resource_builder::ResourceSet; -use crate::authz; +use nexus_auth::authz; use crate::db::model::ArtifactId; use nexus_db_model::SemverVersion; use omicron_common::api::external::LookupType; @@ -367,8 +367,8 @@ pub fn exempted_authz_classes() -> BTreeSet { [ // Non-resources: authz::Action::get_polar_class(), - authz::actor::AnyActor::get_polar_class(), - authz::actor::AuthenticatedActor::get_polar_class(), + authz::AnyActor::get_polar_class(), + authz::AuthenticatedActor::get_polar_class(), // Resources whose behavior should be identical to an existing type // and we don't want to do the test twice for performance reasons: // none yet. diff --git a/nexus/db-queries/tests/output/authz-roles.out b/nexus/db-queries/tests/output/authz-roles.out index 0482cdfd2a..41a1ded3b4 100644 --- a/nexus/db-queries/tests/output/authz-roles.out +++ b/nexus/db-queries/tests/output/authz-roles.out @@ -1,4 +1,4 @@ -resource: authz::oso_generic::Database +resource: authz::Database USER Q R LC RP M MP CC D fleet-admin ✔ ✘ ✘ ✘ ✘ ✘ ✘ ✘ From 58e552e2bdd98c404aad9a1c2d5366e911744a53 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 15:53:03 -0700 Subject: [PATCH 3/9] tests converted --- Cargo.lock | 2 - nexus/auth/src/context.rs | 48 ++++++++++++------- nexus/db-queries/src/db/datastore/auth.rs | 2 +- .../src/db/datastore/cockroachdb_settings.rs | 6 ++- nexus/db-queries/src/db/lookup.rs | 6 ++- nexus/db-queries/src/db/saga_recovery.rs | 10 +++- nexus/db-queries/src/policy_test/mod.rs | 9 ++-- .../src/policy_test/resource_builder.rs | 8 ++-- nexus/db-queries/src/policy_test/resources.rs | 2 +- 9 files changed, 57 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd28f853e0..2e7529b819 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4527,7 +4527,6 @@ dependencies = [ "omicron-common", "omicron-passwords", "omicron-rpaths", - "omicron-sled-agent", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -4655,7 +4654,6 @@ dependencies = [ "omicron-passwords", "omicron-rpaths", "omicron-sled-agent", - "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", "once_cell", diff --git a/nexus/auth/src/context.rs b/nexus/auth/src/context.rs index 0a11a0cff6..01516359c3 100644 --- a/nexus/auth/src/context.rs +++ b/nexus/auth/src/context.rs @@ -347,26 +347,48 @@ impl Session for ConsoleSessionWithSiloId { } } -/* #[cfg(test)] mod test { use super::OpContext; use crate::authn; use crate::authz; use authz::Action; - use nexus_test_utils::db::test_setup_database; + use nexus_db_model::IdentityType; + use nexus_db_model::RoleAssignment; use omicron_common::api::external::Error; + use omicron_common::api::external::ResourceType; use omicron_test_utils::dev; use std::collections::BTreeMap; use std::sync::Arc; + use uuid::Uuid; + + struct FakeStorage {} + + impl FakeStorage { + fn new() -> Arc { + 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, Error> { + todo!(); + } + } #[tokio::test] async fn test_background_context() { let logctx = dev::test_setup_log("test_background_context"); - let mut db = test_setup_database(&logctx.log).await; - let (_, datastore) = - crate::db::datastore::test_utils::datastore_test(&logctx, &db) - .await; + + let datastore = FakeStorage::new(); let opctx = OpContext::for_background( logctx.log.new(o!()), Arc::new(authz::Authz::new(&logctx.log)), @@ -389,17 +411,13 @@ mod test { .await .expect_err("expected authorization error"); assert!(matches!(error, Error::Unauthenticated { .. })); - db.cleanup().await.unwrap(); logctx.cleanup_successful(); } #[tokio::test] async fn test_test_context() { let logctx = dev::test_setup_log("test_background_context"); - let mut db = test_setup_database(&logctx.log).await; - let (_, datastore) = - crate::db::datastore::test_utils::datastore_test(&logctx, &db) - .await; + let datastore = FakeStorage::new(); let opctx = OpContext::for_tests(logctx.log.new(o!()), datastore); // Like in test_background_context(), this is essentially a test of the @@ -411,17 +429,13 @@ mod test { .authorize(Action::Query, &authz::DATABASE) .await .expect("expected authorization to succeed"); - db.cleanup().await.unwrap(); logctx.cleanup_successful(); } #[tokio::test] async fn test_child_context() { let logctx = dev::test_setup_log("test_child_context"); - let mut db = test_setup_database(&logctx.log).await; - let (_, datastore) = - crate::db::datastore::test_utils::datastore_test(&logctx, &db) - .await; + let datastore = FakeStorage::new(); let opctx = OpContext::for_background( logctx.log.new(o!()), Arc::new(authz::Authz::new(&logctx.log)), @@ -459,8 +473,6 @@ mod test { assert_eq!(grandchild_opctx.metadata["one"], "seven"); assert_eq!(grandchild_opctx.metadata["five"], "six"); - db.cleanup().await.unwrap(); logctx.cleanup_successful(); } } -*/ diff --git a/nexus/db-queries/src/db/datastore/auth.rs b/nexus/db-queries/src/db/datastore/auth.rs index 0a41b05887..cb1d90df5d 100644 --- a/nexus/db-queries/src/db/datastore/auth.rs +++ b/nexus/db-queries/src/db/datastore/auth.rs @@ -111,8 +111,8 @@ mod test { .await; // Define a resource that we "forget" to register with Oso. - use nexus_auth::authz::AuthorizedResource; use crate::context::OpContext; + use nexus_auth::authz::AuthorizedResource; use omicron_common::api::external::Error; use oso::PolarClass; #[derive(Clone, PolarClass)] diff --git a/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs b/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs index 39ecc58765..e7a975fa69 100644 --- a/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs +++ b/nexus/db-queries/src/db/datastore/cockroachdb_settings.rs @@ -147,8 +147,10 @@ mod test { let (_, datastore) = crate::db::datastore::test_utils::datastore_test(&logctx, &db) .await; - let opctx = - OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore) as Arc); + let opctx = OpContext::for_tests( + logctx.log.new(o!()), + Arc::clone(&datastore) as Arc, + ); let settings = datastore.cockroachdb_settings(&opctx).await.unwrap(); // With a fresh cluster, this is the expected state diff --git a/nexus/db-queries/src/db/lookup.rs b/nexus/db-queries/src/db/lookup.rs index 65616fa39d..0999694c54 100644 --- a/nexus/db-queries/src/db/lookup.rs +++ b/nexus/db-queries/src/db/lookup.rs @@ -924,8 +924,10 @@ mod test { let (_, datastore) = crate::db::datastore::test_utils::datastore_test(&logctx, &db) .await; - let opctx = - OpContext::for_tests(logctx.log.new(o!()), Arc::clone(&datastore) as Arc); + let opctx = OpContext::for_tests( + logctx.log.new(o!()), + Arc::clone(&datastore) as Arc, + ); let project_name: Name = Name("my-project".parse().unwrap()); let instance_name: Name = Name("my-instance".parse().unwrap()); diff --git a/nexus/db-queries/src/db/saga_recovery.rs b/nexus/db-queries/src/db/saga_recovery.rs index 610c8c76fb..25f8ff788d 100644 --- a/nexus/db-queries/src/db/saga_recovery.rs +++ b/nexus/db-queries/src/db/saga_recovery.rs @@ -447,7 +447,10 @@ mod test { let (storage, sec_client, uctx) = create_storage_sec_and_context(&log, db_datastore.clone(), sec_id); let sec_log = log.new(o!("component" => "SEC")); - let opctx = OpContext::for_tests(log, Arc::clone(&db_datastore) as Arc); + let opctx = OpContext::for_tests( + log, + Arc::clone(&db_datastore) as Arc, + ); // Create and start a saga. // @@ -520,7 +523,10 @@ mod test { let (storage, sec_client, uctx) = create_storage_sec_and_context(&log, db_datastore.clone(), sec_id); let sec_log = log.new(o!("component" => "SEC")); - let opctx = OpContext::for_tests(log, Arc::clone(&db_datastore) as Arc); + let opctx = OpContext::for_tests( + log, + Arc::clone(&db_datastore) as Arc, + ); // Create and start a saga, which we expect to complete successfully. let saga_id = SagaId(Uuid::new_v4()); diff --git a/nexus/db-queries/src/policy_test/mod.rs b/nexus/db-queries/src/policy_test/mod.rs index 42c1131759..395a480c47 100644 --- a/nexus/db-queries/src/policy_test/mod.rs +++ b/nexus/db-queries/src/policy_test/mod.rs @@ -14,14 +14,14 @@ mod coverage; mod resource_builder; mod resources; +use crate::db; +use coverage::Coverage; +use futures::StreamExt; use nexus_auth::authn; use nexus_auth::authn::SiloAuthnPolicy; use nexus_auth::authn::USER_TEST_PRIVILEGED; use nexus_auth::authz; use nexus_auth::context::OpContext; -use crate::db; -use coverage::Coverage; -use futures::StreamExt; use nexus_test_utils::db::test_setup_database; use nexus_types::external_api::shared; use nexus_types::external_api::shared::FleetRole; @@ -440,7 +440,8 @@ async fn test_conferred_roles() { main_silo_id, policy.clone(), ), - Arc::clone(&datastore) as Arc, + Arc::clone(&datastore) + as Arc, ); Arc::new((username.clone(), opctx)) }) diff --git a/nexus/db-queries/src/policy_test/resource_builder.rs b/nexus/db-queries/src/policy_test/resource_builder.rs index 55e37603c3..3d09b2ab2d 100644 --- a/nexus/db-queries/src/policy_test/resource_builder.rs +++ b/nexus/db-queries/src/policy_test/resource_builder.rs @@ -6,14 +6,14 @@ //! IAM policy test use super::coverage::Coverage; -use nexus_auth::authz; -use nexus_auth::authz::ApiResourceWithRolesType; -use nexus_auth::authz::AuthorizedResource; -use nexus_auth::context::OpContext; use crate::db; use authz::ApiResource; use futures::future::BoxFuture; use futures::FutureExt; +use nexus_auth::authz; +use nexus_auth::authz::ApiResourceWithRolesType; +use nexus_auth::authz::AuthorizedResource; +use nexus_auth::context::OpContext; use nexus_db_model::DatabaseString; use nexus_types::external_api::shared; use omicron_common::api::external::Error; diff --git a/nexus/db-queries/src/policy_test/resources.rs b/nexus/db-queries/src/policy_test/resources.rs index fddedae854..478fa169ff 100644 --- a/nexus/db-queries/src/policy_test/resources.rs +++ b/nexus/db-queries/src/policy_test/resources.rs @@ -6,8 +6,8 @@ use super::resource_builder::ResourceBuilder; use super::resource_builder::ResourceSet; -use nexus_auth::authz; use crate::db::model::ArtifactId; +use nexus_auth::authz; use nexus_db_model::SemverVersion; use omicron_common::api::external::LookupType; use omicron_uuid_kinds::GenericUuid; From b274d17ae4b4320dda68beac577f65bee264eae2 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 16:08:02 -0700 Subject: [PATCH 4/9] Reduce deps --- Cargo.lock | 128 +------------------------ nexus/Cargo.toml | 1 - nexus/auth/Cargo.toml | 51 +--------- nexus/db-fixed-data/Cargo.toml | 70 -------------- nexus/db-queries/Cargo.toml | 10 -- nexus/src/populate.rs | 4 +- nexus/tests/integration_tests/silos.rs | 4 +- 7 files changed, 6 insertions(+), 262 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e7529b819..a2860b9976 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4024,7 +4024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -4489,83 +4489,36 @@ name = "nexus-auth" version = "0.1.0" dependencies = [ "anyhow", - "assert_matches", - "async-bb8-diesel", "async-trait", "authz-macros", "base64 0.22.1", - "bb8", - "camino", - "camino-tempfile", "chrono", - "const_format", "cookie 0.18.1", - "db-macros", - "diesel", - "diesel-dtrace", "dropshot", - "expectorate", "futures", - "gateway-client", "headers", "http 0.2.12", "hyper 0.14.28", - "hyper-rustls 0.26.0", - "illumos-utils", - "internal-dns", - "ipnetwork", - "itertools 0.12.1", - "macaddr", "newtype_derive", - "nexus-config", "nexus-db-fixed-data", "nexus-db-model", - "nexus-inventory", - "nexus-reconfigurator-planning", - "nexus-test-utils", "nexus-types", "omicron-common", - "omicron-passwords", "omicron-rpaths", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", "once_cell", - "openapiv3", "openssl", "oso", - "oximeter", - "oxnet", - "paste", - "pem", - "petgraph", "pq-sys", - "predicates", - "pretty_assertions", - "rand 0.8.5", - "rcgen", - "ref-cast", - "regex", - "rustls 0.22.4", "samael", - "schemars", - "semver 1.0.23", "serde", - "serde_json", "serde_urlencoded", - "serde_with", - "sled-agent-client", "slog", - "slog-error-chain", - "static_assertions", - "steno", "strum", - "subprocess", - "swrite", - "term", "thiserror", "tokio", - "usdt 0.5.0", "uuid", ] @@ -4615,83 +4568,14 @@ dependencies = [ name = "nexus-db-fixed-data" version = "0.1.0" dependencies = [ - "anyhow", - "assert_matches", - "async-bb8-diesel", - "async-trait", - "authz-macros", - "base64 0.22.1", - "bb8", - "camino", - "camino-tempfile", - "chrono", - "const_format", - "cookie 0.18.1", - "db-macros", - "diesel", - "diesel-dtrace", - "dropshot", - "expectorate", - "futures", - "gateway-client", - "headers", - "http 0.2.12", - "hyper 0.14.28", - "hyper-rustls 0.26.0", - "illumos-utils", - "internal-dns", - "ipnetwork", - "itertools 0.12.1", - "macaddr", - "newtype_derive", - "nexus-config", "nexus-db-model", - "nexus-inventory", - "nexus-reconfigurator-planning", - "nexus-test-utils", "nexus-types", "omicron-common", - "omicron-passwords", "omicron-rpaths", - "omicron-sled-agent", - "omicron-uuid-kinds", "omicron-workspace-hack", "once_cell", - "openapiv3", - "openssl", - "oso", - "oximeter", - "oxnet", - "paste", - "pem", - "petgraph", "pq-sys", - "predicates", - "pretty_assertions", - "rand 0.8.5", - "rcgen", - "ref-cast", - "regex", - "rustls 0.22.4", - "samael", - "schemars", - "semver 1.0.23", - "serde", - "serde_json", - "serde_urlencoded", - "serde_with", - "sled-agent-client", - "slog", - "slog-error-chain", - "static_assertions", - "steno", "strum", - "subprocess", - "swrite", - "term", - "thiserror", - "tokio", - "usdt 0.5.0", "uuid", ] @@ -4748,14 +4632,11 @@ dependencies = [ "assert_matches", "async-bb8-diesel", "async-trait", - "authz-macros", - "base64 0.22.1", "bb8", "camino", "camino-tempfile", "chrono", "const_format", - "cookie 0.18.1", "db-macros", "diesel", "diesel-dtrace", @@ -4763,16 +4644,12 @@ dependencies = [ "expectorate", "futures", "gateway-client", - "headers", - "http 0.2.12", - "hyper 0.14.28", "hyper-rustls 0.26.0", "illumos-utils", "internal-dns", "ipnetwork", "itertools 0.12.1", "macaddr", - "newtype_derive", "nexus-auth", "nexus-config", "nexus-db-fixed-data", @@ -4790,7 +4667,6 @@ dependencies = [ "omicron-workspace-hack", "once_cell", "openapiv3", - "openssl", "oso", "oximeter", "oxnet", @@ -4805,12 +4681,10 @@ dependencies = [ "ref-cast", "regex", "rustls 0.22.4", - "samael", "schemars", "semver 1.0.23", "serde", "serde_json", - "serde_urlencoded", "serde_with", "sled-agent-client", "slog", diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index a9a89720ce..58a1e824cb 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -88,7 +88,6 @@ uuid.workspace = true nexus-auth.workspace = true nexus-defaults.workspace = true -nexus-db-fixed-data.workspace = true nexus-db-model.workspace = true nexus-db-queries.workspace = true nexus-inventory.workspace = true diff --git a/nexus/auth/Cargo.toml b/nexus/auth/Cargo.toml index 62140377d0..1a926f1789 100644 --- a/nexus/auth/Cargo.toml +++ b/nexus/auth/Cargo.toml @@ -12,84 +12,37 @@ omicron-rpaths.workspace = true [dependencies] anyhow.workspace = true -async-bb8-diesel.workspace = true async-trait.workspace = true base64.workspace = true -bb8.workspace = true -camino.workspace = true chrono.workspace = true -const_format.workspace = true cookie.workspace = true -diesel.workspace = true -diesel-dtrace.workspace = true dropshot.workspace = true futures.workspace = true headers.workspace = true http.workspace = true hyper.workspace = true -ipnetwork.workspace = true -macaddr.workspace = true newtype_derive.workspace = true +# See omicron-rpaths for more about the "pq-sys" dependency. +pq-sys = "*" once_cell.workspace = true openssl.workspace = true oso.workspace = true -oxnet.workspace = true -paste.workspace = true -# See omicron-rpaths for more about the "pq-sys" dependency. -pq-sys = "*" -rand.workspace = true -ref-cast.workspace = true samael.workspace = true -schemars.workspace = true -semver.workspace = true serde.workspace = true -serde_json.workspace = true serde_urlencoded.workspace = true -serde_with.workspace = true -sled-agent-client.workspace = true slog.workspace = true -slog-error-chain.workspace = true -static_assertions.workspace = true -steno.workspace = true strum.workspace = true -swrite.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["full"] } uuid.workspace = true -usdt.workspace = true authz-macros.workspace = true -db-macros.workspace = true -nexus-config.workspace = true nexus-db-fixed-data.workspace = true nexus-db-model.workspace = true nexus-types.workspace = true omicron-common.workspace = true -omicron-passwords.workspace = true omicron-uuid-kinds.workspace = true -oximeter.workspace = true omicron-workspace-hack.workspace = true [dev-dependencies] -assert_matches.workspace = true -camino-tempfile.workspace = true -expectorate.workspace = true -hyper-rustls.workspace = true -gateway-client.workspace = true -illumos-utils.workspace = true -internal-dns.workspace = true -itertools.workspace = true -nexus-inventory.workspace = true -nexus-reconfigurator-planning.workspace = true -nexus-test-utils.workspace = true omicron-test-utils.workspace = true -openapiv3.workspace = true -pem.workspace = true -petgraph.workspace = true -predicates.workspace = true -pretty_assertions.workspace = true -rcgen.workspace = true -regex.workspace = true -rustls.workspace = true -subprocess.workspace = true -term.workspace = true diff --git a/nexus/db-fixed-data/Cargo.toml b/nexus/db-fixed-data/Cargo.toml index d60d3b6015..d24bf25c7d 100644 --- a/nexus/db-fixed-data/Cargo.toml +++ b/nexus/db-fixed-data/Cargo.toml @@ -11,84 +11,14 @@ workspace = true omicron-rpaths.workspace = true [dependencies] -anyhow.workspace = true -async-bb8-diesel.workspace = true -async-trait.workspace = true -base64.workspace = true -bb8.workspace = true -camino.workspace = true -chrono.workspace = true -const_format.workspace = true -cookie.workspace = true -diesel.workspace = true -diesel-dtrace.workspace = true -dropshot.workspace = true -futures.workspace = true -headers.workspace = true -http.workspace = true -hyper.workspace = true -ipnetwork.workspace = true -macaddr.workspace = true -newtype_derive.workspace = true once_cell.workspace = true -openssl.workspace = true -oso.workspace = true -oxnet.workspace = true -paste.workspace = true # See omicron-rpaths for more about the "pq-sys" dependency. pq-sys = "*" -rand.workspace = true -ref-cast.workspace = true -samael.workspace = true -schemars.workspace = true -semver.workspace = true -serde.workspace = true -serde_json.workspace = true -serde_urlencoded.workspace = true -serde_with.workspace = true -sled-agent-client.workspace = true -slog.workspace = true -slog-error-chain.workspace = true -static_assertions.workspace = true -steno.workspace = true strum.workspace = true -swrite.workspace = true -thiserror.workspace = true -tokio = { workspace = true, features = ["full"] } uuid.workspace = true -usdt.workspace = true -authz-macros.workspace = true -db-macros.workspace = true -nexus-config.workspace = true nexus-db-model.workspace = true nexus-types.workspace = true omicron-common.workspace = true -omicron-passwords.workspace = true -omicron-uuid-kinds.workspace = true -oximeter.workspace = true omicron-workspace-hack.workspace = true -[dev-dependencies] -assert_matches.workspace = true -camino-tempfile.workspace = true -expectorate.workspace = true -hyper-rustls.workspace = true -gateway-client.workspace = true -illumos-utils.workspace = true -internal-dns.workspace = true -itertools.workspace = true -nexus-inventory.workspace = true -nexus-reconfigurator-planning.workspace = true -nexus-test-utils.workspace = true -omicron-sled-agent.workspace = true -openapiv3.workspace = true -pem.workspace = true -petgraph.workspace = true -predicates.workspace = true -pretty_assertions.workspace = true -rcgen.workspace = true -regex.workspace = true -rustls.workspace = true -subprocess.workspace = true -term.workspace = true diff --git a/nexus/db-queries/Cargo.toml b/nexus/db-queries/Cargo.toml index f5b46edb75..5fcb98dd9e 100644 --- a/nexus/db-queries/Cargo.toml +++ b/nexus/db-queries/Cargo.toml @@ -14,24 +14,17 @@ omicron-rpaths.workspace = true anyhow.workspace = true async-bb8-diesel.workspace = true async-trait.workspace = true -base64.workspace = true bb8.workspace = true camino.workspace = true chrono.workspace = true const_format.workspace = true -cookie.workspace = true diesel.workspace = true diesel-dtrace.workspace = true dropshot.workspace = true futures.workspace = true -headers.workspace = true -http.workspace = true -hyper.workspace = true ipnetwork.workspace = true macaddr.workspace = true -newtype_derive.workspace = true once_cell.workspace = true -openssl.workspace = true oso.workspace = true oxnet.workspace = true paste.workspace = true @@ -39,12 +32,10 @@ paste.workspace = true pq-sys = "*" rand.workspace = true ref-cast.workspace = true -samael.workspace = true schemars.workspace = true semver.workspace = true serde.workspace = true serde_json.workspace = true -serde_urlencoded.workspace = true serde_with.workspace = true sled-agent-client.workspace = true slog.workspace = true @@ -58,7 +49,6 @@ tokio = { workspace = true, features = ["full"] } uuid.workspace = true usdt.workspace = true -authz-macros.workspace = true db-macros.workspace = true nexus-auth.workspace = true nexus-config.workspace = true diff --git a/nexus/src/populate.rs b/nexus/src/populate.rs index ffe67baeae..724b25162d 100644 --- a/nexus/src/populate.rs +++ b/nexus/src/populate.rs @@ -388,7 +388,7 @@ mod test { logctx.log.clone(), Arc::new(authz::Authz::new(&logctx.log)), authn::Context::internal_db_init(), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); let log = &logctx.log; @@ -444,7 +444,7 @@ mod test { logctx.log.clone(), Arc::new(authz::Authz::new(&logctx.log)), authn::Context::internal_db_init(), - Arc::clone(&datastore), + Arc::clone(&datastore) as Arc, ); info!(&log, "cleaning up database"); diff --git a/nexus/tests/integration_tests/silos.rs b/nexus/tests/integration_tests/silos.rs index e240edac47..2e6c21bb79 100644 --- a/nexus/tests/integration_tests/silos.rs +++ b/nexus/tests/integration_tests/silos.rs @@ -4,9 +4,7 @@ use crate::integration_tests::saml::SAML_IDP_DESCRIPTOR; use dropshot::ResultsPage; -use nexus_db_queries::authn::silos::{ - AuthenticatedSubject, IdentityProviderType, -}; +use nexus_db_queries::authn::silos::AuthenticatedSubject; use nexus_db_queries::authn::{USER_TEST_PRIVILEGED, USER_TEST_UNPRIVILEGED}; use nexus_db_queries::authz::{self}; use nexus_db_queries::context::OpContext; From 63ca0a0143021be61c4863e4ec4eeffafea4d784 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 16:12:38 -0700 Subject: [PATCH 5/9] Lockfile --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a2860b9976..8b9cbd7f04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5496,7 +5496,6 @@ dependencies = [ "nexus-auth", "nexus-client", "nexus-config", - "nexus-db-fixed-data", "nexus-db-model", "nexus-db-queries", "nexus-defaults", From 805c09063c0d2b3630f095a381ede758f08fa92a Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 16:27:32 -0700 Subject: [PATCH 6/9] hakari, less pub interfaces --- Cargo.lock | 1 + nexus/auth/src/authz/context.rs | 2 +- workspace-hack/Cargo.toml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8b9cbd7f04..e3c9eaa6d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5978,6 +5978,7 @@ dependencies = [ "trust-dns-proto", "unicode-bidi", "unicode-normalization", + "unicode-xid", "usdt 0.5.0", "usdt-impl 0.5.0", "uuid", diff --git a/nexus/auth/src/authz/context.rs b/nexus/auth/src/authz/context.rs index 76f0a1f1af..06d48b3f3d 100644 --- a/nexus/auth/src/authz/context.rs +++ b/nexus/auth/src/authz/context.rs @@ -78,7 +78,7 @@ impl Context { Context { authn, authz, datastore } } - pub fn datastore(&self) -> &Arc { + pub(crate) fn datastore(&self) -> &Arc { &self.datastore } diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 3b5e1917d0..0376f1764a 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -220,6 +220,7 @@ tracing = { version = "0.1.40", features = ["log"] } trust-dns-proto = { version = "0.22.0" } unicode-bidi = { version = "0.3.15" } unicode-normalization = { version = "0.1.23" } +unicode-xid = { version = "0.2.4" } usdt = { version = "0.5.0" } usdt-impl = { version = "0.5.0", default-features = false, features = ["asm", "des"] } uuid = { version = "1.8.0", features = ["serde", "v4"] } From a1868ac73c501ae3bcd5d628fd8ec3d46875cc03 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 16:35:26 -0700 Subject: [PATCH 7/9] Rely on FakeStorage more --- nexus/auth/src/authz/context.rs | 119 ++++++++++++++++++++++ nexus/db-queries/src/db/datastore/auth.rs | 92 ----------------- 2 files changed, 119 insertions(+), 92 deletions(-) diff --git a/nexus/auth/src/authz/context.rs b/nexus/auth/src/authz/context.rs index 06d48b3f3d..59b7080fca 100644 --- a/nexus/auth/src/authz/context.rs +++ b/nexus/auth/src/authz/context.rs @@ -191,3 +191,122 @@ pub trait AuthorizedResource: oso::ToPolar + Send + Sync + 'static { /// Returns the Polar class that implements this resource fn polar_class(&self) -> oso::Class; } + +#[cfg(test)] +mod test { + use crate::authn; + use crate::authz::Action; + use crate::authz::AnyActor; + use crate::authz::Authz; + use crate::authz::Context; + use crate::authz::RoleSet; + use crate::context::OpContext; + use omicron_test_utils::dev; + use std::sync::Arc; + + use nexus_db_model::IdentityType; + use nexus_db_model::RoleAssignment; + use omicron_common::api::external::Error; + use omicron_common::api::external::ResourceType; + use uuid::Uuid; + + struct FakeStorage {} + + impl FakeStorage { + fn new() -> Arc { + 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, Error> { + todo!(); + } + } + + fn authz_context_for_actor( + log: &slog::Logger, + authn: authn::Context, + datastore: Arc, + ) -> Context { + let authz = Authz::new(log); + Context::new(Arc::new(authn), Arc::new(authz), datastore) + } + + #[tokio::test] + async fn test_unregistered_resource() { + let logctx = dev::test_setup_log("test_unregistered_resource"); + let datastore = FakeStorage::new(); + let opctx = OpContext::for_background( + logctx.log.new(o!()), + Arc::new(Authz::new(&logctx.log)), + authn::Context::internal_db_init(), + Arc::clone(&datastore) as Arc, + ); + + // Define a resource that we "forget" to register with Oso. + use crate::authz::AuthorizedResource; + use oso::PolarClass; + #[derive(Clone, PolarClass)] + struct UnregisteredResource; + impl AuthorizedResource for UnregisteredResource { + fn load_roles<'a, 'b, 'c, 'd, 'e>( + &'a self, + _: &'b OpContext, + _: &'c authn::Context, + _: &'d mut RoleSet, + ) -> futures::future::BoxFuture<'e, Result<(), Error>> + where + 'a: 'e, + 'b: 'e, + 'c: 'e, + 'd: 'e, + { + // authorize() shouldn't get far enough to call this. + unimplemented!(); + } + + fn on_unauthorized( + &self, + _: &Authz, + _: Error, + _: AnyActor, + _: Action, + ) -> Error { + // authorize() shouldn't get far enough to call this. + unimplemented!(); + } + + fn polar_class(&self) -> oso::Class { + Self::get_polar_class() + } + } + + // Make sure an authz check with this resource fails with a clear + // message. + let unregistered_resource = UnregisteredResource {}; + let authz_privileged = authz_context_for_actor( + &logctx.log, + authn::Context::privileged_test_user(), + Arc::clone(&datastore) as Arc, + ); + let error = authz_privileged + .authorize(&opctx, Action::Read, unregistered_resource) + .await; + println!("{:?}", error); + assert!(matches!(error, Err(Error::InternalError { + internal_message + }) if internal_message == "attempted authz check \ + on unregistered resource: \"UnregisteredResource\"")); + + logctx.cleanup_successful(); + } +} diff --git a/nexus/db-queries/src/db/datastore/auth.rs b/nexus/db-queries/src/db/datastore/auth.rs index cb1d90df5d..3b1d1d18e3 100644 --- a/nexus/db-queries/src/db/datastore/auth.rs +++ b/nexus/db-queries/src/db/datastore/auth.rs @@ -79,95 +79,3 @@ impl Storage for super::DataStore { } } } - -#[cfg(test)] -mod test { - use crate::db::DataStore; - use nexus_auth::authn; - use nexus_auth::authz::Action; - use nexus_auth::authz::AnyActor; - use nexus_auth::authz::Authz; - use nexus_auth::authz::Context; - use nexus_auth::authz::RoleSet; - use nexus_test_utils::db::test_setup_database; - use omicron_test_utils::dev; - use std::sync::Arc; - - fn authz_context_for_actor( - log: &slog::Logger, - authn: authn::Context, - datastore: Arc, - ) -> Context { - let authz = Authz::new(log); - Context::new(Arc::new(authn), Arc::new(authz), datastore) - } - - #[tokio::test] - async fn test_unregistered_resource() { - let logctx = dev::test_setup_log("test_unregistered_resource"); - let mut db = test_setup_database(&logctx.log).await; - let (opctx, datastore) = - crate::db::datastore::test_utils::datastore_test(&logctx, &db) - .await; - - // Define a resource that we "forget" to register with Oso. - use crate::context::OpContext; - use nexus_auth::authz::AuthorizedResource; - use omicron_common::api::external::Error; - use oso::PolarClass; - #[derive(Clone, PolarClass)] - struct UnregisteredResource; - impl AuthorizedResource for UnregisteredResource { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - _: &'b OpContext, - _: &'c authn::Context, - _: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { - // authorize() shouldn't get far enough to call this. - unimplemented!(); - } - - fn on_unauthorized( - &self, - _: &Authz, - _: Error, - _: AnyActor, - _: Action, - ) -> Error { - // authorize() shouldn't get far enough to call this. - unimplemented!(); - } - - fn polar_class(&self) -> oso::Class { - Self::get_polar_class() - } - } - - // Make sure an authz check with this resource fails with a clear - // message. - let unregistered_resource = UnregisteredResource {}; - let authz_privileged = authz_context_for_actor( - &logctx.log, - authn::Context::privileged_test_user(), - Arc::clone(&datastore), - ); - let error = authz_privileged - .authorize(&opctx, Action::Read, unregistered_resource) - .await; - println!("{:?}", error); - assert!(matches!(error, Err(Error::InternalError { - internal_message - }) if internal_message == "attempted authz check \ - on unregistered resource: \"UnregisteredResource\"")); - - db.cleanup().await.unwrap(); - logctx.cleanup_successful(); - } -} From 99671565b951c246d9629d06963583fa67c37f0a Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 16:54:27 -0700 Subject: [PATCH 8/9] oso is only a dev-dependency of nexus/db-queries now --- nexus/auth/src/authz/context.rs | 5 ++--- nexus/db-queries/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nexus/auth/src/authz/context.rs b/nexus/auth/src/authz/context.rs index 59b7080fca..2ca7a23218 100644 --- a/nexus/auth/src/authz/context.rs +++ b/nexus/auth/src/authz/context.rs @@ -201,13 +201,12 @@ mod test { use crate::authz::Context; use crate::authz::RoleSet; use crate::context::OpContext; - use omicron_test_utils::dev; - use std::sync::Arc; - use nexus_db_model::IdentityType; use nexus_db_model::RoleAssignment; use omicron_common::api::external::Error; use omicron_common::api::external::ResourceType; + use omicron_test_utils::dev; + use std::sync::Arc; use uuid::Uuid; struct FakeStorage {} diff --git a/nexus/db-queries/Cargo.toml b/nexus/db-queries/Cargo.toml index 5fcb98dd9e..cb7061f4ce 100644 --- a/nexus/db-queries/Cargo.toml +++ b/nexus/db-queries/Cargo.toml @@ -25,7 +25,6 @@ futures.workspace = true ipnetwork.workspace = true macaddr.workspace = true once_cell.workspace = true -oso.workspace = true oxnet.workspace = true paste.workspace = true # See omicron-rpaths for more about the "pq-sys" dependency. @@ -83,6 +82,7 @@ nexus-test-utils.workspace = true omicron-sled-agent.workspace = true omicron-test-utils.workspace = true openapiv3.workspace = true +oso.workspace = true pem.workspace = true petgraph.workspace = true predicates.workspace = true From deb573e4bc624a0c9aa3644f405610e4797368cc Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 31 May 2024 17:16:11 -0700 Subject: [PATCH 9/9] review feedback --- nexus/auth/src/authz/api_resources.rs | 180 +++++++++----------------- nexus/auth/src/authz/context.rs | 36 ++---- nexus/auth/src/authz/oso_generic.rs | 18 +-- nexus/auth/src/context.rs | 2 +- nexus/db-fixed-data/Cargo.toml | 1 + 5 files changed, 81 insertions(+), 156 deletions(-) diff --git a/nexus/auth/src/authz/api_resources.rs b/nexus/auth/src/authz/api_resources.rs index e2cbddc034..98a24b68b5 100644 --- a/nexus/auth/src/authz/api_resources.rs +++ b/nexus/auth/src/authz/api_resources.rs @@ -109,18 +109,12 @@ impl AuthorizedResource for T where T: ApiResource + oso::PolarClass + Clone, { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> BoxFuture<'fut, Result<(), Error>> { load_roles_for_resource_tree(self, opctx, authn, roleset).boxed() } @@ -261,18 +255,12 @@ impl oso::PolarClass for BlueprintConfig { } impl AuthorizedResource for BlueprintConfig { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // There are no roles on the BlueprintConfig, only permissions. But we // still need to load the Fleet-related roles to verify that the actor // has the "admin" role on the Fleet (possibly conferred from a Silo @@ -318,18 +306,12 @@ impl oso::PolarClass for ConsoleSessionList { } impl AuthorizedResource for ConsoleSessionList { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } @@ -371,18 +353,12 @@ impl oso::PolarClass for DnsConfig { } impl AuthorizedResource for DnsConfig { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } @@ -424,18 +400,12 @@ impl oso::PolarClass for IpPoolList { } impl AuthorizedResource for IpPoolList { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // There are no roles on the IpPoolList, only permissions. But we still // need to load the Fleet-related roles to verify that the actor has the // "admin" role on the Fleet (possibly conferred from a Silo role). @@ -472,18 +442,12 @@ impl oso::PolarClass for DeviceAuthRequestList { } impl AuthorizedResource for DeviceAuthRequestList { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // There are no roles on the DeviceAuthRequestList, only permissions. But we // still need to load the Fleet-related roles to verify that the actor has the // "admin" role on the Fleet. @@ -527,18 +491,12 @@ impl oso::PolarClass for Inventory { } impl AuthorizedResource for Inventory { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed() } @@ -583,18 +541,12 @@ impl oso::PolarClass for SiloCertificateList { } impl AuthorizedResource for SiloCertificateList { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // There are no roles on this resource, but we still need to load the // Silo-related roles. self.silo().load_roles(opctx, authn, roleset) @@ -641,18 +593,12 @@ impl oso::PolarClass for SiloIdentityProviderList { } impl AuthorizedResource for SiloIdentityProviderList { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // There are no roles on this resource, but we still need to load the // Silo-related roles. self.silo().load_roles(opctx, authn, roleset) @@ -696,18 +642,12 @@ impl oso::PolarClass for SiloUserList { } impl AuthorizedResource for SiloUserList { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // There are no roles on this resource, but we still need to load the // Silo-related roles. self.silo().load_roles(opctx, authn, roleset) diff --git a/nexus/auth/src/authz/context.rs b/nexus/auth/src/authz/context.rs index 2ca7a23218..bd375321e3 100644 --- a/nexus/auth/src/authz/context.rs +++ b/nexus/auth/src/authz/context.rs @@ -164,17 +164,12 @@ pub trait AuthorizedResource: oso::ToPolar + Send + Sync + 'static { /// That's how this works for most resources. There are other kinds of /// resources (like the Database itself) that aren't stored in the database /// and for which a different mechanism might be used. - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - opctx: &'b OpContext, - authn: &'c authn::Context, - roleset: &'d mut RoleSet, - ) -> BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e; + fn load_roles<'fut>( + &'fut self, + opctx: &'fut OpContext, + authn: &'fut authn::Context, + roleset: &'fut mut RoleSet, + ) -> BoxFuture<'fut, Result<(), Error>>; /// Invoked on authz failure to determine the final authz result /// @@ -227,7 +222,7 @@ mod test { _resource_type: ResourceType, _resource_id: Uuid, ) -> Result, Error> { - todo!(); + unimplemented!("This test is not expected to access the database"); } } @@ -257,17 +252,12 @@ mod test { #[derive(Clone, PolarClass)] struct UnregisteredResource; impl AuthorizedResource for UnregisteredResource { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - _: &'b OpContext, - _: &'c authn::Context, - _: &'d mut RoleSet, - ) -> futures::future::BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, + fn load_roles<'fut>( + &'fut self, + _: &'fut OpContext, + _: &'fut authn::Context, + _: &'fut mut RoleSet, + ) -> futures::future::BoxFuture<'fut, Result<(), Error>> { // authorize() shouldn't get far enough to call this. unimplemented!(); diff --git a/nexus/auth/src/authz/oso_generic.rs b/nexus/auth/src/authz/oso_generic.rs index 7ad48c1dec..383a06e985 100644 --- a/nexus/auth/src/authz/oso_generic.rs +++ b/nexus/auth/src/authz/oso_generic.rs @@ -266,18 +266,12 @@ impl oso::PolarClass for Database { } impl AuthorizedResource for Database { - fn load_roles<'a, 'b, 'c, 'd, 'e>( - &'a self, - _: &'b OpContext, - _: &'c authn::Context, - _: &'d mut RoleSet, - ) -> BoxFuture<'e, Result<(), Error>> - where - 'a: 'e, - 'b: 'e, - 'c: 'e, - 'd: 'e, - { + fn load_roles<'fut>( + &'fut self, + _: &'fut OpContext, + _: &'fut authn::Context, + _: &'fut mut RoleSet, + ) -> BoxFuture<'fut, Result<(), Error>> { // We don't use (database) roles to grant access to the database. The // role assignment is hardcoded for all authenticated users. See the // "has_role" Polar method above. diff --git a/nexus/auth/src/context.rs b/nexus/auth/src/context.rs index 01516359c3..0aac0900c5 100644 --- a/nexus/auth/src/context.rs +++ b/nexus/auth/src/context.rs @@ -380,7 +380,7 @@ mod test { _resource_type: ResourceType, _resource_id: Uuid, ) -> Result, Error> { - todo!(); + unimplemented!("This test is not expected to access the database"); } } diff --git a/nexus/db-fixed-data/Cargo.toml b/nexus/db-fixed-data/Cargo.toml index d24bf25c7d..486df15686 100644 --- a/nexus/db-fixed-data/Cargo.toml +++ b/nexus/db-fixed-data/Cargo.toml @@ -3,6 +3,7 @@ name = "nexus-db-fixed-data" version = "0.1.0" edition = "2021" license = "MPL-2.0" +description = "Hard-coded database data, including defaults and built-ins" [lints] workspace = true