ldap_rs/
options.rs

1//! LDAP connection options
2
3#[cfg(feature = "tls-native-tls")]
4pub use native_tls;
5
6#[cfg(feature = "tls-rustls")]
7pub use {rustls, rustls_pki_types};
8
9#[cfg(tls)]
10pub use tls::TlsOptions;
11
12#[cfg(tls)]
13pub(crate) use tls::{TlsBackend, TlsKind};
14
15#[cfg(tls)]
16mod tls {
17    #[cfg(feature = "tls-native-tls")]
18    use native_tls::TlsConnector;
19
20    #[cfg(feature = "tls-rustls")]
21    pub use rustls::ClientConfig;
22
23    #[derive(Clone, Copy, Debug, Default, PartialEq)]
24    pub enum TlsKind {
25        #[default]
26        Plain,
27        Tls,
28        StartTls,
29    }
30
31    #[allow(clippy::large_enum_variant)]
32    #[derive(Debug)]
33    pub enum TlsBackend {
34        #[cfg(feature = "tls-native-tls")]
35        Native(TlsConnector),
36        #[cfg(feature = "tls-rustls")]
37        Rustls(ClientConfig),
38    }
39
40    impl Default for TlsBackend {
41        #[cfg(feature = "tls-native-tls")]
42        fn default() -> Self {
43            Self::Native(TlsConnector::new().unwrap())
44        }
45
46        #[cfg(all(feature = "tls-rustls", not(feature = "tls-native-tls")))]
47        fn default() -> Self {
48            pub static CA_CERTS: once_cell::sync::Lazy<rustls::RootCertStore> = once_cell::sync::Lazy::new(|| {
49                let certs = rustls_native_certs::load_native_certs()
50                    .certs
51                    .into_iter()
52                    .map(|c| c)
53                    .collect::<Vec<_>>();
54                let mut store = rustls::RootCertStore::empty();
55                store.add_parsable_certificates(certs);
56                store
57            });
58
59            Self::Rustls(
60                ClientConfig::builder()
61                    .with_root_certificates(CA_CERTS.clone())
62                    .with_no_client_auth(),
63            )
64        }
65    }
66
67    /// TLS options
68    #[derive(Default, Debug)]
69    pub struct TlsOptions {
70        pub(crate) backend: Option<TlsBackend>,
71        pub(crate) kind: TlsKind,
72        pub(crate) domain_name: Option<String>,
73    }
74
75    impl TlsOptions {
76        fn new(kind: TlsKind) -> Self {
77            Self {
78                backend: None,
79                kind,
80                domain_name: None,
81            }
82        }
83
84        /// Connect using TLS transport
85        pub fn tls() -> Self {
86            Self::new(TlsKind::Tls)
87        }
88
89        /// Connect using STARTTLS negotiation
90        pub fn start_tls() -> Self {
91            Self::new(TlsKind::StartTls)
92        }
93
94        #[cfg(feature = "tls-rustls")]
95        /// Configure rustls backend with a given ClientConfig
96        pub fn client_config(mut self, client_config: ClientConfig) -> Self {
97            self.backend = Some(TlsBackend::Rustls(client_config));
98            self
99        }
100
101        #[cfg(feature = "tls-native-tls")]
102        /// Configure native-tls backend with a given TlsConnector
103        pub fn tls_connector(mut self, tls_connector: TlsConnector) -> Self {
104            self.backend = Some(TlsBackend::Native(tls_connector));
105            self
106        }
107
108        /// Specify a custom domain name to use for SNI match. The default is the connection host name
109        pub fn domain_name<S: AsRef<str>>(mut self, domain_name: S) -> Self {
110            self.domain_name = Some(domain_name.as_ref().to_owned());
111            self
112        }
113    }
114}