Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes QEB's max results override validation (#2251) #2252

Merged
merged 2 commits into from Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -552,12 +552,21 @@ private QueryData validateQuery(String queryLogicName, MultivaluedMap<String,Str
throw new BadRequestException(qe, response);
}

// validate the max results override relative to the max results on a query logic
// privileged users or unlimited max results users however, may ignore this limitation.
if (qp.isMaxResultsOverridden() && qd.logic.getMaxResults() >= 0) {
if (!ctx.isCallerInRole(PRIVILEGED_USER) || !ctx.isCallerInRole(UNLIMITED_QUERY_RESULTS_USER)) {
if (qp.getMaxResultsOverride() < 0 || (qd.logic.getMaxResults() < qp.getMaxResultsOverride())) {
log.error("Invalid max results override: " + qp.getMaxResultsOverride() + " vs " + qd.logic.getMaxResults());
// Init a query instance in order to properly compute max results for the user...
// TODO: consider refactoring such that query init happens only once here via Persister, and cache in QueryData instance
MultivaluedMap<String,String> optionalQueryParameters = new MultivaluedMapImpl<>();
optionalQueryParameters.putAll(qp.getUnknownParameters(queryParameters));
Query q = responseObjectFactory.getQueryImpl();
q.initialize(qd.userDn, qd.dnList, queryLogicName, qp, optionalQueryParameters);

long resultLimit = qd.logic.getResultLimit(q);

// validate the user's max results override in the context of all currently configured overrides
// privileged users and unlimited max results users are exempt from limitations
if (qp.isMaxResultsOverridden() && resultLimit >= 0) {
if (!ctx.isCallerInRole(PRIVILEGED_USER) && !ctx.isCallerInRole(UNLIMITED_QUERY_RESULTS_USER)) {
if (qp.getMaxResultsOverride() < 0 || (resultLimit < qp.getMaxResultsOverride())) {
log.error("Invalid max results override: " + qp.getMaxResultsOverride() + " vs " + resultLimit);
GenericResponse<String> response = new GenericResponse<>();
throwBadRequest(DatawaveErrorCode.INVALID_MAX_RESULTS_OVERRIDE, response);
}
Expand Down
@@ -1,5 +1,6 @@
package datawave.webservice.query.runner;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
Expand Down Expand Up @@ -806,6 +807,8 @@ public void testCreateQueryAndNext_HappyPath() throws Exception {
expect(this.queryLogic1.getEnrichedTransformer(this.query)).andReturn(this.transformer);
expect(this.transformer.createResponse(this.resultsPage)).andReturn(this.baseResponse);
expect(this.resultsPage.getStatus()).andReturn(ResultsPage.Status.COMPLETE).times(2);
expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);
this.baseResponse.setHasResults(true);
this.baseResponse.setPageNumber(pageNumber);
expect(this.queryLogic1.getLogicName()).andReturn(queryLogicName);
Expand Down Expand Up @@ -960,6 +963,8 @@ public void testCreateQueryAndNext_BadID() throws Exception {
cache.put(eq(queryId.toString()), isA(RunningQuery.class));
expect(this.genericConfiguration.getQueryString()).andReturn(queryName).once();
expect(this.qlCache.poll(queryId.toString())).andReturn(null);
expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

// Set expectations of the next logic
expect(this.principal.getName()).andReturn(userName);
Expand Down Expand Up @@ -1509,6 +1514,9 @@ public void testCreateQueryAndNext_DoubleAuditValues() throws Exception {
expect(this.transaction.getStatus()).andReturn(Status.STATUS_ACTIVE).anyTimes();
this.transaction.commit();

expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

// Run the test
PowerMock.replayAll();
QueryExecutorBean subject = new QueryExecutorBean();
Expand Down Expand Up @@ -1625,6 +1633,9 @@ public void testCreateQueryAndNext_AddToCacheException() throws Exception {
expect(this.query.getId()).andReturn(queryId).anyTimes();
expect(this.qlCache.poll(queryId.toString())).andReturn(null);

expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

// Run the test
PowerMock.replayAll();
QueryExecutorBean subject = new QueryExecutorBean();
Expand Down Expand Up @@ -1823,6 +1834,9 @@ public void testCreateQueryAndNext_ButNoResults() throws Exception {
// expect(this.runningQuery.getTraceInfo()).andReturn(null);
expect(this.responseObjectFactory.getEventQueryResponse()).andReturn(new DefaultEventQueryResponse());

expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

// Run the test
PowerMock.replayAll();
try {
Expand Down Expand Up @@ -3918,6 +3932,9 @@ public void testPlanQuery() throws Exception {
expect(this.query.getUncaughtExceptionHandler()).andReturn(new QueryUncaughtExceptionHandler()).anyTimes();
expect(this.query.getUserDN()).andReturn(userDN).anyTimes();

expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

// Set expectations of the plan
Authorizations queryAuths = new Authorizations(queryAuthorizations);
expect(this.queryLogic1.getPlan(this.client, this.query, Collections.singleton(queryAuths), true, false)).andReturn("a query plan");
Expand Down Expand Up @@ -4063,6 +4080,9 @@ public void testPlanQueryWithValues() throws Exception {
// expect(this.genericConfiguration.getQueryString()).andReturn(queryName).once();
// expect(this.qlCache.poll(queryId.toString())).andReturn(null);

expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

// Set expectations of the plan
Authorizations queryAuths = new Authorizations(queryAuthorizations);
expect(this.queryLogic1.getPlan(this.client, this.query, Collections.singleton(queryAuths), true, true)).andReturn("a query plan");
Expand Down Expand Up @@ -4153,6 +4173,8 @@ public void testCreateQuery_auditException() throws Exception {
expect(this.principal.getAuthorizations()).andReturn((Collection) Arrays.asList(Arrays.asList(queryAuthorizations)));
expect(this.queryLogic1.getMaxPageSize()).andReturn(10).anyTimes();
expect(queryLogic1.getSelectors(null)).andReturn(null);
expect(this.responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
expect(queryLogic1.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);
expect(auditor.audit(queryParameters)).andThrow(new JMSRuntimeException("EXPECTED TESTING EXCEPTION"));
queryLogic1.close();

Expand Down
Expand Up @@ -112,6 +112,7 @@
import datawave.webservice.query.logic.QueryLogicFactory;
import datawave.webservice.query.logic.QueryLogicFactoryImpl;
import datawave.webservice.query.metric.QueryMetricsBean;
import datawave.webservice.query.result.event.ResponseObjectFactory;
import datawave.webservice.result.GenericResponse;

@RunWith(PowerMockRunner.class)
Expand Down Expand Up @@ -147,6 +148,7 @@ public class QueryExecutorBeanTest {
private QueryExpirationConfiguration queryExpirationConf;
private Persister persister;
private QueryPredictor predictor;
private ResponseObjectFactory responseObjectFactory;
private EJBContext ctx;
private CreatedQueryLogicCacheBean qlCache;
private QueryExecutorBean bean;
Expand Down Expand Up @@ -182,10 +184,12 @@ public void setup() throws Exception {
queryExpirationConf.setPageShortCircuitTimeout(58);
queryExpirationConf.setCallTime(60);
connectionRequestBean = createStrictMock(AccumuloConnectionRequestBean.class);
responseObjectFactory = createStrictMock(ResponseObjectFactory.class);
setInternalState(auditor, AuditService.class, auditService);
setInternalState(auditor, AuditParameterBuilder.class, new DefaultAuditParameterBuilder());
setInternalState(connectionRequestBean, EJBContext.class, ctx);
setInternalState(bean, QueryCache.class, cache);
setInternalState(bean, ResponseObjectFactory.class, responseObjectFactory);
setInternalState(bean, ClosedQueryCache.class, closedCache);
setInternalState(bean, AccumuloConnectionFactory.class, connectionFactory);
setInternalState(bean, AuditBean.class, auditor);
Expand Down Expand Up @@ -295,6 +299,8 @@ private void defineTestRunner(QueryImpl q, MultivaluedMap<String,String> p) thro
EasyMock.expect(logic.getMaxResults()).andReturn(-1L);
logic.preInitialize(q, AuthorizationsUtil.buildAuthorizations(Collections.singleton(Sets.newHashSet("PUBLIC", "PRIVATE"))));
EasyMock.expect(logic.getUserOperations()).andReturn(null);
EasyMock.expect(responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
EasyMock.expect(logic.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);
PowerMock.replayAll();

bean.defineQuery(queryLogicName, p);
Expand Down Expand Up @@ -371,6 +377,9 @@ public void testCreateWithNoSelectedAuths() throws Exception {
EasyMock.expect(persister.create(userDN, dnList, (SecurityMarking) Whitebox.getField(bean.getClass(), "marking").get(bean), queryLogicName,
(QueryParameters) Whitebox.getField(bean.getClass(), "qp").get(bean), optionalParameters)).andReturn(q);

EasyMock.expect(responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
EasyMock.expect(logic.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);

EasyMock.expect(queryLogicFactory.getQueryLogic(queryLogicName, principal)).andReturn(logic);
EasyMock.expect(logic.getRequiredQueryParameters()).andReturn(Collections.EMPTY_SET);
EasyMock.expect(logic.containsDNWithAccess(dnList)).andReturn(true);
Expand Down Expand Up @@ -477,6 +486,8 @@ public boolean equals(Object o) {

Set<Prediction> predictions = new HashSet<>();
predictions.add(new Prediction("source", 1));
EasyMock.expect(responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
EasyMock.expect(logic.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);
EasyMock.expect(predictor.predict(EasyMock.eq(testMetric))).andReturn(predictions);

PowerMock.replayAll();
Expand Down Expand Up @@ -671,6 +682,8 @@ public void testCloseActuallyCloses() throws Exception {
EasyMock.expect(persister.create(principal.getUserDN().subjectDN(), dnList, Whitebox.getInternalState(bean, SecurityMarking.class), queryLogicName,
Whitebox.getInternalState(bean, QueryParameters.class), optionalParameters)).andReturn(q);
EasyMock.expect(persister.findById(EasyMock.anyString())).andReturn(null).anyTimes();
EasyMock.expect(responseObjectFactory.getQueryImpl()).andReturn(new QueryImpl());
EasyMock.expect(logic.getResultLimit(anyObject(QueryImpl.class))).andReturn(-1L);
EasyMock.expect(connectionFactory.getTrackingMap(anyObject())).andReturn(Maps.newHashMap()).anyTimes();

BaseQueryMetric metric = new QueryMetricFactoryImpl().createMetric();
Expand Down