1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
6pub struct Identity {
7 pub id: String,
8 pub name: String,
9 pub email: String,
10 pub active: bool,
11 pub roles: Vec<String>,
12}
13
14impl Identity {
15 pub fn has_role(&self, role: &str) -> bool {
16 self.roles.iter().any(|assigned| assigned == role)
17 }
18}
19
20#[derive(Debug, Error)]
21pub enum AuthError {
22 #[error("invalid credentials")]
23 InvalidCredentials,
24 #[error("identity not found")]
25 NotFound,
26 #[error("email is already registered")]
27 DuplicateEmail,
28 #[error("invalid input: {0}")]
29 InvalidInput(String),
30 #[error("identity store unavailable: {0}")]
31 Store(String),
32}
33
34#[async_trait]
35pub trait IdentityStore: Send + Sync {
36 async fn authenticate(&self, email: &str, password: &str) -> Result<Identity, AuthError>;
37 async fn get(&self, id: &str) -> Result<Option<Identity>, AuthError>;
38 async fn list(&self) -> Result<Vec<Identity>, AuthError>;
39 async fn has_users(&self) -> Result<bool, AuthError>;
40 async fn create_user(
41 &self,
42 name: &str,
43 email: &str,
44 password: &str,
45 roles: &[String],
46 ) -> Result<Identity, AuthError>;
47 async fn update_profile(
48 &self,
49 id: &str,
50 name: &str,
51 email: &str,
52 ) -> Result<Identity, AuthError>;
53 async fn change_password(&self, id: &str, password: &str) -> Result<(), AuthError>;
54 async fn set_roles(&self, id: &str, roles: &[String]) -> Result<Identity, AuthError>;
55 async fn set_active(&self, id: &str, active: bool) -> Result<Identity, AuthError>;
56}
57
58pub trait AuthorizationPolicy: Send + Sync {
59 fn permits(&self, identity: &Identity, required_roles: &[&str]) -> bool;
60}