use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, thiserror::Error)]
pub enum AuthorizationError {
#[error("User not found: {0}")]
UserNotFound(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("Invalid token: {0}")]
InvalidToken(String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Database error: {0}")]
Database(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthUser {
pub user_id: String,
pub username: String,
pub roles: Vec<String>,
pub permissions: Vec<String>,
pub expires_at: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Action {
Create,
Read,
Update,
Delete,
List,
Restore,
}
impl Action {
pub fn all() -> Vec<Self> {
vec![
Self::Create,
Self::Read,
Self::Update,
Self::Delete,
Self::List,
Self::Restore,
]
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Create => write!(f, "create"),
Self::Read => write!(f, "read"),
Self::Update => write!(f, "update"),
Self::Delete => write!(f, "delete"),
Self::List => write!(f, "list"),
Self::Restore => write!(f, "restore"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Resource {
User,
Role,
Permission,
Settings,
}
impl Resource {
pub fn all() -> Vec<Self> {
vec![
Self::User,
Self::Role,
Self::Permission,
Self::Settings,
]
}
}
impl fmt::Display for Resource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::User => write!(f, "user"),
Self::Role => write!(f, "role"),
Self::Permission => write!(f, "permission"),
Self::Settings => write!(f, "settings"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Role {
SuperAdmin,
Admin,
User,
Guest,
}
impl Role {
pub fn all() -> Vec<&'static str> {
vec![
"super_admin",
"admin",
"user",
"guest",
]
}
pub fn as_str(&self) -> &'static str {
match self {
Self::SuperAdmin => "super_admin",
Self::Admin => "admin",
Self::User => "user",
Self::Guest => "guest",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"super_admin" => Some(Self::SuperAdmin),
"admin" => Some(Self::Admin),
"user" => Some(Self::User),
"guest" => Some(Self::Guest),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionCheck {
pub allowed: bool,
pub permission: Permission,
pub reason: String,
}
#[derive(Debug, Clone)]
pub struct AuthorizationRequest {
pub user: AuthUser,
pub resource: Resource,
pub action: Action,
pub resource_id: Option<String>,
}
#[derive(Debug, Clone)]
pub struct AuthorizationResponse {
pub allowed: bool,
pub checks: Vec<PermissionCheck>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthorizationConfig {
pub cache_ttl_seconds: u64,
pub default_role: String,
pub enable_permission_caching: bool,
}
impl Default for AuthorizationConfig {
fn default() -> Self {
Self {
cache_ttl_seconds: 300, default_role: "guest".to_string(),
enable_permission_caching: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UserAction {
Create,
Read,
Update,
Delete,
List,
ResetPassword,
ChangeRole,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RoleAction {
Create,
Read,
Update,
Delete,
List,
AssignPermission,
RevokePermission,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PermissionAction {
Create,
Read,
Update,
Delete,
List,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SettingsAction {
Create,
Read,
Update,
Delete,
List,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Permission {
User(UserAction),
Role(RoleAction),
Permission(PermissionAction),
Settings(SettingsAction),
}
impl Permission {
pub fn all_permissions() -> HashSet<Permission> {
let mut perms = HashSet::new();
for action in UserAction::all() {
perms.insert(Permission::User(action));
}
for action in RoleAction::all() {
perms.insert(Permission::Role(action));
}
for action in PermissionAction::all() {
perms.insert(Permission::Permission(action));
}
for action in SettingsAction::all() {
perms.insert(Permission::Settings(action));
}
perms
}
pub fn from_action_resource(action: Action, resource: Resource) -> Self {
match resource {
Resource::User => match action {
Action::Create => Permission::User(UserAction::Create),
Action::Read => Permission::User(UserAction::Read),
Action::Update => Permission::User(UserAction::Update),
Action::Delete => Permission::User(UserAction::Delete),
Action::List => Permission::User(UserAction::List),
Action::Restore => Permission::User(UserAction::Delete),
},
Resource::Role => match action {
Action::Create => Permission::Role(RoleAction::Create),
Action::Read => Permission::Role(RoleAction::Read),
Action::Update => Permission::Role(RoleAction::Update),
Action::Delete => Permission::Role(RoleAction::Delete),
Action::List => Permission::Role(RoleAction::List),
Action::Restore => Permission::Role(RoleAction::Delete),
},
Resource::Permission => match action {
Action::Create => Permission::Permission(PermissionAction::Create),
Action::Read => Permission::Permission(PermissionAction::Read),
Action::Update => Permission::Permission(PermissionAction::Update),
Action::Delete => Permission::Permission(PermissionAction::Delete),
Action::List => Permission::Permission(PermissionAction::List),
Action::Restore => Permission::Permission(PermissionAction::Delete),
},
Resource::Settings => match action {
Action::Create => Permission::Settings(SettingsAction::Create),
Action::Read => Permission::Settings(SettingsAction::Read),
Action::Update => Permission::Settings(SettingsAction::Update),
Action::Delete => Permission::Settings(SettingsAction::Delete),
Action::List => Permission::Settings(SettingsAction::List),
Action::Restore => Permission::Settings(SettingsAction::Delete),
},
}
}
}
impl UserAction {
pub fn all() -> Vec<Self> {
vec![
Self::Create,
Self::Read,
Self::Update,
Self::Delete,
Self::List,
Self::ResetPassword,
Self::ChangeRole,
]
}
}
impl RoleAction {
pub fn all() -> Vec<Self> {
vec![
Self::Create,
Self::Read,
Self::Update,
Self::Delete,
Self::List,
Self::AssignPermission,
Self::RevokePermission,
]
}
}
impl PermissionAction {
pub fn all() -> Vec<Self> {
vec![
Self::Create,
Self::Read,
Self::Update,
Self::Delete,
Self::List,
]
}
}
impl SettingsAction {
pub fn all() -> Vec<Self> {
vec![
Self::Create,
Self::Read,
Self::Update,
Self::Delete,
Self::List,
]
}
}
use std::collections::HashSet;
#[cfg(test)]
mod restore_permission_tests {
use super::*;
#[test]
fn restoring_a_record_requires_the_permission_to_delete_it() {
assert_eq!(
Permission::from_action_resource(Action::Restore, Resource::User),
Permission::User(UserAction::Delete)
);
assert_eq!(
Permission::from_action_resource(Action::Restore, Resource::Role),
Permission::Role(RoleAction::Delete)
);
assert_eq!(
Permission::from_action_resource(Action::Restore, Resource::Permission),
Permission::Permission(PermissionAction::Delete)
);
assert_eq!(
Permission::from_action_resource(Action::Restore, Resource::Settings),
Permission::Settings(SettingsAction::Delete)
);
}
}