1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
use std::marker::PhantomData; use serde::{Deserialize, Serialize}; use merfolk::{ interfaces::{Backend, Middleware}, Call, Reply, }; use anyhow::Result; use thiserror::Error; use wildmatch::WildMatch; #[derive(Debug, Error)] pub enum Error { #[error("could not find scope for procedure: {procedure}")] ScopeNotFound { procedure: String }, #[error("auth was not provided")] NoAuth, #[error("authentication failed for {auth} in scope {scope:?}: {source}")] AuthenticationFailed { auth: String, scope: Option<String>, #[source] source: anyhow::Error, }, #[error("no vaildator was provided during init()")] NoValidatorRegistered, } #[derive(derive_builder::Builder)] #[builder(pattern = "owned")] pub struct Authentication<'a, B> { #[builder(private, default = "PhantomData")] __phantom: PhantomData<B>, #[builder(setter(into, strip_option), default = "None")] scopes: Option<Vec<(String, String)>>, #[allow(clippy::type_complexity)] #[builder(setter(name = "authenticator_setter", strip_option), private, default = "None")] authenticator: Option<Box<dyn Fn((String, String), Vec<String>) -> Result<()> + 'a + Send>>, #[builder(setter(into, strip_option), default = "None")] auth: Option<(String, String)>, } impl<'a, B> AuthenticationBuilder<'a, B> { pub fn authenticator<A>(self, value: A) -> Self where A: Fn((String, String), Vec<String>) -> Result<()> + 'a + Send, { self.authenticator_setter(Box::new(value)) } pub fn build_boxed(self) -> std::result::Result<Box<Authentication<'a, B>>, AuthenticationBuilderError> { self.build().map(Box::new) } } impl<'a, B> Authentication<'a, B> { pub fn builder() -> AuthenticationBuilder<'a, B> { AuthenticationBuilder::default() } } #[derive(Serialize, Deserialize)] struct Intermediate<B: Backend> { auth: (String, String), payload: B::Intermediate, } impl<'staic, B: Backend + 'static> Middleware for Authentication<'static, B> { type Backend = B; fn wrap_call(&self, call: Result<crate::Call<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Call<<Self::Backend as Backend>::Intermediate>> { let auth = self.auth.as_ref().ok_or(Error::NoAuth)?; if let Ok(call) = call { Ok(Call { procedure: call.procedure, payload: B::serialize(&Intermediate::<B> { auth: (auth.0.to_string(), auth.1.to_string()), payload: call.payload, })?, }) } else { call } } fn wrap_reply(&self, reply: Result<crate::Reply<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Reply<<Self::Backend as Backend>::Intermediate>> { reply } fn unwrap_call(&self, call: Result<crate::Call<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Call<<Self::Backend as Backend>::Intermediate>> { if let Ok(call) = call { let mut scope: Vec<String> = vec![]; if let Some(scopes) = &self.scopes { scopes.iter().for_each(|s| { if WildMatch::new(&s.0).is_match(call.procedure.as_str()) { scope.push(s.1.to_string()) }; }); } let intermediate: Intermediate<B> = B::deserialize(&call.payload)?; if let Err(err) = self.authenticator.as_ref().ok_or::<Error>(Error::NoValidatorRegistered)?(intermediate.auth, scope) { Err(err) } else { Ok(Call { procedure: call.procedure, payload: intermediate.payload, }) } } else { call } } fn unwrap_reply(&self, reply: Result<crate::Reply<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Reply<<Self::Backend as Backend>::Intermediate>> { reply } fn as_any(&mut self) -> &mut dyn core::any::Any { self } }