zino_auth/
user_session.rs1use super::{AccessKeyId, SessionId};
2use serde::{Deserialize, Serialize};
3use std::str::FromStr;
4use zino_core::{
5 application::{Agent, Application},
6 crypto::Digest,
7};
8
9#[cfg(feature = "jwt")]
10use super::JwtClaims;
11
12#[cfg(feature = "jwt")]
13use zino_core::{error::Error, extension::JsonObjectExt, warn};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct UserSession<U, R = String, T = U> {
18 user_id: U,
20 session_id: Option<SessionId>,
22 access_key_id: Option<AccessKeyId>,
24 roles: Vec<R>,
26 tenant_id: Option<T>,
28}
29
30impl<U, R, T> UserSession<U, R, T> {
31 #[inline]
33 pub fn new(user_id: U, session_id: impl Into<Option<SessionId>>) -> Self {
34 Self {
35 user_id,
36 session_id: session_id.into(),
37 access_key_id: None,
38 roles: Vec::new(),
39 tenant_id: None,
40 }
41 }
42
43 #[inline]
45 pub fn set_session_id(&mut self, session_id: SessionId) {
46 self.session_id = Some(session_id);
47 }
48
49 #[inline]
51 pub fn set_access_key_id(&mut self, access_key_id: AccessKeyId) {
52 if self.session_id.is_none() {
53 let session_id = SessionId::new::<Digest>(Agent::domain(), access_key_id.as_ref());
54 self.session_id = Some(session_id);
55 }
56 self.access_key_id = Some(access_key_id);
57 }
58
59 #[inline]
61 pub fn set_roles(&mut self, roles: impl Into<Vec<R>>) {
62 self.roles = roles.into();
63 }
64
65 #[inline]
67 pub fn set_tenant_id(&mut self, tenant_id: T) {
68 self.tenant_id = Some(tenant_id);
69 }
70
71 #[inline]
73 pub fn user_id(&self) -> &U {
74 &self.user_id
75 }
76
77 #[inline]
79 pub fn tenant_id(&self) -> Option<&T> {
80 self.tenant_id.as_ref()
81 }
82
83 #[inline]
85 pub fn session_id(&self) -> Option<&SessionId> {
86 self.session_id.as_ref()
87 }
88
89 #[inline]
91 pub fn access_key_id(&self) -> Option<&AccessKeyId> {
92 self.access_key_id.as_ref()
93 }
94
95 #[inline]
97 pub fn roles(&self) -> &[R] {
98 &self.roles
99 }
100}
101
102impl<U, R, T> UserSession<U, R, T>
103where
104 U: FromStr,
105 R: FromStr,
106 T: FromStr,
107 <U as FromStr>::Err: std::error::Error + Send + 'static,
108{
109 #[cfg(feature = "jwt")]
111 pub fn try_from_jwt_claims(claims: JwtClaims) -> Result<Self, Error> {
112 let data = claims.data();
113 let user_id = claims
114 .subject()
115 .map(|s| s.into())
116 .or_else(|| data.parse_string("uid"))
117 .ok_or_else(|| warn!("subject of a JWT token should be specified"))?
118 .parse()?;
119 let mut user_session = Self::new(user_id, None);
120 if let Some(Ok(roles)) = data
121 .parse_array("roles")
122 .or_else(|| data.parse_array("role"))
123 {
124 user_session.set_roles(roles);
125 }
126 if let Some(tenant_id) = data
127 .parse_string("tenant_id")
128 .or_else(|| data.parse_string("tid"))
129 .and_then(|s| s.parse().ok())
130 {
131 user_session.set_tenant_id(tenant_id);
132 }
133 Ok(user_session)
134 }
135}
136
137impl<U, T> UserSession<U, String, T> {
138 #[inline]
140 pub fn is_superuser(&self) -> bool {
141 self.roles() == ["superuser"]
142 }
143
144 #[inline]
146 pub fn is_user(&self) -> bool {
147 self.roles() == ["user"]
148 }
149
150 #[inline]
152 pub fn is_guest(&self) -> bool {
153 self.roles() == ["guest"]
154 }
155
156 #[inline]
158 pub fn is_agent(&self) -> bool {
159 self.roles() == ["agent"]
160 }
161
162 pub fn is_admin(&self) -> bool {
164 let role = "admin";
165 let role_prefix = format!("{role}:");
166 for r in &self.roles {
167 if r == role || r.starts_with(&role_prefix) {
168 return true;
169 }
170 }
171 false
172 }
173
174 pub fn is_worker(&self) -> bool {
176 let role = "worker";
177 let role_prefix = format!("{role}:");
178 for r in &self.roles {
179 if r == role || r.starts_with(&role_prefix) {
180 return true;
181 }
182 }
183 false
184 }
185
186 pub fn is_auditor(&self) -> bool {
188 let role = "auditor";
189 let role_prefix = format!("{role}:");
190 for r in &self.roles {
191 if r == role || r.starts_with(&role_prefix) {
192 return true;
193 }
194 }
195 false
196 }
197
198 pub fn has_user_role(&self) -> bool {
201 self.is_superuser()
202 || self.is_user()
203 || self.is_admin()
204 || self.is_worker()
205 || self.is_auditor()
206 }
207
208 pub fn has_admin_role(&self) -> bool {
210 self.is_superuser() || self.is_admin()
211 }
212
213 pub fn has_worker_role(&self) -> bool {
215 self.is_superuser() || self.is_worker()
216 }
217
218 pub fn has_auditor_role(&self) -> bool {
220 self.is_superuser() || self.is_auditor()
221 }
222
223 pub fn has_role(&self, role: &str) -> bool {
225 let length = role.len();
226 for r in &self.roles {
227 if r == role {
228 return true;
229 } else {
230 let remainder = if r.len() > length {
231 r.strip_prefix(role)
232 } else {
233 role.strip_prefix(r.as_str())
234 };
235 if remainder.is_some_and(|s| s.starts_with(':')) {
236 return true;
237 }
238 }
239 }
240 false
241 }
242
243 pub fn has_any_roles(&self, roles: &[&str]) -> bool {
245 for role in roles {
246 if self.has_role(role) {
247 return true;
248 }
249 }
250 false
251 }
252
253 pub fn has_all_roles(&self, roles: &[&str]) -> bool {
255 for role in roles {
256 if !self.has_role(role) {
257 return false;
258 }
259 }
260 true
261 }
262}