unitycatalog_server/rest/
auth.rs1use std::marker::PhantomData;
3use std::task::{Context, Poll};
4
5use axum::extract::Request;
6use axum::response::{IntoResponse, Response};
7use futures_util::{FutureExt, future::BoxFuture};
8use tower::{Layer, Service};
9use unitycatalog_common::Result;
10
11use crate::api::RequestContext;
12use crate::policy::Principal;
13
14impl<S: Send + Sync> axum::extract::FromRequestParts<S> for RequestContext {
23 type Rejection = std::convert::Infallible;
24
25 async fn from_request_parts(
26 parts: &mut axum::http::request::Parts,
27 _state: &S,
28 ) -> std::result::Result<Self, Self::Rejection> {
29 let recipient = parts
30 .extensions
31 .get::<Principal>()
32 .cloned()
33 .unwrap_or_else(Principal::anonymous);
34 Ok(RequestContext { recipient })
35 }
36}
37
38pub trait Authenticator<I: Clone + Send + Sync + 'static>: Send + Sync + 'static {
43 fn authenticate(&self, request: &Request) -> Result<I>;
48}
49
50#[derive(Clone)]
61pub struct AnonymousAuthenticator;
62
63impl Authenticator<Principal> for AnonymousAuthenticator {
64 fn authenticate(&self, _: &Request) -> Result<Principal> {
65 Ok(Principal::anonymous())
67 }
68}
69
70#[derive(Clone)]
72pub struct AuthenticationMiddleware<S, T, I = Principal> {
73 inner: S,
74 authenticator: T,
75 _identity: PhantomData<I>,
76}
77
78#[allow(unused)]
79impl<S, T, I> AuthenticationMiddleware<S, T, I> {
80 pub fn new(inner: S, authenticator: T) -> Self {
82 Self {
83 inner,
84 authenticator,
85 _identity: PhantomData,
86 }
87 }
88
89 pub fn layer(authenticator: T) -> AuthenticationLayer<T, I> {
93 AuthenticationLayer::new(authenticator)
94 }
95}
96
97impl<S, T, I> Service<Request> for AuthenticationMiddleware<S, T, I>
98where
99 S: Service<Request, Response = Response> + Send + 'static,
100 S::Future: Send + 'static,
101 T: Authenticator<I>,
102 I: Clone + Send + Sync + 'static,
103{
104 type Response = S::Response;
105 type Error = S::Error;
106 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
107
108 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
109 self.inner.poll_ready(cx)
110 }
111
112 fn call(&mut self, mut req: Request) -> Self::Future {
113 match self.authenticator.authenticate(&req) {
114 Ok(identity) => {
115 req.extensions_mut().insert(identity);
116 self.inner.call(req).boxed()
117 }
118 Err(e) => async { Ok(crate::Error::from(e).into_response()) }.boxed(),
119 }
120 }
121}
122
123#[derive(Clone)]
125pub struct AuthenticationLayer<T, I = Principal> {
126 authenticator: T,
127 _identity: PhantomData<I>,
128}
129
130impl<T, I> AuthenticationLayer<T, I> {
131 pub fn new(authenticator: T) -> Self {
133 Self {
134 authenticator,
135 _identity: PhantomData,
136 }
137 }
138}
139
140impl<S, T, I> Layer<S> for AuthenticationLayer<T, I>
141where
142 T: Clone + Send + Sync + 'static,
143 I: Clone + Send + Sync + 'static,
144{
145 type Service = AuthenticationMiddleware<S, T, I>;
146
147 fn layer(&self, inner: S) -> Self::Service {
148 AuthenticationMiddleware {
149 inner,
150 authenticator: self.authenticator.clone(),
151 _identity: PhantomData,
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use axum::body::Body;
159 use axum::extract::Request;
160 use axum::http::{StatusCode, header};
161 use tower::{ServiceBuilder, ServiceExt};
162
163 use super::*;
164
165 async fn check_recipient(req: Request) -> Result<Response<Body>> {
166 assert!(matches!(
167 req.extensions().get::<Principal>(),
168 Some(Principal::Anonymous) | Some(Principal::User(_))
169 ));
170 Ok(Response::new(req.into_body()))
171 }
172
173 #[tokio::test]
174 async fn test_authentication_middleware() {
175 let authenticator = AnonymousAuthenticator {};
176 let mut service = ServiceBuilder::new()
177 .layer(AuthenticationLayer::new(authenticator))
178 .service_fn(check_recipient);
179
180 let request = Request::get("/")
181 .header(header::AUTHORIZATION, "Bearer foo")
182 .body(Body::empty())
183 .unwrap();
184
185 let response = service.ready().await.unwrap().call(request).await.unwrap();
186 assert_eq!(response.status(), StatusCode::OK);
187
188 let request = Request::get("/").body(Body::empty()).unwrap();
189 let response = service.ready().await.unwrap().call(request).await.unwrap();
190 assert_eq!(response.status(), StatusCode::OK);
191 }
192}