Skip to main content

rustack_auth/
credentials.rs

1//! Credential provider trait and implementations.
2//!
3//! This module defines the [`CredentialProvider`] trait for resolving secret access keys
4//! from access key IDs, along with a [`StaticCredentialProvider`] for testing and
5//! development use cases.
6
7use std::collections::HashMap;
8
9use crate::error::AuthError;
10
11/// Trait for looking up secret access keys by access key ID.
12///
13/// Implementations may back this with a database, configuration file,
14/// or any other credential store.
15pub trait CredentialProvider: Send + Sync {
16    /// Retrieve the secret access key for the given access key ID.
17    ///
18    /// # Errors
19    ///
20    /// Returns [`AuthError::AccessKeyNotFound`] if the access key ID is not recognized.
21    fn get_secret_key(&self, access_key_id: &str) -> Result<String, AuthError>;
22}
23
24/// A simple in-memory credential provider backed by a `HashMap`.
25///
26/// Suitable for testing and development environments. For production use,
27/// implement [`CredentialProvider`] with a secure credential store.
28///
29/// # Examples
30///
31/// ```
32/// use rustack_auth::credentials::{CredentialProvider, StaticCredentialProvider};
33///
34/// let provider = StaticCredentialProvider::new(vec![
35///     ("AKIAIOSFODNN7EXAMPLE".to_owned(), "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".to_owned()),
36/// ]);
37///
38/// let secret = provider.get_secret_key("AKIAIOSFODNN7EXAMPLE").unwrap();
39/// assert_eq!(secret, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
40/// ```
41#[derive(Debug, Clone)]
42pub struct StaticCredentialProvider {
43    credentials: HashMap<String, String>,
44}
45
46impl StaticCredentialProvider {
47    /// Create a new `StaticCredentialProvider` from an iterable of (access_key_id, secret_key)
48    /// pairs.
49    pub fn new(credentials: impl IntoIterator<Item = (String, String)>) -> Self {
50        Self {
51            credentials: credentials.into_iter().collect(),
52        }
53    }
54}
55
56impl CredentialProvider for StaticCredentialProvider {
57    fn get_secret_key(&self, access_key_id: &str) -> Result<String, AuthError> {
58        self.credentials
59            .get(access_key_id)
60            .cloned()
61            .ok_or_else(|| AuthError::AccessKeyNotFound(access_key_id.to_owned()))
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_should_return_secret_key_for_known_access_key() {
71        let provider =
72            StaticCredentialProvider::new(vec![("AKID".to_owned(), "secret".to_owned())]);
73
74        let result = provider.get_secret_key("AKID");
75        assert!(result.is_ok());
76        assert_eq!(result.unwrap(), "secret");
77    }
78
79    #[test]
80    fn test_should_return_error_for_unknown_access_key() {
81        let provider = StaticCredentialProvider::new(vec![]);
82
83        let result = provider.get_secret_key("UNKNOWN");
84        assert!(matches!(result, Err(AuthError::AccessKeyNotFound(_))));
85    }
86}