Skip to main content

agp_config/auth/
basic.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use serde::Deserialize;
5use tower_http::auth::{AddAuthorizationLayer, require_authorization::Basic};
6use tower_http::validate_request::ValidateRequestHeaderLayer;
7
8use super::{AuthError, ClientAuthenticator, ServerAuthenticator};
9use crate::opaque::OpaqueString;
10
11#[derive(Debug, Deserialize, Clone, PartialEq)]
12pub struct Config {
13    /// The target the client will connect to.
14    username: String,
15
16    /// Origin for the client.
17    password: OpaqueString,
18}
19
20impl Default for Config {
21    fn default() -> Self {
22        Config {
23            username: "admin".to_string(),
24            password: OpaqueString::new("password"),
25        }
26    }
27}
28
29impl Config {
30    /// Create a new Config
31    pub fn new(username: &str, password: &str) -> Self {
32        Config {
33            username: username.to_string(),
34            password: OpaqueString::new(password),
35        }
36    }
37
38    /// Get the username
39    pub fn username(&self) -> &str {
40        &self.username
41    }
42
43    /// Get the password
44    pub fn password(&self) -> &OpaqueString {
45        &self.password
46    }
47}
48
49impl ClientAuthenticator for Config {
50    // Associated types
51    type ClientLayer = AddAuthorizationLayer;
52
53    fn get_client_layer(&self) -> Result<Self::ClientLayer, AuthError> {
54        match (self.username(), self.password().as_ref()) {
55            ("", _) => Err(AuthError::ConfigError("username is empty".to_string())),
56            (_, "") => Err(AuthError::ConfigError("password is empty".to_string())),
57            _ => Ok(AddAuthorizationLayer::basic(
58                self.username(),
59                self.password(),
60            )),
61        }
62    }
63}
64
65impl<Response> ServerAuthenticator<Response> for Config
66where
67    Response: Default,
68{
69    // Associated types
70    type ServerLayer = ValidateRequestHeaderLayer<Basic<Response>>;
71
72    fn get_server_layer(&self) -> Result<Self::ServerLayer, AuthError> {
73        Ok(ValidateRequestHeaderLayer::basic(
74            self.username(),
75            self.password(),
76        ))
77    }
78}
79
80// tests
81#[cfg(test)]
82mod tests {
83    use tower::ServiceBuilder;
84    use tower_reqwest::HttpClientLayer;
85
86    use super::*;
87
88    #[test]
89    fn test_config() {
90        let username = "admin".to_string();
91        let password = OpaqueString::new("password");
92        let config = Config::new(&username, &password);
93
94        assert_eq!(config.username(), username);
95        assert_eq!(config.password(), &password);
96    }
97
98    #[tokio::test]
99    async fn test_authenticator() {
100        let username = "admin".to_string();
101        let password = OpaqueString::new("password");
102        let config = Config::new(&username, &password);
103
104        let client_layer = config.get_client_layer().unwrap();
105        let server_layer: ValidateRequestHeaderLayer<Basic<String>> =
106            config.get_server_layer().unwrap();
107
108        // Check that we can use the layers when building a service
109        let _ = ServiceBuilder::new().layer(server_layer);
110
111        let _ = ServiceBuilder::new()
112            .layer(HttpClientLayer)
113            .layer(client_layer);
114    }
115}