oci_api/auth/
key_loader.rs

1//! Private key file loader
2//!
3//! Reads RSA private key files in PEM format.
4
5use crate::error::{OciError, Result};
6use std::fs;
7use std::path::Path;
8
9/// Private key loader
10pub struct KeyLoader;
11
12impl KeyLoader {
13    /// Load private key from input (automatically detects file path vs PEM content)
14    ///
15    /// # Arguments
16    /// * `input` - Either a file path or PEM content string
17    ///
18    /// # Returns
19    /// Private key string in PEM format
20    ///
21    /// # Examples
22    /// ```no_run
23    /// # use oci_api::auth::KeyLoader;
24    /// // Load from file
25    /// let key = KeyLoader::load("~/.oci/key.pem").unwrap();
26    ///
27    /// // Load from PEM content
28    /// let key = KeyLoader::load("-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----").unwrap();
29    /// ```
30    pub fn load(input: &str) -> Result<String> {
31        let trimmed = input.trim();
32
33        // Check if it's PEM content (starts with -----BEGIN)
34        if trimmed.starts_with("-----BEGIN") {
35            // Validate and return PEM content
36            Self::validate_pem(trimmed)?;
37            return Ok(trimmed.to_string());
38        }
39
40        // Otherwise, treat as file path
41        Self::load_from_file(input)
42    }
43
44    /// Load private key from file
45    ///
46    /// # Arguments
47    /// * `path` - Private key file path
48    ///
49    /// # Returns
50    /// Private key string in PEM format
51    pub fn load_from_file(path: &str) -> Result<String> {
52        let key_path = Path::new(path);
53
54        if !key_path.exists() {
55            return Err(OciError::KeyError(format!(
56                "Private key file not found: {}",
57                path
58            )));
59        }
60
61        let content = fs::read_to_string(key_path)
62            .map_err(|e| OciError::KeyError(format!("Failed to read private key file: {}", e)))?;
63
64        // Validate PEM format
65        Self::validate_pem(&content)?;
66
67        Ok(content)
68    }
69
70    /// Validate PEM format
71    ///
72    /// Check if `-----BEGIN` and `-----END` exist
73    fn validate_pem(content: &str) -> Result<()> {
74        if !content.contains("-----BEGIN") || !content.contains("-----END") {
75            return Err(OciError::KeyError("Not a valid PEM format".to_string()));
76        }
77        Ok(())
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use std::io::Write;
85    use tempfile::NamedTempFile;
86
87    #[test]
88    fn test_load_valid_pem_file() {
89        let mut temp_file = NamedTempFile::new().unwrap();
90        let pem_content =
91            "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----\n";
92        temp_file.write_all(pem_content.as_bytes()).unwrap();
93
94        let result = KeyLoader::load_from_file(temp_file.path().to_str().unwrap());
95        assert!(result.is_ok());
96        assert_eq!(result.unwrap(), pem_content);
97    }
98
99    #[test]
100    fn test_load_nonexistent_file() {
101        let result = KeyLoader::load_from_file("/nonexistent/path/to/key.pem");
102        assert!(result.is_err());
103        match result.unwrap_err() {
104            OciError::KeyError(msg) => assert!(msg.contains("not found")),
105            _ => panic!("Expected KeyError"),
106        }
107    }
108
109    #[test]
110    fn test_validate_pem_valid() {
111        let valid_pem = "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----";
112        assert!(KeyLoader::validate_pem(valid_pem).is_ok());
113    }
114
115    #[test]
116    fn test_validate_pem_missing_begin() {
117        let invalid_pem = "some content\n-----END RSA PRIVATE KEY-----";
118        let result = KeyLoader::validate_pem(invalid_pem);
119        assert!(result.is_err());
120        match result.unwrap_err() {
121            OciError::KeyError(msg) => assert!(msg.contains("valid PEM")),
122            _ => panic!("Expected KeyError"),
123        }
124    }
125
126    #[test]
127    fn test_validate_pem_missing_end() {
128        let invalid_pem = "-----BEGIN RSA PRIVATE KEY-----\nsome content";
129        let result = KeyLoader::validate_pem(invalid_pem);
130        assert!(result.is_err());
131        match result.unwrap_err() {
132            OciError::KeyError(msg) => assert!(msg.contains("valid PEM")),
133            _ => panic!("Expected KeyError"),
134        }
135    }
136
137    #[test]
138    fn test_validate_pem_empty() {
139        let result = KeyLoader::validate_pem("");
140        assert!(result.is_err());
141    }
142}