Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions nexus/auth/src/authz/api_resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1429,3 +1429,11 @@ authz_resource! {
roles_allowed = false,
polar_snippet = Custom,
}

authz_resource! {
name = "ScimClientBearerToken",
parent = "Silo",
primary_key = Uuid,
roles_allowed = false,
polar_snippet = Custom,
}
38 changes: 37 additions & 1 deletion nexus/auth/src/authz/omicron.polar
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ resource Fleet {
"viewer",

# Internal-only roles
"external-authenticator"
"external-authenticator",
"external-scim"
];

# Roles implied by other roles on this resource
Expand Down Expand Up @@ -149,6 +150,9 @@ resource Silo {
# external authenticator has to create silo users
"list_children" if "external-authenticator" on "parent_fleet";
"create_child" if "external-authenticator" on "parent_fleet";

# external scim has to be able to read SCIM tokens
"list_children" if "external-scim" on "parent_fleet";
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this needed?

Copy link
Contributor

@david-crespo david-crespo Oct 16, 2025

Choose a reason for hiding this comment

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

Yeah, I can't find a spot where the external-scim opctx needs to do this. Is it intended for the unimplemented list users operation etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, removed in 76b452e

}

has_relation(fleet: Fleet, "parent_fleet", silo: Silo)
Expand Down Expand Up @@ -703,3 +707,35 @@ resource AlertClassList {

has_relation(fleet: Fleet, "parent_fleet", collection: AlertClassList)
if collection.fleet = fleet;

# These rules grant the external scim authenticator role the permission
# required to create the SCIM provider implementation for a Silo

has_permission(actor: AuthenticatedActor, "read", silo: Silo)
if has_role(actor, "external-scim", silo.fleet);

resource ScimClientBearerToken {
permissions = [
"read",
"modify",
"create_child",
"list_children",
];
relations = { parent_silo: Silo, parent_fleet: Fleet };

# Silo-level roles grant privileges for SCIM client tokens.
"read" if "admin" on "parent_silo";
"list_children" if "admin" on "parent_silo";
"modify" if "admin" on "parent_silo";
"create_child" if "admin" on "parent_silo";

# Fleet-level roles also grant privileges for SCIM client tokens.
"read" if "admin" on "parent_fleet";
"list_children" if "admin" on "parent_fleet";
"modify" if "admin" on "parent_fleet";
"create_child" if "admin" on "parent_fleet";
}
has_relation(silo: Silo, "parent_silo", scim_client_bearer_token: ScimClientBearerToken)
if scim_client_bearer_token.silo = silo;
has_relation(fleet: Fleet, "parent_fleet", collection: ScimClientBearerToken)
if collection.silo.fleet = fleet;
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 @@ -175,6 +175,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result<OsoInit, anyhow::Error> {
Zpool::init(),
Service::init(),
UserBuiltin::init(),
ScimClientBearerToken::init(),
];

for init in generated_inits {
Expand Down
21 changes: 21 additions & 0 deletions nexus/db-lookup/src/lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,18 @@ impl<'a> LookupPath<'a> {
{
Alert::PrimaryKey(Root { lookup_root: self }, id)
}

/// Select a resource of type [`ScimClientBearerToken`], identified by its
/// UUID.
pub fn scim_client_bearer_token_id<'b>(
self,
id: Uuid,
) -> ScimClientBearerToken<'b>
where
'a: 'b,
{
ScimClientBearerToken::PrimaryKey(Root { lookup_root: self }, id)
}
}

/// Represents the head of the selection path for a resource
Expand Down Expand Up @@ -909,6 +921,15 @@ lookup_resource! {
]
}

lookup_resource! {
name = "ScimClientBearerToken",
ancestors = ["Silo"],
lookup_by_name = false,
soft_deletes = true,
primary_key_columns = [ { column_name = "id", rust_type = Uuid } ],
visible_outside_silo = true
}

// Helpers for unifying the interfaces around images

pub enum ImageLookup<'a> {
Expand Down
2 changes: 2 additions & 0 deletions nexus/db-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ mod producer_endpoint;
mod project;
mod reconfigurator_config;
mod rendezvous_debug_dataset;
mod scim_client_bearer_token;
mod semver_version;
mod serde_time_delta;
mod silo_auth_settings;
Expand Down Expand Up @@ -223,6 +224,7 @@ pub use rendezvous_debug_dataset::*;
pub use role_assignment::*;
pub use saga_types::*;
pub use schema_versions::*;
pub use scim_client_bearer_token::*;
pub use semver_version::*;
pub use service_kind::*;
pub use silo::*;
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: Version = Version::new(199, 0, 0);
pub const SCHEMA_VERSION: Version = Version::new(200, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -28,6 +28,7 @@ static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(200, "scim-client-bearer-token"),
KnownVersion::new(199, "multicast-pool-support"),
KnownVersion::new(198, "add-ip-pool-reservation-type-column"),
KnownVersion::new(197, "scim-users-and-groups"),
Expand Down
53 changes: 53 additions & 0 deletions nexus/db-model/src/scim_client_bearer_token.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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/.

use chrono::DateTime;
use chrono::Utc;
use nexus_db_schema::schema::scim_client_bearer_token;
use nexus_types::external_api::views;
use uuid::Uuid;

/// A SCIM client sends requests to a SCIM provider (in this case, Nexus) using
/// some sort of authentication. Nexus currently only supports Bearer token auth
/// from SCIM clients, and these tokens are stored here.
#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
#[diesel(table_name = scim_client_bearer_token)]
pub struct ScimClientBearerToken {
pub id: Uuid,

pub time_created: DateTime<Utc>,
pub time_deleted: Option<DateTime<Utc>>,
pub time_expires: Option<DateTime<Utc>>,

pub silo_id: Uuid,

pub bearer_token: String,
}

impl ScimClientBearerToken {
pub fn id(&self) -> Uuid {
self.id
}
}

impl From<ScimClientBearerToken> for views::ScimClientBearerToken {
fn from(t: ScimClientBearerToken) -> views::ScimClientBearerToken {
views::ScimClientBearerToken {
id: t.id,
time_created: t.time_created,
time_expires: t.time_expires,
}
}
}

impl From<ScimClientBearerToken> for views::ScimClientBearerTokenValue {
fn from(t: ScimClientBearerToken) -> views::ScimClientBearerTokenValue {
views::ScimClientBearerTokenValue {
id: t.id,
time_created: t.time_created,
time_expires: t.time_expires,
bearer_token: t.bearer_token,
}
}
}
2 changes: 2 additions & 0 deletions nexus/db-queries/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ diesel.workspace = true
diesel-dtrace.workspace = true
dropshot.workspace = true
futures.workspace = true
hex.workspace = true
id-map.workspace = true
iddqd.workspace = true
internal-dns-resolver.workspace = true
Expand All @@ -39,6 +40,7 @@ rand.workspace = true
ref-cast.workspace = true
regex.workspace = true
schemars.workspace = true
scim2-rs.workspace = true
semver.workspace = true
serde.workspace = true
serde_json.workspace = true
Expand Down
3 changes: 3 additions & 0 deletions nexus/db-queries/src/db/datastore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub mod region_snapshot_replacement;
mod rendezvous_debug_dataset;
mod role;
mod saga;
mod scim;
mod scim_provider_store;
mod silo;
mod silo_auth_settings;
mod silo_group;
Expand Down Expand Up @@ -143,6 +145,7 @@ pub use region::RegionAllocationFor;
pub use region::RegionAllocationParameters;
pub use region_snapshot_replacement::NewRegionVolumeId;
pub use region_snapshot_replacement::OldSnapshotVolumeId;
pub use scim_provider_store::CrdbScimProviderStore;
pub use silo::Discoverability;
pub use silo_group::SiloGroup;
pub use silo_group::SiloGroupApiOnly;
Expand Down
Loading
Loading