Skip to content

Commit 5789479

Browse files
authored
[SCIM 1/4]: Add endpoint stubs (#9070)
First, add endpoint stubs so that console and cli work can start. Use the recently opened scim2-rs repo's structs to create all necessary scim_v2_* endpoints, and also add endpoints for managing the SCIM client bearer tokens (used to authenticate for those SCIM v2 endpoints). The audit logging for the SCIM client bearer token CRUD will use the opctx of the silo admin, but the SCIM v2 endpoints do not have an opctx because they will never authenticate to an Actor: instead, create a new "external-scim" user that will be used for the audit log, and will have (in the 3/4 PR) the necessary permissions to authenticate SCIM clients.
1 parent 51aa5ef commit 5789479

File tree

19 files changed

+1361
-5
lines changed

19 files changed

+1361
-5
lines changed

Cargo.lock

Lines changed: 23 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,8 @@ newtype-uuid = { version = "1.3.1", default-features = false }
795795
newtype-uuid-macros = "0.1.0"
796796
omicron-uuid-kinds = { path = "uuid-kinds", features = ["serde", "schemars08", "uuid-v4"] }
797797

798+
scim2-rs = { git = "https://github.com/oxidecomputer/scim2-rs" }
799+
798800
# NOTE: The test profile inherits from the dev profile, so settings under
799801
# profile.dev get inherited. AVOID setting anything under profile.test: that
800802
# will cause dev and test builds to diverge, which will cause more Cargo build

common/src/api/external/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,7 @@ pub enum ResourceType {
969969
RouterRoute,
970970
SagaDbg,
971971
SamlIdentityProvider,
972+
ScimClientBearerToken,
972973
Service,
973974
ServiceNetworkInterface,
974975
Silo,

nexus/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ oximeter-producer.workspace = true
138138
raw-cpuid = { workspace = true, features = ["std"] }
139139
rustls = { workspace = true }
140140
rustls-pemfile = { workspace = true }
141+
scim2-rs.workspace = true
141142
update-common.workspace = true
142143
update-engine.workspace = true
143144
omicron-workspace-hack.workspace = true

nexus/auth/src/authn/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub use nexus_db_fixed_data::silo_user::USER_TEST_PRIVILEGED;
3232
pub use nexus_db_fixed_data::silo_user::USER_TEST_UNPRIVILEGED;
3333
pub use nexus_db_fixed_data::user_builtin::USER_DB_INIT;
3434
pub use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_AUTHN;
35+
pub use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_SCIM;
3536
pub use nexus_db_fixed_data::user_builtin::USER_INTERNAL_API;
3637
pub use nexus_db_fixed_data::user_builtin::USER_INTERNAL_READ;
3738
pub use nexus_db_fixed_data::user_builtin::USER_SAGA_RECOVERY;
@@ -199,6 +200,12 @@ impl Context {
199200
Context::context_for_builtin_user(USER_SERVICE_BALANCER.id)
200201
}
201202

203+
/// Returns an authenticated context for use for authenticating SCIM
204+
/// requests
205+
pub fn external_scim() -> Context {
206+
Context::context_for_builtin_user(USER_EXTERNAL_SCIM.id)
207+
}
208+
202209
fn context_for_builtin_user(user_builtin_id: BuiltInUserUuid) -> Context {
203210
Context {
204211
kind: Kind::Authenticated(
@@ -300,6 +307,7 @@ mod test {
300307
use super::USER_TEST_PRIVILEGED;
301308
use super::USER_TEST_UNPRIVILEGED;
302309
use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_AUTHN;
310+
use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_SCIM;
303311
use nexus_types::identity::Asset;
304312

305313
#[test]
@@ -342,6 +350,10 @@ mod test {
342350
let authn = Context::internal_api();
343351
let actor = authn.actor().unwrap();
344352
assert_eq!(actor.built_in_user_id(), Some(USER_INTERNAL_API.id));
353+
354+
let authn = Context::external_scim();
355+
let actor = authn.actor().unwrap();
356+
assert_eq!(actor.built_in_user_id(), Some(USER_EXTERNAL_SCIM.id));
345357
}
346358
}
347359

nexus/db-fixed-data/src/role_assignment.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,13 @@ pub static BUILTIN_ROLE_ASSIGNMENTS: LazyLock<Vec<RoleAssignment>> =
5353
*FLEET_ID,
5454
"external-authenticator",
5555
),
56+
// The "external-scim" user gets the "external-scim" role on the
57+
// sole fleet. This grants them the ability to read SCIM tokens.
58+
RoleAssignment::new_for_builtin_user(
59+
user_builtin::USER_EXTERNAL_SCIM.id,
60+
ResourceType::Fleet,
61+
*FLEET_ID,
62+
"external-scim",
63+
),
5664
]
5765
});

nexus/db-fixed-data/src/user_builtin.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,22 @@ pub static USER_EXTERNAL_AUTHN: LazyLock<UserBuiltinConfig> =
9494
)
9595
});
9696

97+
/// Internal user used by Nexus when authenticating SCIM requests
98+
pub static USER_EXTERNAL_SCIM: LazyLock<UserBuiltinConfig> =
99+
LazyLock::new(|| {
100+
UserBuiltinConfig::new_static(
101+
"001de000-05e4-4000-8000-000000000004",
102+
"external-scim",
103+
"used by Nexus when authenticating SCIM requests",
104+
)
105+
});
106+
97107
#[cfg(test)]
98108
mod test {
99109
use super::super::assert_valid_typed_uuid;
100110
use super::USER_DB_INIT;
101111
use super::USER_EXTERNAL_AUTHN;
112+
use super::USER_EXTERNAL_SCIM;
102113
use super::USER_INTERNAL_API;
103114
use super::USER_INTERNAL_READ;
104115
use super::USER_SAGA_RECOVERY;
@@ -112,5 +123,6 @@ mod test {
112123
assert_valid_typed_uuid(&USER_EXTERNAL_AUTHN.id);
113124
assert_valid_typed_uuid(&USER_INTERNAL_READ.id);
114125
assert_valid_typed_uuid(&USER_SAGA_RECOVERY.id);
126+
assert_valid_typed_uuid(&USER_EXTERNAL_SCIM.id);
115127
}
116128
}

nexus/db-queries/src/db/datastore/silo_user.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ impl DataStore {
725725
&authn::USER_INTERNAL_READ,
726726
&authn::USER_EXTERNAL_AUTHN,
727727
&authn::USER_SAGA_RECOVERY,
728+
&authn::USER_EXTERNAL_SCIM,
728729
]
729730
.iter()
730731
.map(|u| {
@@ -741,13 +742,15 @@ impl DataStore {
741742
.collect::<Vec<UserBuiltin>>();
742743

743744
debug!(opctx.log, "attempting to create built-in users");
745+
744746
let count = diesel::insert_into(dsl::user_builtin)
745747
.values(builtin_users)
746748
.on_conflict(dsl::id)
747749
.do_nothing()
748750
.execute_async(&*self.pool_connection_authorized(opctx).await?)
749751
.await
750752
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))?;
753+
751754
info!(opctx.log, "created {} built-in users", count);
752755

753756
Ok(())

nexus/external-api/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ openapiv3.workspace = true
2222
oximeter-types.workspace = true
2323
oxql-types.workspace = true
2424
omicron-uuid-kinds.workspace = true
25+
scim2-rs.workspace = true

nexus/external-api/output/nexus_tags.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ local_idp_user_delete DELETE /v1/system/identity-providers/
272272
local_idp_user_set_password POST /v1/system/identity-providers/local/users/{user_id}/set-password
273273
saml_identity_provider_create POST /v1/system/identity-providers/saml
274274
saml_identity_provider_view GET /v1/system/identity-providers/saml/{provider}
275+
scim_idp_create_token POST /v1/system/scim/tokens
276+
scim_idp_delete_all_tokens DELETE /v1/system/scim/tokens
277+
scim_idp_delete_token_by_id DELETE /v1/system/scim/tokens/{token_id}
278+
scim_idp_get_token_by_id GET /v1/system/scim/tokens/{token_id}
279+
scim_idp_get_tokens GET /v1/system/scim/tokens
275280
silo_create POST /v1/system/silos
276281
silo_delete DELETE /v1/system/silos/{silo}
277282
silo_identity_provider_list GET /v1/system/identity-providers

0 commit comments

Comments
 (0)