From bf1bba808371ff7c3abdca9b365a1c032b92409c Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Tue, 7 Jan 2025 19:56:35 -0300 Subject: [PATCH 1/8] Remove http client --- .../commandmanager/CommandManagerPlugin.java | 1 - .../jobscheduler/SearchThread.java | 49 +---- .../wazuh/commandmanager/model/Orders.java | 19 ++ .../utils/httpclient/AuthHttpRestClient.java | 175 ---------------- .../httpclient/HttpResponseCallback.java | 67 ------ .../utils/httpclient/HttpRestClient.java | 193 ------------------ .../commandmanager/CommandManagerTests.java | 2 - 7 files changed, 24 insertions(+), 482 deletions(-) delete mode 100644 plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/AuthHttpRestClient.java delete mode 100644 plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpResponseCallback.java delete mode 100644 plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpRestClient.java diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java index 93ae2ff5..5f63e9ac 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java @@ -64,7 +64,6 @@ import com.wazuh.commandmanager.jobscheduler.JobDocument; import com.wazuh.commandmanager.rest.RestPostCommandAction; import com.wazuh.commandmanager.settings.PluginSettings; -import com.wazuh.commandmanager.utils.httpclient.HttpRestClient; /** * The Command Manager plugin exposes an HTTP API with a single endpoint to receive raw commands diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java index 8c1b1bf8..ae70c13e 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java @@ -53,7 +53,6 @@ import com.wazuh.commandmanager.CommandManagerPlugin; import com.wazuh.commandmanager.model.*; import com.wazuh.commandmanager.settings.PluginSettings; -import com.wazuh.commandmanager.utils.httpclient.AuthHttpRestClient; /** * The class in charge of searching and managing commands in {@link Status#PENDING} status and of @@ -115,22 +114,11 @@ public static T getNestedObject(Map map, String key, Class) - () -> { - final AuthHttpRestClient httpClient = - new AuthHttpRestClient(); - return httpClient.post(host, orders, null); - }); - } catch (URISyntaxException e) { - log.error("Invalid URI: {}", e.getMessage()); - } catch (Exception e) { - log.error("Error sending data: {}", e.getMessage()); - } - return response; - } /** * Retrieves the hit's contents and updates the {@link Status} field to {@link Status#SENT}. diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java index 78010bf3..31540b81 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java @@ -61,6 +61,25 @@ public static Orders fromSearchHits(SearchHits searchHits) { return orders; } + /** + * Clears the current list of orders and sets the current list of orders to the input list. + * + * @param orders the list of orders to be set. + */ + public void setOrders(ArrayList orders) { + this.orders.clear(); + this.orders.addAll(orders); + } + + /** + * Retrieves the list of orders. + * + * @return the current list of Order objects. + */ + public ArrayList getOrders() { + return this.orders; + } + /** * Adds an order to the orders array. * diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/AuthHttpRestClient.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/AuthHttpRestClient.java deleted file mode 100644 index 5820a19e..00000000 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/AuthHttpRestClient.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2024, Wazuh Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.wazuh.commandmanager.utils.httpclient; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.net.URIBuilder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.opensearch.core.rest.RestStatus; - -import java.net.HttpRetryException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Locale; - -import com.wazuh.commandmanager.auth.AuthCredentials; -import com.wazuh.commandmanager.auth.HTTPAuthenticator; -import com.wazuh.commandmanager.settings.PluginSettings; - -/** HttpRestClient with authentication. */ -public class AuthHttpRestClient extends HttpRestClient implements HTTPAuthenticator { - - private static final Logger log = LogManager.getLogger(AuthHttpRestClient.class); - - /** Wazuh Server Management API endpoint for basic authentication. */ - public static final String SECURITY_USER_AUTHENTICATE = "/security/user/authenticate"; - - /** Maximum number of authentication retries before giving up. */ - public static final int MAX_RETRIES = 3; - - private final AuthCredentials credentials; - - /** Default constructor */ - public AuthHttpRestClient() { - super(); - - this.credentials = - new AuthCredentials( - PluginSettings.getInstance().getAuthUsername(), - PluginSettings.getInstance().getAuthPassword()); - } - - /** - * Sends a POST request. - * - * @param receiverURI Well-formed URI - * @param payload data to send - * @param payloadId payload ID - * @param headers auth value (Basic "user:password", "Bearer token") - * @return SimpleHttpResponse response - */ - @Override - public SimpleHttpResponse post( - URI receiverURI, String payload, String payloadId, Header... headers) { - try { - return this.post(receiverURI, payload, payloadId, 0); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Sends a POST request. - * - * @param receiverURI Well-formed URI - * @param payload data to send - * @param payloadId payload ID - * @param retries retries counter - * @return SimpleHttpResponse response - * @throws HttpRetryException 429 (Too Many Requests) - */ - public SimpleHttpResponse post( - URI receiverURI, String payload, String payloadId, Integer retries) - throws HttpRetryException { - - // Recursive calls exit condition. - if (retries == MAX_RETRIES) { - String message = - String.format( - Locale.ROOT, - "Max retries [%d/%d] reached for POST request with id [%s]", - retries, - MAX_RETRIES, - payloadId); - throw new HttpRetryException(message, RestStatus.TOO_MANY_REQUESTS.getStatus()); - } - - // Authenticate if required. - if (!this.credentials.isTokenSet()) { - this.authenticate(); - } - - // Perform POST request. - SimpleHttpResponse response = - super.post(receiverURI, payload, payloadId, this.credentials.getAuthAsHeaders()); - - // TODO handle 403 forbidden - // TODO handle 429 maximum number of requests reached - // TODO prevent the above from happening: cool down + sleep between retries - - if (response == null) { - log.error("No reply from server."); - return null; - } - // Handle unauthorized responses. - if (response.getCode() == RestStatus.UNAUTHORIZED.getStatus()) { - // Invalidate current token. - this.credentials.setToken(null); - log.info("Token invalidated."); - // Retry request. - this.post(receiverURI, payload, payloadId, ++retries); - } else if (response.getCode() == RestStatus.OK.getStatus()) { - return response; - } - - return null; - } - - @Override - public void authenticate() { - String mApiURI = PluginSettings.getInstance().getUri(); - try { - URI loginUri = new URIBuilder(mApiURI).appendPath(SECURITY_USER_AUTHENTICATE).build(); - - log.info("Attempting authentication at [{}]", loginUri); - SimpleHttpResponse loginResponse = - super.post(loginUri, null, null, this.credentials.getAuthAsHeaders()); - - if (loginResponse == null) { - log.error("Authentication failed. Failure establishing connection."); - return; - } - - if (loginResponse.getCode() == RestStatus.OK.getStatus()) { - // Parse JSON response to extract and save the JWT token. - ObjectMapper mapper = new ObjectMapper(); - JsonNode root = mapper.readTree(loginResponse.getBodyText()); - String token = root.path("data").path("token").asText(); - this.credentials.setToken(token); - log.info("Authentication successful"); - } else { - log.error( - "Authentication failed due to: {} {}", - loginResponse.getCode(), - loginResponse.getBodyText()); - } - } catch (URISyntaxException e) { - log.error("Invalid URI. Is the IP to the Wazuh Server set? - {}", e.getMessage()); - } catch (JsonMappingException e) { - log.error("Mapping error on JSON response: {}", e.getMessage()); - } catch (JsonProcessingException e) { - log.error("Processing error on JSON response: {}", e.getMessage()); - } - } -} diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpResponseCallback.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpResponseCallback.java deleted file mode 100644 index aa4e141f..00000000 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpResponseCallback.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024, Wazuh Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.wazuh.commandmanager.utils.httpclient; - -import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; -import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; -import org.apache.hc.core5.concurrent.FutureCallback; -import org.apache.hc.core5.http.message.StatusLine; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Default callback class for SimpleHttpResponse that implements completed() and failed() response - * methods. - */ -public class HttpResponseCallback implements FutureCallback { - - private static final Logger log = LogManager.getLogger(HttpResponseCallback.class); - - /** The Http get request. */ - SimpleHttpRequest httpRequest; - - /** The error message. */ - String errorMessage; - - /** - * Deafult constructor - * - * @param httpRequest the request - * @param errorMessage the error message - */ - public HttpResponseCallback(SimpleHttpRequest httpRequest, String errorMessage) { - this.httpRequest = httpRequest; - this.errorMessage = errorMessage; - } - - @Override - public void completed(SimpleHttpResponse response) { - log.debug("{}->{}", httpRequest, new StatusLine(response)); - log.debug("Got response: {} {}", response.getCode(), response.getBodyText()); - } - - @Override - public void failed(Exception ex) { - log.error("{}->{}", httpRequest, ex); - // throw new HttpException(errorMessage, ex); - } - - @Override - public void cancelled() { - log.debug("{} cancelled", httpRequest); - } -} diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpRestClient.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpRestClient.java deleted file mode 100644 index d4dd461b..00000000 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/utils/httpclient/HttpRestClient.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2024, Wazuh Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.wazuh.commandmanager.utils.httpclient; - -import org.apache.hc.client5.http.async.methods.*; -import org.apache.hc.client5.http.auth.AuthScope; -import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; -import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; -import org.apache.hc.client5.http.impl.async.HttpAsyncClients; -import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; -import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; -import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; -import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.nio.ssl.TlsStrategy; -import org.apache.hc.core5.io.CloseMode; -import org.apache.hc.core5.net.URIBuilder; -import org.apache.hc.core5.reactor.IOReactorConfig; -import org.apache.hc.core5.ssl.SSLContextBuilder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.net.ssl.SSLContext; - -import java.net.URI; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import com.wazuh.commandmanager.settings.PluginSettings; -import reactor.util.annotation.NonNull; -import reactor.util.annotation.Nullable; - -import static com.wazuh.commandmanager.utils.httpclient.AuthHttpRestClient.SECURITY_USER_AUTHENTICATE; - -/** HTTP Rest client. Currently used to perform POST requests against the Wazuh Server. */ -public class HttpRestClient { - - private static final Logger log = LogManager.getLogger(HttpRestClient.class); - - /** Seconds on which the request times outs. */ - public static final int TIMEOUT = 5; - - static HttpRestClient instance; - private CloseableHttpAsyncClient httpClient; - - /** Private default constructor */ - HttpRestClient() { - startHttpAsyncClient(); - } - - /** - * Singleton instance accessor - * - * @return {@link HttpRestClient#instance} - */ - public static HttpRestClient getInstance() { - if (HttpRestClient.instance == null) { - instance = new HttpRestClient(); - } - return HttpRestClient.instance; - } - - /** Starts http async client. */ - private void startHttpAsyncClient() { - if (this.httpClient == null) { - try { - // From the official example on - // https://opensearch.org/docs/latest/clients/java/#initializing-the-client-with-ssl-and-tls-enabled-using-apache-httpclient-5-transport - - // Basic auth - final String mApiURI = PluginSettings.getInstance().getUri(); - final URI loginUri = - new URIBuilder(mApiURI).appendPath(SECURITY_USER_AUTHENTICATE).build(); - final HttpHost host = HttpHost.create(loginUri); - final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials( - new AuthScope(host), - new UsernamePasswordCredentials( - PluginSettings.getInstance().getAuthUsername(), - PluginSettings.getInstance().getAuthPassword().toCharArray())); - - // Create a custom TrustManager that trusts self-signed certificates - final SSLContext sslContext = - SSLContextBuilder.create() - .loadTrustMaterial(null, (chains, authType) -> true) - .build(); - - final TlsStrategy tlsStrategy = - ClientTlsStrategyBuilder.create().setSslContext(sslContext).build(); - - final PoolingAsyncClientConnectionManager connectionManager = - PoolingAsyncClientConnectionManagerBuilder.create() - .setTlsStrategy(tlsStrategy) - .build(); - - final IOReactorConfig ioReactorConfig = - IOReactorConfig.custom().setSoTimeout(TIMEOUT, TimeUnit.SECONDS).build(); - - httpClient = - HttpAsyncClients.custom() - .setDefaultCredentialsProvider(credentialsProvider) - .setIOReactorConfig(ioReactorConfig) - .setConnectionManager(connectionManager) - .build(); - httpClient.start(); - } catch (Exception e) { // FIXME catch of generic exception - log.error("Error starting async Http client {}", e.getMessage()); - } - } - } - - /** Stop http async client. */ - public void stopHttpAsyncClient() { - if (this.httpClient != null) { - log.info("Shutting down."); - httpClient.close(CloseMode.GRACEFUL); - httpClient = null; - } - } - - /** - * Sends a POST request. - * - * @param receiverURI Well-formed URI - * @param payload data to send - * @param payloadId payload ID - * @param headers auth value (Basic "user:password", "Bearer token") - * @return SimpleHttpResponse response - */ - public SimpleHttpResponse post( - @NonNull URI receiverURI, - @Nullable String payload, - @Nullable String payloadId, - @Nullable Header... headers) { - try { - final HttpHost httpHost = HttpHost.create(receiverURI); - - log.info("Sending payload with id [{}] to [{}]", payloadId, receiverURI); - log.debug("Headers {}", (Object) headers); - - final SimpleRequestBuilder builder = SimpleRequestBuilder.post(); - if (payload != null) { - builder.setBody(payload, ContentType.APPLICATION_JSON); - } - if (headers != null) { - builder.setHeaders(headers); - } - - final SimpleHttpRequest httpPostRequest = - builder.setHttpHost(httpHost).setPath(receiverURI.getPath()).build(); - - final Future future = - this.httpClient.execute( - SimpleRequestProducer.create(httpPostRequest), - SimpleResponseConsumer.create(), - new HttpResponseCallback( - httpPostRequest, - "Failed to execute outgoing POST request with payload id [" - + payloadId - + "]")); - - return future.get(TIMEOUT, TimeUnit.SECONDS); - } catch (InterruptedException e) { - log.error("Operation interrupted {}", e.getMessage()); - } catch (ExecutionException e) { - log.error("Execution failed {}", e.getMessage()); - } catch (TimeoutException e) { - log.error("Operation timed out {}", e.getMessage()); - } catch (Exception e) { - log.error("Error sending payload with id [{}] due to {}", payloadId, e.toString()); - } - - return null; - } -} diff --git a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java index dcf304f4..33b77914 100644 --- a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java +++ b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java @@ -25,8 +25,6 @@ import java.security.AccessController; import java.security.PrivilegedAction; -import com.wazuh.commandmanager.utils.httpclient.HttpRestClient; - public class CommandManagerTests extends OpenSearchTestCase { // Add unit tests for your plugin From 95671bc2550aaf4e23e92f58dcec81a9ae15ded8 Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Thu, 9 Jan 2025 13:42:42 -0300 Subject: [PATCH 2/8] Change the logic of SearchThread to manage the failed commands --- .../commandmanager/CommandManagerPlugin.java | 11 -- .../jobscheduler/SearchThread.java | 74 ++++++------- .../wazuh/commandmanager/model/Command.java | 10 ++ .../wazuh/commandmanager/model/Document.java | 100 +++++++++++++++++- .../wazuh/commandmanager/model/Documents.java | 13 +++ .../wazuh/commandmanager/model/Orders.java | 1 + .../commandmanager/CommandManagerTests.java | 40 ------- 7 files changed, 159 insertions(+), 90 deletions(-) diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java index 5f63e9ac..1402fc53 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java @@ -252,15 +252,4 @@ private Instant parseInstantValue(XContentParser parser) throws IOException { XContentParserUtils.throwUnknownToken(parser.currentToken(), parser.getTokenLocation()); return null; } - - /** - * Close the resources opened by this plugin. - * - * @throws IOException if the plugin failed to close its resources - */ - @Override - public void close() throws IOException { - super.close(); - HttpRestClient.getInstance().stopHttpAsyncClient(); - } } diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java index ae70c13e..8d5e264a 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java @@ -16,8 +16,6 @@ */ package com.wazuh.commandmanager.jobscheduler; -import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; -import org.apache.hc.core5.net.URIBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.action.index.IndexRequest; @@ -27,11 +25,9 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.client.Client; import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.time.DateUtils; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.core.action.ActionListener; -import org.opensearch.core.rest.RestStatus; -import org.opensearch.core.xcontent.ToXContent; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.TermQueryBuilder; import org.opensearch.search.SearchHit; @@ -40,11 +36,7 @@ import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.sort.SortOrder; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.AccessController; -import java.security.PrivilegedAction; +import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -52,7 +44,6 @@ import com.wazuh.commandmanager.CommandManagerPlugin; import com.wazuh.commandmanager.model.*; -import com.wazuh.commandmanager.settings.PluginSettings; /** * The class in charge of searching and managing commands in {@link Status#PENDING} status and of @@ -106,54 +97,58 @@ public static T getNestedObject(Map map, String key, Class commandMap = getNestedObject( hit.getSourceAsMap(), CommandManagerPlugin.COMMAND_DOCUMENT_PARENT_OBJECT_NAME, Map.class); - commandMap.put(Command.STATUS, status); - hit.getSourceAsMap() - .put(CommandManagerPlugin.COMMAND_DOCUMENT_PARENT_OBJECT_NAME, commandMap); - final IndexRequest indexRequest = - new IndexRequest() - .index(CommandManagerPlugin.INDEX_NAME) - .source(hit.getSourceAsMap()) - .id(hit.getId()); - this.client - .index(indexRequest) - .actionGet(CommandManagerPlugin.DEFAULT_TIMEOUT_SECONDS * 1000); + + if(commandMap != null){ + commandMap.put(Command.STATUS, Status.FAILURE); + hit.getSourceAsMap() + .put(CommandManagerPlugin.COMMAND_DOCUMENT_PARENT_OBJECT_NAME, commandMap); + final IndexRequest indexRequest = + new IndexRequest() + .index(CommandManagerPlugin.INDEX_NAME) + .source(hit.getSourceAsMap()) + .id(hit.getId()); + this.client + .index(indexRequest) + .actionGet(CommandManagerPlugin.DEFAULT_TIMEOUT_SECONDS * 1000); + } } /** @@ -171,6 +166,8 @@ public SearchResponse pitQuery(PointInTimeBuilder pointInTimeBuilder, Object[] s QueryBuilders.termQuery(SearchThread.COMMAND_STATUS_FIELD, Status.PENDING); final TimeValue timeout = TimeValue.timeValueSeconds(CommandManagerPlugin.DEFAULT_TIMEOUT_SECONDS); + log.debug("IN PIT query."); + this.searchSourceBuilder .query(termQueryBuilder) .size(CommandManagerPlugin.PAGE_SIZE) @@ -184,6 +181,9 @@ public SearchResponse pitQuery(PointInTimeBuilder pointInTimeBuilder, Object[] s this.searchSourceBuilder.searchAfter(searchAfter); } searchRequest.source(this.searchSourceBuilder); + + log.debug("FINISHING IN PIT query."); + return this.client.search(searchRequest).actionGet(timeout); } @@ -195,6 +195,8 @@ public void run() { final PointInTimeBuilder pointInTimeBuilder = buildPit(); try { do { + log.debug("In the do-while loop."); + this.currentPage = pitQuery( pointInTimeBuilder, diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Command.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Command.java index 7120c293..35a22ac5 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Command.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Command.java @@ -217,6 +217,16 @@ public String getUser() { return this.user; } + /** + * Retrieves the status of this command. + * + * @return the status of the command. + * @see Status + */ + public Status getStatus() { + return this.status; + } + @Override public String toString() { return "Command{" diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java index 23bd2356..eb1a768b 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java @@ -16,13 +16,16 @@ */ package com.wazuh.commandmanager.model; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.opensearch.common.UUIDs; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateUtils; import org.opensearch.common.time.FormatNames; -import org.opensearch.core.xcontent.ToXContentObject; -import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.*; +import org.opensearch.search.SearchHit; import java.io.IOException; import java.time.ZonedDateTime; @@ -40,6 +43,8 @@ public class Document implements ToXContentObject { private final ZonedDateTime timestamp; private final ZonedDateTime deliveryTimestamp; + private static final Logger log = LogManager.getLogger(Document.class); + /** * Default constructor. * @@ -54,6 +59,20 @@ public Document(Agent agent, Command command) { this.deliveryTimestamp = timestamp.plusSeconds(command.getTimeout()); } + /** + * Custom constructor for existent documents. + * + * @param agent "agent" nested fields. + * @param command "command" nested fields. + */ + public Document(String id, Agent agent, Command command, ZonedDateTime timestamp, ZonedDateTime deliveryTimestamp) { + this.id = id; + this.agent = agent; + this.command = command; + this.timestamp = timestamp; + this.deliveryTimestamp = deliveryTimestamp; + } + /** * Parses data from an XContentParser into this model. * @@ -78,6 +97,63 @@ public static Document parse(XContentParser parser) throws IOException { return new Document(agent, command); } + public static Document fromSearchHit(SearchHit hit) { + try { + XContentParser parser = + XContentHelper.createParser( + NamedXContentRegistry.EMPTY, + DeprecationHandler.IGNORE_DEPRECATIONS, + hit.getSourceRef(), + XContentType.JSON); + + Command command = null; + Agent agent = null; + ZonedDateTime deliveryTimestamp = null; + ZonedDateTime timestamp = null; + + // Iterate over the JsonXContentParser's JsonToken until we hit null, + // which corresponds to end of data + while (parser.nextToken() != null) { + // Look for FIELD_NAME JsonToken s + if (parser.currentToken().equals(XContentParser.Token.FIELD_NAME)) { + String fieldName = parser.currentName(); + switch (fieldName) { + case Document.DELIVERY_TIMESTAMP: + deliveryTimestamp = ZonedDateTime.from(DATE_FORMATTER.parse(parser.text())); + break; + + case Document.TIMESTAMP: + timestamp = ZonedDateTime.from(DATE_FORMATTER.parse(parser.text())); + break; + + case Agent.AGENT: + // Parse Agent + agent = Agent.parse(parser); + break; + + case Command.COMMAND: + // Parse Command + command = Command.parse(parser); + break; + + default: + parser.skipChildren(); + } + } + } + // Create a new Document object with the Command's fields + return new Document(hit.getId(), agent, command, timestamp, deliveryTimestamp); + + } catch (IOException e) { + log.error("Document could not be parsed: {}", e.getMessage()); + } catch (NullPointerException e) { + log.error( + "Could not create Document object. One or more of the constructor's arguments was null: {}", + e.getMessage()); + } + return null; + } + /** * Returns the document's "_id". * @@ -87,6 +163,24 @@ public String getId() { return this.id; } + /** + * Returns the Command object associated with this Document. + * + * @return Command object + */ + public Command getCommand() { + return this.command; + } + + /** + * Returns the timestamp at which the Command was delivered to the Agent. + * + * @return ZonedDateTime object representing the delivery timestamp + */ + public ZonedDateTime getDeliveryTimestamp() { + return this.deliveryTimestamp; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java index dc0e5596..c9cb3189 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java @@ -18,6 +18,8 @@ import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.search.SearchHit; +import org.opensearch.search.SearchHits; import java.io.IOException; import java.util.ArrayList; @@ -69,4 +71,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } return builder.endArray(); } + + public static Documents fromSearchHits(SearchHits searchHits) { + Documents documents = new Documents(); + // Iterate over search results + for (SearchHit hit : searchHits) { + // Parse the hit's document + Document document = Document.fromSearchHit(hit); + documents.addDocument(document); + } + return documents; + } } diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java index 31540b81..a2c65e09 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Orders.java @@ -42,6 +42,7 @@ public Orders() { * @param searchHits the commands search result * @return A json string payload with an array of orders to be processed */ + /** * Static builder method that initializes an instance of Orders from a SearchHits instance. * diff --git a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java index 33b77914..cb97c459 100644 --- a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java +++ b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java @@ -26,46 +26,6 @@ import java.security.PrivilegedAction; public class CommandManagerTests extends OpenSearchTestCase { - // Add unit tests for your plugin - - private HttpRestClient httpClient; - - // FIXME Test is flaky - @AwaitsFix(bugUrl = "https://github.com/wazuh/wazuh-indexer-plugins/issues/163") - public void testPost_success() { - try { - AccessController.doPrivileged( - (PrivilegedAction) - () -> { - this.httpClient = HttpRestClient.getInstance(); - URI uri; - try { - uri = new URI("https://httpbin.org/post"); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - String payload = "{\"message\": \"Hello world!\"}"; - SimpleHttpResponse postResponse = - this.httpClient.post( - uri, - payload, - "randomId", - (org.apache.hc.core5.http.Header) null); - - String responseText = postResponse.getBodyText(); - assertNotEquals(null, postResponse); - assertNotEquals(null, responseText); - assertEquals(200, postResponse.getCode()); - assertNotEquals(0, responseText.length()); - assertTrue(responseText.contains("Hello world!")); - return postResponse; - }); - } catch (Exception e) { - Assert.fail("Failed to execute HTTP request: " + e); - } finally { - this.httpClient.stopHttpAsyncClient(); - } - } public void testPost_badUri() {} From dbd3e831c0a80d85e52345d099e98104d976d9fa Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Fri, 10 Jan 2025 09:05:05 -0300 Subject: [PATCH 3/8] Change the parser to only obtain the delivery_timestamp of hit to compare --- .../jobscheduler/SearchThread.java | 24 ++----- .../wazuh/commandmanager/model/Document.java | 66 ++++++++----------- .../commandmanager/CommandManagerTests.java | 7 -- 3 files changed, 33 insertions(+), 64 deletions(-) diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java index 8d5e264a..932a91ba 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java @@ -97,26 +97,20 @@ public static T getNestedObject(Map map, String key, Class Date: Fri, 10 Jan 2025 09:16:07 -0300 Subject: [PATCH 4/8] Delete settings in Command Plugin --- plugins/command-manager/build.gradle | 5 - .../commandmanager/CommandManagerPlugin.java | 24 +--- .../jobscheduler/SearchThread.java | 6 +- .../wazuh/commandmanager/model/Document.java | 1 - .../wazuh/commandmanager/model/Documents.java | 13 -- .../CommandManagerSettingsException.java | 37 ----- .../settings/PluginSettings.java | 136 ------------------ .../settings/PluginSettingsTests.java | 80 ----------- 8 files changed, 5 insertions(+), 297 deletions(-) delete mode 100644 plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/CommandManagerSettingsException.java delete mode 100644 plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/PluginSettings.java delete mode 100644 plugins/command-manager/src/test/java/com/wazuh/commandmanager/settings/PluginSettingsTests.java diff --git a/plugins/command-manager/build.gradle b/plugins/command-manager/build.gradle index 05767572..6655e748 100644 --- a/plugins/command-manager/build.gradle +++ b/plugins/command-manager/build.gradle @@ -175,11 +175,6 @@ testClusters.integTest { if (System.getProperty("test.debug") != null) { jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005' } - - // add customized keystore - keystore 'm_api.auth.username', 'wazuh' - keystore 'm_api.auth.password', 'wazuh' - keystore 'm_api.uri', 'https://127.0.0.1:55000' // base URI of the M_API } run { diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java index 1402fc53..dd4c599b 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/CommandManagerPlugin.java @@ -24,7 +24,6 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.UUIDs; -import org.opensearch.common.settings.*; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; @@ -42,7 +41,6 @@ import org.opensearch.jobscheduler.spi.schedule.ScheduleParser; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.plugins.ReloadablePlugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.*; import org.opensearch.script.ScriptService; @@ -51,7 +49,6 @@ import java.io.IOException; import java.time.Instant; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -63,7 +60,6 @@ import com.wazuh.commandmanager.jobscheduler.CommandManagerJobRunner; import com.wazuh.commandmanager.jobscheduler.JobDocument; import com.wazuh.commandmanager.rest.RestPostCommandAction; -import com.wazuh.commandmanager.settings.PluginSettings; /** * The Command Manager plugin exposes an HTTP API with a single endpoint to receive raw commands @@ -74,8 +70,7 @@ * *

The Command Manager plugin is also a JobScheduler extension plugin. */ -public class CommandManagerPlugin extends Plugin - implements ActionPlugin, ReloadablePlugin, JobSchedulerExtension { +public class CommandManagerPlugin extends Plugin implements ActionPlugin, JobSchedulerExtension { public static final String COMMAND_MANAGER_BASE_URI = "/_plugins/_command_manager"; public static final String COMMANDS_URI = COMMAND_MANAGER_BASE_URI + "/commands"; public static final String INDEX_NAME = ".commands"; @@ -110,9 +105,6 @@ public Collection createComponents( // Command index repository initialization. this.commandIndex = new CommandIndex(client, clusterService, threadPool); - // Plugin settings initialization. - PluginSettings.getInstance(environment.settings()); - // Scheduled job initialization // NOTE it's very likely that client and thread pool may not be required as the command // index @@ -170,20 +162,6 @@ public List getRestHandlers( return Collections.singletonList(new RestPostCommandAction(this.commandIndex)); } - @Override - public List> getSettings() { - return Arrays.asList( - // Register API settings - PluginSettings.M_API_AUTH_USERNAME, - PluginSettings.M_API_AUTH_PASSWORD, - PluginSettings.M_API_URI); - } - - @Override - public void reload(Settings settings) { - // TODO - } - @Override public String getJobType() { return CommandManagerPlugin.JOB_TYPE; diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java index 932a91ba..3d68595b 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java @@ -109,8 +109,10 @@ public void handlePage(SearchResponse searchResponse) throws IllegalStateExcepti ZonedDateTime current_time = DateUtils.nowWithMillisResolution(); for (SearchHit hit : searchHits) { - ZonedDateTime deliveryTimestampFromSearchHit = Document.deliveryTimestampFromSearchHit(hit); - if (deliveryTimestampFromSearchHit != null && deliveryTimestampFromSearchHit.isBefore(current_time)) { + ZonedDateTime deliveryTimestampFromSearchHit = + Document.deliveryTimestampFromSearchHit(hit); + if (deliveryTimestampFromSearchHit != null + && deliveryTimestampFromSearchHit.isBefore(current_time)) { this.setFailureStatus(hit); } } diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java index 3e539fff..9fad78ca 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java @@ -30,7 +30,6 @@ import java.io.IOException; import java.time.ZonedDateTime; import java.util.List; -import java.util.Map; /** Command's target fields. */ public class Document implements ToXContentObject { diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java index c9cb3189..dc0e5596 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Documents.java @@ -18,8 +18,6 @@ import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.search.SearchHit; -import org.opensearch.search.SearchHits; import java.io.IOException; import java.util.ArrayList; @@ -71,15 +69,4 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } return builder.endArray(); } - - public static Documents fromSearchHits(SearchHits searchHits) { - Documents documents = new Documents(); - // Iterate over search results - for (SearchHit hit : searchHits) { - // Parse the hit's document - Document document = Document.fromSearchHit(hit); - documents.addDocument(document); - } - return documents; - } } diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/CommandManagerSettingsException.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/CommandManagerSettingsException.java deleted file mode 100644 index 93cb8786..00000000 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/CommandManagerSettingsException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024, Wazuh Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.wazuh.commandmanager.settings; - -public class CommandManagerSettingsException extends Exception { - - // Constructor that accepts a message - public CommandManagerSettingsException(String message) { - super(message); - } - - // Exception for the case when load keystore failed - public static CommandManagerSettingsException loadSettingsFailed( - String keyStorePath, String errorMessage) { - return new CommandManagerSettingsException( - "Load settings from: " + keyStorePath + " failed. Error: " + errorMessage); - } - - // Exception for the case when reload plugin with the keystore failed - public static CommandManagerSettingsException reloadPluginFailed(String pluginName) { - return new CommandManagerSettingsException("Reload failed for plugin: " + pluginName); - } -} diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/PluginSettings.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/PluginSettings.java deleted file mode 100644 index 1c773f82..00000000 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/settings/PluginSettings.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2024, Wazuh Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.wazuh.commandmanager.settings; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.Setting; -import org.opensearch.common.settings.Settings; -import org.opensearch.core.common.settings.SecureString; - -import reactor.util.annotation.NonNull; - -/** Singleton class to manage the plugin's settings. */ -public class PluginSettings { - - private static final Logger log = LogManager.getLogger(PluginSettings.class); - - /** The access key (ie login username) for connecting to api. */ - public static final Setting M_API_AUTH_USERNAME = - SecureSetting.secureString("m_api.auth.username", null); - - /** The secret key (ie password) for connecting to api. */ - public static final Setting M_API_AUTH_PASSWORD = - SecureSetting.secureString("m_api.auth.password", null); - - /** The uri for connecting to api. */ - public static final Setting M_API_URI = - SecureSetting.secureString("m_api.uri", null); - - /** Singleton instance. */ - private static PluginSettings INSTANCE; - - /** The access key (ie login username) for connecting to api. */ - private final SecureString authUsername; - - /** The password for connecting to api. */ - private final SecureString authPassword; - - /** The uri for connecting to api. */ - private final SecureString uri; - - /** - * Private default constructor - * - * @param settings as obtained in createComponents. - */ - private PluginSettings(@NonNull final Settings settings) { - log.info("Plugin created with the keystore information."); - - this.authUsername = M_API_AUTH_USERNAME.get(settings); - this.authPassword = M_API_AUTH_PASSWORD.get(settings); - this.uri = M_API_URI.get(settings); - } - - /** - * Singleton instance accessor. Initializes the settings - * - * @param settings as obtained in createComponents. - * @return {@link PluginSettings#INSTANCE} - */ - public static PluginSettings getInstance(@NonNull final Settings settings) { - if (PluginSettings.INSTANCE == null) { - INSTANCE = new PluginSettings(settings); - } - return INSTANCE; - } - - /** - * Singleton instance accessor - * - * @return {@link PluginSettings#INSTANCE} - */ - public static PluginSettings getInstance() { - if (PluginSettings.INSTANCE == null) { - throw new IllegalStateException("Plugin settings have not been initialized."); - } - return INSTANCE; - } - - /** - * Get M_API password. - * - * @return M_API password. - */ - public String getAuthPassword() { - return this.authPassword.toString(); - } - - /** - * Get M_API username. - * - * @return M_API username. - */ - public String getAuthUsername() { - return this.authUsername.toString(); - } - - /** - * M_API URL. For example: https://127.0.0.1:55000. - * - * @return M_API URL. - */ - public String getUri() { - return this.uri.toString(); - } - - @Override - public String toString() { - return "PluginSettings{" - + "authUsername='" - + getAuthUsername() - + '\'' - + ", authPassword='" - + getAuthUsername() - + '\'' - + ", uri='" - + getUri() - + '\'' - + '}'; - } -} diff --git a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/settings/PluginSettingsTests.java b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/settings/PluginSettingsTests.java deleted file mode 100644 index eea4ffe9..00000000 --- a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/settings/PluginSettingsTests.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2024, Wazuh Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.wazuh.commandmanager.settings; - -import org.opensearch.common.settings.MockSecureSettings; -import org.opensearch.common.settings.Settings; -import org.opensearch.env.Environment; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.junit.Before; - -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import static org.mockito.Mockito.*; - -@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class PluginSettingsTests extends OpenSearchIntegTestCase { - @Mock private Environment mockEnvironment; - - @InjectMocks private PluginSettings pluginSettings; - - MockSecureSettings secureSettings; - - Settings testSettings; - - @Before - @Override - public void setUp() throws Exception { - secureSettings = new MockSecureSettings(); - secureSettings.setString("m_api.auth.username", "testUser"); - secureSettings.setString("m_api.auth.password", "testPassword"); - secureSettings.setString("m_api.uri", "https://httpbin.org/post"); - testSettings = Settings.builder().setSecureSettings(secureSettings).build(); - mockEnvironment = mock(Environment.class); - when(mockEnvironment.settings()).thenReturn(testSettings); - pluginSettings = PluginSettings.getInstance(mockEnvironment.settings()); - super.setUp(); - } - - public void testInitializeWithValidValues() throws Exception { - // Call getSettings and expect a PluginSettings object - pluginSettings = PluginSettings.getInstance(mockEnvironment.settings()); - - assertNotNull("Expect that the PluginSettings object is not null", pluginSettings); - assertEquals( - "The m_api.auth.username must be the same", - "testUser", - pluginSettings.getAuthUsername()); - assertEquals( - "The m_api.auth.password must be the same", - "testPassword", - pluginSettings.getAuthPassword()); - assertEquals( - "The m_api.uri must be the same", - "https://httpbin.org/post", - pluginSettings.getUri()); // Cleanup - secureSettings.close(); - } - - public void testSingletonBehavior() throws Exception { - final MockSecureSettings secureSettings = new MockSecureSettings(); - PluginSettings settings1 = PluginSettings.getInstance(mockEnvironment.settings()); - PluginSettings settings2 = PluginSettings.getInstance(mockEnvironment.settings()); - assertEquals("Both instances should be the same", settings1, settings2); - } -} From 1977012826d538a0ccff1557ad1f6c7037c2dd6f Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Fri, 10 Jan 2025 09:22:07 -0300 Subject: [PATCH 5/8] Delete not usage constructor and fix java doc of the new method --- .../wazuh/commandmanager/model/Document.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java index 9fad78ca..744e0460 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/model/Document.java @@ -59,25 +59,6 @@ public Document(Agent agent, Command command) { this.deliveryTimestamp = timestamp.plusSeconds(command.getTimeout()); } - /** - * Custom constructor for existent documents. - * - * @param agent "agent" nested fields. - * @param command "command" nested fields. - */ - public Document( - String id, - Agent agent, - Command command, - ZonedDateTime timestamp, - ZonedDateTime deliveryTimestamp) { - this.id = id; - this.agent = agent; - this.command = command; - this.timestamp = timestamp; - this.deliveryTimestamp = deliveryTimestamp; - } - /** * Parses data from an XContentParser into this model. * @@ -107,7 +88,6 @@ public static Document parse(XContentParser parser) throws IOException { * * @param hit search hit parser. * @return delivery timestamp from Document in search hit. - * @throws IOException parsing error occurred. */ public static ZonedDateTime deliveryTimestampFromSearchHit(SearchHit hit) { ZonedDateTime deliveryTimestamp = null; From 05b909655717fc1e71072582913e6f9aad449483 Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Fri, 10 Jan 2025 11:42:08 -0300 Subject: [PATCH 6/8] Fix comments in pull request --- imposter/README.md | 45 --------------- imposter/orders/response.js | 7 --- imposter/security/login.js | 46 ---------------- imposter/wazuh-server-config.yaml | 55 ------------------- .../jobscheduler/SearchThread.java | 5 +- .../wazuh/commandmanager/model/Orders.java | 2 +- .../commandmanager/CommandManagerTests.java | 26 --------- 7 files changed, 4 insertions(+), 182 deletions(-) delete mode 100644 imposter/README.md delete mode 100644 imposter/orders/response.js delete mode 100644 imposter/security/login.js delete mode 100644 imposter/wazuh-server-config.yaml delete mode 100644 plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java diff --git a/imposter/README.md b/imposter/README.md deleted file mode 100644 index d0b5ff6c..00000000 --- a/imposter/README.md +++ /dev/null @@ -1,45 +0,0 @@ -## Imposter Introduction - -Imposter is a mock server that we use to simulate responses from the Wazuh Manager API, allowing testing and development without a live backend. - -### Prerequisites - -To use Imposter, you will need a Java Virtual Machine (JVM) installed. - -### Installation - -To use Imposter for testing during development, you first need to install it by following the [Imposter installation guide](https://github.com/gatehill/imposter-cli/blob/main/docs/install.md). - -### Configuration - -The OpenAPI specification for our service is defined by the URL in the `specFile` attribute within `wazuh-server-config.yaml`. This setup ensures that the specification is automatically updated with new versions. - -In `wazuh-server-config.yaml`, you can also find the configurations for specific endpoints used in our plugins. If you need to modify the default response for any endpoint, adjust the `statusCode` attribute accordingly. The possible values for `statusCode` are outlined in the OpenAPI `specFile`. - -### Usage - -After installing Imposter, set up a new Imposter instance using the following command: - -```bash -IMPOSTER_OPENAPI_REMOTE_FILE_CACHE=true IMPOSTER_JS_PLUGIN=js-graal-compat imposter up -p 55000 -t jvm -``` - -Runing Imposter with SSL / TSL. -```bash -IMPOSTER_OPENAPI_REMOTE_FILE_CACHE=true IMPOSTER_JS_PLUGIN=js-graal-compat java -jar ~/.imposter/engines/imposter-4.2.4.jar --plugin openapi --tlsEnabled --configDir ~/wazuh/wazuh-indexer-plugins/imposter --listenPort 55000 --keystorePath ~/wazuh/wazuh-indexer-plugins/imposter/imposter.jks --keystorePassword password -``` - -- `IMPOSTER_OPENAPI_REMOTE_FILE_CACHE=true` enables caching the `specFile`. -- `IMPOSTER_JS_PLUGIN=js-graal-compat` allows compatibility with JavaScript libraries for dynamic loading. - -Once Imposter is running, you can access the Swagger documentation at [http://localhost:55000/_spec/](http://localhost:55000/_spec/). Use this interface for browsing specifications or testing with tools like `curl`, or integrate it directly into your development tests. - -### Useful Imposter Commands - -- **Check Setup**: Run the following command to verify that everything is in place to start Imposter: - - ```bash - imposter doctor - ``` - -This command checks the configuration and dependencies to ensure Imposter can run correctly. diff --git a/imposter/orders/response.js b/imposter/orders/response.js deleted file mode 100644 index aee600cb..00000000 --- a/imposter/orders/response.js +++ /dev/null @@ -1,7 +0,0 @@ -console.log( - JSON.stringify( - JSON.parse(context.request.body), - undefined, - 2 - ) - ) \ No newline at end of file diff --git a/imposter/security/login.js b/imposter/security/login.js deleted file mode 100644 index 48893408..00000000 --- a/imposter/security/login.js +++ /dev/null @@ -1,46 +0,0 @@ -exports = {}; - -console.log = function(message) { - print("[Log]: " + message); -}; - -load('https://raw.githubusercontent.com/kjur/jsrsasign/master/npm/lib/jsrsasign.js', exports); -header = { - "alg": "HS256", - "typ": "JWT", - "kid": "vpaas-magic-cookie-1fc542a3e4414a44b2611668195e2bfe/4f4910" -}; - -// The second part of the token is the payload, which contains the claims. -// Claims are statements about an entity (typically, the user) and -// additional data. There are three types of claims: -// registered, public, and private claims. - -nbf = Date.now() - 1000; - -claims = { - "iss": "wazuh", - "aud": "Wazuh API REST", - "nbf": nbf, - "exp": nbf + 3600000, - "sub": "wazuh", - "rbac_roles": [ - 1 - ], - "rbac_mode": "white" -}; - - -jwt = KJUR.jws.JWS.sign("HS256", JSON.stringify(header), JSON.stringify(claims), "616161"); -console.log("JWT generated: " + jwt); - -resp = { - "data": { - "token": jwt, - "error": 0 - } -}; - -respond() - .withStatusCode(200) - .withData(JSON.stringify(resp)); \ No newline at end of file diff --git a/imposter/wazuh-server-config.yaml b/imposter/wazuh-server-config.yaml deleted file mode 100644 index 0df80ae5..00000000 --- a/imposter/wazuh-server-config.yaml +++ /dev/null @@ -1,55 +0,0 @@ -plugin: openapi -specFile: https://raw.githubusercontent.com/wazuh/wazuh/refs/heads/master/api/api/spec/spec.yaml - -# ===================================================== # -# SECURITY -# ===================================================== # - -security: - # no requests permitted by default - default: Deny - - # only requests meeting these conditions are permitted - conditions: - - effect: Permit - requestHeaders: - Authorization: - value: Bearer .* - operator: Matches - - -resources: - - method: GET - path: /_spec/* - response: - statusCode: 200 - security: - default: Permit - - - method: GET - path: / - response: - statusCode: 200 - security: - default: Permit - - # Login - - method: POST - path: /security/user/authenticate - response: - statusCode: 200 - scriptFile: security/login.js - security: - conditions: - - effect: Permit - requestHeaders: - Authorization: - value: Basic .* - operator: Matches - - # Orders - - method: POST - path: /orders - response: - scriptFile: - orders/response.js \ No newline at end of file diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java index 3d68595b..3de32894 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java @@ -106,10 +106,10 @@ public static T getNestedObject(Map map, String key, Class. - */ -package com.wazuh.commandmanager; - -import org.opensearch.test.OpenSearchTestCase; - -public class CommandManagerTests extends OpenSearchTestCase { - - public void testPost_badUri() {} - - public void testPost_badPayload() {} -} From 8f4ce34becd0f26e2e61aaf65639213ac5a4b549 Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Fri, 10 Jan 2025 12:15:06 -0300 Subject: [PATCH 7/8] Retrieve the CommandManagerTest class --- .../com/wazuh/commandmanager/CommandManagerTests.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java diff --git a/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java new file mode 100644 index 00000000..919354f4 --- /dev/null +++ b/plugins/command-manager/src/test/java/com/wazuh/commandmanager/CommandManagerTests.java @@ -0,0 +1,8 @@ +package com.wazuh.commandmanager; +import org.opensearch.test.OpenSearchTestCase; + +public class CommandManagerTests extends OpenSearchTestCase { + public void testPost_badUri() {} + public void testPost_badPayload() {} + +} From 187442d5ffcc152cc2e1415271fa673c53578de2 Mon Sep 17 00:00:00 2001 From: Malena Casas Date: Fri, 10 Jan 2025 12:46:51 -0300 Subject: [PATCH 8/8] Improve documentation of handlePage --- .../jobscheduler/SearchThread.java | 4 ++-- .../commandmanager/CommandManagerTests.java | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java index 3de32894..3f82cbc4 100644 --- a/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java +++ b/plugins/command-manager/src/main/java/com/wazuh/commandmanager/jobscheduler/SearchThread.java @@ -97,8 +97,8 @@ public static T getNestedObject(Map map, String key, Class. + */ package com.wazuh.commandmanager; + import org.opensearch.test.OpenSearchTestCase; public class CommandManagerTests extends OpenSearchTestCase { public void testPost_badUri() {} - public void testPost_badPayload() {} + public void testPost_badPayload() {} }