1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use async_trait::async_trait;
use bytes::{BufMut, BytesMut};

/// Type to represent an authentication error message.
pub type AuthError = String;

/// Trait used to represent a user-defined custom authentication.
#[async_trait]
pub trait AuthenticatorSession: Send + Sync {
    /// To handle an authentication challenge initiated by the server.
    /// The information contained in the token parameter is authentication protocol specific.
    /// It may be NULL or empty.
    async fn evaluate_challenge(
        &mut self,
        token: Option<&[u8]>,
    ) -> Result<Option<Vec<u8>>, AuthError>;

    /// To handle the success phase of exchange.
    /// The token parameters contain information that may be used to finalize the request.
    async fn success(&mut self, token: Option<&[u8]>) -> Result<(), AuthError>;
}

/// Trait used to represent a factory of [`AuthenticatorSession`] instances.
/// A new [`AuthenticatorSession`] instance will be created for each session.
///
/// The custom authenticator can be set using SessionBuilder::authenticator_provider method.  
///
/// Default: [`PlainTextAuthenticator`] is the default authenticator which requires username and
/// password. It can be set by using SessionBuilder::user(\"user\", \"pass\") method.
#[async_trait]
pub trait AuthenticatorProvider: Sync + Send {
    /// A pair of initial response and boxed [`AuthenticatorSession`]
    /// should be returned if authentication is required by the server.
    async fn start_authentication_session(
        &self,
        authenticator_name: &str,
    ) -> Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError>;
}

struct PlainTextAuthenticatorSession;

#[async_trait]
impl AuthenticatorSession for PlainTextAuthenticatorSession {
    async fn evaluate_challenge(
        &mut self,
        _token: Option<&[u8]>,
    ) -> Result<Option<Vec<u8>>, AuthError> {
        Err("Challenges are not expected during PlainTextAuthentication".to_string())
    }

    async fn success(&mut self, _token: Option<&[u8]>) -> Result<(), AuthError> {
        Ok(())
    }
}

/// Default authenticator provider that requires username and password if authentication is required.
pub struct PlainTextAuthenticator {
    username: String,
    password: String,
}

impl PlainTextAuthenticator {
    /// Creates new [`PlainTextAuthenticator`] instance with provided username and password.
    pub fn new(username: String, password: String) -> Self {
        PlainTextAuthenticator { username, password }
    }
}

#[async_trait]
impl AuthenticatorProvider for PlainTextAuthenticator {
    async fn start_authentication_session(
        &self,
        _authenticator_name: &str,
    ) -> Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError> {
        let mut response = BytesMut::new();
        let username_as_bytes = self.username.as_bytes();
        let password_as_bytes = self.password.as_bytes();

        response.put_u8(0);
        response.put_slice(username_as_bytes);
        response.put_u8(0);
        response.put_slice(password_as_bytes);

        Ok((
            Some(response.to_vec()),
            Box::new(PlainTextAuthenticatorSession),
        ))
    }
}