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 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}