beep_auth/domain/models/
identity.rs1use serde::{Deserialize, Serialize};
2
3use crate::domain::models::{claims::Claims, client::Client, user::User};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6pub enum Identity {
7 User(User),
8 Client(Client),
9}
10
11impl Identity {
12 pub fn id(&self) -> &str {
13 match self {
14 Identity::User(u) => &u.id,
15 Identity::Client(c) => &c.id,
16 }
17 }
18
19 pub fn is_user(&self) -> bool {
20 matches!(self, Identity::User(_))
21 }
22
23 pub fn is_client(&self) -> bool {
24 matches!(self, Identity::Client(_))
25 }
26
27 pub fn username(&self) -> &str {
28 match self {
29 Identity::User(u) => &u.username,
30 Identity::Client(c) => &c.client_id,
31 }
32 }
33
34 pub fn roles(&self) -> &[String] {
35 match self {
36 Identity::User(u) => &u.roles,
37 Identity::Client(c) => &c.roles,
38 }
39 }
40
41 pub fn has_role(&self, role: &str) -> bool {
42 self.roles().iter().any(|r| r == role)
43 }
44}
45
46impl From<Claims> for Identity {
47 fn from(claims: Claims) -> Self {
48 if let Some(client_id) = claims.client_id {
49 Identity::Client(Client {
50 id: claims.sub.0,
51 client_id: client_id,
52 roles: Vec::new(),
53 scopes: Vec::new(),
54 })
55 } else {
56 Identity::User(User {
57 id: claims.sub.0,
58 email: claims.email,
59 name: claims.name,
60 roles: Vec::new(),
61 username: claims.preferred_username,
62 })
63 }
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use serde_json::json;
70
71 use crate::domain::models::{claims::Claims, identity::Identity};
72
73 fn create_user_claims() -> Claims {
74 Claims {
75 sub: crate::domain::models::claims::Subject("user-123".to_string()),
76 iss: "https://auth.beep.com".to_string(),
77 aud: Some("beep-api".to_string()),
78 email: Some("john.doe@example.com".to_string()),
79 email_verified: true,
80 exp: None,
81 name: Some("John Doe".to_string()),
82 preferred_username: "johndoe".to_string(),
83 given_name: Some("John".to_string()),
84 family_name: Some("Doe".to_string()),
85 scope: "openid profile email".to_string(),
86 client_id: None,
87 extra: {
88 let mut map = serde_json::Map::new();
89 map.insert(
90 "realm_access".to_string(),
91 json!({
92 "roles": ["user", "moderator"]
93 }),
94 );
95 map
96 },
97 }
98 }
99
100 fn create_service_account_claims() -> Claims {
101 Claims {
102 sub: crate::domain::models::claims::Subject("service-123".to_string()),
103 iss: "https://auth.beep.com".to_string(),
104 aud: Some("beep-api".to_string()),
105 email: None,
106 email_verified: false,
107 name: None,
108 exp: None,
109 preferred_username: "service-account-bot".to_string(),
110 given_name: None,
111 family_name: None,
112 scope: "admin:all read:users write:messages".to_string(),
113 client_id: Some("beep-bot".to_string()),
114 extra: {
115 let mut map = serde_json::Map::new();
116 map.insert(
117 "realm_access".to_string(),
118 json!({
119 "roles": ["service", "bot"]
120 }),
121 );
122 map
123 },
124 }
125 }
126
127 #[test]
128 fn test_claims_to_identity_user() {
129 let claims = create_user_claims();
130 let identity: Identity = claims.into();
131
132 match identity {
133 Identity::User(user) => {
134 assert_eq!(user.id, "user-123");
135 assert_eq!(user.username, "johndoe");
136 assert_eq!(user.email, Some("john.doe@example.com".to_string()));
137 assert_eq!(user.name, Some("John Doe".to_string()));
138 }
139 Identity::Client(_) => panic!("Expected User, got Client"),
140 }
141 }
142
143 #[test]
144 fn test_claims_to_identity_service_account() {
145 let claims = create_service_account_claims();
146 let identity: Identity = claims.into();
147
148 match identity {
149 Identity::Client(client) => {
150 assert_eq!(client.id, "service-123");
151 assert_eq!(client.client_id, "beep-bot");
152 }
153 Identity::User(_) => panic!("Expected Client, got User"),
154 }
155 }
156}