surrealdb_core/iam/
auth.rs1use anyhow::Result;
2use revision::revisioned;
3use serde::{Deserialize, Serialize};
4
5use super::{Action, Actor, Level, Resource, Role, is_allowed};
6use crate::iam::AuthLimit;
7
8#[revisioned(revision = 1)]
10#[derive(Clone, Default, Debug, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
11#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
12pub struct Auth {
13 actor: Actor,
14}
15
16impl Auth {
17 pub fn new(actor: Actor) -> Self {
18 Self {
19 actor,
20 }
21 }
22
23 pub fn id(&self) -> &str {
24 self.actor.id()
25 }
26
27 pub fn level(&self) -> &Level {
29 self.actor.level()
30 }
31
32 pub fn is_anon(&self) -> bool {
34 matches!(self.level(), Level::No)
35 }
36
37 pub fn is_root(&self) -> bool {
39 matches!(self.level(), Level::Root)
40 }
41
42 pub fn is_ns(&self) -> bool {
44 matches!(self.level(), Level::Namespace(_))
45 }
46
47 pub fn is_db(&self) -> bool {
49 matches!(self.level(), Level::Database(_, _))
50 }
51
52 pub fn is_record(&self) -> bool {
54 matches!(self.level(), Level::Record(_, _, _))
55 }
56
57 pub fn is_ns_check(&self, ns: &str) -> bool {
59 matches!(self.level(), Level::Namespace(n) if n.eq(ns))
60 }
61
62 pub fn is_db_check(&self, ns: &str, db: &str) -> bool {
65 matches!(self.level(), Level::Database(n, d) if n.eq(ns) && d.eq(db))
66 }
67
68 pub fn can_access_ns_db(&self, ns: &str, db: &str) -> bool {
87 match self.level() {
88 Level::Root => true,
89 Level::Namespace(n) => n.eq(ns),
90 Level::Database(n, d) => n.eq(ns) && d.eq(db),
91 Level::Record(n, d, _) => n.eq(ns) && d.eq(db),
92 Level::No => true,
93 }
94 }
95
96 pub fn for_root(role: Role) -> Self {
101 Self::new(Actor::new("system_auth".into(), vec![role], Level::Root))
102 }
103
104 pub fn for_ns(role: Role, ns: &str) -> Self {
105 Self::new(Actor::new("system_auth".into(), vec![role], Level::Namespace(ns.to_owned())))
106 }
107
108 pub fn for_db(role: Role, ns: &str, db: &str) -> Self {
109 Self::new(Actor::new(
110 "system_auth".into(),
111 vec![role],
112 Level::Database(ns.to_owned(), db.to_owned()),
113 ))
114 }
115
116 pub fn for_record(rid: String, ns: &str, db: &str, ac: &str) -> Self {
117 Self::new(Actor::new(
118 rid,
119 vec![],
120 Level::Record(ns.to_owned(), db.to_owned(), ac.to_owned()),
121 ))
122 }
123
124 pub fn new_limited(&self, limit: &AuthLimit) -> Self {
125 Self::new(self.actor.new_limited(limit))
126 }
127
128 pub fn max_role(&self) -> Option<Role> {
129 self.actor.max_role()
130 }
131
132 pub fn is_allowed(&self, action: Action, res: &Resource) -> Result<()> {
139 is_allowed(&self.actor, &action, res)
140 .map_err(crate::err::Error::from)
141 .map_err(anyhow::Error::new)
142 }
143
144 pub fn has_role(&self, role: Role) -> bool {
146 self.actor.has_role(role)
147 }
148
149 pub fn has_owner_role(&self) -> bool {
151 self.actor.has_owner_role()
152 }
153
154 pub fn has_editor_role(&self) -> bool {
156 self.actor.has_editor_role()
157 }
158
159 pub fn has_viewer_role(&self) -> bool {
161 self.actor.has_viewer_role()
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn can_access_ns_db_enforces_tenant_boundary() {
171 let root = Auth::for_root(Role::Viewer);
173 assert!(root.can_access_ns_db("a", "x"));
174 assert!(root.can_access_ns_db("b", "y"));
175
176 let ns = Auth::for_ns(Role::Viewer, "a");
178 assert!(ns.can_access_ns_db("a", "x"));
179 assert!(ns.can_access_ns_db("a", "y"));
180 assert!(!ns.can_access_ns_db("b", "x"));
181
182 let db = Auth::for_db(Role::Viewer, "a", "x");
184 assert!(db.can_access_ns_db("a", "x"));
185 assert!(!db.can_access_ns_db("a", "y"));
186 assert!(!db.can_access_ns_db("b", "x"));
187
188 let rec = Auth::for_record("user:1".to_string(), "a", "x", "ac");
190 assert!(rec.can_access_ns_db("a", "x"));
191 assert!(!rec.can_access_ns_db("a", "y"));
192 assert!(!rec.can_access_ns_db("b", "x"));
193
194 assert!(Auth::default().can_access_ns_db("a", "x"));
197 }
198}