mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Merged feature/CE-1654-warehouse-security-key-all-access-left-join into dev
This commit is contained in:
@ -82,7 +82,7 @@ public class JoinsContext
|
|||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// we will get a TON of more output if this gets turned up, so be cautious //
|
// we will get a TON of more output if this gets turned up, so be cautious //
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
private Level logLevel = Level.OFF;
|
private Level logLevel = Level.OFF;
|
||||||
private Level logLevelForFilter = Level.OFF;
|
private Level logLevelForFilter = Level.OFF;
|
||||||
|
|
||||||
|
|
||||||
@ -404,6 +404,12 @@ public class JoinsContext
|
|||||||
chainIsInner = false;
|
chainIsInner = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(hasAllAccessKey(recordSecurityLock))
|
||||||
|
{
|
||||||
|
queryJoin.withType(QueryJoin.Type.LEFT);
|
||||||
|
chainIsInner = false;
|
||||||
|
}
|
||||||
|
|
||||||
addQueryJoin(queryJoin, "forRecordSecurityLock (non-flipped)", "- ");
|
addQueryJoin(queryJoin, "forRecordSecurityLock (non-flipped)", "- ");
|
||||||
addedQueryJoins.add(queryJoin);
|
addedQueryJoins.add(queryJoin);
|
||||||
tmpTable = instance.getTable(join.getRightTable());
|
tmpTable = instance.getTable(join.getRightTable());
|
||||||
@ -423,6 +429,12 @@ public class JoinsContext
|
|||||||
chainIsInner = false;
|
chainIsInner = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(hasAllAccessKey(recordSecurityLock))
|
||||||
|
{
|
||||||
|
queryJoin.withType(QueryJoin.Type.LEFT);
|
||||||
|
chainIsInner = false;
|
||||||
|
}
|
||||||
|
|
||||||
addQueryJoin(queryJoin, "forRecordSecurityLock (flipped)", "- ");
|
addQueryJoin(queryJoin, "forRecordSecurityLock (flipped)", "- ");
|
||||||
addedQueryJoins.add(queryJoin);
|
addedQueryJoins.add(queryJoin);
|
||||||
tmpTable = instance.getTable(join.getLeftTable());
|
tmpTable = instance.getTable(join.getLeftTable());
|
||||||
@ -456,44 +468,53 @@ public class JoinsContext
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private boolean hasAllAccessKey(RecordSecurityLock recordSecurityLock)
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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()))
|
||||||
|
{
|
||||||
|
QSession session = QContext.getQSession();
|
||||||
|
if(session.hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
|
||||||
|
{
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void addSubFilterForRecordSecurityLock(RecordSecurityLock recordSecurityLock, QTableMetaData table, String tableNameOrAlias, boolean isOuter, QueryJoin sourceQueryJoin)
|
private void addSubFilterForRecordSecurityLock(RecordSecurityLock recordSecurityLock, QTableMetaData table, String tableNameOrAlias, boolean isOuter, QueryJoin sourceQueryJoin)
|
||||||
{
|
{
|
||||||
QSession session = QContext.getQSession();
|
boolean haveAllAccessKey = hasAllAccessKey(recordSecurityLock);
|
||||||
|
if(haveAllAccessKey)
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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());
|
|
||||||
boolean haveAllAccessKey = false;
|
|
||||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
if(sourceQueryJoin != null)
|
||||||
// if we have all-access on this key, then we don't need a criterion for it (as long as we're in an AND filter) //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(session.hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
|
|
||||||
{
|
{
|
||||||
haveAllAccessKey = true;
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// in case the queryJoin object is re-used between queries, and its security criteria need to be different (!!), reset it //
|
||||||
|
// this can be exposed in tests - maybe not entirely expected in real-world, but seems safe enough //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
sourceQueryJoin.withSecurityCriteria(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
if(sourceQueryJoin != null)
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
{
|
// if we're in an AND filter, then we don't need a criteria for this lock, so return. //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// in case the queryJoin object is re-used between queries, and its security criteria need to be different (!!), reset it //
|
boolean inAnAndFilter = securityFilterCursor.getBooleanOperator() == QQueryFilter.BooleanOperator.AND;
|
||||||
// this can be exposed in tests - maybe not entirely expected in real-world, but seems safe enough //
|
if(inAnAndFilter)
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
{
|
||||||
sourceQueryJoin.withSecurityCriteria(new ArrayList<>());
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// if we're in an AND filter, then we don't need a criteria for this lock, so return. //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
boolean inAnAndFilter = securityFilterCursor.getBooleanOperator() == QQueryFilter.BooleanOperator.AND;
|
|
||||||
if(inAnAndFilter)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +566,7 @@ public class JoinsContext
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List<Serializable> securityKeyValues = session.getSecurityKeyValues(recordSecurityLock.getSecurityKeyType(), type);
|
List<Serializable> securityKeyValues = QContext.getQSession().getSecurityKeyValues(recordSecurityLock.getSecurityKeyType(), type);
|
||||||
if(CollectionUtils.nullSafeIsEmpty(securityKeyValues))
|
if(CollectionUtils.nullSafeIsEmpty(securityKeyValues))
|
||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -212,7 +212,7 @@ public class RDBMSCountActionTest extends RDBMSActionTest
|
|||||||
CountInput countInput = new CountInput();
|
CountInput countInput = new CountInput();
|
||||||
countInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
|
countInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
|
||||||
|
|
||||||
assertThat(new CountAction().execute(countInput).getCount()).isEqualTo(1);
|
assertThat(new CountAction().execute(countInput).getCount()).isEqualTo(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -771,6 +771,61 @@ public class RDBMSQueryActionJoinsTest extends RDBMSActionTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Error seen in CTLive - query for a record in a sub-table, but whose security
|
||||||
|
** key comes from a main table, but the main-table record doesn't exist.
|
||||||
|
**
|
||||||
|
** In this QInstance, our warehouse table's security key comes from
|
||||||
|
** storeWarehouseInt.storeId - so if we insert a warehouse, but no stores, we
|
||||||
|
** might not be able to find it (if this bug exists!)
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testRequestedJoinWithTableWhoseSecurityFieldIsInMainTableAndNoRowIsInMainTable() throws Exception
|
||||||
|
{
|
||||||
|
runTestSql("INSERT INTO warehouse (name) VALUES ('Springfield')", null);
|
||||||
|
|
||||||
|
QueryInput queryInput = new QueryInput();
|
||||||
|
queryInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
|
||||||
|
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Springfield")));
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// with all access key, should find it //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||||
|
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(1);
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// with a regular key, should not find it //
|
||||||
|
////////////////////////////////////////////
|
||||||
|
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||||
|
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(0);
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// now assign the warehouse to a store //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
runTestSql("INSERT INTO warehouse_store_int (store_id, warehouse_id) SELECT 1, id FROM warehouse WHERE name='Springfield'", null);
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// with all access key, should find it //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
|
||||||
|
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(1);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// with a regular key, should find it if key matches //
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
|
||||||
|
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(1);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
// with a regular key, should not find it if key does not match //
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
|
||||||
|
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -888,7 +943,10 @@ public class RDBMSQueryActionJoinsTest extends RDBMSActionTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Note, this test was originally written asserting size=1... but reading
|
||||||
|
** the data, for an all-access key, that seems wrong - as the user should see
|
||||||
|
** all the records in this table, not just ones associated with a store...
|
||||||
|
** so, switching to 4 (same issue in CountActionTest too).
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
void testRecordSecurityWithLockFromJoinTableWhereTheKeyIsOnTheManySide() throws QException
|
void testRecordSecurityWithLockFromJoinTableWhereTheKeyIsOnTheManySide() throws QException
|
||||||
@ -897,8 +955,9 @@ public class RDBMSQueryActionJoinsTest extends RDBMSActionTest
|
|||||||
QueryInput queryInput = new QueryInput();
|
QueryInput queryInput = new QueryInput();
|
||||||
queryInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
|
queryInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
|
||||||
|
|
||||||
assertThat(new QueryAction().execute(queryInput).getRecords())
|
List<QRecord> records = new QueryAction().execute(queryInput).getRecords();
|
||||||
.hasSize(1);
|
assertThat(records)
|
||||||
|
.hasSize(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user