carp_cli/auth/
manager.rs

1use crate::config::ConfigManager;
2use crate::utils::error::{CarpError, CarpResult};
3use colored::*;
4
5/// Authentication manager for handling login/logout
6pub struct AuthManager;
7
8impl AuthManager {
9    /// Set API key for authentication
10    pub async fn set_api_key() -> CarpResult<()> {
11        println!("{}", "Set API Key for Carp Registry".bold().green());
12        println!("Enter your API key (input will be hidden):");
13
14        let api_key = rpassword::prompt_password("API Key: ")?;
15
16        if api_key.trim().is_empty() {
17            return Err(CarpError::Auth("API key cannot be empty".to_string()));
18        }
19
20        println!("Validating API key...");
21
22        // Validate the API key format
23        ConfigManager::set_api_key_secure(api_key)?;
24
25        println!("{}", "API key saved successfully!".green().bold());
26        println!("You can now use authenticated commands.");
27        Ok(())
28    }
29
30    /// Legacy login method (deprecated)
31    #[deprecated(note = "Use set_api_key instead. Username/password authentication is deprecated.")]
32    #[allow(dead_code)]
33    pub async fn login() -> CarpResult<()> {
34        println!(
35            "{}",
36            "Username/password login is deprecated.".yellow().bold()
37        );
38        println!("Please use API key authentication instead:");
39        println!("  Run: carp auth set-api-key");
40        println!("  Or: set CARP_API_KEY environment variable");
41        println!("  Or: use --api-key command line option");
42
43        Err(CarpError::Auth(
44            "Please use API key authentication instead of username/password".to_string(),
45        ))
46    }
47
48    /// Logout by clearing the stored API key
49    pub async fn logout() -> CarpResult<()> {
50        ConfigManager::clear_api_key()?;
51        println!("{}", "Successfully logged out!".green().bold());
52        println!("API key has been removed from configuration.");
53        Ok(())
54    }
55
56    /// Check if user is currently authenticated
57    pub async fn check_auth() -> CarpResult<bool> {
58        let config = ConfigManager::load()?;
59        Ok(config.api_key.is_some())
60    }
61
62    /// Check if user is currently authenticated, considering runtime API key
63    pub async fn check_auth_with_key(api_key: Option<&str>) -> CarpResult<bool> {
64        if api_key.is_some() {
65            return Ok(true);
66        }
67        Self::check_auth().await
68    }
69
70    /// Get current authentication status
71    #[allow(dead_code)]
72    pub async fn status() -> CarpResult<()> {
73        Self::status_with_key(None).await
74    }
75
76    /// Get current authentication status, considering runtime API key
77    pub async fn status_with_key(runtime_api_key: Option<&str>) -> CarpResult<()> {
78        let config = ConfigManager::load()?;
79
80        // Determine which API key to use (runtime takes precedence)
81        let api_key = runtime_api_key.or(config.api_key.as_deref());
82
83        if api_key.is_some() {
84            println!("{}", "Authenticated".green().bold());
85            println!("Registry: {}", config.registry_url);
86
87            if let Some(key) = api_key {
88                // Show only first and last few characters for security
89                let masked_key = if key.len() > 8 {
90                    format!("{}...{}", &key[..4], &key[key.len() - 4..])
91                } else {
92                    "****".to_string()
93                };
94
95                let source = if runtime_api_key.is_some() {
96                    "command line/environment"
97                } else {
98                    "config file"
99                };
100                println!("API Key: {masked_key} (masked, from {source})");
101            }
102
103            println!("Status: {}", "Ready to use authenticated commands".green());
104        } else {
105            println!("{}", "Not authenticated".red().bold());
106            println!("Authenticate using one of these methods:");
107            println!("  1. Run: carp auth set-api-key");
108            println!("  2. Set CARP_API_KEY environment variable");
109            println!("  3. Use --api-key command line option");
110        }
111        Ok(())
112    }
113
114    /// Ensure user is authenticated, prompt to login if not
115    pub async fn ensure_authenticated(api_key: Option<&str>) -> CarpResult<()> {
116        if !Self::check_auth_with_key(api_key).await? {
117            println!("{}", "Authentication required.".yellow().bold());
118            println!("You can authenticate by:");
119            println!("  1. Setting CARP_API_KEY environment variable");
120            println!("  2. Using --api-key command line option");
121            println!("  3. Running 'carp login' to store API key in config");
122            return Err(CarpError::Auth(
123                "No API key provided. Please authenticate to continue.".to_string(),
124            ));
125        }
126        Ok(())
127    }
128}