mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
implementation of record security locks, and permissions
This commit is contained in:
@ -41,6 +41,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
@ -51,7 +52,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
@ -299,11 +303,35 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** method that sub-classes should call to make a full WHERE clause, including
|
||||
** security clauses.
|
||||
*******************************************************************************/
|
||||
protected String makeWhereClause(QInstance instance, QTableMetaData table, JoinsContext joinsContext, QQueryFilter filter, List<Serializable> params) throws IllegalArgumentException, QException
|
||||
protected String makeWhereClause(QInstance instance, QSession session, QTableMetaData table, JoinsContext joinsContext, QQueryFilter filter, List<Serializable> params) throws IllegalArgumentException, QException
|
||||
{
|
||||
String clause = makeSimpleWhereClause(instance, table, joinsContext, filter.getCriteria(), filter.getBooleanOperator(), params);
|
||||
String whereClauseWithoutSecurity = makeWhereClauseWithoutSecurity(instance, table, joinsContext, filter, params);
|
||||
QQueryFilter securityFilter = getSecurityFilter(instance, session, table, joinsContext);
|
||||
if(securityFilter == null || CollectionUtils.nullSafeIsEmpty(securityFilter.getCriteria()))
|
||||
{
|
||||
return (whereClauseWithoutSecurity);
|
||||
}
|
||||
String securityWhereClause = getSqlWhereStringAndPopulateParamsListFromNonNestedFilter(instance, table, joinsContext, securityFilter.getCriteria(), QQueryFilter.BooleanOperator.AND, params);
|
||||
return ("(" + whereClauseWithoutSecurity + ") AND (" + securityWhereClause + ")");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** private method for making the part of a where clause that gets AND'ed to the
|
||||
** security clause. Recursively handles sub-clauses.
|
||||
*******************************************************************************/
|
||||
private String makeWhereClauseWithoutSecurity(QInstance instance, QTableMetaData table, JoinsContext joinsContext, QQueryFilter filter, List<Serializable> params) throws IllegalArgumentException, QException
|
||||
{
|
||||
if(filter == null || !filter.hasAnyCriteria())
|
||||
{
|
||||
return ("1 = 1");
|
||||
}
|
||||
|
||||
String clause = getSqlWhereStringAndPopulateParamsListFromNonNestedFilter(instance, table, joinsContext, filter.getCriteria(), filter.getBooleanOperator(), params);
|
||||
if(!CollectionUtils.nullSafeHasContents(filter.getSubFilters()))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////
|
||||
@ -322,7 +350,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
}
|
||||
for(QQueryFilter subFilter : filter.getSubFilters())
|
||||
{
|
||||
String subClause = makeWhereClause(instance, table, joinsContext, subFilter, params);
|
||||
String subClause = makeWhereClauseWithoutSecurity(instance, table, joinsContext, subFilter, params);
|
||||
if(StringUtils.hasContent(subClause))
|
||||
{
|
||||
clauses.add("(" + subClause + ")");
|
||||
@ -333,10 +361,129 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Build a QQueryFilter to apply record-level security to the query.
|
||||
** Note, it may be empty, if there are no lock fields, or all are all-access.
|
||||
*******************************************************************************/
|
||||
private QQueryFilter getSecurityFilter(QInstance instance, QSession session, QTableMetaData table, JoinsContext joinsContext)
|
||||
{
|
||||
QQueryFilter newFilter = new QQueryFilter();
|
||||
newFilter.setBooleanOperator(QQueryFilter.BooleanOperator.AND);
|
||||
List<QFilterCriteria> securityCriteria = new ArrayList<>();
|
||||
newFilter.setCriteria(securityCriteria);
|
||||
|
||||
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(table.getRecordSecurityLocks()))
|
||||
{
|
||||
addCriteriaForRecordSecurityLock(instance, session, table, securityCriteria, recordSecurityLock, joinsContext, table.getName());
|
||||
}
|
||||
|
||||
for(QueryJoin queryJoin : CollectionUtils.nonNullList(joinsContext.getQueryJoins()))
|
||||
{
|
||||
QTableMetaData joinTable = instance.getTable(queryJoin.getJoinTable());
|
||||
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(joinTable.getRecordSecurityLocks()))
|
||||
{
|
||||
addCriteriaForRecordSecurityLock(instance, session, joinTable, securityCriteria, recordSecurityLock, joinsContext, queryJoin.getJoinTableOrItsAlias());
|
||||
}
|
||||
}
|
||||
|
||||
return (newFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String makeSimpleWhereClause(QInstance instance, QTableMetaData table, JoinsContext joinsContext, List<QFilterCriteria> criteria, QQueryFilter.BooleanOperator booleanOperator, List<Serializable> params) throws IllegalArgumentException
|
||||
private static void addCriteriaForRecordSecurityLock(QInstance instance, QSession session, QTableMetaData table, List<QFilterCriteria> securityCriteria, RecordSecurityLock recordSecurityLock, JoinsContext joinsContext, String tableNameOrAlias)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// check if the key type has an all-access key, and if so, if it's set to true for the current user/session //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QSecurityKeyType securityKeyType = instance.getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
|
||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
||||
{
|
||||
if(session.hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// if we have all-access on this key, then we don't need a criterion for it. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(recordSecurityLock.getJoinChain()))
|
||||
{
|
||||
for(String joinName : recordSecurityLock.getJoinChain())
|
||||
{
|
||||
QJoinMetaData joinMetaData = instance.getJoin(joinName);
|
||||
|
||||
/*
|
||||
for(QueryJoin queryJoin : joinsContext.getQueryJoins())
|
||||
{
|
||||
if(queryJoin.getJoinMetaData().getName().equals(joinName))
|
||||
{
|
||||
joinMetaData = queryJoin.getJoinMetaData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if(joinMetaData == null)
|
||||
{
|
||||
throw (new RuntimeException("Could not find joinMetaData for recordSecurityLock with joinChain member [" + joinName + "]"));
|
||||
}
|
||||
|
||||
table = instance.getTable(joinMetaData.getRightTable());
|
||||
tableNameOrAlias = table.getName();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else - get the key values from the session and decide what kind of criterion to build //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
List<Serializable> securityKeyValues = session.getSecurityKeyValues(recordSecurityLock.getSecurityKeyType(), table.getField(recordSecurityLock.getFieldName()).getType());
|
||||
String fieldName = tableNameOrAlias + "." + recordSecurityLock.getFieldName();
|
||||
if(CollectionUtils.nullSafeIsEmpty(securityKeyValues))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// handle user with no values -- they can only see null values, and only iff the lock's null-value behavior is ALLOW //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(RecordSecurityLock.NullValueBehavior.ALLOW.equals(recordSecurityLock.getNullValueBehavior()))
|
||||
{
|
||||
securityCriteria.add(new QFilterCriteria(fieldName, QCriteriaOperator.IS_BLANK));
|
||||
}
|
||||
else
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, if no user/session values, and null-value behavior is deny, then setup a FALSE condition, to allow no rows. //
|
||||
// todo - make some explicit contradiction here - maybe even avoid running the whole query - as you're not allowed ANY records //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
securityCriteria.add(new QFilterCriteria(fieldName, QCriteriaOperator.IN, Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, if user/session has some values, build an IN rule - //
|
||||
// noting that if the lock's null-value behavior is ALLOW, then we actually want IS_NULL_OR_IN, not just IN //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(RecordSecurityLock.NullValueBehavior.ALLOW.equals(recordSecurityLock.getNullValueBehavior()))
|
||||
{
|
||||
securityCriteria.add(new QFilterCriteria(fieldName, QCriteriaOperator.IS_NULL_OR_IN, securityKeyValues));
|
||||
}
|
||||
else
|
||||
{
|
||||
securityCriteria.add(new QFilterCriteria(fieldName, QCriteriaOperator.IN, securityKeyValues));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getSqlWhereStringAndPopulateParamsListFromNonNestedFilter(QInstance instance, QTableMetaData table, JoinsContext joinsContext, List<QFilterCriteria> criteria, QQueryFilter.BooleanOperator booleanOperator, List<Serializable> params) throws IllegalArgumentException
|
||||
{
|
||||
List<String> clauses = new ArrayList<>();
|
||||
for(QFilterCriteria criterion : criteria)
|
||||
@ -366,10 +513,10 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
{
|
||||
if(values.isEmpty())
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// if there are no values, then we want a false here - so say column != column. //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
clause += " != " + column;
|
||||
///////////////////////////////////////////////////////
|
||||
// if there are no values, then we want a false here //
|
||||
///////////////////////////////////////////////////////
|
||||
clause = " 0 = 1 ";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -377,14 +524,24 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IS_NULL_OR_IN:
|
||||
{
|
||||
clause += " IS NULL ";
|
||||
|
||||
if(!values.isEmpty())
|
||||
{
|
||||
clause += " OR " + column + " IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ")";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NOT_IN:
|
||||
{
|
||||
if(values.isEmpty())
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// if there are no values, then we want a true here - so say column == column. //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
clause += " = " + column;
|
||||
//////////////////////////////////////////////////////
|
||||
// if there are no values, then we want a true here //
|
||||
//////////////////////////////////////////////////////
|
||||
clause = " 1 = 1 ";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -74,10 +74,7 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
||||
|
||||
QQueryFilter filter = aggregateInput.getFilter();
|
||||
List<Serializable> params = new ArrayList<>();
|
||||
if(filter != null && filter.hasAnyCriteria())
|
||||
{
|
||||
sql += " WHERE " + makeWhereClause(aggregateInput.getInstance(), table, joinsContext, filter, params);
|
||||
}
|
||||
sql += " WHERE " + makeWhereClause(aggregateInput.getInstance(), aggregateInput.getSession(), table, joinsContext, filter, params);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(aggregateInput.getGroupBys()))
|
||||
{
|
||||
|
@ -64,10 +64,7 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
||||
|
||||
QQueryFilter filter = countInput.getFilter();
|
||||
List<Serializable> params = new ArrayList<>();
|
||||
if(filter != null && filter.hasAnyCriteria())
|
||||
{
|
||||
sql += " WHERE " + makeWhereClause(countInput.getInstance(), table, joinsContext, filter, params);
|
||||
}
|
||||
sql += " WHERE " + makeWhereClause(countInput.getInstance(), countInput.getSession(), table, joinsContext, filter, params);
|
||||
|
||||
// todo sql customization - can edit sql and/or param list
|
||||
|
||||
|
@ -262,7 +262,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
|
||||
|
||||
String tableName = getTableName(table);
|
||||
JoinsContext joinsContext = new JoinsContext(deleteInput.getInstance(), table.getName(), Collections.emptyList());
|
||||
String whereClause = makeWhereClause(deleteInput.getInstance(), table, joinsContext, filter, params);
|
||||
String whereClause = makeWhereClause(deleteInput.getInstance(), deleteInput.getSession(), table, joinsContext, filter, params);
|
||||
|
||||
// todo sql customization - can edit sql and/or param list?
|
||||
String sql = "DELETE FROM "
|
||||
|
@ -77,10 +77,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
QQueryFilter filter = queryInput.getFilter();
|
||||
List<Serializable> params = new ArrayList<>();
|
||||
|
||||
if(filter != null && filter.hasAnyCriteria())
|
||||
{
|
||||
sql.append(" WHERE ").append(makeWhereClause(queryInput.getInstance(), table, joinsContext, filter, params));
|
||||
}
|
||||
sql.append(" WHERE ").append(makeWhereClause(queryInput.getInstance(), queryInput.getSession(), table, joinsContext, filter, params));
|
||||
|
||||
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getOrderBys()))
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ import java.sql.Connection;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
@ -35,8 +36,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSActionTest;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
@ -60,6 +62,8 @@ public class TestUtils
|
||||
public static final String TABLE_NAME_ITEM = "item";
|
||||
public static final String TABLE_NAME_ORDER_LINE = "orderLine";
|
||||
|
||||
public static final String SECURITY_KEY_STORE_ALL_ACCESS = "storeAllAccess";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -219,21 +223,25 @@ public class TestUtils
|
||||
qInstance.addTable(defineBaseTable(TABLE_NAME_STORE, "store")
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("name")
|
||||
.withRecordSecurityLock(new RecordSecurityLock().withSecurityKeyType(TABLE_NAME_STORE).withFieldName("id"))
|
||||
.withField(new QFieldMetaData("name", QFieldType.STRING))
|
||||
);
|
||||
|
||||
qInstance.addTable(defineBaseTable(TABLE_NAME_ORDER, "order")
|
||||
.withRecordSecurityLock(new RecordSecurityLock().withSecurityKeyType(TABLE_NAME_STORE).withFieldName("storeId"))
|
||||
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id").withPossibleValueSourceName(TABLE_NAME_STORE))
|
||||
.withField(new QFieldMetaData("billToPersonId", QFieldType.INTEGER).withBackendName("bill_to_person_id").withPossibleValueSourceName(TABLE_NAME_PERSON))
|
||||
.withField(new QFieldMetaData("shipToPersonId", QFieldType.INTEGER).withBackendName("ship_to_person_id").withPossibleValueSourceName(TABLE_NAME_PERSON))
|
||||
);
|
||||
|
||||
qInstance.addTable(defineBaseTable(TABLE_NAME_ITEM, "item")
|
||||
.withRecordSecurityLock(new RecordSecurityLock().withSecurityKeyType(TABLE_NAME_STORE).withFieldName("storeId"))
|
||||
.withField(new QFieldMetaData("sku", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id").withPossibleValueSourceName(TABLE_NAME_STORE))
|
||||
);
|
||||
|
||||
qInstance.addTable(defineBaseTable(TABLE_NAME_ORDER_LINE, "order_line")
|
||||
.withRecordSecurityLock(new RecordSecurityLock().withSecurityKeyType(TABLE_NAME_STORE).withFieldName("storeId"))
|
||||
.withField(new QFieldMetaData("orderId", QFieldType.INTEGER).withBackendName("order_id"))
|
||||
.withField(new QFieldMetaData("sku", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id").withPossibleValueSourceName(TABLE_NAME_STORE))
|
||||
@ -295,6 +303,11 @@ public class TestUtils
|
||||
.withTableName(TABLE_NAME_STORE)
|
||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)
|
||||
);
|
||||
|
||||
qInstance.addSecurityKeyType(new QSecurityKeyType()
|
||||
.withName(TABLE_NAME_STORE)
|
||||
.withAllAccessKeyName(SECURITY_KEY_STORE_ALL_ACCESS)
|
||||
.withPossibleValueSourceName(TABLE_NAME_STORE));
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,6 +48,7 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@ -318,7 +319,18 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
|
||||
|
||||
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
|
||||
AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
|
||||
assertNull(aggregateResult.getAggregateValue(sumOfQuantity));
|
||||
|
||||
aggregateInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
|
||||
aggregateResult = aggregateOutput.getResults().get(0);
|
||||
Assertions.assertEquals(43, aggregateResult.getAggregateValue(sumOfQuantity));
|
||||
|
||||
aggregateInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||
aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
|
||||
aggregateResult = aggregateOutput.getResults().get(0);
|
||||
// note - this would be 33, except for that one order line that has a contradictory store id...
|
||||
Assertions.assertEquals(32, aggregateResult.getAggregateValue(sumOfQuantity));
|
||||
}
|
||||
|
||||
|
||||
@ -340,7 +352,10 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
|
||||
aggregateInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE));
|
||||
|
||||
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
|
||||
assertEquals(6, aggregateOutput.getResults().size());
|
||||
assertEquals(0, aggregateOutput.getResults().size());
|
||||
|
||||
aggregateInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
|
||||
assertSkuQuantity("QM-1", 30, aggregateOutput.getResults(), groupBy);
|
||||
assertSkuQuantity("QM-2", 1, aggregateOutput.getResults(), groupBy);
|
||||
assertSkuQuantity("QM-3", 1, aggregateOutput.getResults(), groupBy);
|
||||
|
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
@ -179,4 +180,26 @@ public class RDBMSCountActionTest extends RDBMSActionTest
|
||||
assertEquals(2, countOutput.getCount(), "Right Join count should find 2 rows");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurity() throws QException
|
||||
{
|
||||
CountInput countInput = new CountInput();
|
||||
countInput.setInstance(TestUtils.defineInstance());
|
||||
countInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
countInput.setSession(new QSession());
|
||||
assertThat(new CountAction().execute(countInput).getCount()).isEqualTo(0);
|
||||
|
||||
countInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new CountAction().execute(countInput).getCount()).isEqualTo(8);
|
||||
|
||||
countInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(2, 3)));
|
||||
assertThat(new CountAction().execute(countInput).getCount()).isEqualTo(5);
|
||||
}
|
||||
|
||||
}
|
@ -22,8 +22,11 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Predicate;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
@ -38,6 +41,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
@ -605,6 +609,38 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNestedFilterAndTopLevelFilter() throws QException
|
||||
{
|
||||
QueryInput queryInput = initQueryRequest();
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 3))
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||
.withSubFilters(List.of(
|
||||
new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("James")))
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Tim"))),
|
||||
new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withCriteria(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, List.of("Kelkhoff")))
|
||||
.withCriteria(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, List.of("Chamberlain")))
|
||||
))
|
||||
);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size(), "Complex query should find 1 row");
|
||||
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueInteger("id").equals(3) && r.getValueString("firstName").equals("Tim") && r.getValueString("lastName").equals("Chamberlain"));
|
||||
|
||||
queryInput.getFilter().setCriteria(List.of(new QFilterCriteria("id", QCriteriaOperator.NOT_EQUALS, 3)));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size(), "Next complex query should find 0 rows");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -712,7 +748,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
@Test
|
||||
void testFiveTableOmsJoinFindMismatchedStoreId() throws Exception
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(TestUtils.defineInstance(), new QSession());
|
||||
QueryInput queryInput = new QueryInput(TestUtils.defineInstance(), new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_STORE).withAlias("orderStore").withSelect(true));
|
||||
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE).withSelect(true));
|
||||
@ -754,7 +790,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
orderLineCount.set(rs.getInt(1));
|
||||
});
|
||||
|
||||
QueryInput queryInput = new QueryInput(TestUtils.defineInstance(), new QSession());
|
||||
QueryInput queryInput = new QueryInput(TestUtils.defineInstance(), new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER_LINE);
|
||||
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER).withSelect(true));
|
||||
|
||||
@ -775,7 +811,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
void testOmsQueryByPersons() throws Exception
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(instance, new QSession());
|
||||
QueryInput queryInput = new QueryInput(instance, new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
@ -877,7 +913,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
void testOmsQueryByPersonsExtraKelkhoffOrder() throws Exception
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(instance, new QSession());
|
||||
QueryInput queryInput = new QueryInput(instance, new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -953,4 +989,330 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
.hasRootCauseMessage("Duplicate table name or alias: shipToPerson");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** queries on the store table, where the primary key (id) is the security field
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityPrimaryKeyFieldNoFilters() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_STORE);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(3);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(1)
|
||||
.anyMatch(r -> r.getValueInteger("id").equals(1));
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(1)
|
||||
.anyMatch(r -> r.getValueInteger("id").equals(2));
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(2)
|
||||
.anyMatch(r -> r.getValueInteger("id").equals(1))
|
||||
.anyMatch(r -> r.getValueInteger("id").equals(3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** not really expected to be any different from where we filter on the primary key,
|
||||
** but just good to make sure
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityForeignKeyFieldNoFilters() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(8);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(3)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1));
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(2)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(2));
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(6)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1) || r.getValueInteger("storeId").equals(3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityWithFilters() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(6);
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(2)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1));
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("storeId", QCriteriaOperator.IN, List.of(1, 2))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(3)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityWithOrQueries() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(
|
||||
new QFilterCriteria("billToPersonId", QCriteriaOperator.EQUALS, List.of(1)),
|
||||
new QFilterCriteria("shipToPersonId", QCriteriaOperator.EQUALS, List.of(5))
|
||||
).withBooleanOperator(QQueryFilter.BooleanOperator.OR));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(5)
|
||||
.allMatch(r -> Objects.equals(r.getValueInteger("billToPersonId"), 1) || Objects.equals(r.getValueInteger("shipToPersonId"), 5));
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(
|
||||
new QFilterCriteria("billToPersonId", QCriteriaOperator.EQUALS, List.of(1)),
|
||||
new QFilterCriteria("shipToPersonId", QCriteriaOperator.EQUALS, List.of(5))
|
||||
).withBooleanOperator(QQueryFilter.BooleanOperator.OR));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(1)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(2))
|
||||
.allMatch(r -> Objects.equals(r.getValueInteger("billToPersonId"), 1) || Objects.equals(r.getValueInteger("shipToPersonId"), 5));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityWithSubFilters() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withSubFilters(List.of(
|
||||
new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.GREATER_THAN_OR_EQUALS, 2), new QFilterCriteria("billToPersonId", QCriteriaOperator.EQUALS, 1)),
|
||||
new QQueryFilter(new QFilterCriteria("billToPersonId", QCriteriaOperator.IS_BLANK), new QFilterCriteria("shipToPersonId", QCriteriaOperator.IS_BLANK)).withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
)));
|
||||
Predicate<QRecord> p = r -> r.getValueInteger("billToPersonId") == null || r.getValueInteger("shipToPersonId") == null || (r.getValueInteger("id") >= 2 && r.getValueInteger("billToPersonId") == 1);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(4)
|
||||
.allMatch(p);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(1)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1))
|
||||
.allMatch(p);
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 3));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(3)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(3))
|
||||
.allMatch(p);
|
||||
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityNullValues() throws Exception
|
||||
{
|
||||
runTestSql("INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (9, NULL, 1, 6)", null);
|
||||
runTestSql("INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (10, NULL, 6, 5)", null);
|
||||
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
Predicate<QRecord> hasNullStoreId = r -> r.getValueInteger("storeId") == null;
|
||||
|
||||
////////////////////////////////////////////
|
||||
// all-access user should get all 10 rows //
|
||||
////////////////////////////////////////////
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(10)
|
||||
.anyMatch(hasNullStoreId);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// no-values user should get 0 rows (given that default null-behavior on this key type is DENY) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// user with list of all ids shouldn't see the nulls (given that default null-behavior on this key type is DENY) //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 2, 3, 4, 5)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(8)
|
||||
.noneMatch(hasNullStoreId);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// specifically set the null behavior to deny - repeat the last 2 tests //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().get(0).setNullValueBehavior(RecordSecurityLock.NullValueBehavior.DENY);
|
||||
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 2, 3, 4, 5)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(8)
|
||||
.noneMatch(hasNullStoreId);
|
||||
|
||||
///////////////////////////////////
|
||||
// change null behavior to ALLOW //
|
||||
///////////////////////////////////
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().get(0).setNullValueBehavior(RecordSecurityLock.NullValueBehavior.ALLOW);
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// all-access user should still get all 10 //
|
||||
/////////////////////////////////////////////
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(10)
|
||||
.anyMatch(hasNullStoreId);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// no-values user should only get the rows w/ null //
|
||||
/////////////////////////////////////////////////////
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(2)
|
||||
.allMatch(hasNullStoreId);
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// user with list of all ids should see the nulls //
|
||||
////////////////////////////////////////////////////
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 2, 3, 4, 5)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(10)
|
||||
.anyMatch(hasNullStoreId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordSecurityWithLockFromJoinTable() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// remove the normal lock on the order table - replace it with one from the joined store table //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().clear();
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).withRecordSecurityLock(new RecordSecurityLock()
|
||||
.withSecurityKeyType(TestUtils.TABLE_NAME_STORE)
|
||||
.withJoinChain(List.of("orderJoinStore"))
|
||||
.withFieldName("id"));
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(6);
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(2)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1));
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
|
||||
queryInput.setSession(new QSession());
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("storeId", QCriteriaOperator.IN, List.of(1, 2))));
|
||||
queryInput.setSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
|
||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
||||
.hasSize(3)
|
||||
.allMatch(r -> r.getValueInteger("storeId").equals(1));
|
||||
}
|
||||
|
||||
}
|
@ -196,7 +196,7 @@ public class GenerateReportActionRDBMSTest extends RDBMSActionTest
|
||||
private String runReport(QInstance qInstance) throws QException
|
||||
{
|
||||
ReportInput reportInput = new ReportInput(qInstance);
|
||||
reportInput.setSession(new QSession());
|
||||
reportInput.setSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||
reportInput.setReportName(TEST_REPORT);
|
||||
reportInput.setReportFormat(ReportFormat.CSV);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
Reference in New Issue
Block a user