Compare commits

...

2 Commits

13 changed files with 349 additions and 55 deletions

View File

@ -84,7 +84,7 @@ public class MetaDataAction
}
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
tables.put(tableName, new QFrontendTableMetaData(metaDataInput, backendForTable, table, false, false));
tables.put(tableName, new QFrontendTableMetaData(backendForTable, table, false, false));
treeNodes.put(tableName, new AppTreeNode(table));
}
metaDataOutput.setTables(tables);

View File

@ -54,7 +54,7 @@ public class TableMetaDataAction
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
}
QBackendMetaData backendForTable = tableMetaDataInput.getInstance().getBackendForTable(table.getName());
tableMetaDataOutput.setTable(new QFrontendTableMetaData(tableMetaDataInput, backendForTable, table, true, true));
tableMetaDataOutput.setTable(new QFrontendTableMetaData(backendForTable, table, true, true));
// todo post-customization - can do whatever w/ the result if you want

View File

@ -65,7 +65,7 @@ public class PermissionsHelper
*******************************************************************************/
public static void checkTablePermissionThrowing(AbstractTableActionInput tableActionInput, TablePermissionSubType permissionSubType) throws QPermissionDeniedException
{
checkTablePermissionThrowing(tableActionInput, tableActionInput.getTableName(), permissionSubType);
checkTablePermissionThrowing(tableActionInput.getTableName(), permissionSubType);
}
@ -73,7 +73,7 @@ public class PermissionsHelper
/*******************************************************************************
**
*******************************************************************************/
private static void checkTablePermissionThrowing(AbstractActionInput actionInput, String tableName, TablePermissionSubType permissionSubType) throws QPermissionDeniedException
private static void checkTablePermissionThrowing(String tableName, TablePermissionSubType permissionSubType) throws QPermissionDeniedException
{
warnAboutPermissionSubTypeForTables(permissionSubType);
QTableMetaData table = QContext.getQInstance().getTable(tableName);
@ -99,11 +99,11 @@ public class PermissionsHelper
/*******************************************************************************
**
*******************************************************************************/
public static boolean hasTablePermission(AbstractActionInput actionInput, String tableName, TablePermissionSubType permissionSubType)
public static boolean hasTablePermission(String tableName, TablePermissionSubType permissionSubType)
{
try
{
checkTablePermissionThrowing(actionInput, tableName, permissionSubType);
checkTablePermissionThrowing(tableName, permissionSubType);
return (true);
}
catch(QPermissionDeniedException e)

View File

@ -495,15 +495,15 @@ public class RunProcessAction
String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if backend specifies that it uses variants, look for that data in the session and append to our basepull key //
// if process specifies that it uses variants, look for that data in the session and append to our basepull key //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(process.getSchedule() != null && process.getVariantBackend() != null)
if(process.getVariantBackend() != null)
{
QSession session = QContext.getQSession();
QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(process.getVariantBackend());
if(session.getBackendVariants() == null || !session.getBackendVariants().containsKey(backendMetaData.getVariantOptionsTableTypeValue()))
{
LOG.info("Could not find Backend Variant information for Backend '" + backendMetaData.getName() + "'");
LOG.warn("Could not find Backend Variant information for Backend '" + backendMetaData.getName() + "'");
}
else
{

View File

@ -914,7 +914,7 @@ public class QInstanceValidator
}
assertCondition(fieldSecurityLock.getDefaultBehavior() != null, prefix + "has a fieldSecurityLock that is missing a defaultBehavior");
assertCondition(CollectionUtils.nullSafeHasContents(fieldSecurityLock.getOverrideValues()), prefix + "has a fieldSecurityLock that is missing overrideValues");
assertCondition(CollectionUtils.nullSafeHasContents(fieldSecurityLock.getKeyValueBehaviors()), prefix + "has a fieldSecurityLock that is missing keyValueBehaviors");
}
for(FieldAdornment adornment : CollectionUtils.nonNullList(field.getAdornments()))

View File

@ -35,16 +35,17 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QSupplementalTableMetaData;
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;
@ -87,12 +88,14 @@ public class QFrontendTableMetaData
/*******************************************************************************
**
*******************************************************************************/
public QFrontendTableMetaData(AbstractActionInput actionInput, QBackendMetaData backendForTable, QTableMetaData tableMetaData, boolean includeFullMetaData, boolean includeJoins)
public QFrontendTableMetaData(QBackendMetaData backendForTable, QTableMetaData tableMetaData, boolean includeFullMetaData, boolean includeJoins)
{
this.name = tableMetaData.getName();
this.label = tableMetaData.getLabel();
this.isHidden = tableMetaData.getIsHidden();
QSession qSession = QContext.getQSession();
if(includeFullMetaData)
{
this.primaryKeyField = tableMetaData.getPrimaryKeyField();
@ -100,7 +103,21 @@ public class QFrontendTableMetaData
for(String fieldName : tableMetaData.getFields().keySet())
{
QFieldMetaData field = tableMetaData.getField(fieldName);
if(!field.getIsHidden())
////////////////////////////////////////////////////////
// apply field security lock behaviors, if applicable //
////////////////////////////////////////////////////////
boolean isDenied = false;
if(field.getFieldSecurityLock() != null)
{
FieldSecurityLock.Behavior behavior = field.getFieldSecurityLock().getBehaviorForSession(qSession);
if(FieldSecurityLock.Behavior.DENY.equals(behavior))
{
isDenied = true;
}
}
if(!field.getIsHidden() && !isDenied)
{
this.fields.put(fieldName, new QFrontendFieldMetaData(field));
}
@ -124,7 +141,7 @@ public class QFrontendTableMetaData
QTableMetaData joinTable = qInstance.getTable(exposedJoin.getJoinTable());
frontendExposedJoin.setLabel(exposedJoin.getLabel());
frontendExposedJoin.setIsMany(exposedJoin.getIsMany());
frontendExposedJoin.setJoinTable(new QFrontendTableMetaData(actionInput, backendForTable, joinTable, includeFullMetaData, false));
frontendExposedJoin.setJoinTable(new QFrontendTableMetaData(backendForTable, joinTable, includeFullMetaData, false));
for(String joinName : exposedJoin.getJoinPath())
{
frontendExposedJoin.addJoin(qInstance.getJoin(joinName));
@ -161,16 +178,16 @@ public class QFrontendTableMetaData
setCapabilities(backendForTable, tableMetaData);
readPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.READ);
insertPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.INSERT);
editPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.EDIT);
deletePermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.DELETE);
readPermission = PermissionsHelper.hasTablePermission(tableMetaData.getName(), TablePermissionSubType.READ);
insertPermission = PermissionsHelper.hasTablePermission(tableMetaData.getName(), TablePermissionSubType.INSERT);
editPermission = PermissionsHelper.hasTablePermission(tableMetaData.getName(), TablePermissionSubType.EDIT);
deletePermission = PermissionsHelper.hasTablePermission(tableMetaData.getName(), TablePermissionSubType.DELETE);
QBackendMetaData backend = actionInput.getInstance().getBackend(tableMetaData.getBackendName());
QBackendMetaData backend = QContext.getQInstance().getBackend(tableMetaData.getBackendName());
if(backend != null && backend.getUsesVariants())
{
usesVariants = true;
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
variantTableLabel = QContext.getQInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
}
}

View File

@ -23,17 +23,38 @@ package com.kingsrook.qqq.backend.core.model.metadata.security;
import java.io.Serializable;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/*******************************************************************************
** Define, for a field, a lock that controls if users can or cannot see the field.
**
** The lock has a defaultBehavior, which is how the field should be treated, well,
** by default.
** The lock also references a securityKeyType; whose values, when looked up in
** the lock's keyValueBehaviors map, change the default behavior.
**
** For example, consider a lock with a keyType of 'internalOrExternalUser' (with
** possible values of 'internal' and 'external'), a defaultBehavior of DENY,
** and a keyValueBehaviors map containing internal => ALLOW. If a session has
** no security key of the internalOrExternalUser type, or a key with the value of
** 'external', then the lock's behavior will be the default (DENY). However,
** a key value of 'internal' would trigger the behavior specified for that key
** (ALLOW).
*******************************************************************************/
public class FieldSecurityLock
{
private String securityKeyType;
private Behavior defaultBehavior = Behavior.DENY;
private List<Serializable> overrideValues;
private static final QLogger LOG = QLogger.getLogger(FieldSecurityLock.class);
private String securityKeyType;
private Behavior defaultBehavior = Behavior.DENY;
private Map<Serializable, Behavior> keyValueBehaviors;
@ -89,7 +110,6 @@ public class FieldSecurityLock
/*******************************************************************************
** Getter for defaultBehavior
*******************************************************************************/
@ -122,33 +142,82 @@ public class FieldSecurityLock
/*******************************************************************************
** Getter for overrideValues
** Getter for keyValueBehaviors
*******************************************************************************/
public List<Serializable> getOverrideValues()
public Map<Serializable, Behavior> getKeyValueBehaviors()
{
return (this.overrideValues);
return (this.keyValueBehaviors);
}
/*******************************************************************************
** Setter for overrideValues
** Setter for keyValueBehaviors
*******************************************************************************/
public void setOverrideValues(List<Serializable> overrideValues)
public void setKeyValueBehaviors(Map<Serializable, Behavior> keyValueBehaviors)
{
this.overrideValues = overrideValues;
this.keyValueBehaviors = keyValueBehaviors;
}
/*******************************************************************************
** Fluent setter for overrideValues
** Fluent setter for keyValueBehaviors
*******************************************************************************/
public FieldSecurityLock withOverrideValues(List<Serializable> overrideValues)
public FieldSecurityLock withKeyValueBehaviors(Map<Serializable, Behavior> keyValueBehaviors)
{
this.overrideValues = overrideValues;
this.keyValueBehaviors = keyValueBehaviors;
return (this);
}
/*******************************************************************************
** Fluent setter for a single keyValueBehavior
*******************************************************************************/
public FieldSecurityLock withKeyValueBehavior(Serializable keyValue, Behavior behavior)
{
if(this.keyValueBehaviors == null)
{
this.keyValueBehaviors = new HashMap<>();
}
this.keyValueBehaviors.put(keyValue, behavior);
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public Behavior getBehaviorForSession(QSession session)
{
if(session != null && session.getSecurityKeyValues(this.securityKeyType) != null)
{
QSecurityKeyType securityKeyType = QContext.getQInstance().getSecurityKeyType(this.securityKeyType);
for(Serializable securityKeyValue : session.getSecurityKeyValues(this.securityKeyType))
{
try
{
if(securityKeyType.getValueType() != null)
{
securityKeyValue = ValueUtils.getValueAsFieldType(securityKeyType.getValueType(), securityKeyValue);
}
if(keyValueBehaviors.containsKey(securityKeyValue))
{
return keyValueBehaviors.get(securityKeyValue);
}
}
catch(Exception e)
{
LOG.warn("Error getting field behavior", e);
}
}
}
return getDefaultBehavior();
}
}

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.security;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
/*******************************************************************************
@ -37,6 +38,8 @@ public class QSecurityKeyType implements TopLevelMetaDataInterface
private String nullValueBehaviorKeyName;
private String possibleValueSourceName;
private QFieldType valueType;
/*******************************************************************************
@ -151,6 +154,7 @@ public class QSecurityKeyType implements TopLevelMetaDataInterface
}
/*******************************************************************************
** Getter for nullValueBehaviorKeyName
*******************************************************************************/
@ -181,4 +185,34 @@ public class QSecurityKeyType implements TopLevelMetaDataInterface
}
/*******************************************************************************
** Getter for valueType
*******************************************************************************/
public QFieldType getValueType()
{
return (this.valueType);
}
/*******************************************************************************
** Setter for valueType
*******************************************************************************/
public void setValueType(QFieldType valueType)
{
this.valueType = valueType;
}
/*******************************************************************************
** Fluent setter for valueType
*******************************************************************************/
public QSecurityKeyType withValueType(QFieldType valueType)
{
this.valueType = valueType;
return (this);
}
}

View File

@ -152,10 +152,10 @@ class PermissionsHelperTest extends BaseTest
AbstractTableActionInput actionInput = new InsertInput().withTableName(TABLE_NAME);
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.READ));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.INSERT));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.EDIT));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.DELETE));
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.READ));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.INSERT));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.EDIT));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.DELETE));
PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.READ);
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.INSERT)).isInstanceOf(QPermissionDeniedException.class);
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.EDIT)).isInstanceOf(QPermissionDeniedException.class);
@ -169,10 +169,10 @@ class PermissionsHelperTest extends BaseTest
AbstractTableActionInput actionInput = new InsertInput().withTableName(TABLE_NAME);
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.READ));
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.INSERT));
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.EDIT));
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.DELETE));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.READ));
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.INSERT));
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.EDIT));
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.DELETE));
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.READ)).isInstanceOf(QPermissionDeniedException.class);
PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.INSERT);
PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.EDIT);
@ -244,10 +244,10 @@ class PermissionsHelperTest extends BaseTest
AbstractTableActionInput actionInput = new InsertInput().withTableName(TABLE_NAME);
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.READ));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.INSERT));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.EDIT));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.DELETE));
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.READ));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.INSERT));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.EDIT));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.DELETE));
PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.READ);
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.INSERT)).isInstanceOf(QPermissionDeniedException.class);
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.EDIT)).isInstanceOf(QPermissionDeniedException.class);
@ -261,10 +261,10 @@ class PermissionsHelperTest extends BaseTest
AbstractTableActionInput actionInput = new InsertInput().withTableName(TABLE_NAME);
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.READ));
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.INSERT));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.EDIT));
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, TablePermissionSubType.DELETE));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.READ));
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.INSERT));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.EDIT));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, TablePermissionSubType.DELETE));
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.READ)).isInstanceOf(QPermissionDeniedException.class);
PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.INSERT);
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, TablePermissionSubType.EDIT)).isInstanceOf(QPermissionDeniedException.class);
@ -581,7 +581,7 @@ class PermissionsHelperTest extends BaseTest
for(TablePermissionSubType permissionSubType : TablePermissionSubType.values())
{
assertTrue(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, permissionSubType), "Expected to have permission " + TABLE_NAME + ":" + permissionSubType);
assertTrue(PermissionsHelper.hasTablePermission(TABLE_NAME, permissionSubType), "Expected to have permission " + TABLE_NAME + ":" + permissionSubType);
PermissionsHelper.checkTablePermissionThrowing(actionInput, permissionSubType);
}
@ -600,7 +600,7 @@ class PermissionsHelperTest extends BaseTest
for(TablePermissionSubType permissionSubType : TablePermissionSubType.values())
{
assertFalse(PermissionsHelper.hasTablePermission(actionInput, TABLE_NAME, permissionSubType));
assertFalse(PermissionsHelper.hasTablePermission(TABLE_NAME, permissionSubType));
assertThatThrownBy(() -> PermissionsHelper.checkTablePermissionThrowing(actionInput, permissionSubType))
.isExactlyInstanceOf(QPermissionDeniedException.class);
}

View File

@ -1922,8 +1922,8 @@ public class QInstanceValidatorTest extends BaseTest
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setSecurityKeyType(" ")), "missing a securityKeyType");
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setSecurityKeyType("notAKeyType")), "unrecognized securityKeyType");
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setDefaultBehavior(null)), "missing a defaultBehavior");
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setOverrideValues(null)), "missing overrideValues");
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setOverrideValues(Collections.emptyList())), "missing overrideValues");
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setKeyValueBehaviors(null)), "missing keyValueBehaviors");
assertValidationFailureReasons((qInstance -> lockExtractor.apply(qInstance).setKeyValueBehaviors(Collections.emptyMap())), "missing keyValueBehaviors");
}

View File

@ -0,0 +1,89 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.frontend;
import java.util.function.Supplier;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
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.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for QFrontendTableMetaData
*******************************************************************************/
class QFrontendTableMetaDataTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testFieldLocks()
{
QContext.getQInstance().addSecurityKeyType(new QSecurityKeyType()
.withName("allowedToSeeFirstName")
.withValueType(QFieldType.BOOLEAN));
FieldSecurityLock fieldSecurityLock = new FieldSecurityLock()
.withSecurityKeyType("allowedToSeeFirstName")
.withDefaultBehavior(FieldSecurityLock.Behavior.DENY)
.withKeyValueBehavior(true, FieldSecurityLock.Behavior.ALLOW);
QTableMetaData table = QContext.getQInstance().getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
table.getField("firstName").withFieldSecurityLock(fieldSecurityLock);
Supplier<QFrontendTableMetaData> run = () -> new QFrontendTableMetaData(QContext.getQInstance().getBackendForTable(TestUtils.TABLE_NAME_PERSON_MEMORY), table, true, false);
//////////////////////////////////////////////////////////////
// default session (no key) should NOT get to see firstName //
//////////////////////////////////////////////////////////////
assertFalse(run.get().getFields().containsKey("firstName"));
/////////////////////////////////////
// with the key=true, then allowed //
/////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue("allowedToSeeFirstName", true));
assertTrue(run.get().getFields().containsKey("firstName"));
////////////////////////////////////////
// try a string version of the key... //
////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue("allowedToSeeFirstName", "true"));
assertTrue(run.get().getFields().containsKey("firstName"));
////////////////////////////
// try unrecognized value //
////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue("allowedToSeeFirstName", "nope"));
assertFalse(run.get().getFields().containsKey("firstName"));
}
}

View File

@ -0,0 +1,85 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.security;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for FieldSecurityLock
*******************************************************************************/
class FieldSecurityLockTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetBehaviorForSession()
{
QContext.getQInstance().addSecurityKeyType(new QSecurityKeyType()
.withName("foo")
.withValueType(QFieldType.STRING));
FieldSecurityLock fieldSecurityLock = new FieldSecurityLock()
.withSecurityKeyType("foo")
.withDefaultBehavior(FieldSecurityLock.Behavior.DENY)
.withKeyValueBehavior("bar", FieldSecurityLock.Behavior.ALLOW)
.withKeyValueBehavior("baz", FieldSecurityLock.Behavior.ALLOW)
.withKeyValueBehavior("boo", FieldSecurityLock.Behavior.DENY);
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
.getField("firstName").withFieldSecurityLock(fieldSecurityLock);
////////////////////////////
// no key value = default //
////////////////////////////
assertEquals(FieldSecurityLock.Behavior.DENY, fieldSecurityLock.getBehaviorForSession(new QSession()));
/////////////////////////////////////////////////
// values specified get the behavior specified //
/////////////////////////////////////////////////
assertEquals(FieldSecurityLock.Behavior.ALLOW, fieldSecurityLock.getBehaviorForSession(new QSession().withSecurityKeyValue("foo", "bar")));
assertEquals(FieldSecurityLock.Behavior.ALLOW, fieldSecurityLock.getBehaviorForSession(new QSession().withSecurityKeyValue("foo", "baz")));
assertEquals(FieldSecurityLock.Behavior.DENY, fieldSecurityLock.getBehaviorForSession(new QSession().withSecurityKeyValue("foo", "boo")));
//////////////////////////////////////////////
// unrecognized values get default behavior //
//////////////////////////////////////////////
assertEquals(FieldSecurityLock.Behavior.DENY, fieldSecurityLock.getBehaviorForSession(new QSession().withSecurityKeyValue("foo", "huh")));
/////////////////////////////////////////////////
// if multiple key values, the first one wins. //
/////////////////////////////////////////////////
assertEquals(FieldSecurityLock.Behavior.ALLOW, fieldSecurityLock.getBehaviorForSession(new QSession().withSecurityKeyValue("foo", "bar").withSecurityKeyValue("foo", "boo")));
assertEquals(FieldSecurityLock.Behavior.DENY, fieldSecurityLock.getBehaviorForSession(new QSession().withSecurityKeyValue("foo", "boo").withSecurityKeyValue("foo", "foo")));
}
}

View File

@ -660,7 +660,7 @@ public class TestUtils
.withField(new QFieldMetaData("total", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY).withFieldSecurityLock(new FieldSecurityLock()
.withSecurityKeyType(SECURITY_KEY_TYPE_INTERNAL_OR_EXTERNAL)
.withDefaultBehavior(FieldSecurityLock.Behavior.DENY)
.withOverrideValues(List.of("internal"))
.withKeyValueBehavior("internal", FieldSecurityLock.Behavior.ALLOW)
));
}