agentic_forge_core/security/
mod.rs1use std::collections::HashMap;
4
5pub struct AuthManager {
6 token: Option<String>,
7 sessions: HashMap<String, SessionBinding>,
8 failed_attempts: u32,
9 max_attempts: u32,
10}
11
12impl AuthManager {
13 pub fn new() -> Self {
14 let token = std::env::var("AGENTIC_AUTH_TOKEN").ok();
15 Self {
16 token,
17 sessions: HashMap::new(),
18 failed_attempts: 0,
19 max_attempts: 10,
20 }
21 }
22
23 pub fn is_auth_required(&self) -> bool {
24 self.token.is_some()
25 }
26
27 pub fn authenticate(&mut self, provided_token: &str) -> Result<String, String> {
28 if let Some(ref expected) = self.token {
29 if provided_token == expected {
30 let session_id = uuid::Uuid::new_v4().to_string();
31 self.sessions.insert(
32 session_id.clone(),
33 SessionBinding {
34 session_id: session_id.clone(),
35 created_at: chrono::Utc::now().timestamp(),
36 last_activity: chrono::Utc::now().timestamp(),
37 permissions: Permissions::default(),
38 },
39 );
40 self.failed_attempts = 0;
41 Ok(session_id)
42 } else {
43 self.failed_attempts += 1;
44 Err("Invalid token".into())
45 }
46 } else {
47 Ok("anonymous".into())
48 }
49 }
50
51 pub fn is_rate_limited(&self) -> bool {
52 self.failed_attempts >= self.max_attempts
53 }
54
55 pub fn session_count(&self) -> usize {
56 self.sessions.len()
57 }
58
59 pub fn validate_session(&self, session_id: &str) -> bool {
60 self.sessions.contains_key(session_id)
61 }
62
63 pub fn revoke_session(&mut self, session_id: &str) -> bool {
64 self.sessions.remove(session_id).is_some()
65 }
66}
67
68impl Default for AuthManager {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74#[derive(Debug, Clone)]
75pub struct SessionBinding {
76 pub session_id: String,
77 pub created_at: i64,
78 pub last_activity: i64,
79 pub permissions: Permissions,
80}
81
82#[derive(Debug, Clone, Default)]
83pub struct Permissions {
84 pub can_read: bool,
85 pub can_write: bool,
86 pub can_delete: bool,
87 pub can_admin: bool,
88}
89
90impl Permissions {
91 pub fn full() -> Self {
92 Self {
93 can_read: true,
94 can_write: true,
95 can_delete: true,
96 can_admin: true,
97 }
98 }
99
100 pub fn read_only() -> Self {
101 Self {
102 can_read: true,
103 ..Default::default()
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_auth_no_token() {
114 let mut auth = AuthManager::new();
115 if !auth.is_auth_required() {
117 let session = auth.authenticate("anything").unwrap();
118 assert_eq!(session, "anonymous");
119 }
120 }
121
122 #[test]
123 fn test_rate_limiting() {
124 let mut auth = AuthManager {
125 token: Some("secret".into()),
126 sessions: HashMap::new(),
127 failed_attempts: 0,
128 max_attempts: 3,
129 };
130 assert!(!auth.is_rate_limited());
131 auth.authenticate("wrong").unwrap_err();
132 auth.authenticate("wrong").unwrap_err();
133 auth.authenticate("wrong").unwrap_err();
134 assert!(auth.is_rate_limited());
135 }
136
137 #[test]
138 fn test_session_management() {
139 let mut auth = AuthManager {
140 token: Some("secret".into()),
141 sessions: HashMap::new(),
142 failed_attempts: 0,
143 max_attempts: 10,
144 };
145 let session = auth.authenticate("secret").unwrap();
146 assert!(auth.validate_session(&session));
147 assert_eq!(auth.session_count(), 1);
148 assert!(auth.revoke_session(&session));
149 assert!(!auth.validate_session(&session));
150 }
151
152 #[test]
153 fn test_permissions() {
154 let full = Permissions::full();
155 assert!(full.can_read && full.can_write && full.can_delete && full.can_admin);
156 let ro = Permissions::read_only();
157 assert!(ro.can_read && !ro.can_write);
158 }
159
160 #[test]
161 fn test_default_permissions() {
162 let p = Permissions::default();
163 assert!(!p.can_read);
164 assert!(!p.can_write);
165 }
166}