guts_auth/
permission.rs

1//! Permission levels and access control.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Permission level for repository access.
7///
8/// Permissions are ordered: Read < Write < Admin
9#[derive(
10    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
11)]
12#[serde(rename_all = "lowercase")]
13pub enum Permission {
14    /// Can read repository contents (clone, pull).
15    #[default]
16    Read,
17    /// Can read and write (push commits).
18    Write,
19    /// Full control including settings, collaborators, and deletion.
20    Admin,
21}
22
23impl Permission {
24    /// Check if this permission level grants at least the required level.
25    pub fn has(&self, required: Permission) -> bool {
26        *self >= required
27    }
28
29    /// Parse from string.
30    pub fn parse(s: &str) -> Option<Self> {
31        match s.to_lowercase().as_str() {
32            "read" => Some(Permission::Read),
33            "write" | "push" => Some(Permission::Write),
34            "admin" | "owner" => Some(Permission::Admin),
35            _ => None,
36        }
37    }
38}
39
40impl fmt::Display for Permission {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Permission::Read => write!(f, "read"),
44            Permission::Write => write!(f, "write"),
45            Permission::Admin => write!(f, "admin"),
46        }
47    }
48}
49
50/// A permission grant for a specific resource.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct PermissionGrant {
53    /// The resource key (e.g., "owner/repo").
54    pub resource: String,
55    /// The granted permission level.
56    pub permission: Permission,
57    /// Source of this permission (e.g., "owner", "collaborator", "team:backend").
58    pub source: String,
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_permission_ordering() {
67        assert!(Permission::Read < Permission::Write);
68        assert!(Permission::Write < Permission::Admin);
69        assert!(Permission::Read < Permission::Admin);
70    }
71
72    #[test]
73    fn test_permission_has() {
74        assert!(Permission::Admin.has(Permission::Read));
75        assert!(Permission::Admin.has(Permission::Write));
76        assert!(Permission::Admin.has(Permission::Admin));
77
78        assert!(Permission::Write.has(Permission::Read));
79        assert!(Permission::Write.has(Permission::Write));
80        assert!(!Permission::Write.has(Permission::Admin));
81
82        assert!(Permission::Read.has(Permission::Read));
83        assert!(!Permission::Read.has(Permission::Write));
84        assert!(!Permission::Read.has(Permission::Admin));
85    }
86
87    #[test]
88    fn test_permission_parse() {
89        assert_eq!(Permission::parse("read"), Some(Permission::Read));
90        assert_eq!(Permission::parse("write"), Some(Permission::Write));
91        assert_eq!(Permission::parse("push"), Some(Permission::Write));
92        assert_eq!(Permission::parse("admin"), Some(Permission::Admin));
93        assert_eq!(Permission::parse("owner"), Some(Permission::Admin));
94        assert_eq!(Permission::parse("ADMIN"), Some(Permission::Admin));
95        assert_eq!(Permission::parse("invalid"), None);
96    }
97
98    #[test]
99    fn test_permission_display() {
100        assert_eq!(format!("{}", Permission::Read), "read");
101        assert_eq!(format!("{}", Permission::Write), "write");
102        assert_eq!(format!("{}", Permission::Admin), "admin");
103    }
104}