aep_tower/
authentication.rs1use std::{
2 sync::Arc,
3 task::{Context, Poll},
4};
5
6use aep_service::{ProtectedResourceAuthentication, ProtectedResourceRequest, Service};
7use futures::{FutureExt as _, future::BoxFuture};
8use http::{Request, Response};
9use http_body_util::{Either, Full};
10use tower::{Layer, Service as TowerService};
11use url::Url;
12
13use crate::{
14 TowerError,
15 response::{internal_error, service_response},
16 url::{request_url, validate_origin},
17};
18
19#[derive(Clone, Debug)]
20pub struct AuthenticationOptions {
21 pub allow_insecure_loopback: bool,
22 pub origin: Url,
23}
24
25impl AuthenticationOptions {
26 pub fn new(origin: Url) -> Self {
27 Self {
28 allow_insecure_loopback: false,
29 origin,
30 }
31 }
32}
33
34#[derive(Clone)]
35pub struct AuthenticationLayer {
36 options: AuthenticationOptions,
37 service: Arc<Service>,
38}
39
40impl AuthenticationLayer {
41 pub fn new(service: Arc<Service>, options: AuthenticationOptions) -> Result<Self, TowerError> {
42 validate_origin(&options.origin, options.allow_insecure_loopback)?;
43 Ok(Self { options, service })
44 }
45}
46
47impl<S> Layer<S> for AuthenticationLayer {
48 type Service = AuthenticationService<S>;
49
50 fn layer(&self, inner: S) -> Self::Service {
51 AuthenticationService {
52 inner,
53 options: self.options.clone(),
54 service: self.service.clone(),
55 }
56 }
57}
58
59#[derive(Clone)]
60pub struct AuthenticationService<S> {
61 inner: S,
62 options: AuthenticationOptions,
63 service: Arc<Service>,
64}
65
66impl<S, RequestBody, ResponseBody> TowerService<Request<RequestBody>> for AuthenticationService<S>
67where
68 S: TowerService<Request<RequestBody>, Response = Response<ResponseBody>>
69 + Clone
70 + Send
71 + 'static,
72 S::Future: Send + 'static,
73 S::Error: Send + 'static,
74 RequestBody: Send + 'static,
75 ResponseBody: Send + 'static,
76{
77 type Response = Response<Either<Full<bytes::Bytes>, ResponseBody>>;
78 type Error = S::Error;
79 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
80
81 fn poll_ready(&mut self, context: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
82 self.inner.poll_ready(context)
83 }
84
85 fn call(&mut self, mut request: Request<RequestBody>) -> Self::Future {
86 let clone = self.inner.clone();
87 let mut inner = std::mem::replace(&mut self.inner, clone);
88 let options = self.options.clone();
89 let service = self.service.clone();
90 async move {
91 let authentication = match build_protected_resource_request(&options, &request) {
92 Ok(request) => service
93 .authenticate_protected_resource(request)
94 .await
95 .map_err(TowerError::from),
96 Err(error) => Err(error),
97 };
98 match authentication {
99 Ok(ProtectedResourceAuthentication::Authenticated(principal)) => {
100 request.extensions_mut().insert(principal);
101 let response = inner.call(request).await?;
102 Ok(response.map(Either::Right))
103 }
104 Ok(ProtectedResourceAuthentication::Rejected(response)) => {
105 Ok(service_response(response)
106 .unwrap_or_else(|_| internal_error())
107 .map(Either::Left))
108 }
109 Err(_) => Ok(internal_error().map(Either::Left)),
110 }
111 }
112 .boxed()
113 }
114}
115
116pub fn protected_resource_request<B>(
117 options: &AuthenticationOptions,
118 request: &Request<B>,
119) -> Result<ProtectedResourceRequest, TowerError> {
120 validate_origin(&options.origin, options.allow_insecure_loopback)?;
121 build_protected_resource_request(options, request)
122}
123
124fn build_protected_resource_request<B>(
125 options: &AuthenticationOptions,
126 request: &Request<B>,
127) -> Result<ProtectedResourceRequest, TowerError> {
128 Ok(ProtectedResourceRequest {
129 headers: request.headers().clone(),
130 method: request.method().clone(),
131 url: request_url(&options.origin, request.uri())?,
132 })
133}