Skip to main content

securitydept_creds/
validator.rs

1//! Basic Authentication validator.
2
3use std::collections::HashMap;
4
5use crate::{
6    BasicAuthCred, StaticTokenAuthCred, StaticTokenAuthCredsConfig,
7    config::BasicAuthCredsConfig,
8    error::{CredsError, CredsResult},
9};
10
11/// Validator for Basic Authentication credentials.
12pub trait BasicAuthCredsValidator<Cred>
13where
14    Cred: BasicAuthCred,
15{
16    fn get_cred(&self, username: &str) -> CredsResult<Option<&Cred>>;
17    fn verify_cred(&self, username: &str, password: &str) -> CredsResult<Option<&Cred>> {
18        let Some(cred) = self.get_cred(username)? else {
19            return Ok(None);
20        };
21        if cred.verify_password(password)? {
22            Ok(Some(cred))
23        } else {
24            Ok(None)
25        }
26    }
27}
28
29pub struct MapBasicAuthCredsValidator<Creds>
30where
31    Creds: BasicAuthCred + Clone,
32{
33    pub creds: HashMap<String, Creds>,
34}
35
36impl<Creds> MapBasicAuthCredsValidator<Creds>
37where
38    Creds: BasicAuthCred + Clone,
39{
40    /// Create a new validator from configuration.
41    pub fn from_config(config: &BasicAuthCredsConfig<Creds>) -> CredsResult<Self> {
42        config.validate()?;
43        Ok(Self {
44            creds: config
45                .users
46                .iter()
47                .map(|creds| (creds.username().to_string(), creds.clone()))
48                .collect(),
49        })
50    }
51}
52
53impl<Creds> BasicAuthCredsValidator<Creds> for MapBasicAuthCredsValidator<Creds>
54where
55    Creds: BasicAuthCred + Clone,
56{
57    fn get_cred(&self, username: &str) -> CredsResult<Option<&Creds>> {
58        Ok(self.creds.get(username))
59    }
60}
61
62pub trait StaticTokenAuthCredsValidator<Cred>
63where
64    Cred: StaticTokenAuthCred,
65{
66    fn get_cred(&self, token: &str) -> CredsResult<Option<&Cred>>;
67    fn verify_cred(&self, token: &str) -> CredsResult<Option<&Cred>> {
68        let cred = self
69            .get_cred(token)?
70            .ok_or(CredsError::InvalidStaticTokenCredentials)?;
71        cred.verify_token(token)?;
72        Ok(Some(cred))
73    }
74}
75
76pub struct MapStaticTokenAuthCredsValidator<Creds>
77where
78    Creds: StaticTokenAuthCred + Clone,
79{
80    pub creds: HashMap<String, Creds>,
81}
82
83impl<Creds> MapStaticTokenAuthCredsValidator<Creds>
84where
85    Creds: StaticTokenAuthCred + Clone,
86{
87    /// Create a new validator from configuration.
88    pub fn from_config(config: &StaticTokenAuthCredsConfig<Creds>) -> CredsResult<Self> {
89        config.validate()?;
90        Ok(Self {
91            creds: config
92                .tokens
93                .iter()
94                .map(|creds| (creds.token_hash().to_string(), creds.clone()))
95                .collect(),
96        })
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use crate::{Argon2BasicAuthCred, BasicAuthCred};
104
105    fn test_config() -> BasicAuthCredsConfig<Argon2BasicAuthCred> {
106        BasicAuthCredsConfig {
107            users: vec![
108                Argon2BasicAuthCred::new("admin".to_string(), "secret123".to_string()).unwrap(),
109                Argon2BasicAuthCred::new("user".to_string(), "password".to_string()).unwrap(),
110            ],
111        }
112    }
113
114    #[test]
115    fn test_validate_credentials() -> CredsResult<()> {
116        let validator = MapBasicAuthCredsValidator::from_config(&test_config()).unwrap();
117        assert!(validator.verify_cred("admin", "secret123")?.is_some());
118        assert!(validator.verify_cred("user", "password")?.is_some());
119        assert!(validator.verify_cred("admin", "wrong")?.is_none());
120        assert!(validator.verify_cred("unknown", "password")?.is_none());
121        Ok(())
122    }
123
124    #[test]
125    fn test_get_display_name() -> CredsResult<()> {
126        let validator = MapBasicAuthCredsValidator::from_config(&test_config()).unwrap();
127        assert_eq!(
128            validator.get_cred("admin")?.map(|c| c.display_name()),
129            Some("admin")
130        );
131        assert_eq!(
132            validator.get_cred("user")?.map(|c| c.display_name()),
133            Some("user")
134        );
135        assert!(validator.get_cred("unknown")?.is_none());
136        Ok(())
137    }
138}