diff --git a/contrib/datawave-quickstart/docker/datawave-bootstrap.sh b/contrib/datawave-quickstart/docker/datawave-bootstrap.sh index b3f53f48d77..73a9f7710b5 100755 --- a/contrib/datawave-quickstart/docker/datawave-bootstrap.sh +++ b/contrib/datawave-quickstart/docker/datawave-bootstrap.sh @@ -16,6 +16,13 @@ do --web) START_WEB=true ;; + --webdebug) + START_WEB_DEBUG=true + ;; + --test) + START_TEST=true + START_AS_DAEMON=false + ;; --ingest) START_INGEST=true ;; @@ -29,6 +36,30 @@ done [ "${START_WEB}" == true ] && datawaveWebStart +[ "${START_WEB_DEBUG}" == true ] && datawaveWebStart --debug + +if [ "${START_TEST}" == true ] ; then + datawaveWebStart + status=$? + + if [ "$status" != "0" ] ; then + echo "datawaveWebStart Failed" + cat ${WILDFLY_HOME}/standalone/log/server.log + exit $status + else + datawaveWebTest --blacklist-files QueryMetrics + status=$? + + if [ "$status" != "0" ] ; then + echo "datawaveWebTest Failed" + cat ${WILDFLY_HOME}/standalone/log/server.log + fi + + allStop + exit $status + fi +fi + if [ "${START_AS_DAEMON}" == true ] ; then while true; do sleep 1000; done fi diff --git a/pom.xml b/pom.xml index 912afdf9f8f..155609d2444 100644 --- a/pom.xml +++ b/pom.xml @@ -69,8 +69,8 @@ 1.72 6.1.26 4.0.19.Final - 0.9.1 - 4.13.1 + 0.11.2 + 4.13.2 2.20 2.20 1.0 @@ -79,7 +79,7 @@ 1.1 1.4 1.4 - 1.4 + 1.13 1.2 1.0 1.0 @@ -377,7 +377,12 @@ io.jsonwebtoken - jjwt + jjwt-impl + ${version.jjwt} + + + io.jsonwebtoken + jjwt-jackson ${version.jjwt} diff --git a/warehouse/query-core/src/main/java/datawave/query/config/RemoteQueryConfiguration.java b/warehouse/query-core/src/main/java/datawave/query/config/RemoteQueryConfiguration.java new file mode 100644 index 00000000000..5c8359c83bb --- /dev/null +++ b/warehouse/query-core/src/main/java/datawave/query/config/RemoteQueryConfiguration.java @@ -0,0 +1,149 @@ +package datawave.query.config; + +import datawave.query.tables.RemoteEventQueryLogic; +import datawave.webservice.query.Query; +import datawave.webservice.query.configuration.GenericQueryConfiguration; + +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.Objects; + +/** + *

+ * A GenericQueryConfiguration implementation that provides the additional logic on top of the traditional query that is needed to run a remote query logic + * + */ +public class RemoteQueryConfiguration extends GenericQueryConfiguration implements Serializable { + + private static final long serialVersionUID = -4354990715046146110L; + + // the id of the remote query + private String remoteId; + + private String remoteQueryLogic; + + private Query query; + + /** + * Default constructor + */ + public RemoteQueryConfiguration() { + super(); + } + + /** + * Performs a deep copy of the provided RemoteQueryConfiguration into a new instance + * + * @param other + * - another RemoteQueryConfiguration instance + */ + public RemoteQueryConfiguration(RemoteQueryConfiguration other) { + + // GenericQueryConfiguration copy first + super(other); + + // RemoteQueryConfiguration copy + this.remoteId = other.getRemoteId(); + this.remoteQueryLogic = other.getRemoteQueryLogic(); + this.query = other.getQuery(); + } + + /** + * Delegates deep copy work to appropriate constructor, sets additional values specific to the provided RemoteRemoteQueryLogic + * + * @param logic + * - a RemoteQueryLogic instance or subclass + */ + public RemoteQueryConfiguration(RemoteEventQueryLogic logic) { + this(logic.getConfig()); + } + + /** + * Factory method that instantiates an fresh RemoteQueryConfiguration + * + * @return - a clean RemoteQueryConfiguration + */ + public static RemoteQueryConfiguration create() { + return new RemoteQueryConfiguration(); + } + + /** + * Factory method that returns a deep copy of the provided RemoteQueryConfiguration + * + * @param other + * - another instance of a RemoteQueryConfiguration + * @return - copy of provided RemoteQueryConfiguration + */ + public static RemoteQueryConfiguration create(RemoteQueryConfiguration other) { + return new RemoteQueryConfiguration(other); + } + + /** + * Factory method that creates a RemoteQueryConfiguration deep copy from a RemoteQueryLogic + * + * @param remoteQueryLogic + * - a configured RemoteQueryLogic + * @return - a RemoteQueryConfiguration + */ + public static RemoteQueryConfiguration create(RemoteEventQueryLogic remoteQueryLogic) { + return create(remoteQueryLogic.getConfig()); + } + + /** + * Factory method that creates a RemoteQueryConfiguration from a RemoteQueryLogic and a Query + * + * @param remoteQueryLogic + * - a configured RemoteQueryLogic + * @param query + * - a configured Query object + * @return - a RemoteQueryConfiguration + */ + public static RemoteQueryConfiguration create(RemoteEventQueryLogic remoteQueryLogic, Query query) { + RemoteQueryConfiguration config = create(remoteQueryLogic); + config.setQuery(query); + return config; + } + + public String getRemoteId() { + return remoteId; + } + + public void setRemoteId(String remoteId) { + this.remoteId = remoteId; + } + + public String getRemoteQueryLogic() { + return remoteQueryLogic; + } + + public void setRemoteQueryLogic(String remoteQueryLogic) { + this.remoteQueryLogic = remoteQueryLogic; + } + + public Query getQuery() { + return query; + } + + public void setQuery(Query query) { + this.query = query; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + RemoteQueryConfiguration that = (RemoteQueryConfiguration) o; + return Objects.equals(getRemoteId(), that.getRemoteId()) && Objects.equals(getRemoteQueryLogic(), that.getRemoteQueryLogic()) + && Objects.equals(getQuery(), that.getQuery()); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), getRemoteId(), getRemoteQueryLogic(), getQuery()); + } + +} diff --git a/warehouse/query-core/src/main/java/datawave/query/tables/RemoteEventQueryLogic.java b/warehouse/query-core/src/main/java/datawave/query/tables/RemoteEventQueryLogic.java new file mode 100644 index 00000000000..18b866f32fc --- /dev/null +++ b/warehouse/query-core/src/main/java/datawave/query/tables/RemoteEventQueryLogic.java @@ -0,0 +1,258 @@ +package datawave.query.tables; + +import datawave.marking.MarkingFunctions; +import datawave.query.config.RemoteQueryConfiguration; +import datawave.query.tables.remote.RemoteQueryLogic; +import datawave.query.transformer.EventQueryTransformerSupport; +import datawave.webservice.common.connection.AccumuloConnectionFactory; +import datawave.webservice.common.logging.ThreadConfigurableLogger; +import datawave.webservice.common.remote.RemoteQueryService; +import datawave.webservice.query.Query; +import datawave.webservice.query.configuration.GenericQueryConfiguration; +import datawave.webservice.query.exception.EmptyObjectException; +import datawave.webservice.query.exception.QueryException; +import datawave.webservice.query.logic.BaseQueryLogic; +import datawave.webservice.query.logic.QueryLogicTransformer; +import datawave.webservice.query.result.event.DefaultEvent; +import datawave.webservice.query.result.event.EventBase; +import datawave.webservice.query.result.event.ResponseObjectFactory; +import datawave.webservice.result.EventQueryResponseBase; +import datawave.webservice.result.GenericResponse; +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.security.Authorizations; +import org.apache.log4j.Logger; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + *

Overview

This is a query logic implementation that can handle delegating to a remote event query logic (i.e. one that returns an extension of + * EventQueryResponseBase). + */ +public class RemoteEventQueryLogic extends BaseQueryLogic implements RemoteQueryLogic { + + protected static final Logger log = ThreadConfigurableLogger.getLogger(RemoteEventQueryLogic.class); + + private RemoteQueryConfiguration config; + + private RemoteQueryService remoteQueryService; + + private QueryLogicTransformer transformerInstance = null; + + /** + * Basic constructor + */ + public RemoteEventQueryLogic() { + super(); + if (log.isTraceEnabled()) + log.trace("Creating RemoteQueryLogic: " + System.identityHashCode(this)); + } + + /** + * Copy constructor + * + * @param other + * - another ShardQueryLogic object + */ + public RemoteEventQueryLogic(RemoteEventQueryLogic other) { + super(other); + + if (log.isTraceEnabled()) + log.trace("Creating Cloned RemoteQueryLogic: " + System.identityHashCode(this) + " from " + System.identityHashCode(other)); + + setRemoteQueryService(other.getRemoteQueryService()); + + // Set ShardQueryConfiguration variables + setConfig(RemoteQueryConfiguration.create(other)); + } + + public String getRemoteId() { + return getConfig().getRemoteId(); + } + + public void setRemoteId(String id) { + getConfig().setRemoteId(id); + } + + public String getRemoteQueryLogic() { + return getConfig().getRemoteQueryLogic(); + } + + public void setRemoteQueryLogic(String remoteQueryLogic) { + getConfig().setRemoteQueryLogic(remoteQueryLogic); + } + + public Object getCallerObject() { + return getPrincipal(); + } + + @Override + public GenericQueryConfiguration initialize(Connector connection, Query settings, Set auths) throws Exception { + GenericResponse createResponse = remoteQueryService.createQuery(getRemoteQueryLogic(), settings.toMap(), getCallerObject()); + setRemoteId(createResponse.getResult()); + return getConfig(); + } + + @Override + public String getPlan(Connector connection, Query settings, Set auths, boolean expandFields, boolean expandValues) throws Exception { + GenericResponse planResponse = remoteQueryService.planQuery(getRemoteQueryLogic(), settings.toMap()); + return planResponse.getResult(); + } + + @Override + public void setupQuery(GenericQueryConfiguration genericConfig) throws Exception { + if (!RemoteQueryConfiguration.class.isAssignableFrom(genericConfig.getClass())) { + throw new QueryException("Did not receive a RemoteQueryConfiguration instance!!"); + } + + config = (RemoteQueryConfiguration) genericConfig; + + // Create an iterator that returns a stream of EventBase objects + iterator = new RemoteQueryLogicIterator(); + } + + @Override + public QueryLogicTransformer getTransformer(Query settings) { + // a transformer that turns EventBase objects into a response + if (transformerInstance == null) { + transformerInstance = new EventBaseTransformer(settings, getMarkingFunctions(), getResponseObjectFactory()); + } + + return transformerInstance; + } + + @Override + public RemoteEventQueryLogic clone() { + return new RemoteEventQueryLogic(this); + } + + @Override + public void close() { + + super.close(); + + log.debug("Closing RemoteQueryLogic: " + System.identityHashCode(this)); + + if (getRemoteId() != null) { + try { + remoteQueryService.close(getRemoteId(), getCallerObject()); + } catch (Exception e) { + log.error("Failed to close remote query", e); + } + } + } + + @Override + public RemoteQueryConfiguration getConfig() { + if (config == null) { + config = RemoteQueryConfiguration.create(); + } + + return config; + } + + public void setConfig(RemoteQueryConfiguration config) { + this.config = config; + } + + public RemoteQueryService getRemoteQueryService() { + return remoteQueryService; + } + + public void setRemoteQueryService(RemoteQueryService remoteQueryService) { + this.remoteQueryService = remoteQueryService; + } + + @Override + public AccumuloConnectionFactory.Priority getConnectionPriority() { + return AccumuloConnectionFactory.Priority.NORMAL; + } + + @Override + public Set getOptionalQueryParameters() { + return new ShardQueryLogic().getOptionalQueryParameters(); + } + + @Override + public Set getRequiredQueryParameters() { + return new ShardQueryLogic().getRequiredQueryParameters(); + } + + @Override + public Set getExampleQueries() { + return new ShardQueryLogic().getExampleQueries(); + } + + public Query getSettings() { + return getConfig().getQuery(); + } + + public void setSettings(Query settings) { + getConfig().setQuery(settings); + } + + private class RemoteQueryLogicIterator implements Iterator { + private Queue data = new LinkedList<>(); + private boolean complete = false; + + @Override + public boolean hasNext() { + if (data.isEmpty() && !complete) { + try { + EventQueryResponseBase response = (EventQueryResponseBase) remoteQueryService.next(getRemoteId(), getCallerObject()); + if (response != null) { + if (response.getReturnedEvents() == 0) { + if (response.isPartialResults()) { + DefaultEvent e = new DefaultEvent(); + e.setIntermediateResult(true); + data.add(e); + } else { + complete = true; + } + } else { + for (EventBase event : response.getEvents()) { + data.add(event); + } + } + } else { + // in this case we must have gotten a 204, so we are done + complete = true; + } + } catch (Exception e) { + complete = true; + throw new RuntimeException(e.getMessage(), e); + } + } + return !data.isEmpty(); + } + + @Override + public EventBase next() { + return data.poll(); + } + } + + private class EventBaseTransformer extends EventQueryTransformerSupport { + + public EventBaseTransformer(Query settings, MarkingFunctions markingFunctions, ResponseObjectFactory responseObjectFactory) { + super("notable", settings, markingFunctions, responseObjectFactory); + } + + public EventBaseTransformer(BaseQueryLogic> logic, Query settings, MarkingFunctions markingFunctions, + ResponseObjectFactory responseObjectFactory) { + super(logic, settings, markingFunctions, responseObjectFactory); + } + + @Override + public EventBase transform(EventBase input) throws EmptyObjectException { + return input; + } + + } + +} diff --git a/warehouse/query-core/src/main/java/datawave/query/tables/remote/RemoteQueryLogic.java b/warehouse/query-core/src/main/java/datawave/query/tables/remote/RemoteQueryLogic.java new file mode 100644 index 00000000000..18665e52d1b --- /dev/null +++ b/warehouse/query-core/src/main/java/datawave/query/tables/remote/RemoteQueryLogic.java @@ -0,0 +1,11 @@ +package datawave.query.tables.remote; + +import datawave.webservice.common.remote.RemoteQueryService; +import datawave.webservice.query.logic.QueryLogic; + +/** + * A remote query logic is is a query logic that uses a remote query service. + */ +public interface RemoteQueryLogic extends QueryLogic { + public void setRemoteQueryService(RemoteQueryService service); +} diff --git a/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEventQueryLogicTest.java b/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEventQueryLogicTest.java new file mode 100644 index 00000000000..b4e75aa3118 --- /dev/null +++ b/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEventQueryLogicTest.java @@ -0,0 +1,102 @@ +package datawave.query.tables; + +import datawave.webservice.common.remote.RemoteQueryService; +import datawave.webservice.query.QueryImpl; +import datawave.webservice.query.configuration.GenericQueryConfiguration; +import datawave.webservice.query.result.event.DefaultEvent; +import datawave.webservice.query.result.event.DefaultField; +import datawave.webservice.query.result.event.EventBase; +import datawave.webservice.result.BaseQueryResponse; +import datawave.webservice.result.DefaultEventQueryResponse; +import datawave.webservice.result.GenericResponse; +import datawave.webservice.result.VoidResponse; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; + +public class RemoteEventQueryLogicTest { + + RemoteEventQueryLogic logic = new RemoteEventQueryLogic(); + + @Before + public void setup() { + UUID uuid = UUID.randomUUID(); + GenericResponse createResponse = new GenericResponse(); + createResponse.setResult(uuid.toString()); + + DefaultEventQueryResponse response1 = new DefaultEventQueryResponse(); + DefaultEvent event1 = new DefaultEvent(); + event1.setFields(Collections.singletonList(new DefaultField("FOO1", "FOO|BAR", new HashMap(), -1L, "FOOBAR1"))); + response1.setEvents(Collections.singletonList(event1)); + response1.setReturnedEvents(1L); + + DefaultEventQueryResponse response2 = new DefaultEventQueryResponse(); + DefaultEvent event2 = new DefaultEvent(); + event1.setFields(Collections.singletonList(new DefaultField("FOO2", "FOO|BAR", new HashMap(), -1L, "FOOBAR2"))); + response2.setEvents(Collections.singletonList(event1)); + response2.setReturnedEvents(1L); + + // create a remote event query logic that has our own remote query service behind it + logic.setRemoteQueryService(new TestRemoteQueryService(createResponse, response1, response2)); + logic.setRemoteQueryLogic("TestQuery"); + } + + @Test + public void testRemoteQuery() throws Exception { + GenericQueryConfiguration config = logic.initialize(null, new QueryImpl(), null); + logic.setupQuery(config); + Iterator t = logic.iterator(); + List events = new ArrayList(); + while (t.hasNext()) { + events.add(t.next()); + } + assertEquals(2, events.size()); + } + + public static class TestRemoteQueryService implements RemoteQueryService { + GenericResponse createResponse; + LinkedList nextResponses; + + public TestRemoteQueryService(GenericResponse createResponse, BaseQueryResponse response1, BaseQueryResponse response2) { + this.createResponse = createResponse; + this.nextResponses = new LinkedList<>(); + nextResponses.add(response1); + nextResponses.add(response2); + } + + @Override + public GenericResponse createQuery(String queryLogicName, Map> queryParameters, Object callerObject) { + return createResponse; + } + + @Override + public BaseQueryResponse next(String id, Object callerObject) { + return nextResponses.poll(); + } + + @Override + public VoidResponse close(String id, Object callerObject) { + return new VoidResponse(); + } + + @Override + public GenericResponse planQuery(String queryLogicName, Map> queryParameters, Object callerObject) { + throw new UnsupportedOperationException(); + } + + @Override + public GenericResponse planQuery(String id, Object callerObject) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/web-services/client/src/main/java/datawave/webservice/query/QueryImpl.java b/web-services/client/src/main/java/datawave/webservice/query/QueryImpl.java index b3ca9af3f40..f16294916e8 100644 --- a/web-services/client/src/main/java/datawave/webservice/query/QueryImpl.java +++ b/web-services/client/src/main/java/datawave/webservice/query/QueryImpl.java @@ -817,6 +817,8 @@ public Map> toMap() { throw new RuntimeException("Error formatting date", e); } } + p.set(QueryParameters.QUERY_PAGETIMEOUT, Integer.toString(this.pageTimeout)); + if (this.parameters != null) { for (Parameter parameter : parameters) { p.set(parameter.getParameterName(), parameter.getParameterValue()); diff --git a/web-services/common/pom.xml b/web-services/common/pom.xml index a99dd109232..3c72ad2cf17 100644 --- a/web-services/common/pom.xml +++ b/web-services/common/pom.xml @@ -80,7 +80,11 @@
io.jsonwebtoken - jjwt + jjwt-impl + + + io.jsonwebtoken + jjwt-jackson org.apache.accumulo diff --git a/web-services/common/src/main/java/datawave/webservice/common/json/DefaultMapperDecorator.java b/web-services/common/src/main/java/datawave/webservice/common/json/DefaultMapperDecorator.java index caf22234a30..f73e93fa165 100644 --- a/web-services/common/src/main/java/datawave/webservice/common/json/DefaultMapperDecorator.java +++ b/web-services/common/src/main/java/datawave/webservice/common/json/DefaultMapperDecorator.java @@ -1,5 +1,6 @@ package datawave.webservice.common.json; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -21,6 +22,7 @@ public ObjectMapper decorate(ObjectMapper mapper) { mapper.enable(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME); mapper.registerModule(new GuavaModule()); mapper.registerModule(new JaxbAnnotationModule()); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); registerAbstractTypes(mapper); diff --git a/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java index 4b69d228c71..9c382a812f5 100644 --- a/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java +++ b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java @@ -86,12 +86,13 @@ public abstract class RemoteHttpService { protected ObjectMapperDecorator objectMapperDecorator; protected T execute(HttpRequestBase request, IOFunction resultConverter, Supplier errorSupplier) throws IOException { + log.info("Executing " + request.getClass().getSimpleName() + " against " + request.getURI()); try { activeExecutions.incrementAndGet(); return client.execute( request, r -> { - if (r.getStatusLine().getStatusCode() != 200) { + if (r.getStatusLine().getStatusCode() >= 300) { throw new ClientProtocolException("Unable to " + errorSupplier.get() + ": " + r.getStatusLine() + " " + EntityUtils.toString(r.getEntity())); } else { diff --git a/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpServiceConfiguration.java b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpServiceConfiguration.java new file mode 100644 index 00000000000..86c59efd870 --- /dev/null +++ b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpServiceConfiguration.java @@ -0,0 +1,138 @@ +package datawave.webservice.common.remote; + +import com.codahale.metrics.Counter; + +import java.util.Collections; +import java.util.List; + +public class RemoteHttpServiceConfiguration { + private boolean useSrvDNS = false; + + private List srvDnsServers = Collections.singletonList("127.0.0.1"); + + private int srvDnsPort = 8600; + + private String serviceScheme = "https"; + + private String serviceHost = "localhost"; + + private int servicePort = 8443; + + private String serviceURI = "/"; + + private int maxConnections = 100; + + private int retryCount = 5; + + private int unavailableRetryCount = 15; + + private int unavailableRetryDelay = 2000; + + private Counter retryCounter = new Counter(); + + private Counter failureCounter = new Counter(); + + public void setUseSrvDNS(boolean useSrvDNS) { + this.useSrvDNS = useSrvDNS; + } + + public void setSrvDnsServers(List srvDnsServers) { + this.srvDnsServers = srvDnsServers; + } + + public void setSrvDnsPort(int srvDnsPort) { + this.srvDnsPort = srvDnsPort; + } + + public void setServiceScheme(String serviceScheme) { + this.serviceScheme = serviceScheme; + } + + public void setServiceHost(String serviceHost) { + this.serviceHost = serviceHost; + } + + public void setServicePort(int servicePort) { + this.servicePort = servicePort; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + public void setMaxConnections(int maxConnections) { + this.maxConnections = maxConnections; + } + + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + + public void setUnavailableRetryCount(int unavailableRetryCount) { + this.unavailableRetryCount = unavailableRetryCount; + } + + public void setUnavailableRetryDelay(int unavailableRetryDelay) { + this.unavailableRetryDelay = unavailableRetryDelay; + } + + public boolean isUseSrvDNS() { + return useSrvDNS; + } + + public List getSrvDnsServers() { + return srvDnsServers; + } + + public int getSrvDnsPort() { + return srvDnsPort; + } + + public String getServiceScheme() { + return serviceScheme; + } + + public String getServiceHost() { + return serviceHost; + } + + public int getServicePort() { + return servicePort; + } + + public String getServiceURI() { + return serviceURI; + } + + public int getMaxConnections() { + return maxConnections; + } + + public int getRetryCount() { + return retryCount; + } + + public int getUnavailableRetryCount() { + return unavailableRetryCount; + } + + public int getUnavailableRetryDelay() { + return unavailableRetryDelay; + } + + public Counter getRetryCounter() { + return retryCounter; + } + + public void setRetryCounter(Counter retryCounter) { + this.retryCounter = retryCounter; + } + + public Counter getFailureCounter() { + return failureCounter; + } + + public void setFailureCounter(Counter failureCounter) { + this.failureCounter = failureCounter; + } +} diff --git a/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteQueryService.java b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteQueryService.java new file mode 100644 index 00000000000..39e719d738b --- /dev/null +++ b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteQueryService.java @@ -0,0 +1,61 @@ +package datawave.webservice.common.remote; + +import datawave.webservice.result.BaseQueryResponse; +import datawave.webservice.result.GenericResponse; +import datawave.webservice.result.VoidResponse; + +import java.util.List; +import java.util.Map; + +/** + * A remote query service is one that can pass calls off to another query service + */ +public interface RemoteQueryService { + + /** + * Call the create on a remote query service + * + * @param queryLogicName + * @param queryParameters + * @param callerObject + * @return the generic response + */ + public GenericResponse createQuery(String queryLogicName, Map> queryParameters, Object callerObject); + + /** + * Call next on a remote query service + * + * @param id + * @param callerObject + * @return the base query response + */ + public BaseQueryResponse next(String id, Object callerObject); + + /** + * Call close on a remote query service + * + * @param id + * @param callerObject + * @return the void response + */ + public VoidResponse close(String id, Object callerObject); + + /** + * Plan a query using a remote query service + * + * @param queryLogicName + * @param queryParameters + * @param callerObject + * @return the generic response + */ + public GenericResponse planQuery(String queryLogicName, Map> queryParameters, Object callerObject); + + /** + * Get the plan from a remote query service + * + * @param id + * @param callerObject + * @return a generic response + */ + public GenericResponse planQuery(String id, Object callerObject); +} diff --git a/web-services/deploy/configuration/src/main/resources/datawave/query/QueryLogicFactory.xml b/web-services/deploy/configuration/src/main/resources/datawave/query/QueryLogicFactory.xml index f9389a591d6..2c746901640 100644 --- a/web-services/deploy/configuration/src/main/resources/datawave/query/QueryLogicFactory.xml +++ b/web-services/deploy/configuration/src/main/resources/datawave/query/QueryLogicFactory.xml @@ -129,6 +129,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/web-services/query/src/main/java/datawave/webservice/query/logic/DelegatingQueryLogic.java b/web-services/query/src/main/java/datawave/webservice/query/logic/DelegatingQueryLogic.java new file mode 100644 index 00000000000..29d7765cd16 --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/logic/DelegatingQueryLogic.java @@ -0,0 +1,337 @@ +package datawave.webservice.query.logic; + +import datawave.audit.SelectorExtractor; +import datawave.marking.MarkingFunctions; +import datawave.webservice.common.audit.Auditor; +import datawave.webservice.common.connection.AccumuloConnectionFactory; +import datawave.webservice.query.Query; +import datawave.webservice.query.configuration.GenericQueryConfiguration; +import datawave.webservice.query.exception.QueryException; +import datawave.webservice.query.result.event.ResponseObjectFactory; +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.security.Authorizations; +import org.apache.commons.collections4.iterators.TransformIterator; + +import java.security.Principal; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A delegating query logic that simply passes through to a delegate query logic. Intended to simplify extending classes. + */ +public abstract class DelegatingQueryLogic implements QueryLogic { + + private QueryLogic delegate; + + public DelegatingQueryLogic() {} + + public DelegatingQueryLogic(DelegatingQueryLogic other) throws CloneNotSupportedException { + this.delegate = (QueryLogic) (other.delegate.clone()); + } + + public QueryLogic getDelegate() { + return delegate; + } + + public void setDelegate(QueryLogic delegate) { + this.delegate = delegate; + } + + @Override + public String getPlan(Connector connection, Query settings, Set runtimeQueryAuthorizations, boolean expandFields, boolean expandValues) + throws Exception { + return delegate.getPlan(connection, settings, runtimeQueryAuthorizations, expandFields, expandValues); + } + + @Override + public GenericQueryConfiguration initialize(Connector connection, Query settings, Set runtimeQueryAuthorizations) throws Exception { + return delegate.initialize(connection, settings, runtimeQueryAuthorizations); + } + + @Override + public void setupQuery(GenericQueryConfiguration configuration) throws Exception { + delegate.setupQuery(configuration); + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + public List getSelectors(Query settings) { + return delegate.getSelectors(settings); + } + + @Override + public SelectorExtractor getSelectorExtractor() { + return delegate.getSelectorExtractor(); + } + + @Override + public abstract Object clone() throws CloneNotSupportedException; + + @Override + public AccumuloConnectionFactory.Priority getConnectionPriority() { + return delegate.getConnectionPriority(); + } + + @Override + public QueryLogicTransformer getTransformer(Query settings) { + return delegate.getTransformer(settings); + } + + @Override + public String getResponseClass(Query query) throws QueryException { + return delegate.getResponseClass(query); + } + + @Override + public TransformIterator getTransformIterator(Query settings) { + return delegate.getTransformIterator(settings); + } + + @Override + public boolean isLongRunningQuery() { + return delegate.isLongRunningQuery(); + } + + @Override + public void close() { + delegate.close(); + } + + @Override + public String getTableName() { + return delegate.getTableName(); + } + + @Override + public long getMaxResults() { + return delegate.getMaxResults(); + } + + @Override + @Deprecated + public long getMaxRowsToScan() { + return delegate.getMaxRowsToScan(); + } + + @Override + public long getMaxWork() { + return delegate.getMaxWork(); + } + + @Override + public int getMaxPageSize() { + return delegate.getMaxPageSize(); + } + + @Override + public long getPageByteTrigger() { + return delegate.getPageByteTrigger(); + } + + @Override + public int getBaseIteratorPriority() { + return delegate.getBaseIteratorPriority(); + } + + @Override + public void setTableName(String tableName) { + delegate.setTableName(tableName); + } + + @Override + public void setMaxResults(long maxResults) { + delegate.setMaxResults(maxResults); + } + + @Override + @Deprecated + public void setMaxRowsToScan(long maxRowsToScan) { + delegate.setMaxRowsToScan(maxRowsToScan); + } + + @Override + public void setMaxWork(long maxWork) { + delegate.setMaxWork(maxWork); + } + + @Override + public void setMaxPageSize(int maxPageSize) { + delegate.setMaxPageSize(maxPageSize); + } + + @Override + public void setPageByteTrigger(long pageByteTrigger) { + delegate.setPageByteTrigger(pageByteTrigger); + } + + @Override + public void setBaseIteratorPriority(int priority) { + delegate.setBaseIteratorPriority(priority); + } + + @Override + public void setLogicName(String logicName) { + delegate.setLogicName(logicName); + } + + @Override + public String getLogicName() { + return delegate.getLogicName(); + } + + @Override + public void setLogicDescription(String logicDescription) { + delegate.setLogicDescription(logicDescription); + } + + @Override + public Auditor.AuditType getAuditType(Query query) { + return delegate.getAuditType(query); + } + + @Override + public Auditor.AuditType getAuditType() { + return delegate.getAuditType(); + } + + @Override + public void setAuditType(Auditor.AuditType auditType) { + delegate.setAuditType(auditType); + } + + @Override + public String getLogicDescription() { + return delegate.getLogicDescription(); + } + + @Override + public boolean getCollectQueryMetrics() { + return delegate.getCollectQueryMetrics(); + } + + @Override + public void setCollectQueryMetrics(boolean collectQueryMetrics) { + delegate.setCollectQueryMetrics(collectQueryMetrics); + } + + @Override + public void setRoleManager(RoleManager roleManager) { + delegate.setRoleManager(roleManager); + } + + @Override + public RoleManager getRoleManager() { + return delegate.getRoleManager(); + } + + @Override + public Set getOptionalQueryParameters() { + return delegate.getOptionalQueryParameters(); + } + + @Override + public void setConnPoolName(String connPoolName) { + delegate.setConnPoolName(connPoolName); + } + + @Override + public String getConnPoolName() { + return delegate.getConnPoolName(); + } + + @Override + public boolean canRunQuery(Principal principal) { + return delegate.canRunQuery(principal); + } + + @Override + public boolean canRunQuery() { + return delegate.canRunQuery(); + } + + @Override + public void setPrincipal(Principal principal) { + delegate.setPrincipal(principal); + } + + @Override + public Principal getPrincipal() { + return delegate.getPrincipal(); + } + + @Override + public MarkingFunctions getMarkingFunctions() { + return delegate.getMarkingFunctions(); + } + + @Override + public void setMarkingFunctions(MarkingFunctions markingFunctions) { + delegate.setMarkingFunctions(markingFunctions); + } + + @Override + public ResponseObjectFactory getResponseObjectFactory() { + return delegate.getResponseObjectFactory(); + } + + @Override + public void setResponseObjectFactory(ResponseObjectFactory responseObjectFactory) { + delegate.setResponseObjectFactory(responseObjectFactory); + } + + @Override + public Set getRequiredQueryParameters() { + return delegate.getRequiredQueryParameters(); + } + + @Override + public Set getExampleQueries() { + return delegate.getExampleQueries(); + } + + @Override + public Set getAuthorizedDNs() { + return delegate.getAuthorizedDNs(); + } + + @Override + public void setAuthorizedDNs(Set allowedDNs) { + delegate.setAuthorizedDNs(allowedDNs); + } + + @Override + public boolean containsDNWithAccess(Collection dns) { + return delegate.containsDNWithAccess(dns); + } + + @Override + public void setDnResultLimits(Map dnResultLimits) { + delegate.setDnResultLimits(dnResultLimits); + } + + @Override + public Map getDnResultLimits() { + return delegate.getDnResultLimits(); + } + + @Override + public long getResultLimit(Collection dns) { + return delegate.getResultLimit(dns); + } + + @Override + public void setPageProcessingStartTime(long pageProcessingStartTime) { + delegate.setPageProcessingStartTime(pageProcessingStartTime); + } + + @Override + public void validate(Map> parameters) throws IllegalArgumentException { + delegate.validate(parameters); + } +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTransformer.java b/web-services/query/src/main/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTransformer.java index d219fa2bfdd..4c725fee0cf 100644 --- a/web-services/query/src/main/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTransformer.java +++ b/web-services/query/src/main/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTransformer.java @@ -2,6 +2,7 @@ import java.util.List; +import com.google.common.base.Throwables; import datawave.webservice.query.cache.ResultsPage; import datawave.webservice.query.cachedresults.CacheableLogic; import datawave.webservice.query.cachedresults.CacheableQueryRow; @@ -24,21 +25,8 @@ public CompositeQueryLogicTransformer(List> delegates @Override public O transform(I input) { - O result = null; - Exception ex = null; - for (QueryLogicTransformer t : delegates) { - try { - log.trace("transform"); - result = t.transform(input); - } catch (Exception e) { - log.warn("Error calling transform on delegate, continuing...", e); - ex = e; - } - } - if (null == result && null != ex) { - throw new RuntimeException("Unable to transform result", ex); - } - return result; + // The objects put into the pageQueue have already been transformed, so no transformation required here. + return (O) input; } @Override @@ -75,33 +63,41 @@ public List readFromCache(List row) { @Override public BaseQueryResponse createResponse(ResultsPage resultList) { - BaseQueryResponse result = null; + Exception lastFailure = null; for (QueryLogicTransformer t : delegates) { try { log.trace("createResponse ResultsPage"); - result = t.createResponse(resultList); + return t.createResponse(resultList); } catch (Exception e) { - log.warn("Error calling createResponse on delegate, continuing...", e); + log.warn("Error calling createResponse on delegate, trying the next one...", e); + lastFailure = e; } } - return result; + if (lastFailure != null) { + Throwables.propagate(lastFailure); + } + return null; } @Override public BaseQueryResponse createResponse(List resultList) { - BaseQueryResponse result = null; + Exception lastFailure = null; for (QueryLogicTransformer t : delegates) { if (t instanceof AbstractQueryLogicTransformer) { AbstractQueryLogicTransformer a = (AbstractQueryLogicTransformer) t; try { log.trace("createResponse List"); - result = a.createResponse(resultList); + return a.createResponse(resultList); } catch (Exception e) { - log.warn("Error calling createResponse on delegate, continuing...", e); + log.warn("Error calling createResponse on delegate, trying the next one", e); + lastFailure = e; } } } - return result; + if (lastFailure != null) { + Throwables.propagate(lastFailure); + } + return null; } } diff --git a/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/FilteredQueryLogic.java b/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/FilteredQueryLogic.java new file mode 100644 index 00000000000..c9ea82250a4 --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/FilteredQueryLogic.java @@ -0,0 +1,90 @@ +package datawave.webservice.query.logic.filtered; + +import datawave.webservice.query.Query; +import datawave.webservice.query.configuration.GenericQueryConfiguration; +import datawave.webservice.query.logic.DelegatingQueryLogic; +import datawave.webservice.query.logic.QueryLogic; +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.security.Authorizations; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +/** + * A filtered query logic will only actually execute the delegate query logic if the filter passes. Otherwise this will do nothing and return no results. + */ +public class FilteredQueryLogic extends DelegatingQueryLogic implements QueryLogic { + + private QueryLogicFilter filter; + + private boolean filtered = false; + + public FilteredQueryLogic() {} + + public FilteredQueryLogic(FilteredQueryLogic other) throws CloneNotSupportedException { + super(other); + this.filter = other.filter; + this.filtered = other.filtered; + } + + public QueryLogicFilter getFilter() { + return filter; + } + + public void setFilter(QueryLogicFilter filter) { + this.filter = filter; + } + + public interface QueryLogicFilter { + boolean canRunQuery(Query settings, Set auths); + } + + @Override + public String getPlan(Connector connection, Query settings, Set runtimeQueryAuthorizations, boolean expandFields, boolean expandValues) + throws Exception { + if (!filtered && filter.canRunQuery(settings, runtimeQueryAuthorizations)) { + return super.getPlan(connection, settings, runtimeQueryAuthorizations, expandFields, expandValues); + } else { + filtered = true; + return ""; + } + } + + @Override + public GenericQueryConfiguration initialize(Connector connection, Query settings, Set runtimeQueryAuthorizations) throws Exception { + if (!filtered && filter.canRunQuery(settings, runtimeQueryAuthorizations)) { + return super.initialize(connection, settings, runtimeQueryAuthorizations); + } else { + filtered = true; + GenericQueryConfiguration config = new GenericQueryConfiguration() {}; + config.setConnector(connection); + config.setQueryString(""); + config.setAuthorizations(runtimeQueryAuthorizations); + config.setBeginDate(settings.getBeginDate()); + config.setEndDate(settings.getEndDate()); + return config; + } + } + + @Override + public void setupQuery(GenericQueryConfiguration configuration) throws Exception { + if (!filtered) { + super.setupQuery(configuration); + } + } + + @Override + public Iterator iterator() { + if (!filtered) { + return super.iterator(); + } else { + return Collections.emptyIterator(); + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + return new FilteredQueryLogic(this); + } +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByAuth.java b/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByAuth.java new file mode 100644 index 00000000000..e369c8ba7bf --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByAuth.java @@ -0,0 +1,52 @@ +package datawave.webservice.query.logic.filtered; + +import datawave.webservice.query.Query; +import datawave.webservice.query.predicate.ProxiedAuthorizationsPredicate; +import org.apache.accumulo.core.security.Authorizations; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * This is a filter for the FilteredQueryLogic that will run the delegate query logic if the auths requested match a specified visibility (as defined by + * accumulo's ColumnVisibility). In addition to the visibility, one can specify that only the first proxied user is matched (presumably the user), and one can + * negate the matching of the visibility. + */ +public class QueryLogicFilterByAuth extends ProxiedAuthorizationsPredicate implements FilteredQueryLogic.QueryLogicFilter { + + // if negated than the negation of the match is returned + private boolean negated = false; + + public QueryLogicFilterByAuth() {} + + public QueryLogicFilterByAuth(String visibility) { + setVisibility(visibility); + } + + public QueryLogicFilterByAuth(String visibility, MatchType matchType) { + this(visibility); + setMatchType(matchType); + } + + public QueryLogicFilterByAuth(String visibility, MatchType matchType, boolean negated) { + this(visibility, matchType); + setNegated(negated); + } + + @Override + public boolean canRunQuery(Query settings, Set auths) { + boolean canRunQuery = test(auths.stream().collect(Collectors.toList())); + if (negated) { + canRunQuery = !canRunQuery; + } + return canRunQuery; + } + + public boolean isNegated() { + return negated; + } + + public void setNegated(boolean negated) { + this.negated = negated; + } +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByParameter.java b/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByParameter.java new file mode 100644 index 00000000000..47ec4f8737f --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByParameter.java @@ -0,0 +1,56 @@ +package datawave.webservice.query.logic.filtered; + +import datawave.webservice.query.Query; +import datawave.webservice.query.QueryImpl; +import datawave.webservice.query.predicate.QueryParameterPredicate; +import org.apache.accumulo.core.security.Authorizations; + +import java.util.Set; + +/** + * This is a filter for the FilteredQueryLogic that will run the delegate query logic if a specified query parameter matches a specified value. If no value is + * specified then the parameter is treated as a boolean parameter (i.e. the value is "true"). One can also negate the matching of the parameter. + */ +public class QueryLogicFilterByParameter extends QueryParameterPredicate implements FilteredQueryLogic.QueryLogicFilter { + + // if negated than the negation of the match is returned + private boolean negated = false; + + public QueryLogicFilterByParameter() {} + + public QueryLogicFilterByParameter(String parameter) { + setParameter(parameter); + } + + public QueryLogicFilterByParameter(String parameter, String value) { + this(parameter); + setValue(value); + } + + public QueryLogicFilterByParameter(String parameter, boolean negated) { + this(parameter); + setNegated(negated); + } + + public QueryLogicFilterByParameter(String parameter, String value, boolean negated) { + this(parameter, value); + setNegated(negated); + } + + @Override + public boolean canRunQuery(Query settings, Set auths) { + boolean canRunQuery = test(settings); + if (negated) { + canRunQuery = !canRunQuery; + } + return canRunQuery; + } + + public boolean isNegated() { + return negated; + } + + public void setNegated(boolean negated) { + this.negated = negated; + } +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/predicate/AuthorizationsPredicate.java b/web-services/query/src/main/java/datawave/webservice/query/predicate/AuthorizationsPredicate.java new file mode 100644 index 00000000000..43447cfd260 --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/predicate/AuthorizationsPredicate.java @@ -0,0 +1,48 @@ +package datawave.webservice.query.predicate; + +import org.apache.accumulo.core.security.Authorizations; +import org.apache.accumulo.core.security.ColumnVisibility; +import org.apache.accumulo.core.security.VisibilityEvaluator; +import org.apache.accumulo.core.security.VisibilityParseException; + +import java.util.function.Predicate; + +/** + * This is a predicate that will test the auths against a specified visibility (as defined by accumulo's ColumnVisibility). In addition to the visibility, one + * can specify that only the first of the authorizations is matched (presumably the user). + */ +public class AuthorizationsPredicate implements Predicate { + + // A visibility string to be matched against the auths being used for the query + private ColumnVisibility visibility; + + public AuthorizationsPredicate() {} + + public AuthorizationsPredicate(String visibility) { + setVisibility(visibility); + } + + @Override + public boolean test(Authorizations auths) { + // match the visibility against the auths. + ColumnVisibility vis = getVisibility(); + VisibilityEvaluator ve = new VisibilityEvaluator(auths); + try { + return (ve.evaluate(vis)); + } catch (VisibilityParseException e) { + throw new RuntimeException(e); + } + } + + public ColumnVisibility getVisibility() { + return visibility; + } + + public void setVisibility(ColumnVisibility visibility) { + this.visibility = visibility; + } + + public void setVisibility(String visibility) { + setVisibility(new ColumnVisibility(visibility)); + } +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/predicate/ProxiedAuthorizationsPredicate.java b/web-services/query/src/main/java/datawave/webservice/query/predicate/ProxiedAuthorizationsPredicate.java new file mode 100644 index 00000000000..9bb92e4483e --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/predicate/ProxiedAuthorizationsPredicate.java @@ -0,0 +1,73 @@ +package datawave.webservice.query.predicate; + +import org.apache.accumulo.core.security.Authorizations; +import org.apache.accumulo.core.security.ColumnVisibility; + +import java.util.List; +import java.util.function.Predicate; + +/** + * This is a predicate that will test the auths against a specified visibility (as defined by accumulo's ColumnVisibility). In addition to the visibility, one + * can specify that only the first of the authorizations is matched (presumably the user). + */ +public class ProxiedAuthorizationsPredicate implements Predicate> { + + public static enum MatchType { + // match only the first of the Authorizations (presumably the user) + FIRST, + // match all of the Authorizations (the user and proxies) + ALL, + } + + private MatchType matchType = MatchType.ALL; + + private final AuthorizationsPredicate authsTest = new AuthorizationsPredicate(); + + public ProxiedAuthorizationsPredicate() {} + + public ProxiedAuthorizationsPredicate(String visibility) { + setVisibility(visibility); + } + + public ProxiedAuthorizationsPredicate(String visibility, MatchType matchType) { + this(visibility); + setMatchType(matchType); + } + + @Override + public boolean test(List auths) { + // match the visibility against the auths. + for (Authorizations auth : auths) { + if (authsTest.test(auth)) { + // if only matching the first one, then return true immediately + if (matchType == MatchType.FIRST) { + return true; + } + } else { + return false; + } + } + return true; + } + + public MatchType getMatchType() { + return matchType; + } + + public void setMatchType(MatchType matchType) { + this.matchType = matchType; + } + + public ColumnVisibility getVisibility() { + return authsTest.getVisibility(); + } + + public void setVisibility(ColumnVisibility visibility) { + authsTest.setVisibility(visibility); + } + + public void setVisibility(String visibility) { + authsTest.setVisibility(visibility); + } + +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/predicate/QueryParameterPredicate.java b/web-services/query/src/main/java/datawave/webservice/query/predicate/QueryParameterPredicate.java new file mode 100644 index 00000000000..4ee22210dee --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/predicate/QueryParameterPredicate.java @@ -0,0 +1,60 @@ +package datawave.webservice.query.predicate; + +import datawave.webservice.query.Query; +import datawave.webservice.query.QueryImpl; + +import java.util.function.Predicate; + +/** + * This is a predicate that will test a specified query parameter matches a specified value. If no value is specified then the parameter is treated as a boolean + * parameter (i.e. the value is "true"). + */ +public class QueryParameterPredicate implements Predicate { + + // The parameter to match against + private String parameter; + + // The value to match against. If empty then boolean value of parameter is used. + private String value; + + public QueryParameterPredicate() {} + + public QueryParameterPredicate(String parameter) { + setParameter(parameter); + } + + public QueryParameterPredicate(String parameter, String value) { + this(parameter); + setValue(value); + } + + @Override + public boolean test(Query settings) { + return matches(settings.findParameter(getParameter())); + } + + private boolean matches(QueryImpl.Parameter parameter) { + String parameterValue = (parameter == null ? null : parameter.getParameterValue()); + if (value == null) { + return Boolean.valueOf(parameterValue); + } else { + return value.equals(parameterValue); + } + } + + public String getParameter() { + return parameter; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java b/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java new file mode 100644 index 00000000000..74c38683375 --- /dev/null +++ b/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java @@ -0,0 +1,365 @@ +package datawave.webservice.query.remote; + +import com.codahale.metrics.Counter; +import com.fasterxml.jackson.databind.ObjectReader; +import datawave.security.authorization.DatawavePrincipal; +import datawave.webservice.common.remote.RemoteHttpService; +import datawave.webservice.common.remote.RemoteHttpServiceConfiguration; +import datawave.webservice.common.remote.RemoteQueryService; +import datawave.webservice.query.result.event.DefaultResponseObjectFactory; +import datawave.webservice.query.result.event.ResponseObjectFactory; +import datawave.webservice.result.BaseQueryResponse; +import datawave.webservice.result.GenericResponse; +import datawave.webservice.result.VoidResponse; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PostConstruct; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class RemoteQueryServiceImpl extends RemoteHttpService implements RemoteQueryService { + private static final Logger log = LoggerFactory.getLogger(RemoteQueryServiceImpl.class); + + private static final String CREATE = "%s/create"; + + private static final String NEXT = "%s/next"; + + private static final String CLOSE = "%s/close"; + + private static final String PLAN = "%s/plan"; + + private ObjectReader voidResponseReader; + + private ObjectReader genericResponseReader; + + private ObjectReader baseQueryResponseReader; + + private ObjectReader eventQueryResponseReader; + + private ResponseObjectFactory responseObjectFactory = new DefaultResponseObjectFactory(); + + private RemoteHttpServiceConfiguration config = new RemoteHttpServiceConfiguration(); + + private boolean initialized = false; + + @Override + @PostConstruct + public void init() { + if (!initialized) { + super.init(); + voidResponseReader = objectMapper.readerFor(VoidResponse.class); + genericResponseReader = objectMapper.readerFor(GenericResponse.class); + baseQueryResponseReader = objectMapper.readerFor(BaseQueryResponse.class); + eventQueryResponseReader = objectMapper.readerFor(responseObjectFactory.getEventQueryResponse().getClass()); + initialized = true; + } + } + + @Override + public GenericResponse createQuery(String queryLogicName, Map> queryParameters, Object callerObject) { + return query(CREATE, queryLogicName, queryParameters, callerObject); + } + + @Override + public GenericResponse planQuery(String queryLogicName, Map> queryParameters, Object callerObject) { + return query(PLAN, queryLogicName, queryParameters, callerObject); + } + + private GenericResponse query(String endPoint, String queryLogicName, Map> queryParameters, Object callerObject) { + final String postBody; + final StringEntity post; + try { + URIBuilder uriBuilder = new URIBuilder(); + queryParameters.entrySet().stream().forEach(e -> e.getValue().stream().forEach(v -> uriBuilder.addParameter(e.getKey(), v))); + postBody = uriBuilder.build().getQuery(); + post = new StringEntity(postBody, ContentType.APPLICATION_FORM_URLENCODED); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + log.info("query Parameters : " + queryParameters); + log.info("post body : " + postBody); + + final String suffix = String.format(endPoint, queryLogicName); + // @formatter:off + return executePostMethodWithRuntimeException( + suffix, + uriBuilder -> { }, + httpPost -> { + httpPost.setEntity(post); + httpPost.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + httpPost.setHeader(HttpHeaders.AUTHORIZATION, getBearer(callerObject)); + httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); + }, + entity -> { + return readResponse(entity, genericResponseReader); + }, + () -> suffix); + // @formatter:on + } + + @Override + public BaseQueryResponse next(String id, Object callerObject) { + final String suffix = String.format(NEXT, id); + return executeGetMethodWithRuntimeException(suffix, uriBuilder -> {}, httpGet -> { + httpGet.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + httpGet.setHeader(HttpHeaders.AUTHORIZATION, getBearer(callerObject)); + }, entity -> { + return readResponse(entity, eventQueryResponseReader, baseQueryResponseReader); + }, () -> suffix); + } + + @Override + public VoidResponse close(String id, Object callerObject) { + final String suffix = String.format(CLOSE, id); + return executePostMethodWithRuntimeException(suffix, uriBuilder -> {}, httpPost -> { + httpPost.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + httpPost.setHeader(HttpHeaders.AUTHORIZATION, getBearer(callerObject)); + }, entity -> { + return readVoidResponse(entity); + }, () -> suffix); + } + + @Override + public GenericResponse planQuery(String id, Object callerObject) { + final String suffix = String.format(PLAN, id); + return executePostMethodWithRuntimeException(suffix, uriBuilder -> {}, httpPost -> { + httpPost.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + httpPost.setHeader(HttpHeaders.AUTHORIZATION, getBearer(callerObject)); + }, entity -> { + return readResponse(entity, genericResponseReader); + }, () -> suffix); + } + + public T readResponse(HttpEntity entity, ObjectReader reader) throws IOException { + if (entity == null) { + return null; + } else { + String content = getContent(entity.getContent()); + try { + return reader.readValue(content); + } catch (IOException ioe) { + log.error("Failed to read entity content. Trying as a VoidResponse.", ioe); + log.error(content); + VoidResponse response = voidResponseReader.readValue(content); + throw new RuntimeException(response.getMessages().toString()); + } + } + } + + public T readResponse(HttpEntity entity, ObjectReader reader1, ObjectReader reader2) throws IOException { + if (entity == null) { + return null; + } else { + String content = getContent(entity.getContent()); + try { + return reader1.readValue(content); + } catch (IOException ioe1) { + try { + return reader2.readValue(content); + } catch (IOException ioe) { + log.error("Failed to read entity content. Trying as a VoidResponse.", ioe); + log.error(content); + VoidResponse response = voidResponseReader.readValue(content); + throw new RuntimeException(response.getMessages().toString()); + } + } + } + } + + private String getContent(InputStream content) throws IOException { + StringBuilder builder = new StringBuilder(); + InputStreamReader reader = new InputStreamReader(content, "UTF8"); + char[] buffer = new char[1024]; + int chars = reader.read(buffer); + while (chars >= 0) { + builder.append(buffer, 0, chars); + chars = reader.read(buffer); + } + return builder.toString(); + } + + public VoidResponse readVoidResponse(HttpEntity entity) throws IOException { + if (entity == null) { + return null; + } else { + VoidResponse response = voidResponseReader.readValue(entity.getContent()); + if (response.getHasResults()) { + return response; + } else { + throw new RuntimeException(response.getMessages().toString()); + } + } + } + + protected String getBearer(Object callerObject) { + init(); + if (callerObject instanceof DatawavePrincipal) { + DatawavePrincipal callerPrincipal = (DatawavePrincipal) callerObject; + return "Bearer " + jwtTokenHandler.createTokenFromUsers(callerPrincipal.getName(), callerPrincipal.getProxiedUsers()); + } else { + throw new RuntimeException("Cannot use " + callerObject.getClass().getName() + " as a caller object"); + } + } + + private T executePostMethodWithRuntimeException(String uriSuffix, Consumer uriCustomizer, Consumer requestCustomizer, + IOFunction resultConverter, Supplier errorSupplier) { + init(); + try { + return executePostMethod(uriSuffix, uriCustomizer, requestCustomizer, resultConverter, errorSupplier); + } catch (URISyntaxException e) { + throw new RuntimeException("Invalid URI: " + e.getMessage(), e); + } catch (IOException e) { + config.getFailureCounter().inc(); + throw new RuntimeException(e.getMessage(), e); + } + } + + private T executeGetMethodWithRuntimeException(String uriSuffix, Consumer uriCustomizer, Consumer requestCustomizer, + IOFunction resultConverter, Supplier errorSupplier) { + init(); + try { + return executeGetMethod(uriSuffix, uriCustomizer, requestCustomizer, resultConverter, errorSupplier); + } catch (URISyntaxException e) { + throw new RuntimeException("Invalid URI: " + e.getMessage(), e); + } catch (IOException e) { + config.getFailureCounter().inc(); + throw new RuntimeException(e.getMessage(), e); + } + } + + @Override + protected String serviceHost() { + return config.getServiceHost(); + } + + @Override + protected int servicePort() { + return config.getServicePort(); + } + + @Override + protected String serviceURI() { + return config.getServiceURI(); + } + + @Override + protected boolean useSrvDns() { + return config.isUseSrvDNS(); + } + + @Override + protected List srvDnsServers() { + return config.getSrvDnsServers(); + } + + @Override + protected int srvDnsPort() { + return config.getSrvDnsPort(); + } + + @Override + protected String serviceScheme() { + return config.getServiceScheme(); + } + + @Override + protected int maxConnections() { + return config.getMaxConnections(); + } + + @Override + protected int retryCount() { + return config.getRetryCount(); + } + + @Override + protected int unavailableRetryCount() { + return config.getUnavailableRetryCount(); + } + + @Override + protected int unavailableRetryDelay() { + return config.getUnavailableRetryDelay(); + } + + @Override + protected Counter retryCounter() { + return config.getRetryCounter(); + } + + public void setUseSrvDNS(boolean useSrvDNS) { + config.setUseSrvDNS(useSrvDNS); + } + + public void setSrvDnsServers(List srvDnsServers) { + config.setSrvDnsServers(srvDnsServers); + } + + public void setSrvDnsPort(int srvDnsPort) { + config.setSrvDnsPort(srvDnsPort); + } + + public void setQueryServiceScheme(String queryServiceScheme) { + config.setServiceScheme(queryServiceScheme); + } + + public void setQueryServiceHost(String queryServiceHost) { + config.setServiceHost(queryServiceHost); + } + + public void setQueryServicePort(int queryServicePort) { + config.setServicePort(queryServicePort); + } + + public void setQueryServiceURI(String queryServiceURI) { + config.setServiceURI(queryServiceURI); + } + + public void setMaxConnections(int maxConnections) { + config.setMaxConnections(maxConnections); + } + + public void setRetryCount(int retryCount) { + config.setRetryCount(retryCount); + } + + public void setUnavailableRetryCount(int unavailableRetryCount) { + config.setUnavailableRetryCount(unavailableRetryCount); + } + + public void setUnavailableRetryDelay(int unavailableRetryDelay) { + config.setUnavailableRetryDelay(unavailableRetryDelay); + } + + public ResponseObjectFactory getResponseObjectFactory() { + return responseObjectFactory; + } + + public void setResponseObjectFactory(ResponseObjectFactory responseObjectFactory) { + this.responseObjectFactory = responseObjectFactory; + } + + public RemoteHttpServiceConfiguration getConfig() { + return config; + } + + public void setConfig(RemoteHttpServiceConfiguration config) { + this.config = config; + } +} diff --git a/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/FilteredQueryLogicTest.java b/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/FilteredQueryLogicTest.java new file mode 100644 index 00000000000..743b03fe0f3 --- /dev/null +++ b/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/FilteredQueryLogicTest.java @@ -0,0 +1,74 @@ +package datawave.webservice.query.logic.filtered; + +import datawave.webservice.query.Query; +import datawave.webservice.query.QueryImpl; +import datawave.webservice.query.configuration.GenericQueryConfiguration; +import datawave.webservice.query.logic.QueryLogic; +import org.apache.accumulo.core.security.Authorizations; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.powermock.api.easymock.PowerMock; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +public class FilteredQueryLogicTest { + + FilteredQueryLogic logic; + QueryLogic delegate; + + @Before + public void setup() { + delegate = PowerMock.createMock(QueryLogic.class); + logic = new FilteredQueryLogic(); + logic.setDelegate(delegate); + logic.setFilter(new QueryLogicFilterByAuth("FOO|BAR")); + } + + @After + public void cleanup() { + PowerMock.resetAll(); + } + + @Test + public void testFiltered() throws Exception { + Query settings = new QueryImpl(); + Set auths = Collections.singleton(new Authorizations("FILTERME")); + + PowerMock.replayAll(); + GenericQueryConfiguration config = logic.initialize(null, settings, auths); + logic.setupQuery(config); + Iterator it = logic.iterator(); + Assert.assertFalse(it.hasNext()); + String plan = logic.getPlan(null, settings, auths, true, true); + Assert.assertEquals("", plan); + PowerMock.verifyAll(); + } + + @Test + public void testNotFiltered() throws Exception { + Query settings = new QueryImpl(); + Set auths = Collections.singleton(new Authorizations("FOO")); + GenericQueryConfiguration config = new GenericQueryConfiguration() {}; + + EasyMock.expect(delegate.initialize(null, settings, auths)).andReturn(config); + delegate.setupQuery(config); + EasyMock.expect(delegate.iterator()).andReturn(Collections.singleton(new Object()).iterator()); + EasyMock.expect(delegate.getPlan(null, settings, auths, true, true)).andReturn("a plan"); + + PowerMock.replayAll(); + logic.initialize(null, new QueryImpl(), Collections.singleton(new Authorizations("FOO"))); + logic.setupQuery(config); + Iterator it = logic.iterator(); + Assert.assertTrue(it.hasNext()); + it.next(); + Assert.assertFalse(it.hasNext()); + String plan = logic.getPlan(null, settings, auths, true, true); + Assert.assertEquals("a plan", plan); + PowerMock.verifyAll(); + } +} diff --git a/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByAuthTest.java b/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByAuthTest.java new file mode 100644 index 00000000000..060ee4b649d --- /dev/null +++ b/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByAuthTest.java @@ -0,0 +1,59 @@ +package datawave.webservice.query.logic.filtered; + +import org.apache.accumulo.core.security.Authorizations; +import org.junit.Test; + +import java.util.Collections; +import java.util.LinkedHashSet; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class QueryLogicFilterByAuthTest { + @Test + public void testDefaults() { + QueryLogicFilterByAuth filter = new QueryLogicFilterByAuth(); + filter.setVisibility("FOO|BAR"); + assertTrue(filter.canRunQuery(null, Collections.singleton(new Authorizations("FOO")))); + assertTrue(filter.canRunQuery(null, Collections.singleton(new Authorizations("BAR")))); + assertFalse(filter.canRunQuery(null, Collections.singleton(new Authorizations("FOOBAR")))); + LinkedHashSet set = new LinkedHashSet<>(); + set.add(new Authorizations("FOO")); + set.add(new Authorizations("BAR")); + assertTrue(filter.canRunQuery(null, set)); + set.add(new Authorizations("FOOBAR")); + assertFalse(filter.canRunQuery(null, set)); + } + + @Test + public void testFirstMatch() { + QueryLogicFilterByAuth filter = new QueryLogicFilterByAuth(); + filter.setVisibility("FOO|BAR"); + filter.setMatchType(QueryLogicFilterByAuth.MatchType.FIRST); + assertTrue(filter.canRunQuery(null, Collections.singleton(new Authorizations("FOO")))); + assertTrue(filter.canRunQuery(null, Collections.singleton(new Authorizations("BAR")))); + assertFalse(filter.canRunQuery(null, Collections.singleton(new Authorizations("FOOBAR")))); + LinkedHashSet set = new LinkedHashSet<>(); + set.add(new Authorizations("FOO")); + set.add(new Authorizations("BAR")); + assertTrue(filter.canRunQuery(null, set)); + set.add(new Authorizations("FOOBAR")); + assertTrue(filter.canRunQuery(null, set)); + } + + @Test + public void testNegated() { + QueryLogicFilterByAuth filter = new QueryLogicFilterByAuth(); + filter.setVisibility("FOO|BAR"); + filter.setNegated(true); + assertFalse(filter.canRunQuery(null, Collections.singleton(new Authorizations("FOO")))); + assertFalse(filter.canRunQuery(null, Collections.singleton(new Authorizations("BAR")))); + assertTrue(filter.canRunQuery(null, Collections.singleton(new Authorizations("FOOBAR")))); + LinkedHashSet set = new LinkedHashSet<>(); + set.add(new Authorizations("FOO")); + set.add(new Authorizations("BAR")); + assertFalse(filter.canRunQuery(null, set)); + set.add(new Authorizations("FOOBAR")); + assertTrue(filter.canRunQuery(null, set)); + } +} diff --git a/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByParameterTest.java b/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByParameterTest.java new file mode 100644 index 00000000000..6f67f951b54 --- /dev/null +++ b/web-services/query/src/test/java/datawave/webservice/query/logic/filtered/QueryLogicFilterByParameterTest.java @@ -0,0 +1,59 @@ +package datawave.webservice.query.logic.filtered; + +import datawave.webservice.query.QueryImpl; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class QueryLogicFilterByParameterTest { + @Test + public void testDefaults() { + QueryLogicFilterByParameter filter = new QueryLogicFilterByParameter(); + filter.setParameter("foo"); + QueryImpl query = new QueryImpl(); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("bar", "true"); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("foo", "false"); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("foo", "true"); + assertTrue(filter.canRunQuery(query, null)); + } + + @Test + public void testValue() { + QueryLogicFilterByParameter filter = new QueryLogicFilterByParameter(); + filter.setParameter("foo"); + filter.setValue("bar"); + QueryImpl query = new QueryImpl(); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("bar", "true"); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("foo", "false"); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("foo", "true"); + assertFalse(filter.canRunQuery(query, null)); + query.addParameter("foo", "bar"); + assertTrue(filter.canRunQuery(query, null)); + } + + @Test + public void testNegates() { + QueryLogicFilterByParameter filter = new QueryLogicFilterByParameter(); + filter.setParameter("foo"); + filter.setValue("bar"); + filter.setNegated(true); + QueryImpl query = new QueryImpl(); + assertTrue(filter.canRunQuery(query, null)); + query.addParameter("bar", "true"); + assertTrue(filter.canRunQuery(query, null)); + query.addParameter("foo", "false"); + assertTrue(filter.canRunQuery(query, null)); + query.addParameter("foo", "true"); + assertTrue(filter.canRunQuery(query, null)); + query.addParameter("foo", "bar"); + assertFalse(filter.canRunQuery(query, null)); + } + +} diff --git a/web-services/security/src/main/java/datawave/security/authorization/test/TestDatawaveUserService.java b/web-services/security/src/main/java/datawave/security/authorization/test/TestDatawaveUserService.java index 50c856363d8..9feaec37333 100644 --- a/web-services/security/src/main/java/datawave/security/authorization/test/TestDatawaveUserService.java +++ b/web-services/security/src/main/java/datawave/security/authorization/test/TestDatawaveUserService.java @@ -191,7 +191,8 @@ protected void readTestUsers() { auths.removeIf(a -> !accumuloAuthorizations.contains(a)); authMapping.entries().removeIf(e -> !accumuloAuthorizations.contains(e.getValue())); - user = new DatawaveUser(user.getDn(), user.getUserType(), auths, user.getRoles(), authMapping, user.getCreationTime(), user.getExpirationTime()); + user = new DatawaveUser(user.getDn(), user.getUserType(), user.getEmail(), auths, user.getRoles(), authMapping, user.getCreationTime(), user + .getExpirationTime()); cannedUsers.put(user.getDn(), user); } catch (IOException e) { diff --git a/web-services/security/src/main/java/datawave/security/system/SecurityDomainProducer.java b/web-services/security/src/main/java/datawave/security/system/SecurityDomainProducer.java new file mode 100644 index 00000000000..94d2598f575 --- /dev/null +++ b/web-services/security/src/main/java/datawave/security/system/SecurityDomainProducer.java @@ -0,0 +1,41 @@ +package datawave.security.system; + +import datawave.configuration.DatawaveEmbeddedProjectStageHolder; +import org.apache.deltaspike.core.api.exclude.Exclude; +import org.jboss.security.AuthenticationManager; +import org.jboss.security.CacheableManager; +import org.jboss.security.JSSESecurityDomain; + +import javax.annotation.Resource; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import java.security.Principal; + +/** + * A producer class for generating server-security related artifacts. For one, we produce the server DN of the server that we are running inside of. We allso + * produce the {@link JSSESecurityDomain} for our application. We use this rather than directly injecting at each site using {@link Resource} since the producer + * allows us to use a plain {@link Inject} annotation versus having to specify the resource name each time we inject with {@link Resource}. This way, we only + * name the resource once. + */ +@ApplicationScoped +@Exclude(ifProjectStage = DatawaveEmbeddedProjectStageHolder.DatawaveEmbedded.class) +public class SecurityDomainProducer { + // Allow injection of JSSESecurityDomain without having to specify the JNDI name at each injection point. + // Instead, users can simply do: + // @Inject private JSSESecurityDomain jsseSecurityDomain + // and the specification of the resource location is limited to this class. + @Produces + @Resource(name = "java:jboss/jaas/datawave/jsse") + private JSSESecurityDomain domain; + + @Resource(name = "java:jboss/jaas/datawave") + private AuthenticationManager authenticationManager; + + @Produces + @AuthorizationCache + @SuppressWarnings("unchecked") + public CacheableManager produceAuthManager() { + return (authenticationManager instanceof CacheableManager) ? (CacheableManager) authenticationManager : null; + } +} diff --git a/web-services/security/src/main/java/datawave/security/system/ServerSecurityProducer.java b/web-services/security/src/main/java/datawave/security/system/ServerSecurityProducer.java index b20efc6e3af..1a49ee8e7bb 100644 --- a/web-services/security/src/main/java/datawave/security/system/ServerSecurityProducer.java +++ b/web-services/security/src/main/java/datawave/security/system/ServerSecurityProducer.java @@ -1,19 +1,11 @@ package datawave.security.system; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.Principal; -import java.security.cert.X509Certificate; -import java.util.Collections; - import datawave.configuration.DatawaveEmbeddedProjectStageHolder; import datawave.security.authorization.DatawavePrincipal; import datawave.security.authorization.DatawaveUserService; import datawave.security.authorization.SubjectIssuerDNPair; import datawave.security.user.UserOperationsBean; import org.apache.deltaspike.core.api.exclude.Exclude; -import org.jboss.security.AuthenticationManager; -import org.jboss.security.CacheableManager; import org.jboss.security.JSSESecurityDomain; import javax.annotation.Resource; @@ -21,6 +13,10 @@ import javax.enterprise.context.RequestScoped; import javax.enterprise.inject.Produces; import javax.inject.Inject; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.X509Certificate; +import java.util.Collections; /** * A producer class for generating server-security related artifacts. For one, we produce the server DN of the server that we are running inside of. We allso @@ -31,17 +27,9 @@ @ApplicationScoped @Exclude(ifProjectStage = DatawaveEmbeddedProjectStageHolder.DatawaveEmbedded.class) public class ServerSecurityProducer { - // Allow injection of JSSESecurityDomain without having to specify the JNDI name at each injection point. - // Instead, users can simply do: - // @Inject private JSSESecurityDomain jsseSecurityDomain - // and the specification of the resource location is limited to this class. - @Produces - @Resource(name = "java:jboss/jaas/datawave/jsse") + @Inject private JSSESecurityDomain domain; - @Resource(name = "java:jboss/jaas/datawave") - private AuthenticationManager authenticationManager; - @Inject private DatawaveUserService datawaveUserService; @@ -71,13 +59,6 @@ public DatawavePrincipal produceServerPrincipal() throws Exception { return new DatawavePrincipal(datawaveUserService.lookup(Collections.singleton(lookupServerDN()))); } - @Produces - @AuthorizationCache - @SuppressWarnings("unchecked") - public CacheableManager produceAuthManager() { - return (authenticationManager instanceof CacheableManager) ? (CacheableManager) authenticationManager : null; - } - private SubjectIssuerDNPair lookupServerDN() throws KeyStoreException { if (domain == null) { throw new IllegalArgumentException("Unable to find security domain."); diff --git a/web-services/security/src/test/java/datawave/security/authorization/test/TestDatawaveUserServiceTest.java b/web-services/security/src/test/java/datawave/security/authorization/test/TestDatawaveUserServiceTest.java index 7cdde64be50..581e652b863 100644 --- a/web-services/security/src/test/java/datawave/security/authorization/test/TestDatawaveUserServiceTest.java +++ b/web-services/security/src/test/java/datawave/security/authorization/test/TestDatawaveUserServiceTest.java @@ -190,7 +190,7 @@ protected abstract static class DefaultDatawaveUserService implements DatawaveUs @Override public Collection lookup(Collection dns) throws AuthorizationException { - return dns.stream().map(dn -> new DatawaveUser(dn, UserType.USER, null, Collections.singleton(getName()), null, -1L, -1L)) + return dns.stream().map(dn -> new DatawaveUser(dn, UserType.USER, null, null, Collections.singleton(getName()), null, -1L, -1L)) .collect(Collectors.toList()); } }