Skip to main content

road_runner_common/
auth.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use crate::error::AppError;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct UserContext {
8    pub user_id: Uuid,
9    pub email: String,
10
11    #[serde(default)]
12    pub roles: Vec<String>,
13
14    #[serde(default)]
15    pub scopes: Vec<String>,
16
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub tenant_id: Option<String>,
19}
20
21impl UserContext {
22    pub fn has_role(&self, role: &str) -> bool {
23        self.roles.iter().any(|r| r == role)
24    }
25
26    pub fn require_role(&self, role: &str) -> Result<(), AppError> {
27        if self.has_role(role) {
28            Ok(())
29        } else {
30            Err(AppError::Forbidden {
31                message: format!("Missing role: {}", role),
32            })
33        }
34    }
35}
36
37#[cfg(feature = "web")]
38mod web_impl {
39    use super::*;
40    use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
41    use futures_util::future::{ready, Ready};
42
43    /// Actix extractor:
44    /// Middleware'in `req.extensions_mut().insert(UserContext)` yaptığı varsayımıyla çalışır.
45    impl FromRequest for UserContext {
46        type Error = AppError;
47        type Future = Ready<Result<Self, Self::Error>>;
48
49        fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
50            match req.extensions().get::<UserContext>().cloned() {
51                Some(ctx) => ready(Ok(ctx)),
52                None => ready(Err(AppError::Unauthorized {
53                    message: "Missing user context".to_string(),
54                })),
55            }
56        }
57    }
58}