use crate::authz::access_hierarchy::AccessHierarchy;
use crate::permissions::Permissions;
use crate::permissions::permission_id::PermissionId;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Account<R, G>
where
R: AccessHierarchy + Eq,
G: Eq + Clone,
{
pub account_id: Uuid,
pub user_id: String,
pub roles: Vec<R>,
pub groups: Vec<G>,
pub permissions: Permissions,
}
impl<R, G> Account<R, G>
where
R: AccessHierarchy + Eq + Clone + Default,
G: Eq + Clone,
{
pub fn new(user_id: &str) -> Self {
Self {
account_id: Uuid::now_v7(),
user_id: user_id.to_string(),
groups: Vec::new(),
roles: vec![R::default()],
permissions: Permissions::new(),
}
}
pub fn with_roles(self, roles: Vec<R>) -> Self {
Self { roles, ..self }
}
pub fn with_groups(self, groups: Vec<G>) -> Self {
Self { groups, ..self }
}
pub fn with_permissions(self, permissions: Permissions) -> Self {
Self {
permissions,
..self
}
}
pub fn grant_permission<P>(&mut self, permission: P)
where
P: Into<PermissionId>,
{
self.permissions.grant(permission);
}
pub fn revoke_permission<P>(&mut self, permission: P)
where
P: Into<PermissionId>,
{
self.permissions.revoke(permission);
}
pub fn has_role(&self, role: &R) -> bool {
self.roles.contains(role)
}
pub fn is_member_of(&self, group: &G) -> bool {
self.groups.contains(group)
}
pub fn has_permission<P>(&self, permission: P) -> bool
where
P: Into<PermissionId>,
{
self.permissions.has(permission)
}
}
#[cfg(test)]
mod tests {
use super::Account;
use crate::groups::Group;
use crate::permissions::Permissions;
use crate::roles::Role;
#[test]
fn new_uses_default_role_and_empty_groups() {
let account = Account::<Role, Group>::new("user@example.com");
assert_eq!(account.user_id, "user@example.com");
assert_eq!(account.roles, vec![Role::User]);
assert!(account.groups.is_empty());
assert!(account.permissions.is_empty());
}
#[test]
fn with_roles_replaces_default_role_set() {
let account = Account::<Role, Group>::new("user@example.com").with_roles(vec![Role::Admin]);
assert_eq!(account.roles, vec![Role::Admin]);
}
#[test]
fn with_groups_replaces_group_set() {
let groups = vec![Group::new("engineering"), Group::new("backend-team")];
let account = Account::<Role, Group>::new("user@example.com").with_groups(groups.clone());
assert_eq!(account.groups, groups);
}
#[test]
fn with_permissions_replaces_permission_set() {
let permissions = Permissions::from_iter(["read:api", "write:api"]);
let account = Account::<Role, Group>::new("user@example.com").with_permissions(permissions);
assert!(account.has_permission("read:api"));
assert!(account.has_permission("write:api"));
}
#[test]
fn grant_and_revoke_permission_update_account_permissions() {
let mut account = Account::<Role, Group>::new("user@example.com");
account.grant_permission("read:api");
assert!(account.has_permission("read:api"));
account.revoke_permission("read:api");
assert!(!account.has_permission("read:api"));
}
#[test]
fn role_and_group_queries_reflect_membership() {
let mut account = Account::<Role, Group>::new("user@example.com");
account.roles = vec![Role::Admin];
account.groups = vec![Group::new("engineering")];
assert!(account.has_role(&Role::Admin));
assert!(!account.has_role(&Role::User));
assert!(account.is_member_of(&Group::new("engineering")));
assert!(!account.is_member_of(&Group::new("marketing")));
}
}