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 = service
92 .authenticate_protected_resource(build_protected_resource_request(
93 &options, &request,
94 ))
95 .await
96 .map_err(TowerError::from);
97 match authentication {
98 Ok(ProtectedResourceAuthentication::Authenticated(principal)) => {
99 request.extensions_mut().insert(principal);
100 let response = inner.call(request).await?;
101 Ok(response.map(Either::Right))
102 }
103 Ok(ProtectedResourceAuthentication::Rejected(response)) => {
104 Ok(service_response(response)
105 .unwrap_or_else(|_| internal_error())
106 .map(Either::Left))
107 }
108 Err(_) => Ok(internal_error().map(Either::Left)),
109 }
110 }
111 .boxed()
112 }
113}
114
115pub fn protected_resource_request<B>(
116 options: &AuthenticationOptions,
117 request: &Request<B>,
118) -> Result<ProtectedResourceRequest, TowerError> {
119 validate_origin(&options.origin, options.allow_insecure_loopback)?;
120 Ok(build_protected_resource_request(options, request))
121}
122
123fn build_protected_resource_request<B>(
124 options: &AuthenticationOptions,
125 request: &Request<B>,
126) -> ProtectedResourceRequest {
127 ProtectedResourceRequest {
128 headers: request.headers().clone(),
129 method: request.method().clone(),
130 url: request_url(&options.origin, request.uri()),
131 }
132}