Skip to main content

platform_http/
auth.rs

1use crate::{ApiErrorResponse, HttpRequestContext};
2use axum::extract::FromRequestParts;
3use axum::http::request::Parts;
4use platform_core::{ActorContext, AppError, ErrorCode};
5
6#[derive(Debug, Clone)]
7pub struct OptionalActor(pub ActorContext);
8
9#[derive(Debug, Clone)]
10pub struct AuthenticatedActor(pub ActorContext);
11
12#[derive(Debug, Clone)]
13pub struct UserActor {
14    pub user_id: String,
15    pub scopes: Vec<String>,
16}
17
18#[derive(Debug, Clone)]
19pub struct ServiceActor {
20    pub service_id: String,
21    pub scopes: Vec<String>,
22}
23
24#[derive(Debug, Clone)]
25pub enum AdminActor {
26    Service {
27        service_id: String,
28        scopes: Vec<String>,
29    },
30    System,
31}
32
33impl<S> FromRequestParts<S> for OptionalActor
34where
35    S: Send + Sync,
36{
37    type Rejection = ApiErrorResponse;
38
39    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
40        let HttpRequestContext(ctx) = HttpRequestContext::from_request_parts(parts, state).await?;
41        Ok(Self(ctx.actor))
42    }
43}
44
45impl<S> FromRequestParts<S> for AuthenticatedActor
46where
47    S: Send + Sync,
48{
49    type Rejection = ApiErrorResponse;
50
51    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
52        let HttpRequestContext(ctx) = HttpRequestContext::from_request_parts(parts, state).await?;
53        match ctx.actor {
54            ActorContext::Anonymous => Err(ApiErrorResponse::with_context(
55                AppError::new(ErrorCode::Unauthorized, "Authentication is required"),
56                &ctx,
57            )),
58            actor => Ok(Self(actor)),
59        }
60    }
61}
62
63impl<S> FromRequestParts<S> for UserActor
64where
65    S: Send + Sync,
66{
67    type Rejection = ApiErrorResponse;
68
69    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
70        let HttpRequestContext(ctx) = HttpRequestContext::from_request_parts(parts, state).await?;
71        match ctx.actor {
72            ActorContext::Anonymous => Err(ApiErrorResponse::with_context(
73                AppError::new(ErrorCode::Unauthorized, "Authentication is required"),
74                &ctx,
75            )),
76            ActorContext::User { user_id, scopes } => Ok(Self { user_id, scopes }),
77            ActorContext::Service { .. } | ActorContext::System => {
78                Err(ApiErrorResponse::with_context(
79                    AppError::new(ErrorCode::Forbidden, "User authentication is required"),
80                    &ctx,
81                ))
82            }
83        }
84    }
85}
86
87impl<S> FromRequestParts<S> for ServiceActor
88where
89    S: Send + Sync,
90{
91    type Rejection = ApiErrorResponse;
92
93    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
94        let HttpRequestContext(ctx) = HttpRequestContext::from_request_parts(parts, state).await?;
95        match ctx.actor {
96            ActorContext::Anonymous => Err(ApiErrorResponse::with_context(
97                AppError::new(ErrorCode::Unauthorized, "Authentication is required"),
98                &ctx,
99            )),
100            ActorContext::Service { service_id, scopes } => Ok(Self { service_id, scopes }),
101            ActorContext::User { .. } | ActorContext::System => {
102                Err(ApiErrorResponse::with_context(
103                    AppError::new(ErrorCode::Forbidden, "Service authentication is required"),
104                    &ctx,
105                ))
106            }
107        }
108    }
109}
110
111impl<S> FromRequestParts<S> for AdminActor
112where
113    S: Send + Sync,
114{
115    type Rejection = ApiErrorResponse;
116
117    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
118        let HttpRequestContext(ctx) = HttpRequestContext::from_request_parts(parts, state).await?;
119        match ctx.actor {
120            ActorContext::Anonymous => Err(ApiErrorResponse::with_context(
121                AppError::new(ErrorCode::Unauthorized, "Authentication is required"),
122                &ctx,
123            )),
124            ActorContext::Service { service_id, scopes } => {
125                Ok(Self::Service { service_id, scopes })
126            }
127            ActorContext::System => Ok(Self::System),
128            ActorContext::User { .. } => Err(ApiErrorResponse::with_context(
129                AppError::new(
130                    ErrorCode::Forbidden,
131                    "Service or system authentication is required",
132                ),
133                &ctx,
134            )),
135        }
136    }
137}