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: {path}"
57            )));
58        }
59
60        let content = fs::read_to_string(key_path)
61            .map_err(|e| OciError::KeyError(format!("Failed to read private key file: {e}")))?;
62
63        // Validate PEM format
64        Self::validate_pem(&content)?;
65
66        Ok(content)
67    }
68
69    /// Validate PEM format
70    ///
71    /// Check if `-----BEGIN` and `-----END` exist
72    fn validate_pem(content: &str) -> Result<()> {
73        if !content.contains("-----BEGIN") || !content.contains("-----END") {
74            return Err(OciError::KeyError("Not a valid PEM format".to_string()));
75        }
76        Ok(())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use std::io::Write;
84    use tempfile::NamedTempFile;
85
86    #[test]
87    fn test_load_valid_pem_file() {
88        let mut temp_file = NamedTempFile::new().unwrap();
89        let pem_content =
90            "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----\n";
91        temp_file.write_all(pem_content.as_bytes()).unwrap();
92
93        let result = KeyLoader::load_from_file(temp_file.path().to_str().unwrap());
94        assert!(result.is_ok());
95        assert_eq!(result.unwrap(), pem_content);
96    }
97
98    #[test]
99    fn test_load_nonexistent_file() {
100        let result = KeyLoader::load_from_file("/nonexistent/path/to/key.pem");
101        assert!(result.is_err());
102        match result.unwrap_err() {
103            OciError::KeyError(msg) => assert!(msg.contains("not found")),
104            _ => panic!("Expected KeyError"),
105        }
106    }
107
108    #[test]
109    fn test_validate_pem_valid() {
110        let valid_pem = "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----";
111        assert!(KeyLoader::validate_pem(valid_pem).is_ok());
112    }
113
114    #[test]
115    fn test_validate_pem_missing_begin() {
116        let invalid_pem = "some content\n-----END RSA PRIVATE KEY-----";
117        let result = KeyLoader::validate_pem(invalid_pem);
118        assert!(result.is_err());
119        match result.unwrap_err() {
120            OciError::KeyError(msg) => assert!(msg.contains("valid PEM")),
121            _ => panic!("Expected KeyError"),
122        }
123    }
124
125    #[test]
126    fn test_validate_pem_missing_end() {
127        let invalid_pem = "-----BEGIN RSA PRIVATE KEY-----\nsome content";
128        let result = KeyLoader::validate_pem(invalid_pem);
129        assert!(result.is_err());
130        match result.unwrap_err() {
131            OciError::KeyError(msg) => assert!(msg.contains("valid PEM")),
132            _ => panic!("Expected KeyError"),
133        }
134    }
135
136    #[test]
137    fn test_validate_pem_empty() {
138        let result = KeyLoader::validate_pem("");
139        assert!(result.is_err());
140    }
141}