guts_auth/
collaborator.rs

1//! Repository collaborator types.
2
3use crate::permission::Permission;
4use serde::{Deserialize, Serialize};
5
6/// A collaborator on a repository.
7///
8/// Collaborators are users who have been explicitly granted access to a repository,
9/// separate from organization membership or team access.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Collaborator {
12    /// Unique collaborator ID.
13    pub id: u64,
14    /// Repository key (e.g., "owner/repo").
15    pub repo_key: String,
16    /// User's public key (hex-encoded).
17    pub user: String,
18    /// Permission level granted.
19    pub permission: Permission,
20    /// Who added this collaborator (public key).
21    pub added_by: String,
22    /// When the collaborator was added (Unix timestamp).
23    pub created_at: u64,
24    /// When the permission was last updated (Unix timestamp).
25    pub updated_at: u64,
26}
27
28impl Collaborator {
29    /// Create a new collaborator.
30    pub fn new(
31        id: u64,
32        repo_key: String,
33        user: String,
34        permission: Permission,
35        added_by: String,
36    ) -> Self {
37        let now = Self::now();
38        Self {
39            id,
40            repo_key,
41            user,
42            permission,
43            added_by,
44            created_at: now,
45            updated_at: now,
46        }
47    }
48
49    /// Check if this collaborator has at least the required permission.
50    pub fn has_permission(&self, required: Permission) -> bool {
51        self.permission.has(required)
52    }
53
54    /// Update the permission level.
55    pub fn set_permission(&mut self, permission: Permission) {
56        self.permission = permission;
57        self.updated_at = Self::now();
58    }
59
60    fn now() -> u64 {
61        std::time::SystemTime::now()
62            .duration_since(std::time::UNIX_EPOCH)
63            .unwrap_or_default()
64            .as_secs()
65    }
66}
67
68/// Request to add or update a collaborator.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct CollaboratorRequest {
71    /// User's public key (hex-encoded).
72    pub user: String,
73    /// Permission level to grant.
74    pub permission: Permission,
75}
76
77/// Response with collaborator information.
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct CollaboratorResponse {
80    /// User's public key (hex-encoded).
81    pub user: String,
82    /// Permission level.
83    pub permission: Permission,
84    /// When the collaborator was added.
85    pub created_at: u64,
86}
87
88impl From<&Collaborator> for CollaboratorResponse {
89    fn from(c: &Collaborator) -> Self {
90        Self {
91            user: c.user.clone(),
92            permission: c.permission,
93            created_at: c.created_at,
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_collaborator_creation() {
104        let collab = Collaborator::new(
105            1,
106            "acme/api".into(),
107            "user123".into(),
108            Permission::Write,
109            "owner".into(),
110        );
111
112        assert_eq!(collab.id, 1);
113        assert_eq!(collab.repo_key, "acme/api");
114        assert_eq!(collab.user, "user123");
115        assert_eq!(collab.permission, Permission::Write);
116        assert!(collab.has_permission(Permission::Read));
117        assert!(collab.has_permission(Permission::Write));
118        assert!(!collab.has_permission(Permission::Admin));
119    }
120
121    #[test]
122    fn test_collaborator_permission_update() {
123        let mut collab = Collaborator::new(
124            1,
125            "acme/api".into(),
126            "user123".into(),
127            Permission::Read,
128            "owner".into(),
129        );
130
131        assert!(!collab.has_permission(Permission::Write));
132
133        collab.set_permission(Permission::Admin);
134        assert!(collab.has_permission(Permission::Admin));
135        assert!(collab.updated_at >= collab.created_at);
136    }
137}