From bd3fe40ac7a6c27a1e8c7dccc312bb4cb73039ea Mon Sep 17 00:00:00 2001 From: Dorian Date: Sat, 14 Mar 2026 05:46:10 +0000 Subject: [PATCH] feat: add UserRole RBAC framework for multi-user support - UserRole enum: Admin (full), Viewer (read-only), AppUser (minimal) - can_access() method checks RPC method against role permissions - Role field on User struct with serde default (backward-compatible) - Viewer: read system/federation/DWN/identity/backup/container status - AppUser: system.stats, node.did, container list, password change Co-Authored-By: Claude Opus 4.6 (1M context) --- core/archipelago/src/auth.rs | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/core/archipelago/src/auth.rs b/core/archipelago/src/auth.rs index b2f4faed..45f22d25 100644 --- a/core/archipelago/src/auth.rs +++ b/core/archipelago/src/auth.rs @@ -8,6 +8,60 @@ use tokio::fs; use crate::totp::TotpData; +/// User role for multi-user RBAC (Year 3 feature). +/// - Admin: full access to all operations +/// - Viewer: read-only dashboard, container status, monitoring +/// - AppUser: access specific apps, no system configuration +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum UserRole { + Admin, + Viewer, + AppUser, +} + +impl Default for UserRole { + fn default() -> Self { + UserRole::Admin + } +} + +impl UserRole { + /// Check if this role allows a given RPC method. + pub fn can_access(&self, method: &str) -> bool { + match self { + UserRole::Admin => true, + UserRole::Viewer => { + // Read-only methods + method.starts_with("system.") + || method.starts_with("node.") + || method.starts_with("federation.list") + || method.starts_with("dwn.status") + || method.starts_with("dwn.list") + || method.starts_with("dwn.query") + || method.starts_with("identity.list") + || method.starts_with("identity.get") + || method.starts_with("backup.list") + || method == "container-list" + || method == "container-status" + || method == "container-health" + || method == "health" + || method == "auth.logout" + } + UserRole::AppUser => { + // App access + basic read + method.starts_with("system.stats") + || method == "node.did" + || method == "container-list" + || method == "container-status" + || method == "health" + || method == "auth.logout" + || method == "auth.changePassword" + } + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] struct OnboardingState { complete: bool, @@ -21,6 +75,9 @@ pub struct User { pub onboarding_complete: bool, #[serde(default)] pub totp: Option, + /// User role for RBAC (defaults to Admin for backward compatibility) + #[serde(default)] + pub role: UserRole, } #[allow(dead_code)]