reqsign_oracle/provide_credential/
default.rs1use crate::Credential;
19use crate::provide_credential::{ConfigFileCredentialProvider, EnvCredentialProvider};
20use reqsign_core::{Context, ProvideCredential, ProvideCredentialChain, Result};
21
22#[derive(Debug)]
28pub struct DefaultCredentialProvider {
29 chain: ProvideCredentialChain<Credential>,
30}
31
32impl Default for DefaultCredentialProvider {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl DefaultCredentialProvider {
39 pub fn builder() -> DefaultCredentialProviderBuilder {
41 DefaultCredentialProviderBuilder::default()
42 }
43
44 pub fn new() -> Self {
46 Self::builder().build()
47 }
48
49 pub fn with_chain(chain: ProvideCredentialChain<Credential>) -> Self {
51 Self { chain }
52 }
53
54 pub fn push_front(
68 mut self,
69 provider: impl ProvideCredential<Credential = Credential> + 'static,
70 ) -> Self {
71 self.chain = self.chain.push_front(provider);
72 self
73 }
74}
75
76pub struct DefaultCredentialProviderBuilder {
96 env: Option<EnvCredentialProvider>,
97 config_file: Option<ConfigFileCredentialProvider>,
98}
99
100impl Default for DefaultCredentialProviderBuilder {
101 fn default() -> Self {
102 Self {
103 env: Some(EnvCredentialProvider::default()),
104 config_file: Some(ConfigFileCredentialProvider::default()),
105 }
106 }
107}
108
109impl DefaultCredentialProviderBuilder {
110 pub fn new() -> Self {
112 Self::default()
113 }
114
115 pub fn env(mut self, provider: EnvCredentialProvider) -> Self {
117 self.env = Some(provider);
118 self
119 }
120
121 pub fn no_env(mut self) -> Self {
123 self.env = None;
124 self
125 }
126
127 pub fn config_file(mut self, provider: ConfigFileCredentialProvider) -> Self {
129 self.config_file = Some(provider);
130 self
131 }
132
133 pub fn no_config_file(mut self) -> Self {
135 self.config_file = None;
136 self
137 }
138
139 pub fn build(self) -> DefaultCredentialProvider {
141 let mut chain = ProvideCredentialChain::new();
142 if let Some(p) = self.env {
143 chain = chain.push(p);
144 }
145 if let Some(p) = self.config_file {
146 chain = chain.push(p);
147 }
148 DefaultCredentialProvider::with_chain(chain)
149 }
150}
151impl ProvideCredential for DefaultCredentialProvider {
152 type Credential = Credential;
153
154 async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
155 self.chain.provide_credential(ctx).await
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use crate::constants::{
163 ORACLE_CONFIG_FILE, ORACLE_FINGERPRINT, ORACLE_KEY_FILE, ORACLE_TENANCY, ORACLE_USER,
164 };
165 use reqsign_core::{Context, StaticEnv};
166 use reqsign_file_read_tokio::TokioFileRead;
167 use reqsign_http_send_reqwest::ReqwestHttpSend;
168 use std::collections::HashMap;
169 use std::fs;
170 use std::time::{SystemTime, UNIX_EPOCH};
171
172 #[tokio::test]
173 async fn test_default_matches_new() {
174 let ctx = Context::new().with_env(StaticEnv {
175 home_dir: None,
176 envs: HashMap::from([
177 (ORACLE_USER.to_string(), "test_user".to_string()),
178 (ORACLE_TENANCY.to_string(), "test_tenancy".to_string()),
179 (ORACLE_KEY_FILE.to_string(), "/tmp/key.pem".to_string()),
180 (
181 ORACLE_FINGERPRINT.to_string(),
182 "test_fingerprint".to_string(),
183 ),
184 ]),
185 });
186
187 let from_default = DefaultCredentialProvider::default()
188 .provide_credential(&ctx)
189 .await
190 .expect("load must succeed")
191 .expect("credential must exist");
192 let from_new = DefaultCredentialProvider::new()
193 .provide_credential(&ctx)
194 .await
195 .expect("load must succeed")
196 .expect("credential must exist");
197
198 assert_eq!(from_default.user, from_new.user);
199 assert_eq!(from_default.tenancy, from_new.tenancy);
200 assert_eq!(from_default.key_file, from_new.key_file);
201 assert_eq!(from_default.fingerprint, from_new.fingerprint);
202 assert!(from_default.expires_in.is_some());
203 assert!(from_new.expires_in.is_some());
204 }
205
206 #[tokio::test]
207 async fn test_builder_no_env_removes_env_provider() {
208 let ctx = Context::new()
209 .with_file_read(TokioFileRead)
210 .with_http_send(ReqwestHttpSend::default())
211 .with_env(StaticEnv {
212 home_dir: Some("/tmp".into()),
213 envs: HashMap::from([
214 (ORACLE_USER.to_string(), "test_user".to_string()),
215 (ORACLE_TENANCY.to_string(), "test_tenancy".to_string()),
216 (ORACLE_KEY_FILE.to_string(), "/tmp/key.pem".to_string()),
217 (
218 ORACLE_FINGERPRINT.to_string(),
219 "test_fingerprint".to_string(),
220 ),
221 ]),
222 });
223
224 let credential = DefaultCredentialProvider::builder()
225 .no_env()
226 .build()
227 .provide_credential(&ctx)
228 .await
229 .expect("load must succeed");
230
231 assert!(credential.is_none());
232 }
233
234 #[tokio::test]
235 async fn test_builder_no_config_file_removes_config_file_provider() {
236 let unique = SystemTime::now()
237 .duration_since(UNIX_EPOCH)
238 .expect("system time must be after unix epoch")
239 .as_nanos();
240 let root = std::env::temp_dir().join(format!("reqsign-oracle-default-provider-{unique}"));
241 let config_dir = root.join(".oci");
242 let config_path = config_dir.join("config");
243
244 fs::create_dir_all(&config_dir).expect("create config dir must succeed");
245 fs::write(
246 &config_path,
247 "[DEFAULT]\ntenancy=test_tenancy\nuser=test_user\nkey_file=/tmp/key.pem\nfingerprint=test_fingerprint\n",
248 )
249 .expect("write config file must succeed");
250
251 let ctx = Context::new()
252 .with_file_read(TokioFileRead)
253 .with_http_send(ReqwestHttpSend::default())
254 .with_env(StaticEnv {
255 home_dir: Some(root.clone()),
256 envs: HashMap::from([(
257 ORACLE_CONFIG_FILE.to_string(),
258 "~/.oci/config".to_string(),
259 )]),
260 });
261
262 let from_default = DefaultCredentialProvider::new()
263 .provide_credential(&ctx)
264 .await
265 .expect("load must succeed");
266 assert!(from_default.is_some());
267
268 let without_config_file = DefaultCredentialProvider::builder()
269 .no_config_file()
270 .build()
271 .provide_credential(&ctx)
272 .await
273 .expect("load must succeed");
274
275 assert!(without_config_file.is_none());
276
277 fs::remove_dir_all(&root).expect("cleanup temp dir must succeed");
278 }
279}