refactor(oidc): prep. support of Open Cloud Mesh
add federation kind (OCM, OIDC, MagicLink)
rename oidc_provider into federation_issuer
rename oidc_subject into federation_subject
This commit is contained in:
@@ -110,8 +110,9 @@ mod tests {
|
||||
"alice@example.com".to_string(),
|
||||
Some("alice".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
UserRole::User,
|
||||
0,
|
||||
false,
|
||||
@@ -152,8 +153,9 @@ mod tests {
|
||||
"bob@example.com".to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
UserRole::User,
|
||||
0,
|
||||
false,
|
||||
|
||||
@@ -131,7 +131,7 @@ pub struct AdminUserSummaryDto {
|
||||
pub is_external: bool,
|
||||
/// TRUE when the user has a server-verifiable password on file
|
||||
/// (`password_hash IS NOT NULL`). The admin table uses this
|
||||
/// alongside `oidc_provider` and `opaque_registered` to render
|
||||
/// alongside `federation_issuer` and `opaque_registered` to render
|
||||
/// the user's full capability set: a `password` chip lights up
|
||||
/// here, an OIDC provider name renders the SSO badge, an
|
||||
/// envelope-on-file flips the OPAQUE chip. A user with none of
|
||||
@@ -171,7 +171,7 @@ impl From<UserListEntry> for AdminUserSummaryDto {
|
||||
storage_used_bytes: entry.storage_used_bytes,
|
||||
last_login_at: entry.last_login_at,
|
||||
active: entry.active,
|
||||
auth_provider: entry.oidc_provider.unwrap_or_else(|| "local".to_string()),
|
||||
auth_provider: entry.federation_issuer.unwrap_or_else(|| "local".to_string()),
|
||||
is_external: entry.is_external,
|
||||
has_password: entry.has_password,
|
||||
opaque_registered: entry.opaque_registered,
|
||||
@@ -208,7 +208,7 @@ impl From<User> for UserDto {
|
||||
last_login_at: p.last_login_at,
|
||||
active: p.active,
|
||||
// Some(provider) moves the String; None still allocates "local".
|
||||
auth_provider: p.oidc_provider.unwrap_or_else(|| "local".to_string()),
|
||||
auth_provider: p.federation_issuer.unwrap_or_else(|| "local".to_string()),
|
||||
image: p.image,
|
||||
can_edit_image,
|
||||
is_external: p.is_external,
|
||||
|
||||
@@ -194,10 +194,14 @@ pub trait UserStoragePort: Send + Sync + 'static {
|
||||
/// Changes a user's password
|
||||
async fn change_password(&self, user_id: Uuid, password_hash: &str) -> Result<(), DomainError>;
|
||||
|
||||
/// Finds a user by OIDC provider + subject pair
|
||||
async fn get_user_by_oidc_subject(
|
||||
/// Finds a user by federation (issuer, subject) pair. Historically
|
||||
/// called for OIDC lookups (the only federation kind in-tree at rename
|
||||
/// time); after Phase B/C of the federation-identity rename the
|
||||
/// caller passes the true `iss` URL rather than a display label. See
|
||||
/// `docs/plan/ocm.md § Schema rename` for the transition.
|
||||
async fn get_user_by_federation_subject(
|
||||
&self,
|
||||
provider: &str,
|
||||
issuer: &str,
|
||||
subject: &str,
|
||||
) -> Result<User, DomainError>;
|
||||
|
||||
@@ -383,12 +387,12 @@ pub trait SessionStoragePort: Send + Sync + 'static {
|
||||
|
||||
/// OIDC Back-Channel Logout fallback when the IdP didn't supply a `sid`:
|
||||
/// revoke every session belonging to the user identified by
|
||||
/// `(oidc_provider, oidc_subject)`. Returns the affected user id, or
|
||||
/// `None` if we don't know that user.
|
||||
async fn revoke_user_sessions_by_oidc_subject(
|
||||
/// `(federation_issuer, federation_subject)`. Returns the affected
|
||||
/// user id, or `None` if we don't know that user.
|
||||
async fn revoke_user_sessions_by_federation_subject(
|
||||
&self,
|
||||
oidc_provider: &str,
|
||||
oidc_subject: &str,
|
||||
issuer: &str,
|
||||
subject: &str,
|
||||
) -> Result<Option<Uuid>, DomainError>;
|
||||
}
|
||||
|
||||
|
||||
@@ -560,8 +560,9 @@ impl AuthApplicationService {
|
||||
dto.email.clone(),
|
||||
dto.username.clone(),
|
||||
password_hash,
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind: local password registration
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
role,
|
||||
quota,
|
||||
false,
|
||||
@@ -662,8 +663,9 @@ impl AuthApplicationService {
|
||||
email,
|
||||
Some(username.clone()),
|
||||
Some(password_hash),
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind: setup admin is local
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
role,
|
||||
quota,
|
||||
false,
|
||||
@@ -1523,7 +1525,7 @@ impl AuthApplicationService {
|
||||
.await?
|
||||
} else if let Some(sub) = claims.sub.as_ref() {
|
||||
self.session_storage
|
||||
.revoke_user_sessions_by_oidc_subject(&provider_name, sub)
|
||||
.revoke_user_sessions_by_federation_subject(&provider_name, sub)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect()
|
||||
@@ -2810,8 +2812,9 @@ impl AuthApplicationService {
|
||||
email,
|
||||
Some(dto.username.clone()),
|
||||
Some(password_hash),
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind: admin-created external, no federation link yet
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
UserRole::User,
|
||||
0,
|
||||
true,
|
||||
@@ -2821,8 +2824,9 @@ impl AuthApplicationService {
|
||||
email,
|
||||
Some(dto.username.clone()),
|
||||
Some(password_hash),
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind: admin-created local user
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
role,
|
||||
quota,
|
||||
false,
|
||||
@@ -3395,7 +3399,7 @@ impl AuthApplicationService {
|
||||
// 5. Look up existing user by OIDC subject
|
||||
let user = match self
|
||||
.user_storage
|
||||
.get_user_by_oidc_subject(&provider_name, &claims.sub)
|
||||
.get_user_by_federation_subject(&provider_name, &claims.sub)
|
||||
.await
|
||||
{
|
||||
Ok(mut existing_user) => {
|
||||
@@ -3511,6 +3515,14 @@ impl AuthApplicationService {
|
||||
oidc_email,
|
||||
Some(username.clone()),
|
||||
None,
|
||||
Some(crate::domain::entities::user::FederationKind::Oidc),
|
||||
// TODO Phase B: `provider_name` still carries the
|
||||
// OXICLOUD_OIDC_PROVIDER_NAME display label instead
|
||||
// of the true `iss` URL. Lazy-rebind on subsequent
|
||||
// logins converts the row (see docs/plan/ocm.md
|
||||
// § Rename PR — Phase B). First-login value is the
|
||||
// label for backwards compatibility with existing
|
||||
// rows.
|
||||
Some(provider_name.clone()),
|
||||
Some(claims.sub.clone()),
|
||||
role,
|
||||
|
||||
@@ -225,12 +225,18 @@ impl MagicLinkInviteService {
|
||||
|
||||
// External users are created without a username or password.
|
||||
// `password_hash IS NULL` is the canonical no-password marker.
|
||||
//
|
||||
// federation_kind stays None in Phase A of the federation-identity
|
||||
// rename — magic-link externals get their `federation_kind` stamp
|
||||
// in a future PR when the invite handler is refactored to opt into
|
||||
// the composable federation model. Behaviour is unchanged today.
|
||||
let mut user = User::new(
|
||||
normalised_email.to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None, // federation_kind
|
||||
None, // federation_issuer
|
||||
None, // federation_subject
|
||||
UserRole::User,
|
||||
0,
|
||||
true,
|
||||
@@ -1006,15 +1012,21 @@ mod tests {
|
||||
use crate::domain::entities::user::{User, UserRole};
|
||||
|
||||
fn user(password: Option<&str>, oidc: Option<(&str, &str)>) -> User {
|
||||
let (provider, subject) = match oidc {
|
||||
Some((p, s)) => (Some(p.to_string()), Some(s.to_string())),
|
||||
None => (None, None),
|
||||
use crate::domain::entities::user::FederationKind;
|
||||
let (kind, issuer, subject) = match oidc {
|
||||
Some((p, s)) => (
|
||||
Some(FederationKind::Oidc),
|
||||
Some(p.to_string()),
|
||||
Some(s.to_string()),
|
||||
),
|
||||
None => (None, None, None),
|
||||
};
|
||||
User::new(
|
||||
"test@example.com".to_string(),
|
||||
None,
|
||||
password.map(str::to_string),
|
||||
provider,
|
||||
kind,
|
||||
issuer,
|
||||
subject,
|
||||
UserRole::User,
|
||||
0,
|
||||
|
||||
Reference in New Issue
Block a user