use actix_web::Either;
use super::{Authenticate, Authenticated};
pub trait EitherExt<T> {
fn data(&self) -> &T;
}
impl<L, R, T> EitherExt<T> for Either<Authenticated<L>, Authenticated<R>>
where
L: Authenticate<Output = T>,
R: Authenticate<Output = T>,
{
fn data(&self) -> &T {
match self {
Self::Left(value) => value.data(),
Self::Right(value) => value.data(),
}
}
}
#[cfg(test)]
mod tests {
use actix_http::Request;
use actix_web::{
body::MessageBody,
dev::{Service, ServiceResponse},
error, get, test,
web::Bytes,
App, Either, Error, HttpRequest, HttpResponse, Responder,
};
use super::EitherExt;
use crate::authentication::{Authenticate, Authenticated};
macro_rules! impl_authenticate {
($($name:ident),+) => {$(
struct $name(String);
impl Authenticate for $name {
type Output = String;
type Error = Error;
fn authenticate(request: &HttpRequest) -> Result<Self, Self::Error> {
let value = request
.headers()
.get(stringify!($name))
.ok_or(error::ErrorUnauthorized("unauthorized"))?
.to_str()
.map_err(|e| error::ErrorUnauthorized(e))?
.to_owned();
Ok(Self(value))
}
fn data(&self) -> &Self::Output {
&self.0
}
}
)+};
}
impl_authenticate!(A, B);
#[get("/")]
async fn get_data(
authentication: Either<Authenticated<A>, Authenticated<B>>,
) -> impl Responder {
HttpResponse::Ok().body(authentication.data().to_owned())
}
async fn create_app(
) -> impl Service<Request, Response = ServiceResponse<impl MessageBody>, Error = actix_web::Error>
{
test::init_service(App::new().service(get_data)).await
}
#[actix_web::test]
async fn either_authentication() {
let app = create_app().await;
let request = test::TestRequest::get()
.uri("/")
.insert_header(("A", "testA"))
.to_request();
let result = test::call_service(&app, request).await;
assert_eq!(result.status(), 200);
let body = test::read_body(result).await;
assert_eq!(body, Bytes::from_static(b"testA"));
let request = test::TestRequest::get()
.uri("/")
.insert_header(("B", "testB"))
.to_request();
let result = test::call_service(&app, request).await;
assert!(result.status().is_success());
let body = test::read_body(result).await;
assert_eq!(body, Bytes::from_static(b"testB"));
let request = test::TestRequest::get().uri("/").to_request();
let result = test::call_service(&app, request).await;
assert!(result.status().is_client_error());
}
}