reqsign_oracle/provide_credential/
config_file.rs1use crate::Credential;
19use crate::constants::{
20 ORACLE_CONFIG_FILE, ORACLE_CONFIG_PATH, ORACLE_DEFAULT_PROFILE, ORACLE_PROFILE,
21};
22use log::debug;
23use reqsign_core::time::Timestamp;
24use reqsign_core::{Context, ProvideCredential, Result};
25use std::time::Duration;
26
27#[derive(Debug, Default, Clone)]
34pub struct ConfigFileCredentialProvider {}
35
36impl ConfigFileCredentialProvider {
37 pub fn new() -> Self {
39 Self {}
40 }
41}
42impl ProvideCredential for ConfigFileCredentialProvider {
43 type Credential = Credential;
44
45 async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
46 let envs = ctx.env_vars();
47
48 let config_file = envs
50 .get(ORACLE_CONFIG_FILE)
51 .map(|s| s.as_str())
52 .unwrap_or(ORACLE_CONFIG_PATH);
53
54 let expanded_path = ctx
56 .expand_home_dir(config_file)
57 .ok_or_else(|| reqsign_core::Error::unexpected("Failed to expand home directory"))?;
58
59 let content = match ctx.file_read_as_string(&expanded_path).await {
61 Ok(content) => content,
62 Err(_) => {
63 debug!("Oracle config file not found at {expanded_path:?}");
64 return Ok(None);
65 }
66 };
67
68 let profile = envs
70 .get(ORACLE_PROFILE)
71 .map(|s| s.as_str())
72 .unwrap_or(ORACLE_DEFAULT_PROFILE);
73
74 let ini = ini::Ini::read_from(&mut content.as_bytes()).map_err(|e| {
76 reqsign_core::Error::config_invalid(format!("Failed to parse config file: {e}"))
77 })?;
78 let section = match ini.section(Some(profile)) {
79 Some(section) => section,
80 None => {
81 debug!("Profile {profile} not found in config file");
82 return Ok(None);
83 }
84 };
85
86 match (
88 section.get("tenancy"),
89 section.get("user"),
90 section.get("key_file"),
91 section.get("fingerprint"),
92 ) {
93 (Some(tenancy), Some(user), Some(key_file), Some(fingerprint)) => {
94 debug!("loading credential from config file");
95
96 let expanded_key_file = if key_file.starts_with('~') {
98 ctx.expand_home_dir(key_file).ok_or_else(|| {
99 reqsign_core::Error::unexpected("Failed to expand home directory")
100 })?
101 } else {
102 key_file.to_string()
103 };
104
105 Ok(Some(Credential {
106 tenancy: tenancy.to_string(),
107 user: user.to_string(),
108 key_file: expanded_key_file,
109 fingerprint: fingerprint.to_string(),
110 expires_in: Some(Timestamp::now() + Duration::from_secs(600)),
111 }))
112 }
113 _ => {
114 debug!("incomplete config in file, skipping");
115 Ok(None)
116 }
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use reqsign_core::{OsEnv, StaticEnv};
125 use reqsign_file_read_tokio::TokioFileRead;
126 use reqsign_http_send_reqwest::ReqwestHttpSend;
127 use std::collections::HashMap;
128
129 #[tokio::test]
130 async fn test_config_file_credential_provider_file_not_found() -> anyhow::Result<()> {
131 let ctx = Context::new()
132 .with_file_read(TokioFileRead)
133 .with_http_send(ReqwestHttpSend::default())
134 .with_env(OsEnv)
135 .with_env(StaticEnv {
136 home_dir: Some("/home/user".into()),
137 envs: HashMap::new(),
138 });
139
140 let provider = ConfigFileCredentialProvider::new();
141 let cred = provider.provide_credential(&ctx).await?;
142 assert!(cred.is_none());
143
144 Ok(())
145 }
146}