1use serde::{Deserialize, Serialize};
3
4use crate::{Permission, scope_contains};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct AuthToken {
8 pub id: String,
9 pub user: String,
10 pub permissions: Vec<Permission>,
11 pub expires_at: u64,
12 pub scope: TokenScope,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub enum TokenScope {
17 Global,
18 Repositories(Vec<String>),
19 NamespaceTree(String),
20}
21
22impl AuthToken {
23 pub fn new(id: impl Into<String>, user: impl Into<String>) -> Self {
24 Self {
25 id: id.into(),
26 user: user.into(),
27 permissions: vec![Permission::Read],
28 expires_at: 0,
29 scope: TokenScope::Global,
30 }
31 }
32
33 pub fn with_permissions(mut self, permissions: Vec<Permission>) -> Self {
34 self.permissions = permissions;
35 self
36 }
37
38 pub fn with_expiration(mut self, expires_at: u64) -> Self {
39 self.expires_at = expires_at;
40 self
41 }
42
43 pub fn with_scope(mut self, scope: TokenScope) -> Self {
44 self.scope = scope;
45 self
46 }
47
48 pub fn has_permission(&self, perm: Permission) -> bool {
49 self.permissions.contains(&perm)
50 }
51
52 pub fn is_expired(&self) -> bool {
53 if self.expires_at == 0 {
54 return false;
55 }
56 let now = match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
57 Ok(d) => d.as_secs(),
58 Err(_) => return true,
59 };
60 now > self.expires_at
61 }
62
63 pub fn can_access_repo(&self, repo: &str) -> bool {
64 match &self.scope {
65 TokenScope::Global => true,
66 TokenScope::Repositories(repos) => repos.contains(&repo.to_string()),
67 TokenScope::NamespaceTree(namespace) => scope_contains(namespace, repo),
68 }
69 }
70
71 pub fn to_bytes(&self) -> Vec<u8> {
72 self.id.as_bytes().to_vec()
73 }
74
75 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
76 let token_str = std::str::from_utf8(bytes).ok()?.trim().to_string();
77 if token_str.is_empty() {
78 return None;
79 }
80 let id = token_str.split(':').next()?.to_string();
81 Some(Self {
82 id,
83 user: String::new(),
84 permissions: vec![],
85 expires_at: 0,
86 scope: TokenScope::Global,
87 })
88 }
89}