mod error; use std::any::{Any, TypeId}; use std::collections::HashMap; use std::marker::PhantomData; use std::ops::Deref; use actix_web::FromRequest; use futures::future::err; use futures::future::{ok, Ready}; use crate::error::ResponseError; use error::AuthenticationError; macro_rules! create_policies { ($($name:ident), *) => { pub mod policies { use std::collections::HashSet; use crate::extractors::authentication::Policy; $( #[derive(Debug, Default)] pub struct $name { inner: HashSet> } impl $name { pub fn new() -> Self { Self { inner: HashSet::new() } } pub fn add(&mut self, token: Vec) { self.inner.insert(token); } } impl Policy for $name { fn authenticate(&self, token: &[u8]) -> bool { self.inner.contains(token) } } )* } }; } create_policies!(Public, Private, Admin); /// Instanciate a `Policies`, filled with the given policies. macro_rules! init_policies { ($($name:ident), *) => { { let mut policies = crate::extractors::authentication::Policies::new(); $( let policy = $name::new(); policies.insert(policy); )* policies } }; } /// Adds user to all specified policies. macro_rules! create_users { ($policies:ident, $($user:expr => { $($policy:ty), * }), *) => { { $( $( $policies.get_mut::<$policy>().map(|p| p.add($user.to_owned())); )* )* } }; } pub struct GuardedData { data: D, _marker: PhantomData, } impl Deref for GuardedData { type Target = D; fn deref(&self) -> &Self::Target { &self.data } } pub trait Policy { fn authenticate(&self, token: &[u8]) -> bool; } #[derive(Debug)] pub struct Policies { inner: HashMap>, } impl Policies { pub fn new() -> Self { Self { inner: HashMap::new(), } } pub fn insert(&mut self, policy: S) { self.inner.insert(TypeId::of::(), Box::new(policy)); } pub fn get(&self) -> Option<&S> { self.inner .get(&TypeId::of::()) .and_then(|p| p.downcast_ref::()) } pub fn get_mut(&mut self) -> Option<&mut S> { self.inner .get_mut(&TypeId::of::()) .and_then(|p| p.downcast_mut::()) } } impl Default for Policies { fn default() -> Self { Self::new() } } pub enum AuthConfig { NoAuth, Auth(Policies), } impl Default for AuthConfig { fn default() -> Self { Self::NoAuth } } impl FromRequest for GuardedData { type Config = AuthConfig; type Error = ResponseError; type Future = Ready>; fn from_request( req: &actix_web::HttpRequest, _payload: &mut actix_web::dev::Payload, ) -> Self::Future { match req.app_data::() { Some(config) => match config { AuthConfig::NoAuth => match req.app_data::().cloned() { Some(data) => ok(Self { data, _marker: PhantomData, }), None => err(AuthenticationError::IrretrievableState.into()), }, AuthConfig::Auth(policies) => match policies.get::

() { Some(policy) => match req.headers().get("x-meili-api-key") { Some(token) => { if policy.authenticate(token.as_bytes()) { match req.app_data::().cloned() { Some(data) => ok(Self { data, _marker: PhantomData, }), None => err(AuthenticationError::IrretrievableState.into()), } } else { err(AuthenticationError::InvalidToken(String::from("hello")).into()) } } None => err(AuthenticationError::MissingAuthorizationHeader.into()), }, None => err(AuthenticationError::UnknownPolicy.into()), }, }, None => err(AuthenticationError::IrretrievableState.into()), } } }