From 30fece065a35088b890b00dced07b4e5914a7ffa Mon Sep 17 00:00:00 2001 From: John OHara Date: Thu, 8 Feb 2024 10:27:26 +0000 Subject: [PATCH] Implement Basic Authentication for Elasticsearch datastore: Fixes #1295 --- docs/site/content/en/openapi/openapi.yaml | 7 ++- .../data/datastore/BaseDatastoreConfig.java | 2 + .../ElasticsearchDatastoreConfig.java | 42 ++++++++++++++- .../datastore/PostgresDatastoreConfig.java | 4 ++ .../horreum/api/services/ConfigService.java | 2 +- .../tools/horreum/datastore/Datastore.java | 3 ++ .../datastore/ElasticsearchDatastore.java | 52 ++++++++++++++++--- .../horreum/datastore/PostgresDatastore.java | 8 +++ .../tools/horreum/svc/ConfigServiceImpl.java | 37 +++++++++++-- horreum-web/package-lock.json | 36 ++++++------- horreum-web/src/App.tsx | 1 + horreum-web/src/domain/admin/Datastores.tsx | 20 +++---- .../admin/datastore/ModifyDatastoreModal.tsx | 47 ++++++++++++++++- 13 files changed, 217 insertions(+), 44 deletions(-) diff --git a/docs/site/content/en/openapi/openapi.yaml b/docs/site/content/en/openapi/openapi.yaml index 8fb15e934..388a43426 100644 --- a/docs/site/content/en/openapi/openapi.yaml +++ b/docs/site/content/en/openapi/openapi.yaml @@ -2557,7 +2557,6 @@ components: description: Type of backend datastore required: - builtIn - - apiKey - url type: object properties: @@ -2570,6 +2569,12 @@ components: url: description: Elasticsearch url type: string + username: + description: Elasticsearch username + type: string + password: + description: Elasticsearch password + type: string ErrorDetails: required: - type diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/BaseDatastoreConfig.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/BaseDatastoreConfig.java index 816812de1..1c75f54ba 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/BaseDatastoreConfig.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/BaseDatastoreConfig.java @@ -15,4 +15,6 @@ public BaseDatastoreConfig() { public BaseDatastoreConfig(Boolean builtIn) { this.builtIn = builtIn; } + + public abstract String validateConfig(); } diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/ElasticsearchDatastoreConfig.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/ElasticsearchDatastoreConfig.java index 3021c47b1..440968646 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/ElasticsearchDatastoreConfig.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/ElasticsearchDatastoreConfig.java @@ -1,6 +1,7 @@ package io.hyperfoil.tools.horreum.api.data.datastore; - +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; import org.eclipse.microprofile.openapi.annotations.media.Schema; @@ -12,7 +13,7 @@ public ElasticsearchDatastoreConfig() { super(false); } - @Schema(type = SchemaType.STRING, required = true, + @Schema(type = SchemaType.STRING, description = "Elasticsearch API KEY") public String apiKey; @@ -21,4 +22,41 @@ public ElasticsearchDatastoreConfig() { public String url; + @Schema(type = SchemaType.STRING, + description = "Elasticsearch username") + public String username; + + @Schema(type = SchemaType.STRING, + description = "Elasticsearch password") + @JsonIgnore + public String password; + + + @JsonProperty("password") + public void setSecrets(String password) { + this.password = password; + } + + @JsonProperty("password") + public String getMaskedSecrets() { + if (this.password != null) { + return "********"; + } else { + return null; + } + } + @Override + public String validateConfig() { + if ( "".equals(apiKey) && ( "".equals(username) || "".equals(password)) ) { + return "Either apiKey or username and password must be set"; + } + + if( !"".equals(apiKey) && !( "".equals(username) || "".equals(password) ) ) { + return "Only apiKey or username and password can be set"; + } + + return null; + } + + } diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/PostgresDatastoreConfig.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/PostgresDatastoreConfig.java index 3a24c2cee..f023fdfb4 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/PostgresDatastoreConfig.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/data/datastore/PostgresDatastoreConfig.java @@ -8,4 +8,8 @@ description = "Built in backend datastore") public class PostgresDatastoreConfig extends BaseDatastoreConfig { + @Override + public String validateConfig() { + return null; + } } diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/ConfigService.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/ConfigService.java index c0000b5ab..92c1187d4 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/ConfigService.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/ConfigService.java @@ -54,7 +54,7 @@ public interface ConfigService { @APIResponseSchema(value = Integer.class, responseDescription = "The ID for the new Datastore", responseCode = "200") - Integer newDatastore(Datastore datastore); + Integer newDatastore(@NotNull Datastore datastore); @PUT @Path("datastore") diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/Datastore.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/Datastore.java index 3ee365938..672eb4b33 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/Datastore.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/Datastore.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.hyperfoil.tools.horreum.api.data.datastore.DatastoreType; import io.hyperfoil.tools.horreum.entity.backend.DatastoreConfigDAO; import jakarta.ws.rs.BadRequestException; @@ -22,6 +23,8 @@ public interface Datastore{ UploadType uploadType(); + String validateConfig(Object config); + enum UploadType { SINGLE, MUILTI } diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/ElasticsearchDatastore.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/ElasticsearchDatastore.java index 5ada965bd..0481b5152 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/ElasticsearchDatastore.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/ElasticsearchDatastore.java @@ -9,9 +9,15 @@ import io.hyperfoil.tools.horreum.api.data.datastore.ElasticsearchDatastoreConfig; import io.hyperfoil.tools.horreum.entity.backend.DatastoreConfigDAO; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import jakarta.ws.rs.BadRequestException; import org.apache.http.Header; import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.message.BasicHeader; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; @@ -30,6 +36,9 @@ public class ElasticsearchDatastore implements Datastore { protected static final Logger log = Logger.getLogger(ElasticsearchDatastore.class); + @Inject + ObjectMapper mapper; + Map hostCache = new ConcurrentHashMap<>(); @Override @@ -40,6 +49,8 @@ public DatastoreResponse handleRun(JsonNode payload, ObjectMapper mapper) throws BadRequestException{ + RestClient restClient = null; + try { if ( metaData != null ){ @@ -52,12 +63,21 @@ public DatastoreResponse handleRun(JsonNode payload, if ( elasticsearchDatastoreConfig != null ){ - RestClientBuilder builder = RestClient.builder(HttpHost.create(elasticsearchDatastoreConfig.url)) - .setDefaultHeaders(new Header[]{ - new BasicHeader("Authorization", "ApiKey " + elasticsearchDatastoreConfig.apiKey) - }); + RestClientBuilder builder = RestClient.builder(HttpHost.create(elasticsearchDatastoreConfig.url)); + if( elasticsearchDatastoreConfig.apiKey != null) { + builder.setDefaultHeaders(new Header[]{ + new BasicHeader("Authorization", "ApiKey " + elasticsearchDatastoreConfig.apiKey) + }); + } else { + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(elasticsearchDatastoreConfig.username, elasticsearchDatastoreConfig.password)); + + builder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder + .setDefaultCredentialsProvider(credentialsProvider)); + } - RestClient restClient = builder.build(); + restClient = builder.build(); if ( restClient == null ) { log.warn("Could not find elasticsearch datastore: " + configuration.name); @@ -134,6 +154,8 @@ public DatastoreResponse handleRun(JsonNode payload, extractedResults = mapper.createArrayNode(); //2nd retrieve the docs from 2nd Index and combine into a single result with metadata and doc contents + final RestClient finalRestClient = restClient; //copy of restClient for use in lambda + elasticResults.forEach(jsonNode -> { ObjectNode result = ((ObjectNode) jsonNode.get("_source")).put("$schema", schemaUri); @@ -149,7 +171,7 @@ public DatastoreResponse handleRun(JsonNode payload, "/" + multiIndexQuery.targetIndex + "/_doc/" + jsonNode.get("_source").get(multiIndexQuery.docField).textValue()); try { - docString = extracted(restClient, subRequest); + docString = extracted(finalRestClient, subRequest); } catch (IOException e) { @@ -184,6 +206,15 @@ public DatastoreResponse handleRun(JsonNode payload, } catch (IOException e) { throw new RuntimeException(e); } + finally { + if ( restClient != null ) { + try { + restClient.close(); + } catch (IOException e) { + log.errorf("Error closing rest client: %s", e.getMessage()); + } + } + } } private static String extracted(RestClient restClient, Request request) throws IOException { @@ -205,6 +236,15 @@ public UploadType uploadType() { return UploadType.MUILTI; } + @Override + public String validateConfig(Object config) { + try { + return mapper.treeToValue((ObjectNode) config, ElasticsearchDatastoreConfig.class).validateConfig(); + } catch (JsonProcessingException e) { + return "Unable to read configuration. if the problem persists, please contact a system administrator"; + } + } + private static class ElasticRequest { public ElasticRequest() { } diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/PostgresDatastore.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/PostgresDatastore.java index 1d72a4133..dc5ad1a77 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/PostgresDatastore.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/datastore/PostgresDatastore.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.hyperfoil.tools.horreum.api.data.datastore.DatastoreType; +import io.hyperfoil.tools.horreum.api.data.datastore.PostgresDatastoreConfig; import io.hyperfoil.tools.horreum.entity.backend.DatastoreConfigDAO; import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.BadRequestException; @@ -32,5 +34,11 @@ public UploadType uploadType() { return UploadType.SINGLE; } + @Override + public String validateConfig(Object config) { + //do not validate internal datastore + return null; + } + } diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java index 8ad180f21..6613cfab7 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java @@ -5,6 +5,8 @@ import io.hyperfoil.tools.horreum.api.data.Test; import io.hyperfoil.tools.horreum.api.data.datastore.Datastore; import io.hyperfoil.tools.horreum.api.services.ConfigService; +import io.hyperfoil.tools.horreum.datastore.BackendResolver; +import io.hyperfoil.tools.horreum.datastore.DatastoreResponse; import io.hyperfoil.tools.horreum.entity.backend.DatastoreConfigDAO; import io.hyperfoil.tools.horreum.entity.data.TestDAO; import io.hyperfoil.tools.horreum.mapper.DatasourceMapper; @@ -16,14 +18,18 @@ import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; +import jakarta.validation.constraints.NotNull; import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import static org.keycloak.util.JsonSerialization.mapper; + @ApplicationScoped public class ConfigServiceImpl implements ConfigService { @@ -35,6 +41,9 @@ public class ConfigServiceImpl implements ConfigService { @Inject EntityManager em; + @Inject + BackendResolver backendResolver; + @Override public KeycloakConfig keycloak() { @@ -60,7 +69,7 @@ public List datastores(String team) { String queryWhere = "where access = 0"; Set roles = identity.getRoles(); long rolesCount = roles.stream().filter(role -> role.endsWith("-team")).count(); - if (rolesCount != 0) { //user has access to team, retrieve the team datastores as well + if (rolesCount != 0) { //user has access to team, retrieve the team datastore as well queryWhere = queryWhere.concat(" or owner in ('" + team + "')"); } List backends = DatastoreConfigDAO.list(queryWhere); @@ -82,19 +91,37 @@ public Integer newDatastore(Datastore datastore) { if (dao.owner == null) { List uploaders = identity.getRoles().stream().filter(role -> role.endsWith("-uploader")).collect(Collectors.toList()); if (uploaders.size() != 1) { - log.debugf("Failed to create new backend %s: no owner, available uploaders: %s", dao.name, uploaders); + log.debugf("Failed to create datastore %s: no owner, available uploaders: %s", dao.name, uploaders); throw ServiceException.badRequest("Missing owner and cannot select single default owners; this user has these uploader roles: " + uploaders); } String uploader = uploaders.get(0); dao.owner = uploader.substring(0, uploader.length() - 9) + "-team"; } else if (!identity.getRoles().contains(dao.owner)) { - log.debugf("Failed to create backend configuration %s: requested owner %s, available roles: %s", dao.name, dao.owner, identity.getRoles()); - throw ServiceException.badRequest("This user does not have permissions to upload backend configuration for owner=" + dao.owner); + log.debugf("Failed to create datastore %s: requested owner %s, available roles: %s", dao.name, dao.owner, identity.getRoles()); + throw ServiceException.badRequest("This user does not have permissions to upload datastore for owner=" + dao.owner); } if (dao.access == null) { dao.access = Access.PRIVATE; } - log.debugf("Uploading with owner=%s and access=%s", dao.owner, dao.access); + + io.hyperfoil.tools.horreum.datastore.Datastore datastoreImpl; + try { + datastoreImpl = backendResolver.getBackend(datastore.type); + } catch (IllegalStateException e) { + throw ServiceException.badRequest("Unknown datastore type: " + datastore.type + ". Please try again, if the problem persists please contact the system administrator."); + } + + if ( datastoreImpl == null ){ + throw ServiceException.badRequest("Unknown datastore type: " + datastore.type); + } + + String error = datastoreImpl.validateConfig(datastore.config); + + if ( error != null ) { + throw ServiceException.badRequest(error); + } + + log.debugf("Creating new Datastore with owner=%s and access=%s", dao.owner, dao.access); try { em.persist(dao); diff --git a/horreum-web/package-lock.json b/horreum-web/package-lock.json index 6fe1b248a..b89cc7526 100644 --- a/horreum-web/package-lock.json +++ b/horreum-web/package-lock.json @@ -1242,9 +1242,9 @@ } }, "node_modules/@types/d3-path": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", - "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" }, "node_modules/@types/d3-scale": { "version": "4.0.8", @@ -1791,9 +1791,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001584", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001584.tgz", - "integrity": "sha512-LOz7CCQ9M1G7OjJOF9/mzmqmj3jE/7VOmrfw6Mgs0E8cjOsbRXQJHsPBfmBOXDskXKrHLyyW3n7kpDW/4BsfpQ==", + "version": "1.0.30001585", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz", + "integrity": "sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q==", "funding": [ { "type": "opencollective", @@ -2167,9 +2167,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.657", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.657.tgz", - "integrity": "sha512-On2ymeleg6QbRuDk7wNgDdXtNqlJLM2w4Agx1D/RiTmItiL+a9oq5p7HUa2ZtkAtGBe/kil2dq/7rPfkbe0r5w==" + "version": "1.4.661", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.661.tgz", + "integrity": "sha512-AFg4wDHSOk5F+zA8aR+SVIOabu7m0e7BiJnigCvPXzIGy731XENw/lmNxTySpVFtkFEy+eyt4oHhh5FF3NjQNw==" }, "node_modules/entities": { "version": "1.0.0", @@ -3973,9 +3973,9 @@ } }, "node_modules/postcss": { - "version": "8.4.34", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.34.tgz", - "integrity": "sha512-4eLTO36woPSocqZ1zIrFD2K1v6wH7pY1uBh0JIM2KKfrVtGvPFiAku6aNOP0W1Wr9qwnaCsF0Z+CrVnryB2A8Q==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "funding": [ { "type": "opencollective", @@ -4744,9 +4744,9 @@ } }, "node_modules/tsconfck": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.0.1.tgz", - "integrity": "sha512-7ppiBlF3UEddCLeI1JRx5m2Ryq+xk4JrZuq4EuYXykipebaq1dV0Fhgr1hb7CkmHt32QSgOZlcqVLEtHBG4/mg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.0.2.tgz", + "integrity": "sha512-6lWtFjwuhS3XI4HsX4Zg0izOI3FU/AI9EGVlPEUMDIhvLPMD4wkiof0WCoDgW7qY+Dy198g4d9miAqUHWHFH6Q==", "bin": { "tsconfck": "bin/tsconfck.js" }, @@ -5016,9 +5016,9 @@ } }, "node_modules/victory-vendor": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.0.tgz", - "integrity": "sha512-n1A0J1xgwHb5nh56M0d8XlQabMCeTktvEqqr5WNAHspWEsVVGGaaaRg0TcQUtyC1akX0Cox1lMZdIv0Jl7o0ew==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.1.tgz", + "integrity": "sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==", "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", diff --git a/horreum-web/src/App.tsx b/horreum-web/src/App.tsx index 781975209..33cf51565 100644 --- a/horreum-web/src/App.tsx +++ b/horreum-web/src/App.tsx @@ -80,6 +80,7 @@ const router = createBrowserRouter( export default function App() { initKeycloak(store.getState()) + console.trace(); return ( diff --git a/horreum-web/src/domain/admin/Datastores.tsx b/horreum-web/src/domain/admin/Datastores.tsx index 1bb0f4e98..22620e0d7 100644 --- a/horreum-web/src/domain/admin/Datastores.tsx +++ b/horreum-web/src/domain/admin/Datastores.tsx @@ -58,7 +58,7 @@ const DatastoresTable = ( props: dataStoreTableProps) => { }, ]; - const newBackendConfig : ElasticsearchDatastoreConfig = { + const newBackendConfig: ElasticsearchDatastoreConfig = { url: "", apiKey: "", builtIn: false @@ -75,7 +75,7 @@ const DatastoresTable = ( props: dataStoreTableProps) => { } const [deleteModalOpen, setDeleteModalOpen] = useState(false); - const [editModalOpen, setEditModalOpen] = useState(false); + const [editModalOpen, setEditModalOpen] = useState(false); const [verifyModalOpen, setVerifyModalOpen] = useState(false); const [datastore, setDatastore] = useState(newDataStore) @@ -138,7 +138,7 @@ const DatastoresTable = ( props: dataStoreTableProps) => { - + { isOpen={deleteModalOpen} onClose={() => deleteModalToggle(0)} onDelete={() => { - props.deleteDatastore(datastore?.id?.toString() || "").then(noop) + props.deleteDatastore(datastore?.id?.toString() || "") deleteModalToggle(0) return Promise.resolve() } @@ -164,9 +164,9 @@ const DatastoresTable = ( props: dataStoreTableProps) => { dataStore={datastore} updateDatastore={updateDatastore} onDelete={() => { - editModalToggle(0) - return Promise.resolve() - } + editModalToggle(0) + return Promise.resolve() + } } /> @@ -177,9 +177,9 @@ const DatastoresTable = ( props: dataStoreTableProps) => { isOpen={verifyModalOpen} onClose={() => verifyModalToggle(0)} onDelete={() => { - verifyModalToggle(0) - return Promise.resolve() - } + verifyModalToggle(0) + return Promise.resolve() + } } /> diff --git a/horreum-web/src/domain/admin/datastore/ModifyDatastoreModal.tsx b/horreum-web/src/domain/admin/datastore/ModifyDatastoreModal.tsx index cc3424487..3e3e08088 100644 --- a/horreum-web/src/domain/admin/datastore/ModifyDatastoreModal.tsx +++ b/horreum-web/src/domain/admin/datastore/ModifyDatastoreModal.tsx @@ -58,7 +58,7 @@ export default function ModifyDatastoreModal({isOpen, onClose, persistDatastore, onClose(); alerting.dispatchInfo("SAVE", "Saved!", "Datastore was successfully updated!", 3000) }) - .catch(reason => alerting.dispatchError("SAVE", "Saved!", "Failed to save changes to Datastore")) + .catch(reason => alerting.dispatchError(reason, "Saved!", "Failed to save changes to Datastore")) } const options : datastoreOption[] = [ @@ -154,6 +154,51 @@ export default function ModifyDatastoreModal({isOpen, onClose, persistDatastore, + + + { + const config :ElasticsearchDatastoreConfig = dataStore.config as ElasticsearchDatastoreConfig; + config.username = value + updateDatastore({...dataStore, config: config}) + }}isDisabled={enabledToken} + type="text" + id="horizontal-form-username" + aria-describedby="horizontal-form-token-helper" + name="horizontal-form-token" + /> + + + Please provide a Username to authenticate against datastore + + + + + { + const config :ElasticsearchDatastoreConfig = dataStore.config as ElasticsearchDatastoreConfig; + config.password = value + updateDatastore({...dataStore, config: config}) + }}isDisabled={enabledToken} + type="text" + id="horizontal-form-password" + aria-describedby="horizontal-form-token-helper" + name="horizontal-form-token" + /> + + + Please provide a Password to authenticate against datastore + + + )