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#[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 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 #[allow(dead_code)]
63 pub fn can_issue_grants(&self) -> bool {
64 match self {
65 AccessType::Jwt(_) => false,
67 AccessType::Record(ac) => ac.bearer.is_some(),
70 AccessType::Bearer(_) => true,
71 }
72 }
73 #[allow(dead_code)]
76 pub fn can_issue_tokens(&self) -> bool {
77 match self {
78 AccessType::Jwt(jwt) => jwt.issue.is_some(),
80 _ => true,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Eq, PartialEq)]
86pub struct JwtAccess {
87 pub verify: JwtAccessVerify,
89 pub issue: Option<JwtAccessIssue>,
92}
93
94impl Default for JwtAccess {
96 fn default() -> Self {
97 let alg = Algorithm::Hs512;
99 let key = random_key();
100 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 Self {
141 alg: Algorithm::Hs512,
143 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}