drogue_bazaar/actix/auth/authorization/
mod.rs1use crate::auth::{AuthError, UserInformation};
2use actix_web::dev::ServiceRequest;
3use async_trait::async_trait;
4use drogue_client::user::v1::authz::Outcome;
5use std::sync::Arc;
6use tracing::instrument;
7
8mod app;
9mod middleware;
10
11pub use app::*;
12
13#[derive(Clone)]
18pub struct AuthZ {
19 pub authorizer: Arc<dyn Authorizer>,
20}
21
22impl AuthZ {
23 pub fn new<A>(authorizer: A) -> Self
24 where
25 A: Authorizer + 'static,
26 {
27 Self {
28 authorizer: Arc::new(authorizer),
29 }
30 }
31
32 #[instrument(skip_all, err)]
34 pub async fn authorize(&self, context: Context<'_>) -> Result<(), AuthError> {
35 match self.authorizer.authorize(&context).await {
36 Ok(Some(Outcome::Allow)) => Ok(()),
37 Ok(None) | Ok(Some(Outcome::Deny)) => Err(AuthError::Forbidden),
38 Err(err) => Err(err),
39 }
40 }
41}
42
43pub struct Context<'a> {
44 pub request: &'a ServiceRequest,
45 pub identity: &'a UserInformation,
46}
47
48#[async_trait(?Send)]
49pub trait Authorizer {
50 async fn authorize(&self, context: &Context<'_>) -> Result<Option<Outcome>, AuthError>;
63}
64
65#[async_trait(?Send)]
67impl Authorizer for Vec<Box<dyn Authorizer>> {
68 async fn authorize(&self, context: &Context<'_>) -> Result<Option<Outcome>, AuthError> {
69 for a in self {
70 match a.authorize(context).await? {
71 None => {
72 }
74 Some(outcome) => return Ok(Some(outcome)),
75 }
76 }
77
78 Ok(None)
80 }
81}
82
83#[async_trait(?Send)]
84impl<A: Authorizer> Authorizer for Option<A> {
85 async fn authorize(&self, context: &Context<'_>) -> Result<Option<Outcome>, AuthError> {
86 match &self {
87 Some(authorizer) => authorizer.authorize(context).await,
88 None => Ok(None),
89 }
90 }
91}
92
93pub struct OrElseAuthorizer<A>(A, Outcome)
95where
96 A: Authorizer;
97
98#[async_trait(?Send)]
99impl<A> Authorizer for OrElseAuthorizer<A>
100where
101 A: Authorizer,
102{
103 async fn authorize(&self, context: &Context<'_>) -> Result<Option<Outcome>, AuthError> {
104 self.0
105 .authorize(context)
106 .await
107 .map(|r| Some(r.unwrap_or(self.1)))
108 }
109}
110
111pub struct IntoNotFound<A, F>(A, F)
112where
113 A: Authorizer,
114 F: Fn(&Context) -> (String, String);
115
116#[async_trait(?Send)]
117impl<A, F> Authorizer for IntoNotFound<A, F>
118where
119 A: Authorizer,
120 F: Fn(&Context) -> (String, String),
121{
122 async fn authorize(&self, context: &Context<'_>) -> Result<Option<Outcome>, AuthError> {
123 match self.0.authorize(context).await {
124 Ok(None) => Ok(None),
125 Ok(Some(Outcome::Allow)) => Ok(Some(Outcome::Allow)),
126 Ok(Some(Outcome::Deny)) => {
127 let (r#type, id) = self.1(context);
128 Err(AuthError::NotFound(r#type, id))
129 }
130 Err(err) => Err(err),
131 }
132 }
133}
134
135pub trait AuthorizerExt: Authorizer + Sized {
136 fn or_else(self, outcome: Outcome) -> OrElseAuthorizer<Self> {
137 OrElseAuthorizer(self, outcome)
138 }
139
140 fn or_else_allow(self) -> OrElseAuthorizer<Self> {
141 self.or_else(Outcome::Allow)
142 }
143
144 fn or_else_deny(self) -> OrElseAuthorizer<Self> {
145 self.or_else(Outcome::Deny)
146 }
147
148 fn into_not_found<F>(self, f: F) -> IntoNotFound<Self, F>
149 where
150 F: Fn(&Context) -> (String, String),
151 {
152 IntoNotFound(self, f)
153 }
154}
155
156impl<A> AuthorizerExt for A where A: Authorizer {}
157
158pub struct NotAnonymous;
160
161#[async_trait(?Send)]
162impl Authorizer for NotAnonymous {
163 async fn authorize(&self, context: &Context<'_>) -> Result<Option<Outcome>, AuthError> {
164 Ok(match context.identity {
165 UserInformation::Anonymous => Some(Outcome::Deny),
166 UserInformation::Authenticated(_) => None,
167 })
168 }
169}