Skip to main content

beep_authz/
object.rs

1use crate::authzed::api::v1::ObjectReference;
2
3type ObjectId = String;
4
5/// Represents different types of objects in the SpiceDB authorization system.
6///
7/// `SpiceDbObject` is used to identify resources and subjects when performing
8/// permission checks. Each variant represents a different type of entity in your
9/// application's domain model.
10///
11/// # Object Types
12///
13/// - **Server** - A server/workspace that contains channels, users, and roles
14/// - **Channel** - A communication channel within a server
15/// - **User** - A user/subject that can have permissions
16/// - **PermissionOverride** - A permission override rule
17///
18/// # Examples
19///
20/// ```no_run
21/// use authz::{SpiceDbObject, SpiceDbRepository, Permissions};
22///
23/// # async fn example(repo: SpiceDbRepository) {
24/// // Check if a user can view a channel
25/// let result = repo.check_permissions(
26///     SpiceDbObject::Channel("general-chat".to_string()),
27///     Permissions::ViewChannels,
28///     SpiceDbObject::User("user-123".to_string()),
29/// ).await;
30///
31/// // Check if a user is a server admin
32/// let is_admin = repo.check_permissions(
33///     SpiceDbObject::Server("my-server".to_string()),
34///     Permissions::Administrator,
35///     SpiceDbObject::User("user-456".to_string()),
36/// ).await.has_permissions();
37/// # }
38/// ```
39///
40/// # SpiceDB Integration
41///
42/// Each `SpiceDbObject` is converted into a SpiceDB `ObjectReference` when
43/// communicating with the SpiceDB API. The object type determines the namespace
44/// used in SpiceDB's schema.
45pub enum SpiceDbObject {
46    /// A server object identified by its unique ID.
47    ///
48    /// Servers are top-level containers that can have channels, users, roles,
49    /// and other resources. They correspond to the "server" object type in SpiceDB.
50    ///
51    /// # Example
52    ///
53    /// ```
54    /// use authz::SpiceDbObject;
55    ///
56    /// let server = SpiceDbObject::Server("server-abc-123".to_string());
57    /// ```
58    Server(ObjectId),
59
60    /// A channel object identified by its unique ID.
61    ///
62    /// Channels are communication spaces within a server where users can view
63    /// and send messages. They correspond to the "channel" object type in SpiceDB.
64    ///
65    /// # Example
66    ///
67    /// ```
68    /// use authz::SpiceDbObject;
69    ///
70    /// let channel = SpiceDbObject::Channel("channel-xyz-789".to_string());
71    /// ```
72    Channel(ObjectId),
73
74    /// A user object identified by its unique ID.
75    ///
76    /// Users are subjects that can have permissions on resources. They correspond
77    /// to the "user" object type in SpiceDB.
78    ///
79    /// # Example
80    ///
81    /// ```
82    /// use authz::SpiceDbObject;
83    ///
84    /// let user = SpiceDbObject::User("user-def-456".to_string());
85    /// ```
86    User(ObjectId),
87
88    /// A permission override object identified by its unique ID.
89    ///
90    /// Permission overrides allow fine-grained control over access rules.
91    /// They correspond to the "permission_override" object type in SpiceDB.
92    ///
93    /// # Example
94    ///
95    /// ```
96    /// use authz::SpiceDbObject;
97    ///
98    /// let override_rule = SpiceDbObject::PermissionOverride("override-001".to_string());
99    /// ```
100    PermissionOverride(ObjectId),
101}
102
103impl SpiceDbObject {
104    /// Returns the object's unique identifier.
105    ///
106    /// Extracts the ID string from the object, regardless of its type.
107    pub(crate) fn id(&self) -> ObjectId {
108        match self {
109            SpiceDbObject::Server(id) => id.clone(),
110            SpiceDbObject::Channel(id) => id.clone(),
111            SpiceDbObject::User(id) => id.clone(),
112            SpiceDbObject::PermissionOverride(id) => id.clone(),
113        }
114    }
115
116    /// Returns the SpiceDB object type name.
117    ///
118    /// This corresponds to the object type namespace defined in your SpiceDB schema.
119    ///
120    /// # Returns
121    ///
122    /// - `"server"` for `SpiceDbObject::Server`
123    /// - `"channel"` for `SpiceDbObject::Channel`
124    /// - `"user"` for `SpiceDbObject::User`
125    /// - `"permission_override"` for `SpiceDbObject::PermissionOverride`
126    pub(crate) fn object_name(&self) -> String {
127        match self {
128            SpiceDbObject::Server(_) => "server".to_string(),
129            SpiceDbObject::Channel(_) => "channel".to_string(),
130            SpiceDbObject::User(_) => "user".to_string(),
131            SpiceDbObject::PermissionOverride(_) => "permission_override".to_string(),
132        }
133    }
134
135    /// Returns the object type for tracing purposes.
136    pub(crate) fn get_object_type(&self) -> &str {
137        match self {
138            SpiceDbObject::Server(_) => "server",
139            SpiceDbObject::Channel(_) => "channel",
140            SpiceDbObject::User(_) => "user",
141            SpiceDbObject::PermissionOverride(_) => "permission_override",
142        }
143    }
144
145    /// Returns the object ID for tracing purposes.
146    pub(crate) fn get_object_id(&self) -> &str {
147        match self {
148            SpiceDbObject::Server(id) => id,
149            SpiceDbObject::Channel(id) => id,
150            SpiceDbObject::User(id) => id,
151            SpiceDbObject::PermissionOverride(id) => id,
152        }
153    }
154}
155
156/// Converts a `SpiceDbObject` into a SpiceDB `ObjectReference`.
157///
158/// This implementation allows `SpiceDbObject` to be used directly in permission
159/// check operations. The conversion maps the object to SpiceDB's wire format.
160impl Into<ObjectReference> for SpiceDbObject {
161    fn into(self) -> ObjectReference {
162        ObjectReference {
163            object_type: self.object_name(),
164            object_id: self.id(),
165        }
166    }
167}