wazuh_client/
client_factory.rs

1use tracing::{debug, info};
2
3use super::active_response::ActiveResponseClient;
4use super::agents::AgentsClient;
5use super::cluster::ClusterClient;
6use super::configuration::ConfigurationClient;
7use super::error::WazuhApiError;
8use super::indexer_client::WazuhIndexerClient;
9use super::logs::LogsClient;
10use super::rules::RulesClient;
11use super::vulnerability::VulnerabilityClient;
12use super::wazuh_client::WazuhApiClient;
13
14#[derive(Debug, Clone)]
15pub struct WazuhClientFactory {
16    api_host: String,
17    api_port: u16,
18    api_username: String,
19    api_password: String,
20    indexer_host: String,
21    indexer_port: u16,
22    indexer_username: String,
23    indexer_password: String,
24    verify_ssl: bool,
25    protocol: String,
26}
27
28impl WazuhClientFactory {
29    pub fn builder() -> WazuhClientFactoryBuilder {
30        WazuhClientFactoryBuilder::default()
31    }
32
33    #[deprecated(
34        since = "0.2.0",
35        note = "Please use the builder pattern instead, e.g., `WazuhClientFactory::builder().build()`"
36    )]
37    #[allow(clippy::too_many_arguments)]
38    pub fn new(
39        api_host: String,
40        api_port: u16,
41        api_username: String,
42        api_password: String,
43        indexer_host: String,
44        indexer_port: u16,
45        indexer_username: String,
46        indexer_password: String,
47        verify_ssl: bool,
48        protocol: Option<String>,
49    ) -> Self {
50        let mut builder = Self::builder()
51            .api_host(api_host)
52            .api_port(api_port)
53            .api_credentials(api_username, api_password)
54            .indexer_host(indexer_host)
55            .indexer_port(indexer_port)
56            .indexer_credentials(indexer_username, indexer_password)
57            .verify_ssl(verify_ssl);
58
59        if let Some(protocol) = protocol {
60            builder = builder.protocol(protocol);
61        }
62
63        builder.build()
64    }
65
66    pub fn create_api_client(&self) -> WazuhApiClient {
67        debug!("Creating base Wazuh API client");
68        WazuhApiClient::new_with_protocol(
69            self.api_host.clone(),
70            self.api_port,
71            self.api_username.clone(),
72            self.api_password.clone(),
73            self.verify_ssl,
74            &self.protocol,
75        )
76    }
77
78    pub fn create_indexer_client(&self) -> WazuhIndexerClient {
79        debug!("Creating Wazuh Indexer client");
80        WazuhIndexerClient::new_with_protocol(
81            self.indexer_host.clone(),
82            self.indexer_port,
83            self.indexer_username.clone(),
84            self.indexer_password.clone(),
85            self.verify_ssl,
86            &self.protocol,
87        )
88    }
89
90    pub fn create_agents_client(&self) -> AgentsClient {
91        debug!("Creating Agents client");
92        let api_client = self.create_api_client();
93        AgentsClient::new(api_client)
94    }
95
96    pub fn create_rules_client(&self) -> RulesClient {
97        debug!("Creating Rules client");
98        let api_client = self.create_api_client();
99        RulesClient::new(api_client)
100    }
101
102    pub fn create_configuration_client(&self) -> ConfigurationClient {
103        debug!("Creating Configuration client");
104        let api_client = self.create_api_client();
105        ConfigurationClient::new(api_client)
106    }
107
108    pub fn create_vulnerability_client(&self) -> VulnerabilityClient {
109        debug!("Creating Vulnerability client");
110        let api_client = self.create_api_client();
111        let indexer_client = self.create_indexer_client();
112        VulnerabilityClient::new(api_client, indexer_client)
113    }
114
115    pub fn create_active_response_client(&self) -> ActiveResponseClient {
116        debug!("Creating Active Response client");
117        let api_client = self.create_api_client();
118        ActiveResponseClient::new(api_client)
119    }
120
121    pub fn create_cluster_client(&self) -> ClusterClient {
122        debug!("Creating Cluster client");
123        let api_client = self.create_api_client();
124        ClusterClient::new(api_client)
125    }
126
127    pub fn create_logs_client(&self) -> LogsClient {
128        debug!("Creating Logs client");
129        let api_client = self.create_api_client();
130        LogsClient::new(api_client)
131    }
132
133    pub fn create_all_clients(&self) -> WazuhClients {
134        debug!("Creating all Wazuh clients");
135
136        WazuhClients {
137            indexer: self.create_indexer_client(),
138            agents: self.create_agents_client(),
139            rules: self.create_rules_client(),
140            configuration: self.create_configuration_client(),
141            vulnerability: self.create_vulnerability_client(),
142            active_response: self.create_active_response_client(),
143            cluster: self.create_cluster_client(),
144            logs: self.create_logs_client(),
145        }
146    }
147
148    pub async fn test_connectivity(&self) -> Result<ConnectivityStatus, WazuhApiError> {
149        debug!("Testing connectivity to Wazuh API and Indexer");
150
151        let mut api_status = false;
152        let mut indexer_status = false;
153        let mut api_error = None;
154        let mut indexer_error = None;
155
156        // Test API connectivity
157        let mut cluster_client = self.create_cluster_client();
158        match cluster_client.get_manager_info().await {
159            Ok(_) => {
160                api_status = true;
161                info!("Wazuh API connectivity test: SUCCESS");
162            }
163            Err(e) => {
164                api_error = Some(format!("API connectivity failed: {}", e));
165                debug!("Wazuh API connectivity test: FAILED - {}", e);
166            }
167        }
168
169        let indexer_client = self.create_indexer_client();
170        match indexer_client.get_alerts(None).await {
171            Ok(_) => {
172                indexer_status = true;
173                info!("Wazuh Indexer connectivity test: SUCCESS");
174            }
175            Err(e) => {
176                indexer_error = Some(format!("Indexer connectivity failed: {}", e));
177                debug!("Wazuh Indexer connectivity test: FAILED - {}", e);
178            }
179        }
180
181        Ok(ConnectivityStatus {
182            api_connected: api_status,
183            indexer_connected: indexer_status,
184            api_error,
185            indexer_error,
186        })
187    }
188}
189
190#[derive(Default)]
191pub struct WazuhClientFactoryBuilder {
192    api_host: Option<String>,
193    api_port: Option<u16>,
194    api_username: Option<String>,
195    api_password: Option<String>,
196    indexer_host: Option<String>,
197    indexer_port: Option<u16>,
198    indexer_username: Option<String>,
199    indexer_password: Option<String>,
200    verify_ssl: Option<bool>,
201    protocol: Option<String>,
202}
203
204impl WazuhClientFactoryBuilder {
205    pub fn api_host(mut self, api_host: impl Into<String>) -> Self {
206        self.api_host = Some(api_host.into());
207        self
208    }
209
210    pub fn api_port(mut self, api_port: u16) -> Self {
211        self.api_port = Some(api_port);
212        self
213    }
214
215    pub fn api_credentials(
216        mut self,
217        username: impl Into<String>,
218        password: impl Into<String>,
219    ) -> Self {
220        self.api_username = Some(username.into());
221        self.api_password = Some(password.into());
222        self
223    }
224
225    pub fn indexer_host(mut self, indexer_host: impl Into<String>) -> Self {
226        self.indexer_host = Some(indexer_host.into());
227        self
228    }
229
230    pub fn indexer_port(mut self, indexer_port: u16) -> Self {
231        self.indexer_port = Some(indexer_port);
232        self
233    }
234
235    pub fn indexer_credentials(
236        mut self,
237        username: impl Into<String>,
238        password: impl Into<String>,
239    ) -> Self {
240        self.indexer_username = Some(username.into());
241        self.indexer_password = Some(password.into());
242        self
243    }
244
245    pub fn verify_ssl(mut self, verify_ssl: bool) -> Self {
246        self.verify_ssl = Some(verify_ssl);
247        self
248    }
249
250    pub fn protocol(mut self, protocol: impl Into<String>) -> Self {
251        self.protocol = Some(protocol.into());
252        self
253    }
254
255    pub fn build(self) -> WazuhClientFactory {
256        debug!("Building WazuhClientFactory");
257
258        WazuhClientFactory {
259            api_host: self.api_host.unwrap_or_else(|| "localhost".to_string()),
260            api_port: self.api_port.unwrap_or(55000),
261            api_username: self.api_username.unwrap_or_else(|| "wazuh".to_string()),
262            api_password: self.api_password.unwrap_or_else(|| "wazuh".to_string()),
263            indexer_host: self.indexer_host.unwrap_or_else(|| "localhost".to_string()),
264            indexer_port: self.indexer_port.unwrap_or(9200),
265            indexer_username: self.indexer_username.unwrap_or_else(|| "admin".to_string()),
266            indexer_password: self.indexer_password.unwrap_or_else(|| "admin".to_string()),
267            verify_ssl: self.verify_ssl.unwrap_or(true),
268            protocol: self.protocol.unwrap_or_else(|| "https".to_string()),
269        }
270    }
271}
272
273#[derive(Debug)]
274pub struct WazuhClients {
275    pub indexer: WazuhIndexerClient,
276    pub agents: AgentsClient,
277    pub rules: RulesClient,
278    pub configuration: ConfigurationClient,
279    pub vulnerability: VulnerabilityClient,
280    pub active_response: ActiveResponseClient,
281    pub cluster: ClusterClient,
282    pub logs: LogsClient,
283}
284
285#[derive(Debug, Clone)]
286pub struct ConnectivityStatus {
287    pub api_connected: bool,
288    pub indexer_connected: bool,
289    pub api_error: Option<String>,
290    pub indexer_error: Option<String>,
291}
292
293impl ConnectivityStatus {
294    pub fn is_fully_connected(&self) -> bool {
295        self.api_connected && self.indexer_connected
296    }
297
298    pub fn has_any_connection(&self) -> bool {
299        self.api_connected || self.indexer_connected
300    }
301
302    pub fn get_status_summary(&self) -> String {
303        match (self.api_connected, self.indexer_connected) {
304            (true, true) => "All services connected".to_string(),
305            (true, false) => format!(
306                "API connected, Indexer failed: {}",
307                self.indexer_error.as_deref().unwrap_or("Unknown error")
308            ),
309            (false, true) => format!(
310                "Indexer connected, API failed: {}",
311                self.api_error.as_deref().unwrap_or("Unknown error")
312            ),
313            (false, false) => format!(
314                "All services failed - API: {}, Indexer: {}",
315                self.api_error.as_deref().unwrap_or("Unknown error"),
316                self.indexer_error.as_deref().unwrap_or("Unknown error")
317            ),
318        }
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn test_client_factory_creation() {
328        let factory = WazuhClientFactory::builder()
329            .api_host("localhost")
330            .api_port(55000)
331            .api_credentials("wazuh", "password")
332            .indexer_host("localhost")
333            .indexer_port(9200)
334            .indexer_credentials("admin", "admin")
335            .verify_ssl(false)
336            .protocol("https")
337            .build();
338
339        let _agents_client = factory.create_agents_client();
340        let _rules_client = factory.create_rules_client();
341        let _config_client = factory.create_configuration_client();
342        let _vuln_client = factory.create_vulnerability_client();
343        let _ar_client = factory.create_active_response_client();
344        let _cluster_client = factory.create_cluster_client();
345        let _logs_client = factory.create_logs_client();
346        let _indexer_client = factory.create_indexer_client();
347    }
348
349    #[test]
350    fn test_connectivity_status() {
351        let status = ConnectivityStatus {
352            api_connected: true,
353            indexer_connected: false,
354            api_error: None,
355            indexer_error: Some("Connection refused".to_string()),
356        };
357
358        assert!(status.has_any_connection());
359        assert!(!status.is_fully_connected());
360        assert!(status.get_status_summary().contains("API connected"));
361        assert!(status.get_status_summary().contains("Indexer failed"));
362    }
363}