Class CorePermissionEnforcer
java.lang.Object
com.kingsrook.qqq.backend.core.actions.permissions.CorePermissionEnforcer
Permission enforcement inside the core actions (issues #1061 and #1062).
Historically, table/process/widget/report permissions were checked only by
the middlewares (Javalin, API, picocli, Slack) before they called a core
action - so any other entry point (MCP, an AI assistant, custom routes,
scripts) bypassed them, and FieldSecurityLock was not enforced at all.
The core actions now call this class. Whether a given call is enforced:
- input.getPermissionEnforcement() == SKIP (set only through
withPermissionBypass(reason)): never enforced.
- == ENFORCE: always enforced - even when nested inside another action, and
even if the instance setting is off.
- == DEFAULT: enforced only for an "outermost" call (one not running inside
another core action - see QContext.getAuthorizedActionScopeDepth) and only
if the instance setting enforcePermissionsInCoreActions is on.
Calls nested inside a core action (pre/post customizers, process steps,
audits, possible-value translation, widget renderers, async children of
those) are framework-internal and trusted - the outer action already applied
the check owed to its caller. This preserves QQQ's existing model where,
e.g., a process the user may run can read tables the user can't.
System user sessions (QSystemUserSession) pass every permission check and
field lock, so scheduled jobs, automations and ESB handlers that run as the
system user are unaffected; the framework's own schedulers, automation and
queue pollers also mark their process runs with withPermissionBypass.
Record security locks are not handled here - they are enforced (as before)
inside the backend modules and the DML actions, for every call.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic classAutoCloseable handle on an authorized action scope. -
Field Summary
Fields -
Method Summary
Modifier and TypeMethodDescriptionstatic voidFor write actions that are being enforced: check permissions (and field security locks) for the associated child records the write will insert, update or delete - including an update's omitted children and a delete's cascades.static booleancheckProcessPermission(AbstractActionInput input, String processName, Map<String, Serializable> processValues) If enforcing for this input, check the process permission (using the input's process values, for custom permission checkers that consider them).checkReadCriteria(AbstractTableActionInput input, List<QueryJoin> queryJoins, QQueryFilter filter, Collection<String> additionalFieldNames) For read-style actions that are being enforced: check READ on every joined table (explicit or implied by the filter), and that the filter / additional field names satisfy field access policy.static voidcheckRestoreWriteFlag(AbstractTableActionInput input, Set<ActionFlag> flags) Reject a write that carries a RestoreWriteFlag unless the session has the RestoreWriteFlag.REQUIRED_PERMISSION permission.static booleancheckTablePermissions(AbstractTableActionInput input, TablePermissionSubType... permissionSubTypes) If enforcing for this input, check the table permission(s) - throwing QPermissionDeniedException if denied.static booleanThe effective instance-level setting: QInstance.enforcePermissionsInCoreActions if set; else the system property / environment variable if set; else false.Open an authorized action scope, to be closed via try-with-resources.static <T,E extends Exception>
TrunTrusted(String reason, UnsafeSupplier<T, E> supplier) Run framework- or application-internal code as trusted: core actions called by the supplier (with PermissionEnforcement.DEFAULT) are not subject to the instance-level permission enforcement.static booleanshouldEnforce(AbstractActionInput input) Should permissions (and field security locks) be enforced for this input, at this point (e.g., before its action opens its authorized scope)?static booleanField policy opt-in does not opt an application into core table permissions.
-
Field Details
-
SYSTEM_PROPERTY_NAME
- See Also:
-
ENVIRONMENT_VARIABLE_NAME
- See Also:
-
-
Method Details
-
isInstanceEnforcementEnabled
public static boolean isInstanceEnforcementEnabled()The effective instance-level setting: QInstance.enforcePermissionsInCoreActions if set; else the system property / environment variable if set; else false. -
shouldEnforce
Should permissions (and field security locks) be enforced for this input, at this point (e.g., before its action opens its authorized scope)? -
shouldEnforceFieldAccess
Field policy opt-in does not opt an application into core table permissions. -
checkTablePermissions
public static boolean checkTablePermissions(AbstractTableActionInput input, TablePermissionSubType... permissionSubTypes) throws QException If enforcing for this input, check the table permission(s) - throwing QPermissionDeniedException if denied. Returns whether enforcement applied (so the caller can also apply field security locks and the like).- Throws:
QException
-
checkReadCriteria
public static FieldAccessHelper.PendingExactMatch checkReadCriteria(AbstractTableActionInput input, List<QueryJoin> queryJoins, QQueryFilter filter, Collection<String> additionalFieldNames) throws QException For read-style actions that are being enforced: check READ on every joined table (explicit or implied by the filter), and that the filter / additional field names satisfy field access policy. Validate literal-only restrictions before session expressions are expanded by the underlying action, preserving reusable filters.- Throws:
QException
-
checkRestoreWriteFlag
public static void checkRestoreWriteFlag(AbstractTableActionInput input, Set<ActionFlag> flags) throws QPermissionDeniedException Reject a write that carries a RestoreWriteFlag unless the session has the RestoreWriteFlag.REQUIRED_PERMISSION permission. Unlike the other checks in this class, this one is NOT conditional on the instance setting, on the nesting of the call, or on a permission bypass: the flag relaxes integrity behaviors (automations, customizers, audit, events), so it is an escalation that must always be earned. QSystemUserSession has all permissions, so framework-internal system work is unaffected.- Throws:
QPermissionDeniedException
-
checkProcessPermission
public static boolean checkProcessPermission(AbstractActionInput input, String processName, Map<String, Serializable> processValues) throws QExceptionIf enforcing for this input, check the process permission (using the input's process values, for custom permission checkers that consider them).- Throws:
QException
-
checkAssociatedWrites
For write actions that are being enforced: check permissions (and field security locks) for the associated child records the write will insert, update or delete - including an update's omitted children and a delete's cascades. Discovery queries run inside an authorized scope; the permission checks themselves are explicit.- Throws:
QException
-
openAuthorizedScope
Open an authorized action scope, to be closed via try-with-resources. Core actions do this after applying whatever enforcement is owed to their caller, so the actions they call internally are trusted. -
runTrusted
public static <T,E extends Exception> T runTrusted(String reason, UnsafeSupplier<T, E> supplier) throws ERun framework- or application-internal code as trusted: core actions called by the supplier (with PermissionEnforcement.DEFAULT) are not subject to the instance-level permission enforcement. The reason is required, and logged at debug level - grep for runTrusted / withPermissionBypass to audit.- Throws:
E
-