Skip to main content

aauth/server/policy/
test.rs

1use crate::interaction_code::generate_code;
2use crate::server::deferred::{DeferRequirement, PendingInput};
3use crate::types::AAuthProtocolError;
4
5use super::access::{AccessTokenContext, AccessTokenPolicy};
6use super::decision::{AuthGrant, PersonTokenDecision, ResourceConsentDecision, TokenPolicyDecision};
7use super::error::PolicyError;
8use super::person::{PersonTokenContext, PersonTokenPolicy};
9use super::resource::{ResourceAccessContext, ResourceConsentPolicy};
10
11#[derive(Debug, Clone, Default)]
12pub struct AlwaysGrantPersonPolicy {
13    pub sub: String,
14}
15
16impl AlwaysGrantPersonPolicy {
17    pub fn new(sub: impl Into<String>) -> Self {
18        Self { sub: sub.into() }
19    }
20}
21
22#[async_trait::async_trait]
23impl PersonTokenPolicy for AlwaysGrantPersonPolicy {
24    async fn evaluate(&self, ctx: &PersonTokenContext) -> Result<PersonTokenDecision, PolicyError> {
25        if ctx.audience_is_person_server() {
26            Ok(PersonTokenDecision::Grant(AuthGrant {
27                sub: self.sub.clone(),
28                scope: ctx.resource_claims.scope.clone(),
29            }))
30        } else {
31            Ok(PersonTokenDecision::Federate)
32        }
33    }
34
35    async fn resume(
36        &self,
37        ctx: &PersonTokenContext,
38        input: PendingInput,
39    ) -> Result<PersonTokenDecision, PolicyError> {
40        match input {
41            PendingInput::InteractionCompleted | PendingInput::ClarificationResponse(_) => {
42                self.evaluate(ctx).await
43            }
44            PendingInput::Cancelled => Ok(PersonTokenDecision::Deny(AAuthProtocolError {
45                error: "access_denied".into(),
46                error_description: Some("Request cancelled".into()),
47                error_uri: None,
48            })),
49            PendingInput::ClaimsSubmission(_) => Err(PolicyError::Message(
50                "claims submission not expected".into(),
51            )),
52        }
53    }
54}
55
56#[derive(Debug, Clone)]
57pub struct FixedSubPersonPolicy {
58    pub sub: String,
59}
60
61#[async_trait::async_trait]
62impl PersonTokenPolicy for FixedSubPersonPolicy {
63    async fn evaluate(&self, ctx: &PersonTokenContext) -> Result<PersonTokenDecision, PolicyError> {
64        AlwaysGrantPersonPolicy::new(&self.sub)
65            .evaluate(ctx)
66            .await
67    }
68
69    async fn resume(
70        &self,
71        ctx: &PersonTokenContext,
72        input: PendingInput,
73    ) -> Result<PersonTokenDecision, PolicyError> {
74        AlwaysGrantPersonPolicy::new(&self.sub)
75            .resume(ctx, input)
76            .await
77    }
78}
79
80#[derive(Debug, Clone, Default)]
81pub struct AlwaysGrantAccessPolicy {
82    pub sub: String,
83}
84
85impl AlwaysGrantAccessPolicy {
86    pub fn new(sub: impl Into<String>) -> Self {
87        Self { sub: sub.into() }
88    }
89}
90
91#[async_trait::async_trait]
92impl AccessTokenPolicy for AlwaysGrantAccessPolicy {
93    async fn evaluate(&self, ctx: &AccessTokenContext) -> Result<TokenPolicyDecision, PolicyError> {
94        Ok(TokenPolicyDecision::Grant(AuthGrant {
95            sub: self.sub.clone(),
96            scope: ctx.resource_claims.scope.clone(),
97        }))
98    }
99
100    async fn resume(
101        &self,
102        ctx: &AccessTokenContext,
103        input: PendingInput,
104    ) -> Result<TokenPolicyDecision, PolicyError> {
105        match input {
106            PendingInput::InteractionCompleted | PendingInput::ClarificationResponse(_) => {
107                self.evaluate(ctx).await
108            }
109            PendingInput::Cancelled => Ok(TokenPolicyDecision::Deny(AAuthProtocolError {
110                error: "access_denied".into(),
111                error_description: Some("Request cancelled".into()),
112                error_uri: None,
113            })),
114            PendingInput::ClaimsSubmission(_) => self.evaluate(ctx).await,
115        }
116    }
117}
118
119#[derive(Debug, Clone, Default)]
120pub struct AlwaysGrantResourcePolicy;
121
122#[async_trait::async_trait]
123impl ResourceConsentPolicy for AlwaysGrantResourcePolicy {
124    async fn evaluate(
125        &self,
126        _ctx: &ResourceAccessContext,
127    ) -> Result<ResourceConsentDecision, PolicyError> {
128        Ok(ResourceConsentDecision::GrantOpaque)
129    }
130
131    async fn resume(
132        &self,
133        _ctx: &ResourceAccessContext,
134        input: PendingInput,
135    ) -> Result<ResourceConsentDecision, PolicyError> {
136        match input {
137            PendingInput::InteractionCompleted => Ok(ResourceConsentDecision::GrantOpaque),
138            PendingInput::Cancelled => Ok(ResourceConsentDecision::Deny(AAuthProtocolError {
139                error: "access_denied".into(),
140                error_description: Some("Request cancelled".into()),
141                error_uri: None,
142            })),
143            _ => Ok(ResourceConsentDecision::GrantOpaque),
144        }
145    }
146}
147
148#[derive(Debug, Clone)]
149pub struct DeferInteractionResourcePolicy {
150    pub interaction_url: String,
151}
152
153#[async_trait::async_trait]
154impl ResourceConsentPolicy for DeferInteractionResourcePolicy {
155    async fn evaluate(
156        &self,
157        _ctx: &ResourceAccessContext,
158    ) -> Result<ResourceConsentDecision, PolicyError> {
159        Ok(ResourceConsentDecision::Defer(DeferRequirement::Interaction {
160            url: self.interaction_url.clone(),
161            code: generate_code(),
162        }))
163    }
164
165    async fn resume(
166        &self,
167        _ctx: &ResourceAccessContext,
168        input: PendingInput,
169    ) -> Result<ResourceConsentDecision, PolicyError> {
170        match input {
171            PendingInput::InteractionCompleted => Ok(ResourceConsentDecision::GrantOpaque),
172            PendingInput::Cancelled => Ok(ResourceConsentDecision::Deny(AAuthProtocolError {
173                error: "access_denied".into(),
174                error_description: Some("Request cancelled".into()),
175                error_uri: None,
176            })),
177            _ => Ok(ResourceConsentDecision::GrantOpaque),
178        }
179    }
180}
181
182#[derive(Debug, Clone)]
183pub struct ClarificationThenGrantPersonPolicy {
184    pub sub: String,
185    pub question: String,
186}
187
188#[async_trait::async_trait]
189impl PersonTokenPolicy for ClarificationThenGrantPersonPolicy {
190    async fn evaluate(&self, _ctx: &PersonTokenContext) -> Result<PersonTokenDecision, PolicyError> {
191        Ok(PersonTokenDecision::Defer(DeferRequirement::Clarification {
192            question: self.question.clone(),
193            timeout: None,
194        }))
195    }
196
197    async fn resume(
198        &self,
199        ctx: &PersonTokenContext,
200        input: PendingInput,
201    ) -> Result<PersonTokenDecision, PolicyError> {
202        match input {
203            PendingInput::ClarificationResponse(_) | PendingInput::InteractionCompleted => {
204                if ctx.audience_is_person_server() {
205                    Ok(PersonTokenDecision::Grant(AuthGrant {
206                        sub: self.sub.clone(),
207                        scope: ctx.resource_claims.scope.clone(),
208                    }))
209                } else {
210                    Ok(PersonTokenDecision::Federate)
211                }
212            }
213            PendingInput::Cancelled => Ok(PersonTokenDecision::Deny(AAuthProtocolError {
214                error: "access_denied".into(),
215                error_description: Some("Request cancelled".into()),
216                error_uri: None,
217            })),
218            PendingInput::ClaimsSubmission(_) => Err(PolicyError::Message(
219                "claims submission not expected".into(),
220            )),
221        }
222    }
223}
224
225#[derive(Debug, Clone)]
226pub struct DeferInteractionPersonPolicy<P> {
227    pub inner: P,
228    pub interaction_url: String,
229}
230
231#[async_trait::async_trait]
232impl<P> PersonTokenPolicy for DeferInteractionPersonPolicy<P>
233where
234    P: PersonTokenPolicy + Send + Sync + Clone,
235{
236    async fn evaluate(&self, _ctx: &PersonTokenContext) -> Result<PersonTokenDecision, PolicyError> {
237        Ok(PersonTokenDecision::Defer(DeferRequirement::Interaction {
238            url: self.interaction_url.clone(),
239            code: generate_code(),
240        }))
241    }
242
243    async fn resume(
244        &self,
245        ctx: &PersonTokenContext,
246        input: PendingInput,
247    ) -> Result<PersonTokenDecision, PolicyError> {
248        self.inner.resume(ctx, input).await
249    }
250}
251
252#[derive(Debug, Clone)]
253pub struct ClarificationThenGrantAccessPolicy {
254    pub sub: String,
255    pub question: String,
256}
257
258#[async_trait::async_trait]
259impl AccessTokenPolicy for ClarificationThenGrantAccessPolicy {
260    async fn evaluate(&self, _ctx: &AccessTokenContext) -> Result<TokenPolicyDecision, PolicyError> {
261        Ok(TokenPolicyDecision::Defer(DeferRequirement::Clarification {
262            question: self.question.clone(),
263            timeout: None,
264        }))
265    }
266
267    async fn resume(
268        &self,
269        ctx: &AccessTokenContext,
270        input: PendingInput,
271    ) -> Result<TokenPolicyDecision, PolicyError> {
272        match input {
273            PendingInput::ClarificationResponse(_) | PendingInput::InteractionCompleted => {
274                Ok(TokenPolicyDecision::Grant(AuthGrant {
275                    sub: self.sub.clone(),
276                    scope: ctx.resource_claims.scope.clone(),
277                }))
278            }
279            PendingInput::Cancelled => Ok(TokenPolicyDecision::Deny(AAuthProtocolError {
280                error: "access_denied".into(),
281                error_description: Some("Request cancelled".into()),
282                error_uri: None,
283            })),
284            PendingInput::ClaimsSubmission(_) => Err(PolicyError::Message(
285                "claims submission not expected".into(),
286            )),
287        }
288    }
289}
290
291#[derive(Debug, Clone)]
292pub struct DeferInteractionAccessPolicy<P> {
293    pub inner: P,
294    pub interaction_url: String,
295}
296
297#[async_trait::async_trait]
298impl<P> AccessTokenPolicy for DeferInteractionAccessPolicy<P>
299where
300    P: AccessTokenPolicy + Send + Sync + Clone,
301{
302    async fn evaluate(&self, _ctx: &AccessTokenContext) -> Result<TokenPolicyDecision, PolicyError> {
303        Ok(TokenPolicyDecision::Defer(DeferRequirement::Interaction {
304            url: self.interaction_url.clone(),
305            code: generate_code(),
306        }))
307    }
308
309    async fn resume(
310        &self,
311        ctx: &AccessTokenContext,
312        input: PendingInput,
313    ) -> Result<TokenPolicyDecision, PolicyError> {
314        self.inner.resume(ctx, input).await
315    }
316}
317
318#[derive(Debug, Clone)]
319pub struct DeferClaimsAccessPolicy {
320    pub sub: String,
321    pub required_claims: Vec<String>,
322}
323
324#[async_trait::async_trait]
325impl AccessTokenPolicy for DeferClaimsAccessPolicy {
326    async fn evaluate(&self, _ctx: &AccessTokenContext) -> Result<TokenPolicyDecision, PolicyError> {
327        Ok(TokenPolicyDecision::Defer(DeferRequirement::Claims {
328            required_claims: self.required_claims.clone(),
329        }))
330    }
331
332    async fn resume(
333        &self,
334        ctx: &AccessTokenContext,
335        input: PendingInput,
336    ) -> Result<TokenPolicyDecision, PolicyError> {
337        match input {
338            PendingInput::ClaimsSubmission(_) | PendingInput::InteractionCompleted => {
339                Ok(TokenPolicyDecision::Grant(AuthGrant {
340                    sub: self.sub.clone(),
341                    scope: ctx.resource_claims.scope.clone(),
342                }))
343            }
344            PendingInput::Cancelled => Ok(TokenPolicyDecision::Deny(AAuthProtocolError {
345                error: "access_denied".into(),
346                error_description: Some("Request cancelled".into()),
347                error_uri: None,
348            })),
349            PendingInput::ClarificationResponse(_) => Err(PolicyError::Message(
350                "clarification not expected".into(),
351            )),
352        }
353    }
354}
355
356#[derive(Debug, Clone, Default)]
357pub struct DeferApprovalAccessPolicy {
358    pub sub: String,
359}
360
361#[async_trait::async_trait]
362impl AccessTokenPolicy for DeferApprovalAccessPolicy {
363    async fn evaluate(&self, _ctx: &AccessTokenContext) -> Result<TokenPolicyDecision, PolicyError> {
364        Ok(TokenPolicyDecision::Defer(DeferRequirement::Approval))
365    }
366
367    async fn resume(
368        &self,
369        ctx: &AccessTokenContext,
370        input: PendingInput,
371    ) -> Result<TokenPolicyDecision, PolicyError> {
372        match input {
373            PendingInput::InteractionCompleted => Ok(TokenPolicyDecision::Grant(AuthGrant {
374                sub: self.sub.clone(),
375                scope: ctx.resource_claims.scope.clone(),
376            })),
377            PendingInput::Cancelled => Ok(TokenPolicyDecision::Deny(AAuthProtocolError {
378                error: "access_denied".into(),
379                error_description: Some("Request cancelled".into()),
380                error_uri: None,
381            })),
382            _ => Ok(TokenPolicyDecision::Grant(AuthGrant {
383                sub: self.sub.clone(),
384                scope: ctx.resource_claims.scope.clone(),
385            })),
386        }
387    }
388}