Skip to content

crm_opportunity_line_item has no object-level CRUD grant in any app-crm permission set — the platform's own build lint already flags it #8164

Description

@os-zhuang

What was measured

Building examples/app-crm (pnpm --filter '@objectstack/example-crm...' build)
emits an author-time warning nothing has acted on:

⚠ object "crm_opportunity_line_item": detail object "crm_opportunity_line_item"
(master_detail "opportunity" → "crm_opportunity") has no object-level CRUD
grant in any permission set. A master-detail child derives its RECORD-level
access from the master (ADR-0055 controlled_by_parent), but object-level CRUD
is a SEPARATE gate that is never derived — role-bound non-admin users are
denied (403) before the parent-derived access is ever consulted (the silent
"can't submit the subtable" trap).
Grant "crm_opportunity_line_item" in at least one permission set that already
grants its master "crm_opportunity" — e.g. permissions[i].objects.crm_opportunity_line_item
= { allowRead: true, allowCreate: true, allowEdit: true }.
rule: security-master-detail-ungranted at objects[5].fields.opportunity

examples/app-crm/src/security/sales-positions.ts declares the app's only
non-guest permission set (crm_sales_user) with grants on 5 objects
(crm_account, crm_contact, crm_opportunity, crm_lead, crm_activity)
crm_opportunity_line_item is not among them, even though the object exists
specifically to be edited inline on the Opportunity form
(inlineEdit: 'grid', crm_opportunity_line_item.object.ts).

Confirmed live: objectstack verify --app examples/app-crm/objectstack.config.ts --rls (run against main after #8060 bound the app's three positions to
crm_sales_user) shows every position persona probe-blocked on exactly this
one object — LIST 403, object gate refuses before record scope is ever
consulted — while the other 5 granted objects resolve normally.

Why this is a real gap, not decoration

The build's own lint rule (security-master-detail-ungranted) names the
consequence precisely: any role-bound (non-admin) user — including one holding
sales_rep/sales_manager, the positions this app ships to demonstrate
selling — gets a silent 403 the moment they try to add/edit a product line on
an Opportunity, despite fully owning the parent record. The example ships
inlineEdit: 'grid' specifically to make line-item editing look like a normal
part of the Opportunity form; nothing in the UI would explain the resulting
403 to whoever hits it.

Suggested fix

Add crm_opportunity_line_item to crm_sales_user's objects map in
examples/app-crm/src/security/sales-positions.ts, e.g.
{ allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false }
— matching the grant shape already used for its parent crm_opportunity.

Not fixed here

Found while implementing #8060 (bind positions → crm_sales_user), which was
scoped to the missing sys_position_permission_set join only, per the
triage ruling — deliberately not the permission set's own grant contents.
Filed unassigned, unlabeled for triage.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions