graphql_starter/graphql/
guard.rs

1use std::marker::PhantomData;
2
3use async_graphql::{Context, Guard, Result};
4
5use crate::{
6    auth::{AuthErrorCode, AuthState, AuthorizationService, Subject},
7    error::{err, GraphQLError},
8};
9
10/// Authorization [Guard].
11///
12/// This guard will use the `Option<S>` and the state from the GraphQL context
13/// to authorize an action, failing if they're not available.
14pub struct AuthGuard<S: Subject, St: AuthState<S>> {
15    relation: &'static str,
16    object: &'static str,
17    // Needed for the compiler
18    sub_type: PhantomData<S>,
19    state_type: PhantomData<St>,
20}
21
22impl<S: Subject, St: AuthState<S>> AuthGuard<S, St> {
23    /// Creates a new authorization guard for a given relation of an object.
24    pub fn new(relation: &'static str, object: &'static str) -> Self {
25        AuthGuard {
26            relation,
27            object,
28            sub_type: PhantomData,
29            state_type: PhantomData,
30        }
31    }
32}
33
34impl<S: Subject, St: AuthState<S>> Guard for AuthGuard<S, St> {
35    async fn check(&self, ctx: &Context<'_>) -> Result<()> {
36        let sub = ctx.data::<Option<S>>().map_err(Box::<GraphQLError>::from)?.as_ref();
37        match sub {
38            Some(sub) => {
39                let state = ctx.data::<St>().map_err(Box::<GraphQLError>::from)?;
40                Ok(state.authz().authorize(sub, self.relation, self.object).await?)
41            }
42            None => Err(
43                GraphQLError::from_err(err!(AuthErrorCode::AuthMissing, "The subject must be authenticated")).into(),
44            ),
45        }
46    }
47}