quincy_server/
auth.rs

1use ipnet::IpNet;
2use quinn::Connection;
3use std::time::Duration;
4use tokio::time::timeout;
5
6use quincy::{
7    auth::{
8        stream::{AuthMessage, AuthStreamBuilder, AuthStreamMode},
9        ServerAuthenticator,
10    },
11    error::AuthError,
12    Result,
13};
14
15/// Represents an authentication server handling initial authentication and session management.
16pub struct AuthServer {
17    authenticator: Box<dyn ServerAuthenticator>,
18    server_address: IpNet,
19    auth_timeout: Duration,
20}
21
22impl AuthServer {
23    /// Creates a new `AuthServer` with a provided authenticator.
24    pub fn new(
25        authenticator: Box<dyn ServerAuthenticator>,
26        server_address: IpNet,
27        auth_timeout: Duration,
28    ) -> Self {
29        Self {
30            authenticator,
31            server_address,
32            auth_timeout,
33        }
34    }
35
36    /// Handles authentication for a client.
37    ///
38    /// # Arguments
39    /// * `connection` - The connection to the client
40    ///
41    /// # Returns
42    /// A tuple containing the authenticated username and assigned client IP address
43    ///
44    /// # Errors
45    /// Returns `AuthError` variants for authentication failures:
46    /// - `InvalidCredentials` - When provided credentials are invalid
47    /// - `Timeout` - When authentication times out
48    /// - `StreamError` - When communication fails
49    /// - `InvalidPayload` - When authentication data is malformed
50    pub async fn handle_authentication(&self, connection: &Connection) -> Result<(String, IpNet)> {
51        let auth_stream_builder = AuthStreamBuilder::new(AuthStreamMode::Server);
52        let mut auth_stream = auth_stream_builder
53            .connect(connection, self.auth_timeout)
54            .await?;
55
56        let message = timeout(self.auth_timeout, auth_stream.recv_message())
57            .await
58            .map_err(|_| AuthError::Timeout)??;
59
60        let auth_result = match message {
61            AuthMessage::Authenticate { payload } => {
62                let (username, client_address) =
63                    self.authenticator.authenticate_user(payload).await?;
64
65                auth_stream
66                    .send_message(AuthMessage::Authenticated {
67                        client_address,
68                        server_address: self.server_address,
69                    })
70                    .await?;
71
72                (username, client_address)
73            }
74            _ => {
75                // Send failure message to client if authentication format is invalid
76                let _ = auth_stream.send_message(AuthMessage::Failed).await;
77                return Err(AuthError::InvalidPayload.into());
78            }
79        };
80
81        auth_stream.close()?;
82
83        Ok(auth_result)
84    }
85}