1use common::fmt::Fmt;
2use rand::distr::{Alphanumeric, SampleString};
3use surrealdb_types::{SqlFormat, ToSql, write_sql};
4
5use super::Expr;
6use crate::{Algorithm, CoverStmts, Literal};
7
8pub fn random_key() -> String {
9 Alphanumeric.sample_string(&mut rand::rng(), 128)
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15pub enum AccessType {
16 Record(Box<RecordAccess>),
17 Jwt(JwtAccess),
18 Bearer(BearerAccess),
19}
20
21impl Default for AccessType {
22 fn default() -> Self {
23 Self::Record(Box::new(RecordAccess {
25 ..Default::default()
26 }))
27 }
28}
29
30impl ToSql for AccessType {
31 fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
32 match self {
33 AccessType::Jwt(ac) => {
34 write_sql!(f, sql_fmt, "JWT {}", ac);
35 }
36 AccessType::Record(ac) => {
37 write_sql!(f, sql_fmt, "RECORD");
38 if let Some(ref v) = ac.signup {
39 write_sql!(f, sql_fmt, " SIGNUP {}", CoverStmts(v));
40 }
41 if let Some(ref v) = ac.signin {
42 write_sql!(f, sql_fmt, " SIGNIN {}", CoverStmts(v));
43 }
44 if ac.bearer.is_some() {
45 write_sql!(f, sql_fmt, " WITH REFRESH")
46 }
47 write_sql!(f, sql_fmt, " WITH JWT {}", ac.jwt);
48 }
49 AccessType::Bearer(ac) => {
50 write_sql!(f, sql_fmt, "BEARER");
51 match ac.subject {
52 BearerAccessSubject::User => write_sql!(f, sql_fmt, " FOR USER"),
53 BearerAccessSubject::Record => write_sql!(f, sql_fmt, " FOR RECORD"),
54 }
55 }
72 }
73 }
74}
75
76impl AccessType {
77 #[allow(dead_code)]
80 pub fn can_issue_grants(&self) -> bool {
81 match self {
82 AccessType::Jwt(_) => false,
84 AccessType::Record(ac) => ac.bearer.is_some(),
87 AccessType::Bearer(_) => true,
88 }
89 }
90 #[allow(dead_code)]
93 pub fn can_issue_tokens(&self) -> bool {
94 match self {
95 AccessType::Jwt(jwt) => jwt.issue.is_some(),
97 _ => true,
98 }
99 }
100}
101
102#[derive(Debug, Clone, Eq, PartialEq)]
103pub struct JwtAccess {
104 pub verify: JwtAccessVerify,
106 pub issue: Option<JwtAccessIssue>,
109 pub audience: Option<Vec<Expr>>,
112}
113
114impl Default for JwtAccess {
116 fn default() -> Self {
117 let alg = Algorithm::Hs512;
119 let key = random_key();
120 Self {
122 verify: JwtAccessVerify::Key(JwtAccessVerifyKey {
123 alg,
124 key: Expr::Literal(Literal::String(key.as_str().into())),
125 }),
126 issue: Some(JwtAccessIssue {
127 alg,
128 key: Expr::Literal(Literal::String(key.into())),
129 }),
130 audience: None,
131 }
132 }
133}
134
135impl ToSql for JwtAccess {
136 fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
137 match &self.verify {
138 JwtAccessVerify::Key(v) => {
139 write_sql!(f, sql_fmt, "ALGORITHM {} KEY {}", v.alg, CoverStmts(&v.key));
140 }
141 JwtAccessVerify::Jwks(v) => {
142 write_sql!(f, sql_fmt, "URL {}", CoverStmts(&v.url));
143 }
144 }
145 if let Some(aud) = &self.audience {
146 write_sql!(
147 f,
148 sql_fmt,
149 " AUDIENCE {}",
150 Fmt::comma_separated(aud.iter().map(CoverStmts))
151 );
152 }
153 if let Some(iss) = &self.issue {
154 write_sql!(f, sql_fmt, " WITH ISSUER KEY {}", CoverStmts(&iss.key));
155 }
156 }
157}
158
159#[derive(Debug, Clone, Eq, PartialEq)]
160#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
161pub struct JwtAccessIssue {
162 pub alg: Algorithm,
163 pub key: Expr,
164}
165
166impl Default for JwtAccessIssue {
167 fn default() -> Self {
168 Self {
170 alg: Algorithm::Hs512,
172 key: Expr::Literal(Literal::String(random_key().into())),
174 }
175 }
176}
177
178#[derive(Debug, Clone, Eq, PartialEq)]
179#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
180pub enum JwtAccessVerify {
181 Key(JwtAccessVerifyKey),
182 Jwks(JwtAccessVerifyJwks),
183}
184
185#[derive(Debug, Clone, Eq, PartialEq)]
186#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
187pub struct JwtAccessVerifyKey {
188 pub alg: Algorithm,
189 pub key: Expr,
190}
191
192#[derive(Debug, Clone, Eq, PartialEq)]
193#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
194pub struct JwtAccessVerifyJwks {
195 pub url: Expr,
196}
197
198#[derive(Debug, Clone, Default, PartialEq, Eq)]
199#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
200pub struct RecordAccess {
201 pub signup: Option<Expr>,
202 pub signin: Option<Expr>,
203 pub jwt: JwtAccess,
204 pub bearer: Option<BearerAccess>,
205}
206
207#[derive(Debug, Clone, Eq, PartialEq)]
208#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
209pub struct BearerAccess {
210 pub kind: BearerAccessType,
211 pub subject: BearerAccessSubject,
212 pub jwt: JwtAccess,
213}
214
215impl Default for BearerAccess {
216 fn default() -> Self {
217 Self {
218 kind: BearerAccessType::Bearer,
219 subject: BearerAccessSubject::User,
220 jwt: JwtAccess::default(),
221 }
222 }
223}
224
225#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd)]
226#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
227pub enum BearerAccessType {
228 Bearer,
229 Refresh,
230}
231
232#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd)]
233#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
234pub enum BearerAccessSubject {
235 Record,
236 User,
237}