files_sdk/users/
permissions.rs

1//! Permission management for Files.com
2//!
3//! Permissions grant access to specific paths for users or groups.
4//! They can be recursive (apply to subfolders) or non-recursive.
5
6use crate::{FilesClient, Result};
7use serde::{Deserialize, Serialize};
8
9/// Permission types available in Files.com
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum PermissionType {
13    /// List, preview, read, write, move, delete, rename, manage permissions
14    Admin,
15    /// Share files via bundles (share links)
16    Bundle,
17    /// Read, write, move, delete, rename files
18    Full,
19    /// View history and create email notifications
20    History,
21    /// List files and folders only
22    List,
23    /// List, preview, and download files
24    Readonly,
25    /// Readonly site admin on child sites
26    #[serde(rename = "readonly_site_admin")]
27    ReadonlySiteAdmin,
28    /// Site admin on child sites
29    #[serde(rename = "site_admin")]
30    SiteAdmin,
31    /// Upload files and create folders
32    Writeonly,
33}
34
35/// A permission entity
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct PermissionEntity {
38    /// Permission ID
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub id: Option<i64>,
41
42    /// Folder path this permission applies to
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub path: Option<String>,
45
46    /// User ID (if permission is for a user)
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub user_id: Option<i64>,
49
50    /// Username (if applicable)
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub username: Option<String>,
53
54    /// Group ID (if permission is for a group)
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub group_id: Option<i64>,
57
58    /// Group name (if applicable)
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub group_name: Option<String>,
61
62    /// Permission type
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub permission: Option<String>,
65
66    /// Apply to subfolders recursively
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub recursive: Option<bool>,
69
70    /// Site ID
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub site_id: Option<i64>,
73}
74
75/// Handler for permission operations
76pub struct PermissionHandler {
77    client: FilesClient,
78}
79
80impl PermissionHandler {
81    /// Create a new permission handler
82    pub fn new(client: FilesClient) -> Self {
83        Self { client }
84    }
85
86    /// List permissions
87    ///
88    /// # Arguments
89    /// * `cursor` - Pagination cursor
90    /// * `per_page` - Number of records per page (max 10000)
91    ///
92    /// # Returns
93    /// A tuple of (permissions, pagination_info)
94    pub async fn list(
95        &self,
96        cursor: Option<&str>,
97        per_page: Option<i64>,
98    ) -> Result<(Vec<PermissionEntity>, crate::types::PaginationInfo)> {
99        let mut params = vec![];
100        if let Some(c) = cursor {
101            params.push(("cursor", c.to_string()));
102        }
103        if let Some(pp) = per_page {
104            params.push(("per_page", pp.to_string()));
105        }
106
107        let query = if params.is_empty() {
108            String::new()
109        } else {
110            format!(
111                "?{}",
112                params
113                    .iter()
114                    .map(|(k, v)| format!("{}={}", k, v))
115                    .collect::<Vec<_>>()
116                    .join("&")
117            )
118        };
119
120        let response = self
121            .client
122            .get_raw(&format!("/permissions{}", query))
123            .await?;
124        let permissions: Vec<PermissionEntity> = serde_json::from_value(response)?;
125
126        // Get pagination info from response headers if available
127        let pagination = crate::types::PaginationInfo {
128            cursor_next: None,
129            cursor_prev: None,
130        };
131
132        Ok((permissions, pagination))
133    }
134
135    /// Create a permission
136    ///
137    /// # Arguments
138    /// * `path` - Folder path (required)
139    /// * `permission` - Permission type (admin, full, readonly, writeonly, list, history)
140    /// * `user_id` - User ID (provide user_id or username)
141    /// * `username` - Username (provide user_id or username)
142    /// * `group_id` - Group ID (provide group_id or group_name)
143    /// * `group_name` - Group name (provide group_id or group_name)
144    /// * `recursive` - Apply to subfolders recursively
145    ///
146    /// # Returns
147    /// The created permission
148    #[allow(clippy::too_many_arguments)]
149    pub async fn create(
150        &self,
151        path: &str,
152        permission: Option<&str>,
153        user_id: Option<i64>,
154        username: Option<&str>,
155        group_id: Option<i64>,
156        group_name: Option<&str>,
157        recursive: Option<bool>,
158    ) -> Result<PermissionEntity> {
159        let mut params = vec![("path", path.to_string())];
160
161        if let Some(p) = permission {
162            params.push(("permission", p.to_string()));
163        }
164        if let Some(uid) = user_id {
165            params.push(("user_id", uid.to_string()));
166        }
167        if let Some(u) = username {
168            params.push(("username", u.to_string()));
169        }
170        if let Some(gid) = group_id {
171            params.push(("group_id", gid.to_string()));
172        }
173        if let Some(gn) = group_name {
174            params.push(("group_name", gn.to_string()));
175        }
176        if let Some(r) = recursive {
177            params.push(("recursive", r.to_string()));
178        }
179
180        let response = self.client.post_form("/permissions", &params).await?;
181        Ok(serde_json::from_value(response)?)
182    }
183
184    /// Delete a permission
185    ///
186    /// # Arguments
187    /// * `id` - Permission ID
188    pub async fn delete(&self, id: i64) -> Result<()> {
189        self.client
190            .delete_raw(&format!("/permissions/{}", id))
191            .await?;
192        Ok(())
193    }
194
195    /// List permissions for a specific user
196    ///
197    /// # Arguments
198    /// * `user_id` - User ID
199    /// * `cursor` - Pagination cursor
200    /// * `per_page` - Number of records per page
201    pub async fn list_for_user(
202        &self,
203        user_id: i64,
204        cursor: Option<&str>,
205        per_page: Option<i64>,
206    ) -> Result<(Vec<PermissionEntity>, crate::types::PaginationInfo)> {
207        let mut params = vec![];
208        if let Some(c) = cursor {
209            params.push(("cursor", c.to_string()));
210        }
211        if let Some(pp) = per_page {
212            params.push(("per_page", pp.to_string()));
213        }
214
215        let query = if params.is_empty() {
216            String::new()
217        } else {
218            format!(
219                "?{}",
220                params
221                    .iter()
222                    .map(|(k, v)| format!("{}={}", k, v))
223                    .collect::<Vec<_>>()
224                    .join("&")
225            )
226        };
227
228        let response = self
229            .client
230            .get_raw(&format!("/users/{}/permissions{}", user_id, query))
231            .await?;
232        let permissions: Vec<PermissionEntity> = serde_json::from_value(response)?;
233
234        let pagination = crate::types::PaginationInfo {
235            cursor_next: None,
236            cursor_prev: None,
237        };
238
239        Ok((permissions, pagination))
240    }
241
242    /// List permissions for a specific group
243    ///
244    /// # Arguments
245    /// * `group_id` - Group ID
246    /// * `cursor` - Pagination cursor
247    /// * `per_page` - Number of records per page
248    pub async fn list_for_group(
249        &self,
250        group_id: i64,
251        cursor: Option<&str>,
252        per_page: Option<i64>,
253    ) -> Result<(Vec<PermissionEntity>, crate::types::PaginationInfo)> {
254        let mut params = vec![];
255        if let Some(c) = cursor {
256            params.push(("cursor", c.to_string()));
257        }
258        if let Some(pp) = per_page {
259            params.push(("per_page", pp.to_string()));
260        }
261
262        let query = if params.is_empty() {
263            String::new()
264        } else {
265            format!(
266                "?{}",
267                params
268                    .iter()
269                    .map(|(k, v)| format!("{}={}", k, v))
270                    .collect::<Vec<_>>()
271                    .join("&")
272            )
273        };
274
275        let response = self
276            .client
277            .get_raw(&format!("/groups/{}/permissions{}", group_id, query))
278            .await?;
279        let permissions: Vec<PermissionEntity> = serde_json::from_value(response)?;
280
281        let pagination = crate::types::PaginationInfo {
282            cursor_next: None,
283            cursor_prev: None,
284        };
285
286        Ok((permissions, pagination))
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn test_handler_creation() {
296        let client = FilesClient::builder().api_key("test-key").build().unwrap();
297        let _handler = PermissionHandler::new(client);
298    }
299}