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) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-14 05:46:10 +00:00
parent 22adbdd05b
commit bd3fe40ac7

View File

@@ -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<TotpData>,
/// User role for RBAC (defaults to Admin for backward compatibility)
#[serde(default)]
pub role: UserRole,
}
#[allow(dead_code)]