Skip to main content

surrealdb_sql/
access_type.rs

1use rand::distr::{Alphanumeric, SampleString};
2use surrealdb_types::{SqlFormat, ToSql, write_sql};
3
4use super::Expr;
5use crate::{Algorithm, CoverStmts, Literal};
6
7pub fn random_key() -> String {
8	Alphanumeric.sample_string(&mut rand::rng(), 128)
9}
10
11/// The type of access methods available
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
14pub enum AccessType {
15	Record(Box<RecordAccess>),
16	Jwt(JwtAccess),
17	Bearer(BearerAccess),
18}
19
20impl Default for AccessType {
21	fn default() -> Self {
22		// Access type defaults to the most specific
23		Self::Record(Box::new(RecordAccess {
24			..Default::default()
25		}))
26	}
27}
28
29impl ToSql for AccessType {
30	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
31		match self {
32			AccessType::Jwt(ac) => {
33				write_sql!(f, sql_fmt, "JWT {}", ac);
34			}
35			AccessType::Record(ac) => {
36				write_sql!(f, sql_fmt, "RECORD");
37				if let Some(ref v) = ac.signup {
38					write_sql!(f, sql_fmt, " SIGNUP {}", CoverStmts(v));
39				}
40				if let Some(ref v) = ac.signin {
41					write_sql!(f, sql_fmt, " SIGNIN {}", CoverStmts(v));
42				}
43				if ac.bearer.is_some() {
44					write_sql!(f, sql_fmt, " WITH REFRESH")
45				}
46				write_sql!(f, sql_fmt, " WITH JWT {}", ac.jwt);
47			}
48			AccessType::Bearer(ac) => {
49				write_sql!(f, sql_fmt, "BEARER");
50				match ac.subject {
51					BearerAccessSubject::User => write_sql!(f, sql_fmt, " FOR USER"),
52					BearerAccessSubject::Record => write_sql!(f, sql_fmt, " FOR RECORD"),
53				}
54			}
55		}
56	}
57}
58
59impl AccessType {
60	/// Returns whether or not the access method can issue non-token grants
61	/// In this context, token refers exclusively to JWT
62	#[allow(dead_code)]
63	pub fn can_issue_grants(&self) -> bool {
64		match self {
65			// The JWT access method cannot issue stateful grants.
66			AccessType::Jwt(_) => false,
67			// The record access method can be used to issue grants if defined with bearer AKA
68			// refresh.
69			AccessType::Record(ac) => ac.bearer.is_some(),
70			AccessType::Bearer(_) => true,
71		}
72	}
73	/// Returns whether or not the access method can issue tokens
74	/// In this context, tokens refers exclusively to JWT
75	#[allow(dead_code)]
76	pub fn can_issue_tokens(&self) -> bool {
77		match self {
78			// The JWT access method can only issue tokens if an issuer is set
79			AccessType::Jwt(jwt) => jwt.issue.is_some(),
80			_ => true,
81		}
82	}
83}
84
85#[derive(Debug, Clone, Eq, PartialEq)]
86pub struct JwtAccess {
87	// Verify is required
88	pub verify: JwtAccessVerify,
89	// Issue is optional
90	// It is possible to only verify externally issued tokens
91	pub issue: Option<JwtAccessIssue>,
92}
93
94//TODO: Move this logic out of the parser
95impl Default for JwtAccess {
96	fn default() -> Self {
97		// Defaults to HS512 with a randomly generated key
98		let alg = Algorithm::Hs512;
99		let key = random_key();
100		// By default the access method can verify and issue tokens
101		Self {
102			verify: JwtAccessVerify::Key(JwtAccessVerifyKey {
103				alg,
104				key: Expr::Literal(Literal::String(key.as_str().into())),
105			}),
106			issue: Some(JwtAccessIssue {
107				alg,
108				key: Expr::Literal(Literal::String(key.into())),
109			}),
110		}
111	}
112}
113
114impl ToSql for JwtAccess {
115	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
116		match &self.verify {
117			JwtAccessVerify::Key(v) => {
118				write_sql!(f, sql_fmt, "ALGORITHM {} KEY {}", v.alg, CoverStmts(&v.key));
119			}
120			JwtAccessVerify::Jwks(v) => {
121				write_sql!(f, sql_fmt, "URL {}", CoverStmts(&v.url));
122			}
123		}
124		if let Some(iss) = &self.issue {
125			write_sql!(f, sql_fmt, " WITH ISSUER KEY {}", CoverStmts(&iss.key));
126		}
127	}
128}
129
130#[derive(Debug, Clone, Eq, PartialEq)]
131#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
132pub struct JwtAccessIssue {
133	pub alg: Algorithm,
134	pub key: Expr,
135}
136
137impl Default for JwtAccessIssue {
138	fn default() -> Self {
139		// TODO: Move this computation out of the AST
140		Self {
141			// Defaults to HS512
142			alg: Algorithm::Hs512,
143			// Avoid defaulting to empty key
144			key: Expr::Literal(Literal::String(random_key().into())),
145		}
146	}
147}
148
149#[derive(Debug, Clone, Eq, PartialEq)]
150#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
151pub enum JwtAccessVerify {
152	Key(JwtAccessVerifyKey),
153	Jwks(JwtAccessVerifyJwks),
154}
155
156#[derive(Debug, Clone, Eq, PartialEq)]
157#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
158pub struct JwtAccessVerifyKey {
159	pub alg: Algorithm,
160	pub key: Expr,
161}
162
163#[derive(Debug, Clone, Eq, PartialEq)]
164#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
165pub struct JwtAccessVerifyJwks {
166	pub url: Expr,
167}
168
169#[derive(Debug, Clone, Default, PartialEq, Eq)]
170#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
171pub struct RecordAccess {
172	pub signup: Option<Expr>,
173	pub signin: Option<Expr>,
174	pub jwt: JwtAccess,
175	pub bearer: Option<BearerAccess>,
176}
177
178#[derive(Debug, Clone, Eq, PartialEq)]
179#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
180pub struct BearerAccess {
181	pub kind: BearerAccessType,
182	pub subject: BearerAccessSubject,
183	pub jwt: JwtAccess,
184}
185
186impl Default for BearerAccess {
187	fn default() -> Self {
188		Self {
189			kind: BearerAccessType::Bearer,
190			subject: BearerAccessSubject::User,
191			jwt: JwtAccess::default(),
192		}
193	}
194}
195
196#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd)]
197#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
198pub enum BearerAccessType {
199	Bearer,
200	Refresh,
201}
202
203#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd)]
204#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
205pub enum BearerAccessSubject {
206	Record,
207	User,
208}