oci_rust_sdk/auth/
config.rs1use crate::auth::provider::AuthProvider;
2use crate::core::region::Region;
3use configparser::ini::Ini;
4use std::fs;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7
8#[derive(Debug, Clone)]
12pub struct ConfigFileAuthProvider {
13 tenancy: String,
14 user: String,
15 fingerprint: String,
16 key_content: String,
17 passphrase: Option<String>,
18 region: Option<Region>,
19}
20
21impl ConfigFileAuthProvider {
22 pub fn from_file(config_path: impl AsRef<Path>, profile: &str) -> crate::core::Result<Self> {
28 let config_path = expand_tilde(config_path.as_ref());
29 let mut ini = Ini::new();
30 ini.load(&config_path).map_err(|e| {
31 crate::core::OciError::ConfigError(format!("Failed to load config file: {}", e))
32 })?;
33
34 let tenancy = ini.get(profile, "tenancy").ok_or_else(|| {
35 crate::core::OciError::ConfigError(format!(
36 "Missing 'tenancy' in profile '{}'",
37 profile
38 ))
39 })?;
40
41 let user = ini.get(profile, "user").ok_or_else(|| {
42 crate::core::OciError::ConfigError(format!("Missing 'user' in profile '{}'", profile))
43 })?;
44
45 let fingerprint = ini.get(profile, "fingerprint").ok_or_else(|| {
46 crate::core::OciError::ConfigError(format!(
47 "Missing 'fingerprint' in profile '{}'",
48 profile
49 ))
50 })?;
51
52 let key_file = ini.get(profile, "key_file").ok_or_else(|| {
53 crate::core::OciError::ConfigError(format!(
54 "Missing 'key_file' in profile '{}'",
55 profile
56 ))
57 })?;
58
59 let key_path = expand_tilde(Path::new(&key_file));
60 let key_content = fs::read_to_string(&key_path).map_err(|e| {
61 crate::core::OciError::ConfigError(format!("Failed to read key file: {}", e))
62 })?;
63
64 let passphrase = ini.get(profile, "pass_phrase");
65
66 let region = ini
67 .get(profile, "region")
68 .and_then(|r| Region::from_str(&r).ok());
69
70 Ok(Self {
71 tenancy,
72 user,
73 fingerprint,
74 key_content,
75 passphrase,
76 region,
77 })
78 }
79
80 pub fn from_default() -> crate::core::Result<Self> {
82 let config_path = dirs::home_dir()
83 .ok_or_else(|| {
84 crate::core::OciError::ConfigError("Could not determine home directory".to_string())
85 })?
86 .join(".oci")
87 .join("config");
88
89 Self::from_file(config_path, "DEFAULT")
90 }
91
92 pub fn region(&self) -> Option<Region> {
94 self.region
95 }
96}
97
98impl AuthProvider for ConfigFileAuthProvider {
99 fn get_key_id(&self) -> String {
100 format!("{}/{}/{}", self.tenancy, self.user, self.fingerprint)
101 }
102
103 fn get_private_key(&self) -> &str {
104 &self.key_content
105 }
106
107 fn get_passphrase(&self) -> Option<&str> {
108 self.passphrase.as_deref()
109 }
110}
111
112fn expand_tilde(path: &Path) -> PathBuf {
114 if let Ok(p) = path.strip_prefix("~")
115 && let Some(home) = dirs::home_dir()
116 {
117 return home.join(p);
118 }
119 path.to_path_buf()
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_expand_tilde() {
128 let path = Path::new("~/.oci/config");
129 let expanded = expand_tilde(path);
130 assert!(!expanded.to_str().unwrap().contains('~'));
131 }
132}