refactor(oidc): migrate provider into issuer
this make OIDC compliant with the invariant binding (issuer and subject) admin can now rename their provider without breaking clarifing federation_kind: report the kind of federation wired not the allowed login method hybryd login method are still allowed
This commit is contained in:
@@ -783,6 +783,7 @@ impl UserRepository for UserPgRepository {
|
||||
Option<chrono::DateTime<chrono::Utc>>,
|
||||
bool,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
bool,
|
||||
bool,
|
||||
bool,
|
||||
@@ -797,13 +798,15 @@ impl UserRepository for UserPgRepository {
|
||||
// pays. `has_password` on the password_hash column tells
|
||||
// the admin table whether a server-verifiable password is
|
||||
// on file; combined with the two OPAQUE flags and
|
||||
// federation_issuer, the SPA derives the full "capability
|
||||
// set" per user (password / OPAQUE / SSO / passwordless).
|
||||
// federation_kind / federation_issuer, the SPA derives the
|
||||
// full "capability set" per user (password / OPAQUE / SSO /
|
||||
// passwordless).
|
||||
r#"
|
||||
SELECT
|
||||
id, username, email, role::text,
|
||||
storage_quota_bytes, storage_used_bytes,
|
||||
last_login_at, active, federation_issuer, is_external,
|
||||
last_login_at, active,
|
||||
federation_kind, federation_issuer, is_external,
|
||||
(password_hash IS NOT NULL) AS has_password,
|
||||
(opaque_envelope IS NOT NULL) AS opaque_registered,
|
||||
(opaque_migrated_at IS NOT NULL) AS opaque_migrated
|
||||
@@ -832,6 +835,7 @@ impl UserRepository for UserPgRepository {
|
||||
storage_used_bytes,
|
||||
last_login_at,
|
||||
active,
|
||||
federation_kind,
|
||||
federation_issuer,
|
||||
is_external,
|
||||
has_password,
|
||||
@@ -850,6 +854,7 @@ impl UserRepository for UserPgRepository {
|
||||
storage_used_bytes,
|
||||
last_login_at,
|
||||
active,
|
||||
federation_kind,
|
||||
federation_issuer,
|
||||
is_external,
|
||||
has_password,
|
||||
@@ -1369,6 +1374,34 @@ impl UserStoragePort for UserPgRepository {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rebind_federation_issuer(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
new_issuer: &str,
|
||||
) -> Result<(), DomainError> {
|
||||
// Same `IS DISTINCT FROM` guard as sync_oidc_login_profile: this
|
||||
// fires on every OIDC login, so the common already-migrated case
|
||||
// must be a zero-write no-op. Only actually flips the column
|
||||
// when the stored value is stale (legacy display label vs the
|
||||
// real issuer URL from the id_token's `iss` claim).
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE auth.users
|
||||
SET federation_issuer = $2,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
AND federation_issuer IS DISTINCT FROM $2
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(new_issuer)
|
||||
.execute(&*self.pool)
|
||||
.await
|
||||
.map_err(Self::map_sqlx_error)
|
||||
.map_err(DomainError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_users_by_role(&self, role: &str) -> Result<Vec<User>, DomainError> {
|
||||
UserRepository::list_users_by_role(self, role)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user