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}