use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Role {
Admin,
User,
Guest,
Moderator,
Custom(String),
}
impl Role {
pub fn from_str(s: &str) -> Self {
match s.to_uppercase().as_str() {
"ADMIN" => Role::Admin,
"USER" => Role::User,
"GUEST" => Role::Guest,
"MODERATOR" => Role::Moderator,
custom => Role::Custom(custom.to_string()),
}
}
pub fn name(&self) -> &str {
match self {
Role::Admin => "ADMIN",
Role::User => "USER",
Role::Guest => "GUEST",
Role::Moderator => "MODERATOR",
Role::Custom(name) => name,
}
}
pub fn with_prefix(&self) -> String {
format!("{}{}", crate::DEFAULT_ROLE_PREFIX, self.name())
}
pub fn is_admin(&self) -> bool {
matches!(self, Role::Admin)
}
}
impl fmt::Display for Role {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl From<String> for Role {
fn from(s: String) -> Self {
Role::from_str(&s)
}
}
impl From<&str> for Role {
fn from(s: &str) -> Self {
Role::from_str(s)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Roles {
pub roles: Vec<Role>,
}
impl Roles {
pub fn new() -> Self {
Self { roles: Vec::new() }
}
pub fn add(mut self, role: Role) -> Self {
self.roles.push(role);
self
}
pub fn contains(&self, role: &Role) -> bool {
self.roles.contains(role)
}
pub fn contains_any(&self, roles: &[Role]) -> bool {
roles.iter().any(|r| self.roles.contains(r))
}
pub fn contains_all(&self, roles: &[Role]) -> bool {
roles.iter().all(|r| self.roles.contains(r))
}
}
impl Default for Roles {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Authority {
Role(Role),
Permission(String),
Custom(String),
}
impl Authority {
pub fn authority(&self) -> String {
match self {
Authority::Role(role) => role.with_prefix(),
Authority::Permission(perm) => perm.clone(),
Authority::Custom(auth) => auth.clone(),
}
}
pub fn role(role: Role) -> Self {
Authority::Role(role)
}
pub fn permission(perm: impl Into<String>) -> Self {
Authority::Permission(perm.into())
}
pub fn custom(auth: impl Into<String>) -> Self {
Authority::Custom(auth.into())
}
pub fn is_role(&self) -> bool {
matches!(self, Authority::Role(_))
}
pub fn is_permission(&self) -> bool {
matches!(self, Authority::Permission(_))
}
pub fn from_string(s: &str) -> Option<Self> {
if s.starts_with("ROLE_") {
let role_str = s.strip_prefix("ROLE_")?;
Some(Authority::Role(Role::from_str(role_str)))
} else {
Some(Authority::Permission(s.to_string()))
}
}
}
impl fmt::Display for Authority {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.authority())
}
}
pub trait GrantedAuthority: Send + Sync {
fn get_authority(&self) -> String;
fn equals(&self, other: &dyn GrantedAuthority) -> bool;
}
impl GrantedAuthority for Authority {
fn get_authority(&self) -> String {
self.authority()
}
fn equals(&self, other: &dyn GrantedAuthority) -> bool {
self.authority() == other.get_authority()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Permission {
Read,
Write,
Create,
Delete,
Update,
Admin,
Execute,
Custom(String),
}
impl Permission {
pub fn from_str(s: &str) -> Self {
match s.to_uppercase().as_str() {
"READ" => Permission::Read,
"WRITE" => Permission::Write,
"CREATE" => Permission::Create,
"DELETE" => Permission::Delete,
"UPDATE" => Permission::Update,
"ADMIN" => Permission::Admin,
"EXECUTE" => Permission::Execute,
custom => Permission::Custom(custom.to_string()),
}
}
pub fn name(&self) -> &str {
match self {
Permission::Read => "READ",
Permission::Write => "WRITE",
Permission::Create => "CREATE",
Permission::Delete => "DELETE",
Permission::Update => "UPDATE",
Permission::Admin => "ADMIN",
Permission::Execute => "EXECUTE",
Permission::Custom(name) => name,
}
}
}
impl fmt::Display for Permission {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl From<String> for Permission {
fn from(s: String) -> Self {
Permission::from_str(&s)
}
}
impl From<&str> for Permission {
fn from(s: &str) -> Self {
Permission::from_str(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_role_from_str() {
assert_eq!(Role::from_str("admin"), Role::Admin);
assert_eq!(Role::from_str("USER"), Role::User);
assert_eq!(Role::from_str("custom_role"), Role::Custom("CUSTOM_ROLE".to_string()));
}
#[test]
fn test_role_with_prefix() {
assert_eq!(Role::Admin.with_prefix(), "ROLE_ADMIN");
assert_eq!(Role::Custom("EDITOR".to_string()).with_prefix(), "ROLE_EDITOR");
}
#[test]
fn test_authority() {
let auth = Authority::role(Role::Admin);
assert_eq!(auth.authority(), "ROLE_ADMIN");
let auth = Authority::permission("user:read");
assert_eq!(auth.authority(), "user:read");
}
#[test]
fn test_roles() {
let roles = Roles::new()
.add(Role::Admin)
.add(Role::User)
.add(Role::Moderator);
assert!(roles.contains(&Role::User));
assert!(roles.contains_all(&[Role::User, Role::Admin]));
assert!(roles.contains_any(&[Role::Guest, Role::Admin]));
assert!(!roles.contains(&Role::Guest));
}
}