ldap_rs/
options.rs

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! LDAP connection options

#[cfg(feature = "tls-native-tls")]
pub use native_tls;

#[cfg(feature = "tls-rustls")]
pub use {rustls, rustls_pki_types};

#[cfg(tls)]
pub use tls::TlsOptions;

#[cfg(tls)]
pub(crate) use tls::{TlsBackend, TlsKind};

#[cfg(tls)]
mod tls {
    #[cfg(feature = "tls-native-tls")]
    use native_tls::TlsConnector;

    #[cfg(feature = "tls-rustls")]
    pub use rustls::ClientConfig;

    #[derive(Clone, Copy, Debug, Default, PartialEq)]
    pub enum TlsKind {
        #[default]
        Plain,
        Tls,
        StartTls,
    }

    #[derive(Debug)]
    pub enum TlsBackend {
        #[cfg(feature = "tls-native-tls")]
        Native(TlsConnector),
        #[cfg(feature = "tls-rustls")]
        Rustls(ClientConfig),
    }

    impl Default for TlsBackend {
        #[cfg(feature = "tls-native-tls")]
        fn default() -> Self {
            Self::Native(TlsConnector::new().unwrap())
        }

        #[cfg(all(feature = "tls-rustls", not(feature = "tls-native-tls")))]
        fn default() -> Self {
            pub static CA_CERTS: once_cell::sync::Lazy<rustls::RootCertStore> = once_cell::sync::Lazy::new(|| {
                let certs = rustls_native_certs::load_native_certs()
                    .certs
                    .into_iter()
                    .map(|c| c)
                    .collect::<Vec<_>>();
                let mut store = rustls::RootCertStore::empty();
                store.add_parsable_certificates(certs);
                store
            });

            Self::Rustls(
                ClientConfig::builder()
                    .with_root_certificates(CA_CERTS.clone())
                    .with_no_client_auth(),
            )
        }
    }

    /// TLS options
    #[derive(Default, Debug)]
    pub struct TlsOptions {
        pub(crate) backend: Option<TlsBackend>,
        pub(crate) kind: TlsKind,
        pub(crate) domain_name: Option<String>,
    }

    impl TlsOptions {
        fn new(kind: TlsKind) -> Self {
            Self {
                backend: None,
                kind,
                domain_name: None,
            }
        }

        /// Connect using TLS transport
        pub fn tls() -> Self {
            Self::new(TlsKind::Tls)
        }

        /// Connect using STARTTLS negotiation
        pub fn start_tls() -> Self {
            Self::new(TlsKind::StartTls)
        }

        #[cfg(feature = "tls-rustls")]
        /// Configure rustls backend with a given ClientConfig
        pub fn client_config(mut self, client_config: ClientConfig) -> Self {
            self.backend = Some(TlsBackend::Rustls(client_config));
            self
        }

        #[cfg(feature = "tls-native-tls")]
        /// Configure native-tls backend with a given TlsConnector
        pub fn tls_connector(mut self, tls_connector: TlsConnector) -> Self {
            self.backend = Some(TlsBackend::Native(tls_connector));
            self
        }

        /// Specify custom domain name to use for SNI match. The default is the connection host name
        pub fn domain_name<S: AsRef<str>>(mut self, domain_name: S) -> Self {
            self.domain_name = Some(domain_name.as_ref().to_owned());
            self
        }
    }
}