Port of #52 to the 0.1 release line. When a host enforces permissions inside core actions, the service's own system writes (the operation log row for every stored file among them) were refused by the QBit's own checker, so files were stored with no log row. The service now marks its own writes with core's per-call permission bypass; the checker refuses those tables to every session, the system user included. A failed log row is now an error, an alert and a count. Reviewed independently on #52; full build green on this branch.
Port of #52 to the 0.1 release line. When a host enforces permissions inside core actions, the service's own system writes (the operation log row for every stored file among them) were refused by the QBit's own checker, so files were stored with no log row. The service now marks its own writes with core's per-call permission bypass; the checker refuses those tables to every session, the system user included. A failed log row is now an error, an alert and a count. Reviewed independently on #52; full build green on this branch.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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>
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>
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.
Port of #52 to the 0.1 release line. When a host enforces permissions inside core actions, the service's own system writes (the operation log row for every stored file among them) were refused by the QBit's own checker, so files were stored with no log row. The service now marks its own writes with core's per-call permission bypass; the checker refuses those tables to every session, the system user included. A failed log row is now an error, an alert and a count. Reviewed independently on #52; full build green on this branch.
🤖 Generated with Claude Code
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>