rama_http/layer/auth/
validate_authorization.rs1use std::{fmt, marker::PhantomData};
4
5use rama_core::{extensions::ExtensionsRef, telemetry::tracing};
6use rama_http_headers::{Authorization, HeaderMapExt, authorization::Credentials};
7use rama_http_types::{Body, HeaderValue, Request, Response, StatusCode, header};
8use rama_net::user::{
9 Basic, Bearer, RawToken, UserId,
10 authority::{AuthorizeResult, Authorizer, StaticAuthorizer},
11};
12
13use crate::{
14 layer::validate_request::{ValidateRequest, ValidateRequestHeader, ValidateRequestHeaderLayer},
15 service::web::response::IntoResponse,
16};
17
18pub struct HttpAuthorizer<A, C> {
23 authorizer: A,
24 allow_anonymous: bool,
25 _credentials: PhantomData<fn() -> C>,
26}
27
28impl From<Basic> for HttpAuthorizer<StaticAuthorizer<Basic>, Basic> {
29 fn from(value: Basic) -> Self {
30 Self::new(StaticAuthorizer::new(value))
31 }
32}
33
34impl From<Bearer> for HttpAuthorizer<StaticAuthorizer<Bearer>, Bearer> {
35 fn from(value: Bearer) -> Self {
36 Self::new(StaticAuthorizer::new(value))
37 }
38}
39
40impl From<RawToken> for HttpAuthorizer<StaticAuthorizer<RawToken>, RawToken> {
41 fn from(value: RawToken) -> Self {
42 Self::new(StaticAuthorizer::new(value))
43 }
44}
45
46impl<C: Credentials> From<Vec<C>> for HttpAuthorizer<Vec<C>, C> {
47 fn from(value: Vec<C>) -> Self {
48 Self::new(value)
49 }
50}
51
52impl<const N: usize, C: Credentials> From<[C; N]> for HttpAuthorizer<[C; N], C> {
53 fn from(value: [C; N]) -> Self {
54 Self::new(value)
55 }
56}
57
58impl<A, C> HttpAuthorizer<A, C> {
59 pub fn new(authorizer: A) -> Self {
60 Self {
61 authorizer,
62 allow_anonymous: false,
63 _credentials: PhantomData,
64 }
65 }
66
67 rama_utils::macros::generate_set_and_with! {
68 pub fn allow_anonymous(mut self, allow: bool) -> Self {
73 self.allow_anonymous = allow;
74 self
75 }
76 }
77}
78
79impl<A: fmt::Debug, C> fmt::Debug for HttpAuthorizer<A, C> {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 f.debug_struct("HttpAuthorizer")
82 .field("authorizer", &self.authorizer)
83 .field("allow_anonymous", &self.allow_anonymous)
84 .field(
85 "_credentials",
86 &format_args!("{}", std::any::type_name::<fn() -> C>()),
87 )
88 .finish()
89 }
90}
91
92impl<A: Clone, C> Clone for HttpAuthorizer<A, C> {
93 fn clone(&self) -> Self {
94 Self {
95 authorizer: self.authorizer.clone(),
96 allow_anonymous: self.allow_anonymous,
97 _credentials: PhantomData,
98 }
99 }
100}
101
102impl<A, C> Authorizer<C> for HttpAuthorizer<A, C>
103where
104 A: Authorizer<C, Error: fmt::Debug>,
105 C: Credentials + Send + 'static,
106{
107 type Error = Response;
108
109 async fn authorize(&self, credentials: C) -> AuthorizeResult<C, Self::Error> {
110 let AuthorizeResult {
111 credentials,
112 result,
113 } = self.authorizer.authorize(credentials).await;
114
115 let result = result.map_err(|err| {
116 tracing::trace!("input credentials were not authorized: {err:?}");
117 let mut res = Response::new(Body::empty());
118 *res.status_mut() = StatusCode::UNAUTHORIZED;
119 if !C::SCHEME.is_empty() {
124 res.headers_mut().insert(
125 header::WWW_AUTHENTICATE,
126 HeaderValue::from_static(C::SCHEME),
127 );
128 }
129 res
130 });
131
132 AuthorizeResult {
133 credentials,
134 result,
135 }
136 }
137}
138
139impl<ReqBody, A, C> ValidateRequest<ReqBody> for HttpAuthorizer<A, C>
140where
141 ReqBody: Send + 'static,
142 A: Authorizer<C, Error: fmt::Debug>,
143 C: Credentials + Send + 'static,
144{
145 type ResponseBody = Body;
146
147 async fn validate(
148 &self,
149 request: Request<ReqBody>,
150 ) -> Result<Request<ReqBody>, Response<Self::ResponseBody>> {
151 match request.headers().typed_get::<Authorization<C>>() {
152 Some(auth) => {
153 let AuthorizeResult { result, .. } = self.authorize(auth.into_inner()).await;
154 match result {
155 Ok(maybe_ext) => {
156 if let Some(ext) = maybe_ext {
157 request.extensions().extend(&ext);
158 }
159 Ok(request)
160 }
161 Err(response) => Err(response),
162 }
163 }
164 None => {
165 if self.allow_anonymous {
166 request.extensions().insert(UserId::Anonymous);
167 Ok(request)
168 } else {
169 Err(StatusCode::UNAUTHORIZED.into_response())
170 }
171 }
172 }
173 }
174}
175
176impl<S, A, C> ValidateRequestHeader<S, HttpAuthorizer<A, C>> {
177 #[inline]
178 pub fn auth(inner: S, authorizer: impl Into<HttpAuthorizer<A, C>>) -> Self {
180 Self::custom(inner, authorizer.into())
181 }
182}
183
184impl<A, C> ValidateRequestHeaderLayer<HttpAuthorizer<A, C>> {
185 #[inline]
186 pub fn auth(authorizer: impl Into<HttpAuthorizer<A, C>>) -> Self {
188 Self::custom(authorizer.into())
189 }
190}