Skip to main content

slim_config/auth/
basic.rs

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