feat(opaque): show users migrated in admin panel
This commit is contained in:
@@ -115,6 +115,24 @@ pub struct AdminUserSummaryDto {
|
||||
pub active: bool,
|
||||
pub auth_provider: String,
|
||||
pub is_external: bool,
|
||||
/// Mirrors `UserListEntry::opaque_registered` — TRUE when the user
|
||||
/// has an OPAQUE envelope on file. Surfaced on the admin table so
|
||||
/// operators can see per-user rollout progress during the
|
||||
/// migration window. **Admin-only exposure**: this field is NOT
|
||||
/// on `UserDto` — putting it there would leak adoption status
|
||||
/// through every user-directory-adjacent endpoint (share targets,
|
||||
/// group members, invite listings). `#[serde(default)]` keeps
|
||||
/// older SPA builds tolerant of the added field.
|
||||
#[serde(default)]
|
||||
pub opaque_registered: bool,
|
||||
/// Mirrors `UserListEntry::opaque_migrated` — TRUE when the user
|
||||
/// has completed at least one successful OPAQUE login. Distinct
|
||||
/// from `opaque_registered`: an admin can invalidate the envelope
|
||||
/// (`clear_registration`) leaving the user registered=false but
|
||||
/// with a historical migrated=true; the SPA's admin table shows
|
||||
/// both so this operational nuance is visible.
|
||||
#[serde(default)]
|
||||
pub opaque_migrated: bool,
|
||||
}
|
||||
|
||||
impl From<UserListEntry> for AdminUserSummaryDto {
|
||||
@@ -130,6 +148,8 @@ impl From<UserListEntry> for AdminUserSummaryDto {
|
||||
active: entry.active,
|
||||
auth_provider: entry.oidc_provider.unwrap_or_else(|| "local".to_string()),
|
||||
is_external: entry.is_external,
|
||||
opaque_registered: entry.opaque_registered,
|
||||
opaque_migrated: entry.opaque_migrated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,18 @@ pub struct UserListEntry {
|
||||
pub active: bool,
|
||||
pub oidc_provider: Option<String>,
|
||||
pub is_external: bool,
|
||||
/// TRUE when `auth.users.opaque_envelope IS NOT NULL` — the user
|
||||
/// has completed OPAQUE registration (typically via the Phase 2
|
||||
/// silent-migration hook after a successful legacy login). Surfaced
|
||||
/// on the admin user table so operators can see rollout progress
|
||||
/// per-user. Admin-only exposure — see `AdminUserSummaryDto`.
|
||||
pub opaque_registered: bool,
|
||||
/// TRUE when `auth.users.opaque_migrated_at IS NOT NULL` — the
|
||||
/// user has completed at least one successful OPAQUE login. Distinct
|
||||
/// from `opaque_registered` because a user can have an envelope on
|
||||
/// file without having actually logged in via OPAQUE yet (e.g.
|
||||
/// admin cleared the envelope, silent-migration hasn't re-run).
|
||||
pub opaque_migrated: bool,
|
||||
}
|
||||
|
||||
// Conversion from UserRepositoryError to DomainError
|
||||
|
||||
@@ -767,17 +767,26 @@ impl UserRepository for UserPgRepository {
|
||||
bool,
|
||||
Option<String>,
|
||||
bool,
|
||||
bool,
|
||||
bool,
|
||||
),
|
||||
>(
|
||||
// OPAQUE columns are projected as booleans via `IS NOT NULL`
|
||||
// rather than as timestamps so the row-mapping tuple stays
|
||||
// small and the wire shape is exactly what the admin table
|
||||
// needs. Both are per-row scalar tests — no cost beyond the
|
||||
// full-table sequential scan the LIMIT/OFFSET already pays.
|
||||
r#"
|
||||
SELECT
|
||||
id, username, email, role::text,
|
||||
storage_quota_bytes, storage_used_bytes,
|
||||
last_login_at, active, oidc_provider, is_external
|
||||
FROM auth.users
|
||||
WHERE ($3 OR is_external = FALSE)
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
last_login_at, active, oidc_provider, is_external,
|
||||
(opaque_envelope IS NOT NULL) AS opaque_registered,
|
||||
(opaque_migrated_at IS NOT NULL) AS opaque_migrated
|
||||
FROM auth.users
|
||||
WHERE ($3 OR is_external = FALSE)
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
"#,
|
||||
)
|
||||
.bind(limit)
|
||||
@@ -801,6 +810,8 @@ impl UserRepository for UserPgRepository {
|
||||
active,
|
||||
oidc_provider,
|
||||
is_external,
|
||||
opaque_registered,
|
||||
opaque_migrated,
|
||||
)| UserListEntry {
|
||||
id,
|
||||
username,
|
||||
@@ -816,6 +827,8 @@ impl UserRepository for UserPgRepository {
|
||||
active,
|
||||
oidc_provider,
|
||||
is_external,
|
||||
opaque_registered,
|
||||
opaque_migrated,
|
||||
},
|
||||
)
|
||||
.collect())
|
||||
|
||||
Reference in New Issue
Block a user