Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dev-tools/omdb/src/bin/omdb/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ use nexus_db_model::VolumeResourceUsage;
use nexus_db_model::VpcSubnet;
use nexus_db_model::Zpool;
use nexus_db_model::to_db_typed_uuid;
use nexus_db_queries::authz;
use nexus_db_queries::context::OpContext;
use nexus_db_queries::db;
use nexus_db_queries::db::DataStore;
Expand Down Expand Up @@ -8165,8 +8166,9 @@ async fn cmd_db_trust_quorum_list_configs(
}

let limit = fetch_opts.fetch_limit;
let authz_tq = authz::TrustQuorumConfig::for_rack_id(args.rack_id);
let configs = datastore
.tq_list_config(opctx, args.rack_id, &first_page::<i64>(limit))
.tq_list_config(opctx, authz_tq, &first_page::<i64>(limit))
.await
.context("listing trust quorum configurations")?;

Expand Down
67 changes: 67 additions & 0 deletions nexus/auth/src/authz/api_resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use futures::future::BoxFuture;
use nexus_db_fixed_data::FLEET_ID;
use nexus_types::external_api::policy::{FleetRole, ProjectRole, SiloRole};
use omicron_common::api::external::{Error, LookupType, ResourceType};
use omicron_uuid_kinds::{GenericUuid, RackUuid};
use oso::PolarClass;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
Expand Down Expand Up @@ -681,6 +682,72 @@ impl AuthorizedResource for Inventory {
}
}

/// Synthetic resource to model accessing trust quorum configurations for a
/// given rack
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TrustQuorumConfig(Rack);

impl TrustQuorumConfig {
pub fn for_rack_id(rack_id: RackUuid) -> TrustQuorumConfig {
Self::new(Rack::new(
FLEET,
rack_id.into_untyped_uuid(),
LookupType::ById(rack_id.into_untyped_uuid()),
))
}

pub fn new(rack: Rack) -> TrustQuorumConfig {
TrustQuorumConfig(rack)
}

pub fn rack(&self) -> &Rack {
&self.0
}
}

impl oso::PolarClass for TrustQuorumConfig {
fn get_polar_class_builder() -> oso::ClassBuilder<Self> {
oso::Class::builder()
.with_equality_check()
.add_attribute_getter("rack", |config: &TrustQuorumConfig| {
config.0.clone()
})
}
}

impl AuthorizedResource for TrustQuorumConfig {
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 walk the
// tree to get to the `fleet`.
self.rack().load_roles(opctx, authn, roleset)
}

// We want the trust quorum config to have the same visibility as the rack
// it is a part of.
//
// In a multirack world, we'll probably end up providing roles for racks.
// For now though, we just ensure that unauthorized users cannot know that a
// rack id exists, in the same manner as is done for an [`ApiResource`].
fn on_unauthorized(
&self,
authz: &Authz,
error: Error,
actor: AnyActor,
action: Action,
) -> Error {
self.rack().on_unauthorized(authz, error, actor, action)
}

fn polar_class(&self) -> oso::Class {
Self::get_polar_class()
}
}

/// Synthetic resource describing the list of Certificates associated with a
/// Silo
#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down
11 changes: 11 additions & 0 deletions nexus/auth/src/authz/omicron.polar
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,17 @@ resource DeviceAuthRequestList {
has_relation(fleet: Fleet, "parent_fleet", collection: DeviceAuthRequestList)
if collection.fleet = fleet;

# Describes the policy for creating and managing trust quorum configurations
# This may change in a multirack future to a per rack parent
resource TrustQuorumConfig {
permissions = [ "read", "modify" ];
relations = { parent_fleet: Fleet };
"read" if "viewer" on "parent_fleet";
"modify" if "admin" on "parent_fleet";
}
has_relation(fleet: Fleet, "parent_fleet", config: TrustQuorumConfig)
if config.rack.fleet = fleet;

# Describes the policy for creating and managing Silo certificates
resource SiloCertificateList {
permissions = [ "list_children", "create_child" ];
Expand Down
1 change: 1 addition & 0 deletions nexus/auth/src/authz/oso_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result<OsoInit, anyhow::Error> {
AlertClassList::get_polar_class(),
ScimClientBearerTokenList::get_polar_class(),
MulticastGroupList::get_polar_class(),
TrustQuorumConfig::get_polar_class(),
];
for c in classes {
oso_builder = oso_builder.register_class(c)?;
Expand Down
5 changes: 4 additions & 1 deletion nexus/db-queries/src/db/datastore/rack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,10 +980,13 @@ impl DataStore {

// Insert the initial trust quorum configuration
if let Some(tq_config) = rack_init.initial_trust_quorum_configuration {
let authz_tq = authz::TrustQuorumConfig::for_rack_id(
RackUuid::from_untyped_uuid(rack_id),
);
Self::tq_insert_rss_config_after_handoff(
opctx,
&conn,
RackUuid::from_untyped_uuid(rack_id),
authz_tq,
tq_config.members,
tq_config.coordinator
).await.map_err(|e| {
Expand Down
Loading
Loading