Component Authorization

The generic Component boundary controls which actions and fields are presented, but it does not establish that a selected record belongs to the current user, tenant, office, or role scope. Hashed identifiers are decoded into selectors; they are not ownership checks.

[!WARNING] Treat getPermission, row-level permission helpers, and the list_update session check as presentation or session-presence controls—not record authorization. In the inspected generic paths, reads, regular writes, batch upserts, inline updates, and image updates do not add a source-proven owner or tenant predicate. List filter values are also interpolated into SQL without an inspected validation, allowlist, or parameter-binding boundary.

List-to-submit handoff

The source-defined flow is implemented by imspr/novusoft/modules/Component/Controllers/Component.php, with persistence and permission lookups in imspr/novusoft/models/MainModel.php.

flowchart TD
    list["Component::list"]
    permission["MainModel::getPermission"]
    editorUrl["component/editor/{action}/{panelHash}/{recordHash}"]
    editor["Component::editor"]
    editorInclude["module-specific editor include"]
    submitUrl["component/submit/{action}/{hash}/{idhash}"]
    submit["Component::submit"]
    regular["MainModel::postProcess"]
    batch["MainModel::batch_update_insert"]

    list --> permission
    list -->|rendered action URL| editorUrl
    editorUrl --> editor
    editor --> editorInclude
    editor -->|constructs after include| submitUrl
    submitUrl --> submit
    submit -->|regular identifiers| regular
    submit -->|batch identifiers| batch
    editor -.->|selected IDs use a different key| submit

Component::list decodes the panel/table selector, applies the baseline deleted = 0 predicate, appends request and configuration filters, and obtains rows through a module-specific list-data include or MainModel’s getResults. It then hashes row primary keys for generated action URLs.

MainModel::getPermission derives action and field permissions. For non-admin roles, it looks up user_role_id and module_id in tbl_role_permissions; role 1 derives actions from the module’s default permission. For non-admin roles, restrictCondition and permissionOveride are applied while rendering each row’s actions and columns. Role 1 uses the base permission array directly and skips both helper calls. These operations change presentation data; they are not predicates on the list query.

Component::editor builds batch_ids from postData['data']['ids'], reads a selected record for actions other than add and batch, and includes the module-specific editor file. After that include, the controller adds the generated component/submit/{action}/{hash}/{idhash} target to the modal view data.

The selected-ID handoff is unresolved: the editor context reads postData['data']['ids'], while the batch branch of Component::submit parses postData['ids']. The inspected source does not prove a transfer or normalization between those keys.

Selectors and predicates

Component::list_update decodes the table hash, then rejects an empty session user_id with a JSON failure response and skips the later table lookups and update logic. Its update modes are distinct: when postData['type'] == 'single', it first calls updateAll without an inspected where predicate and then calls updateField for decoded row keys; in the other branch, it decodes row keys, accumulates update rows, and calls batch_update_insert. The table below keeps those branches to their selector and predicate evidence.

Boundary Source-defined selector or target Predicate or operation evidence
List read Decoded panel/table hash and request or configured filters Baseline deleted = 0; filter values are interpolated into SQL.
Editor read Decoded panel hash and record hash for non-add/non-batch actions getMainData uses the configured table, primary field, and primary value.
Regular submit Decoded edit or delete primary ID(s) postProcess updates by one primary key or a primary-key list; delete sets deleted = 1.
Batch submit postData['ids'], split and decoded when possible batch_update_insert receives rows keyed by the configured primary column and builds an upsert statement.
Single-mode inline update Decoded row values from postData['update'] updateField updates by the configured primary key.
Non-single inline update Row keys decoded from postData['update'] Accumulated rows are passed to batch_update_insert.
Image selection Decoded panel and record hashes plus the selected image updateImage uses the configured primary-key equality.
Image upload Decoded panel context and uploaded file updateImageData records the upload metadata.

For regular additions, postProcess adds session attribution fields such as added_by and latest_edit_by. For non-add actions, its update target remains the supplied primary key or primary-key list; those attribution fields describe the session that submitted the change, not the ownership of the target.

Feature-specific include boundaries

Component::editor constructs a module-specific editor-file path and includes it when the file exists. It supplies permission values, selected-record data, batch IDs, field metadata, and the generated submit target to the modal view.

This include is a feature-specific extension point for editor data and fields.

Module-specific submit includes can prepare feature data before the generic controller hands assembled data and identifiers to postProcess or batch_update_insert.

Updated