fix(security): hide the Assistant Admin app and tables from non-admins in meta-data
adminRules() set only the custom checker. The enricher then copied the host's default level (NOT_PROTECTED on kof22) into it, and PermissionsHelper's meta-data path returns ALLOW for NOT_PROTECTED without calling the checker, so every user saw the Assistant Admin app, its tables and the retention process in the nav (actions on them were still denied). The rules are now HAS_ACCESS_PERMISSION with deny behavior HIDDEN, plus the checker. Found while adopting the QBit in kof22 Website-Backend. Regression test runs on an enriched NOT_PROTECTED host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a1400d6f05
commit
5508f4619d
2 files changed
+50
-2
No files matched your search
+11
-2
@@ -36,6 +36,8 @@ 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.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.DenyBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
@@ -209,11 +211,18 @@ public class AssistantMetaDataBuilder
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** assistant.admin, through the custom checker.
|
||||
** assistant.admin, through the custom checker. The level and deny behavior
|
||||
** are explicit: left null, the enricher copies the host's default level
|
||||
** (often NOT_PROTECTED), and the meta-data path (app tree, table list) then
|
||||
** allows the object without ever calling the checker, so every user would
|
||||
** see the Assistant Admin app and its tables in the nav.
|
||||
*******************************************************************************/
|
||||
public static QPermissionRules adminRules()
|
||||
{
|
||||
return (new QPermissionRules().withCustomPermissionChecker(new QCodeReference(AssistantAdminPermissionChecker.class)));
|
||||
return (new QPermissionRules()
|
||||
.withLevel(PermissionLevel.HAS_ACCESS_PERMISSION)
|
||||
.withDenyBehavior(DenyBehavior.HIDDEN)
|
||||
.withCustomPermissionChecker(new QCodeReference(AssistantAdminPermissionChecker.class)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+39
@@ -26,12 +26,19 @@ import com.kingsrook.qbits.assistant.model.AssistantPolicy;
|
||||
import com.kingsrook.qbits.assistant.model.AssistantUsageDaily;
|
||||
import com.kingsrook.qbits.assistant.testing.AssistantTestFixture;
|
||||
import com.kingsrook.qbits.assistant.testing.BaseAssistantTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
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.permissions.DenyBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
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;
|
||||
@@ -110,6 +117,38 @@ class AssistantQBitProducerTest extends BaseAssistantTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** On a host whose default permission level is NOT_PROTECTED (enriched),
|
||||
** the meta-data path hides the admin app, tables and retention process from
|
||||
** a user without assistant.admin, and shows them to an admin.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAdminObjectsHiddenInMetaDataOnUnprotectedHost() throws Exception
|
||||
{
|
||||
QInstance host = AssistantTestFixture.build(config);
|
||||
host.setDefaultPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED).withDenyBehavior(DenyBehavior.DISABLED));
|
||||
new QInstanceEnricher(host).enrich();
|
||||
|
||||
List<MetaDataWithPermissionRules> adminObjects = List.of(host.getApp(AssistantMetaDataBuilder.APP_NAME),
|
||||
host.getProcess(AssistantMetaDataBuilder.RETENTION_PROCESS_NAME), host.getTable(AssistantPolicy.TABLE_NAME), host.getTable(AssistantConversation.TABLE_NAME));
|
||||
|
||||
QContext.init(host, AssistantTestFixture.sessionFor(AssistantTestFixture.EDITOR));
|
||||
for(MetaDataWithPermissionRules object : adminObjects)
|
||||
{
|
||||
assertThat(PermissionsHelper.getPermissionCheckResult(new MetaDataInput(), object)).as(object.getName()).isEqualTo(PermissionCheckResult.DENY_HIDE);
|
||||
}
|
||||
QContext.clear();
|
||||
|
||||
QContext.init(host, AssistantTestFixture.sessionFor(AssistantTestFixture.ADMIN));
|
||||
for(MetaDataWithPermissionRules object : adminObjects)
|
||||
{
|
||||
assertThat(PermissionsHelper.getPermissionCheckResult(new MetaDataInput(), object)).as(object.getName()).isEqualTo(PermissionCheckResult.ALLOW);
|
||||
}
|
||||
QContext.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
||||
Reference in new issue
Block a user