diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/search/DataWarehouseSearchType.java b/graylog2-server/src/main/java/org/graylog/plugins/views/search/DataWarehouseSearchType.java new file mode 100644 index 000000000000..ce1505360a43 --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/search/DataWarehouseSearchType.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.views.search; + +/** + * Marker interface for search types that are not search engine related, but Data Warehouse/Iceberg related + */ +public interface DataWarehouseSearchType extends SearchType { + + String PREFIX = "data_warehouse_"; +} diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/EngineBindings.java b/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/EngineBindings.java index 0c45326af3b6..fbdc1b9e0848 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/EngineBindings.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/EngineBindings.java @@ -20,6 +20,7 @@ import org.graylog.plugins.views.search.engine.normalization.DecorateQueryStringsNormalizer; import org.graylog.plugins.views.search.engine.normalization.PluggableSearchNormalization; import org.graylog.plugins.views.search.engine.normalization.SearchNormalization; +import org.graylog.plugins.views.search.engine.validation.DataWarehouseSearchValidator; import org.graylog.plugins.views.search.engine.validation.PluggableSearchValidation; import org.graylog.plugins.views.search.engine.validation.SearchValidation; import org.graylog.plugins.views.search.engine.validation.TimeRangeValidator; @@ -34,5 +35,7 @@ protected void configure() { // Triggering set binder explicitly, so no injection errors are being caused if no implementation is bound. searchPostValidationNormalizerBinder(); registerSearchValidator(TimeRangeValidator.class); + //while DW searches are enterprise only, the validation should probably be present in gl-server, i.e. to discover invalid searches even if a client has lost the license + registerSearchValidator(DataWarehouseSearchValidator.class); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/validation/DataWarehouseSearchValidator.java b/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/validation/DataWarehouseSearchValidator.java new file mode 100644 index 000000000000..a28685512e8e --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/search/engine/validation/DataWarehouseSearchValidator.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.views.search.engine.validation; + +import com.google.common.collect.ImmutableSet; +import org.graylog.plugins.views.search.DataWarehouseSearchType; +import org.graylog.plugins.views.search.Query; +import org.graylog.plugins.views.search.Search; +import org.graylog.plugins.views.search.SearchType; +import org.graylog.plugins.views.search.errors.QueryError; +import org.graylog.plugins.views.search.errors.SearchError; +import org.graylog.plugins.views.search.errors.SearchTypeError; +import org.graylog.plugins.views.search.permissions.SearchUser; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public class DataWarehouseSearchValidator implements SearchValidator { + + @Override + public Set validate(final Search search, + final SearchUser searchUser) { + //this should be either validated elsewhere or impossible + assert search.queries() != null; + assert !search.queries().isEmpty(); + + if (containsDataWarehouseSearchElements(search)) { + if (search.queries().size() > 1) { + return wholeSearchInvalid(search, "Data Warehouse elements present in Search, only 1 query allowed for those type of searches"); + } + return validate(search.queries().stream().findFirst().get(), searchUser); + } else { + return Set.of(); + } + } + + @Override + public Set validate(final Query query, + final SearchUser searchUser) { + if (containsDataWarehouseSearchElements(query)) { + final ImmutableSet searchTypes = query.searchTypes(); + if (searchTypes.size() != 1) { + return Set.of(new QueryError(query, "Data Warehouse query can contain only one search type")); + } + final Optional first = searchTypes.stream().findFirst(); + if (!(first.get() instanceof DataWarehouseSearchType)) { + return Set.of(new SearchTypeError(query, first.get().id(), "Data Warehouse query can contain only data warehouse search types")); + } else { + final Set streams = first.get().streams(); + if (streams == null || streams.size() > 1) { + return Set.of(new SearchTypeError(query, first.get().id(), "Data Warehouse preview can be executed on only 1 stream, search type contained more")); + } + } + } + return Set.of(); + } + + private boolean containsDataWarehouseSearchElements(final Search search) { + return search.queries().stream().anyMatch(this::containsDataWarehouseSearchElements); + } + + private boolean containsDataWarehouseSearchElements(final Query query) { + return (query.query().type().startsWith(DataWarehouseSearchType.PREFIX)) + || (query.searchTypes().stream().anyMatch(searchType -> searchType instanceof DataWarehouseSearchType)); + } + + private Set wholeSearchInvalid(final Search search, final String explanation) { + return search.queries() + .stream() + .map(query -> new QueryError(query, explanation)) + .collect(Collectors.toSet()); + } +}