backend/config/auth/
user_info.rs

1//! Contains [`UserInfo`] which stores information about the current user.
2
3use actix_http::{HttpMessage, StatusCode};
4use actix_utils::future::{ready, Ready};
5use actix_web::FromRequest;
6use serde::Deserialize;
7use uuid::Uuid;
8
9use crate::error::ServiceError;
10
11use super::claims::Claims;
12
13/// Information about the user extracted from the token provided.
14#[derive(Debug, Clone, Deserialize)]
15pub struct UserInfo {
16    /// The current users id.
17    pub id: Uuid,
18    /// The scopes the current user has.
19    pub scopes: Vec<String>,
20    /// The roles the current user has.
21    pub roles: Vec<Role>,
22}
23
24/// Roles a user can have
25#[derive(Debug, Clone, Deserialize)]
26pub enum Role {
27    /// The user is a member.
28    Member,
29}
30
31impl UserInfo {
32    /// Checks if the user is a member.
33    #[must_use]
34    pub fn is_member(&self) -> bool {
35        self.roles.iter().any(|role| matches!(role, Role::Member))
36    }
37}
38
39impl Role {
40    /// Convert a role from a string.
41    #[must_use]
42    pub fn from_string(str: &str) -> Option<Self> {
43        match str {
44            "member" => Some(Self::Member),
45            _ => None,
46        }
47    }
48}
49
50// Trait implementations
51
52impl From<Claims> for UserInfo {
53    fn from(value: Claims) -> Self {
54        let roles = match value.realm_access {
55            Some(realm_access) => realm_access
56                .roles
57                .into_iter()
58                .filter_map(|s| Role::from_string(&s))
59                .collect::<Vec<_>>(),
60            None => Vec::new(),
61        };
62
63        Self {
64            id: value.sub,
65            scopes: value.scope.split(' ').map(str::to_owned).collect(),
66            roles,
67        }
68    }
69}
70
71impl FromRequest for UserInfo {
72    type Future = Ready<Result<Self, Self::Error>>;
73    type Error = ServiceError;
74
75    fn from_request(
76        req: &actix_web::HttpRequest,
77        _payload: &mut actix_http::Payload,
78    ) -> Self::Future {
79        let extensions = req.extensions();
80        ready({
81            extensions.get::<Self>().map_or_else(
82                || {
83                    Err(ServiceError::new(
84                        StatusCode::INTERNAL_SERVER_ERROR,
85                        &StatusCode::INTERNAL_SERVER_ERROR.to_string(),
86                    ))
87                },
88                |user_info| Ok(user_info.clone()),
89            )
90        })
91    }
92}