grafbase_sdk/types/
authorization.rs

1use crate::{
2    types::{AsHeaderName, AsHeaderValue, Headers},
3    wit,
4};
5
6use super::Error;
7
8/// Error identifier to allow re-using the same error for multiple elements. In the gateway
9/// response, the error will be repeated if necessary during serialization.
10#[derive(Clone, Copy)]
11pub struct ErrorId(u32);
12
13/// Output type for the [authorize_query()](crate::AuthorizationExtension::authorize_query())
14/// method.
15pub struct AuthorizeQueryOutput {
16    /// Authorization decisions for each query element to be applied by the GraphQL engine.
17    pub(crate) decisions: AuthorizationDecisions,
18    /// Authorization context if any.
19    pub(crate) context: Vec<u8>,
20    /// Authorization state if any.
21    pub(crate) state: Vec<u8>,
22    /// Additional headers to add to the subgraph headers if any.
23    pub(crate) additional_headers: Option<Headers>,
24}
25
26impl AuthorizeQueryOutput {
27    /// Create a new `AuthorizeQueryOutput` with the given decisions.
28    pub fn new(decisions: AuthorizationDecisions) -> Self {
29        Self {
30            decisions,
31            context: Vec::new(),
32            state: Vec::new(),
33            additional_headers: None,
34        }
35    }
36
37    /// Set the authorization context for the request and extension.
38    /// Accessible by other extensions.
39    pub fn context(mut self, context: impl Into<Vec<u8>>) -> Self {
40        self.context = context.into();
41        self
42    }
43
44    /// Set the authorization state for the request.
45    /// Only accessible by [authorize_response()](crate::AuthorizationExtension::authorize_response())
46    /// of the same extensions.
47    pub fn state(mut self, state: impl Into<Vec<u8>>) -> Self {
48        self.state = state.into();
49        self
50    }
51
52    /// Add a single additional header to the subgraph headers.
53    pub fn header(mut self, name: impl AsHeaderName, value: impl AsHeaderValue) -> Self {
54        let headers = self.additional_headers.get_or_insert_default();
55        headers.append(name, value);
56        self
57    }
58
59    /// Set additional headers to be added to the subgraph headers.
60    pub fn headers(mut self, headers: Headers) -> Self {
61        self.additional_headers = Some(headers);
62        self
63    }
64}
65
66/// Authorization decisions for each query elements to be applied by the GraphQL engine.
67pub struct AuthorizationDecisions(wit::AuthorizationDecisions);
68
69impl From<AuthorizationDecisions> for wit::AuthorizationDecisions {
70    fn from(value: AuthorizationDecisions) -> Self {
71        value.0
72    }
73}
74
75impl AuthorizationDecisions {
76    /// Grant access all elements in the query.
77    pub fn grant_all() -> Self {
78        Self(wit::AuthorizationDecisions::GrantAll)
79    }
80
81    /// Deny access to all elements in the query with the specified error
82    pub fn deny_all(error: impl Into<Error>) -> Self {
83        Self(wit::AuthorizationDecisions::DenyAll(Into::<Error>::into(error).into()))
84    }
85
86    /// Create a `DenySomeBuilder` best suited to deny some elements. By
87    /// default, all elements are granted access.
88    pub fn deny_some_builder() -> DenySomeBuilder {
89        DenySomeBuilder(wit::AuthorizationDecisionsDenySome {
90            element_to_error: Vec::new(),
91            errors: Vec::new(),
92        })
93    }
94}
95
96/// To be used when denying some of the elements. By default everything is granted.
97pub struct DenySomeBuilder(wit::AuthorizationDecisionsDenySome);
98
99impl DenySomeBuilder {
100    /// Deny access to the specified element in the query with the specified error.
101    pub fn deny(&mut self, x: impl private::QueryElementOrResponseItem, error: impl Into<Error>) {
102        let error_id = self.push_error(error);
103        self.deny_with_error_id(x, error_id)
104    }
105
106    /// Deny access to the specified element in the query, re-using an existing error.
107    pub fn deny_with_error_id(&mut self, x: impl private::QueryElementOrResponseItem, error_id: ErrorId) {
108        self.0.element_to_error.push((x.ix(), error_id.0));
109    }
110
111    /// Returns an ErrorId that can be used to reference this error later in `deny_with_error_id`.
112    pub fn push_error(&mut self, error: impl Into<Error>) -> ErrorId {
113        let error_ix = self.0.errors.len() as u32;
114        self.0.errors.push(Into::<Error>::into(error).into());
115        ErrorId(error_ix)
116    }
117
118    /// Build the final AuthorizationDecisions
119    pub fn build(self) -> AuthorizationDecisions {
120        AuthorizationDecisions(wit::AuthorizationDecisions::DenySome(self.0))
121    }
122}
123
124pub(super) mod private {
125    /// Either a `QueryElement` or a `ResponseItem`.
126    pub trait QueryElementOrResponseItem: crate::sealed::Sealed {
127        #[doc(hidden)]
128        fn ix(&self) -> u32;
129    }
130}