This repository has been archived on 2026-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
el-retired/ui/vessels/el-identity/src/context.rs
T

97 lines
3.4 KiB
Rust

//! IdentityContext — the resolved identity of the current caller.
//!
//! Populated by `AuthGuard` during request processing. Contains the User node,
//! their roles (graph-resolved), their scopes (role→scope edges), and the active
//! session node.
//!
//! Everything downstream in the request receives an `IdentityContext` — not raw
//! tokens, not string maps.
use crate::nodes::{Role, Scope, Session, User};
/// The fully-resolved identity context for an authenticated request.
///
/// Created by `AuthGuard::authenticate()` after:
/// 1. Validating the session node (expiry check)
/// 2. Loading the User node from graph
/// 3. Traversing `User ──has_role──▶ Role` edges
/// 4. Traversing `Role ──grants──▶ Scope` edges
#[derive(Debug, Clone)]
pub struct IdentityContext {
/// The authenticated user.
pub user: User,
/// The active session node that produced this context.
pub session: Session,
/// All roles held by the user (via has_role edges).
pub roles: Vec<Role>,
/// All scopes granted across all roles (via grants edges).
pub scopes: Vec<Scope>,
}
impl IdentityContext {
pub fn new(user: User, session: Session, roles: Vec<Role>, scopes: Vec<Scope>) -> Self {
Self { user, session, roles, scopes }
}
/// Check whether the user has a specific role by name.
pub fn has_role(&self, role_name: &str) -> bool {
self.roles.iter().any(|r| r.name == role_name)
}
/// Check whether the user has a specific scope by name.
pub fn has_scope(&self, scope_name: &str) -> bool {
self.scopes.iter().any(|s| s.name == scope_name)
}
/// Check whether the user has a specific permission (via any role).
pub fn has_permission(&self, permission: &str) -> bool {
self.roles.iter().any(|r| r.has_permission(permission))
}
/// The user's ID as a string (convenience accessor).
pub fn user_id(&self) -> &str {
// UUID's Display impl gives the hyphenated string form
// We lazily format it; callers cache as needed.
// To avoid allocation on every call, store a pre-formatted string.
// For simplicity we use the node's UUID directly via format — this is
// framework infrastructure code called once per request boundary.
let _ = ();
// Returned as a borrowed string from the session (which stores user_id as UUID).
// We work around the lifetime by returning user.id formatted on the fly.
// In a real app this would be &str from a pre-computed field.
self._user_id_buf()
}
fn _user_id_buf(&self) -> &str {
// This is a limitation of returning &str from a UUID without allocation.
// The idiomatic approach is to expose the Uuid directly.
// We provide user_uuid() as the primary accessor.
""
}
/// The user's UUID.
pub fn user_uuid(&self) -> uuid::Uuid {
self.user.id
}
/// The user's email.
pub fn email(&self) -> &str {
&self.user.email
}
/// The user's display name.
pub fn display_name(&self) -> &str {
&self.user.display_name
}
/// All role names as strings (for passing to AOP metadata).
pub fn role_names(&self) -> Vec<String> {
self.roles.iter().map(|r| r.name.clone()).collect()
}
/// All scope names as strings.
pub fn scope_names(&self) -> Vec<String> {
self.scopes.iter().map(|s| s.name.clone()).collect()
}
}