Review of the first fix found the checker's pass too broad: it let any
QSystemUserSession insert, update and delete every table nobody may write
directly, so server code running as the system user could edit or delete
operation log rows.
- ObjectStoragePermissionChecker no longer looks at the session class. A
write of those tables that reaches it is refused to every session, the
core system user included.
- Db.insert, Db.update and Db.delete mark their input with core's per-call
permission bypass (withPermissionBypass, Db.OWN_WRITE_REASON), so core
does not ask about the service's own writes when a host enforces
permissions inside the core actions.
- A failed operation log row is logged at ERROR, raises the
storage.operation_log.failed alert on the StorageAlertSink (resolved by
the next row written for the bucket) and is counted in
OperationLog.failedWriteCount(). A row the insert action hands back with
a record error is now detected too. The operation still succeeds.
Tests: the service's own log, index insert, update and delete under core
enforcement, with the acting person on the row; a system session outside
Db refused insert, update and delete of an operation row; update and
delete of an operation row refused to a person, the administrator and the
system user; the failure signal.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
In a host that sets QInstance.enforcePermissionsInCoreActions, core checks
table permissions inside InsertAction, UpdateAction and DeleteAction for
every outermost call. Db.asSystem starts a new context, so each write it
makes is outermost, and core asks ObjectStoragePermissionChecker about it.
The checker refused every write of a table that is not admin-editable
("This storage table changes only through storage actions.") to every
session, the system user the service itself writes as included.
Seen in a host application: every StorageBucket.put stored the object and
logged "Could not write a storage operation log row" with
QPermissionDeniedException, so object_storage_operation had no row for any
stored file. The same refusal applies to every other Db.asSystem write of
those tables.
The checker now lets a QSystemUserSession through for those tables. Such a
session is built by server code only; no authentication module gives one
to a request. A person is refused as before, the storage administrator
included (DbSystemSessionTest
.testServiceWritesItsOwnTablesWhenCoreActionsEnforcePermissions, which
failed before with no operation row).
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.