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 }
56 }
57 }
58}
59
60impl AccessType {
61 #[allow(dead_code)]
64 pub fn can_issue_grants(&self) -> bool {
65 match self {
66 AccessType::Jwt(_) => false,
68 AccessType::Record(ac) => ac.bearer.is_some(),
71 AccessType::Bearer(_) => true,
72 }
73 }
74 #[allow(dead_code)]
77 pub fn can_issue_tokens(&self) -> bool {
78 match self {
79 AccessType::Jwt(jwt) => jwt.issue.is_some(),
81 _ => true,
82 }
83 }
84}
85
86#[derive(Debug, Clone, Eq, PartialEq)]
87pub struct JwtAccess {
88 pub verify: JwtAccessVerify,
90 pub issue: Option<JwtAccessIssue>,
93 pub audience: Option<Vec<Expr>>,
96}
97
98impl Default for JwtAccess {
100 fn default() -> Self {
101 let alg = Algorithm::Hs512;
103 let key = random_key();
104 Self {
106 verify: JwtAccessVerify::Key(JwtAccessVerifyKey {
107 alg,
108 key: Expr::Literal(Literal::String(key.as_str().into())),
109 }),
110 issue: Some(JwtAccessIssue {
111 alg,
112 key: Expr::Literal(Literal::String(key.into())),
113 }),
114 audience: None,
115 }
116 }
117}
118
119impl ToSql for JwtAccess {
120 fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
121 match &self.verify {
122 JwtAccessVerify::Key(v) => {
123 write_sql!(f, sql_fmt, "ALGORITHM {} KEY {}", v.alg, CoverStmts(&v.key));
124 }
125 JwtAccessVerify::Jwks(v) => {
126 write_sql!(f, sql_fmt, "URL {}", CoverStmts(&v.url));
127 }
128 }
129 if let Some(aud) = &self.audience {
130 write_sql!(
131 f,
132 sql_fmt,
133 " AUDIENCE {}",
134 Fmt::comma_separated(aud.iter().map(CoverStmts))
135 );
136 }
137 if let Some(iss) = &self.issue {
138 write_sql!(f, sql_fmt, " WITH ISSUER KEY {}", CoverStmts(&iss.key));
139 }
140 }
141}
142
143#[derive(Debug, Clone, Eq, PartialEq)]
144#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
145pub struct JwtAccessIssue {
146 pub alg: Algorithm,
147 pub key: Expr,
148}
149
150impl Default for JwtAccessIssue {
151 fn default() -> Self {
152 Self {
154 alg: Algorithm::Hs512,
156 key: Expr::Literal(Literal::String(random_key().into())),
158 }
159 }
160}
161
162#[derive(Debug, Clone, Eq, PartialEq)]
163#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
164pub enum JwtAccessVerify {
165 Key(JwtAccessVerifyKey),
166 Jwks(JwtAccessVerifyJwks),
167}
168
169#[derive(Debug, Clone, Eq, PartialEq)]
170#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
171pub struct JwtAccessVerifyKey {
172 pub alg: Algorithm,
173 pub key: Expr,
174}
175
176#[derive(Debug, Clone, Eq, PartialEq)]
177#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
178pub struct JwtAccessVerifyJwks {
179 pub url: Expr,
180}
181
182#[derive(Debug, Clone, Default, PartialEq, Eq)]
183#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
184pub struct RecordAccess {
185 pub signup: Option<Expr>,
186 pub signin: Option<Expr>,
187 pub jwt: JwtAccess,
188 pub bearer: Option<BearerAccess>,
189}
190
191#[derive(Debug, Clone, Eq, PartialEq)]
192#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
193pub struct BearerAccess {
194 pub kind: BearerAccessType,
195 pub subject: BearerAccessSubject,
196 pub jwt: JwtAccess,
197}
198
199impl Default for BearerAccess {
200 fn default() -> Self {
201 Self {
202 kind: BearerAccessType::Bearer,
203 subject: BearerAccessSubject::User,
204 jwt: JwtAccess::default(),
205 }
206 }
207}
208
209#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd)]
210#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
211pub enum BearerAccessType {
212 Bearer,
213 Refresh,
214}
215
216#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd)]
217#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
218pub enum BearerAccessSubject {
219 Record,
220 User,
221}