task-l.3.1
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# Task L.3.1: Customer, Contact & Approval Visibility Hardening
|
||||
|
||||
## Summary
|
||||
|
||||
Task L.3.1 closes the next set of CRM visibility gaps after Task L.3 by moving customer, contact, and approval visibility closer to the resolved CRM access model already used by quotations and dashboard security.
|
||||
|
||||
## Implemented
|
||||
|
||||
- customer list/detail/update/delete routes now pass resolved CRM scope context
|
||||
- customer service now filters visibility by:
|
||||
- branch scope
|
||||
- ownership scope
|
||||
- related enquiry/quotation visibility
|
||||
- marketing monitor visibility through enquiry relationships
|
||||
- contact routes now inherit resolved customer visibility instead of organization-only reads
|
||||
- contact access additionally preserves creator visibility
|
||||
- approval list/detail now require either:
|
||||
- related quotation visibility
|
||||
- current approval actor visibility
|
||||
- admin / organization-wide visibility
|
||||
- dashboard approval analytics now reuse access-scoped approval listing
|
||||
- added `customer_scope_violation`, `contact_scope_violation`, and `approval_scope_violation` security audit actions
|
||||
- expanded static security verification coverage
|
||||
- documented team-scope and contact-sharing limitations
|
||||
|
||||
## Important limitation
|
||||
|
||||
The production schema still has no persisted contact-sharing model. That means L.3.1 hardens creator/customer/admin visibility, but it cannot preserve a live shared-contact workflow that does not yet exist in persistence.
|
||||
|
||||
## Verification
|
||||
|
||||
- `npx tsc --noEmit`
|
||||
- `node --experimental-strip-types scripts/security/verify-crm-access.ts`
|
||||
@@ -430,3 +430,13 @@ Future:
|
||||
|
||||
- define the canonical team relationship source
|
||||
- replace current coarse team handling with explicit team membership or manager hierarchy logic
|
||||
|
||||
### Contact-sharing persistence gap
|
||||
|
||||
Current:
|
||||
Task L.3.1 hardens customer/contact visibility, but the production schema still has no persisted contact-sharing relation. Legacy shared-contact behavior exists only in demo/mock material.
|
||||
|
||||
Future:
|
||||
|
||||
- add a first-class contact-sharing table and audit trail if the business still needs shared-contact workflows
|
||||
- extend security verification from creator/customer/admin visibility to explicit share-based visibility once persistence exists
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
|
||||
| Area | Route / Module | Status | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| Customers | `/api/crm/customers` | `Partially Protected` | Permission-gated, but service is still mostly organization-wide and not yet ownership-scoped |
|
||||
| Contacts | `/api/crm/customers/[id]/contacts` | `Partially Protected` | Bound to customer org, but no dedicated resolved ownership/contact-sharing policy yet |
|
||||
| Customers | `/api/crm/customers` | `Protected` | List/detail/update/delete now pass resolved CRM scope context and customer visibility is filtered by ownership plus related enquiry/quotation signals |
|
||||
| Contacts | `/api/crm/customers/[id]/contacts` | `Protected` | Contact APIs now inherit customer visibility and creator/admin/team visibility rules from resolved CRM access |
|
||||
| Leads / Enquiries | `/api/crm/enquiries` and child routes | `Partially Protected` | Uses branch/product/own-scope logic already, but still passes legacy `businessRole` fields through route/service seams |
|
||||
| Quotations | `/api/crm/quotations` and `[id]` | `Protected` | List/detail/create/update/delete now pass resolved CRM scope context and enforce branch/product/own visibility |
|
||||
| Quotation pricing surfaces | document data, preview, PDF preview/download, approved PDF | `Protected` | Pricing-bearing outputs now require pricing visibility and emit `crm_security_access` audit events when denied |
|
||||
| Document artifacts | `/api/crm/document-artifacts/[id]/download` | `Protected` | Approved quotation artifacts now require artifact permission plus pricing visibility guard |
|
||||
| Approvals | `/api/crm/approvals`, approve/reject/return | `Legacy Protected` | Permission-gated; step actor resolution now uses resolved CRM access union, but request listing is still organization-wide |
|
||||
| Approvals | `/api/crm/approvals`, approve/reject/return | `Protected` | Approval list/detail now require related quotation visibility or current-actor/admin visibility; step actor resolution uses resolved CRM access union |
|
||||
| Dashboard | `/api/crm/dashboard` | `Protected` | Route uses resolved context; revenue widgets are hidden unless quotation pricing is visible |
|
||||
| Dashboard export | `/api/crm/dashboard/export` | `Protected` | Revenue export explicitly blocked without pricing visibility |
|
||||
| CRM settings: roles | `/api/crm/settings/roles*` | `Partially Protected` | Permission-gated, but inventory still needs full pass for every mutation route |
|
||||
@@ -28,14 +28,12 @@
|
||||
|
||||
## High-Risk Findings
|
||||
|
||||
1. Customer/contact services still need full ownership-scope enforcement rather than organization-only reads.
|
||||
2. Approval request listing and detail visibility are still broader than the final resolved-ownership model.
|
||||
1. Team-scope semantics are still coarse because the current model does not yet carry a first-class subordinate/team graph.
|
||||
2. Production contact sharing is not yet backed by a persisted sharing table; current enforcement covers creator/customer/admin visibility only.
|
||||
3. Enquiry services already enforce meaningful scope, but several route files still pass legacy role-shaped context instead of a dedicated security context object.
|
||||
4. Team-scope semantics are still coarse because the current model does not yet carry a first-class subordinate/team graph.
|
||||
|
||||
## Follow-up Focus
|
||||
|
||||
- Finish customer/contact ownership enforcement.
|
||||
- Convert approval list/detail visibility from organization scope to resolved quotation visibility.
|
||||
- Normalize remaining enquiry routes onto the same security-context builder used by quotations and dashboard.
|
||||
- Add runtime scenario tests once seeded multi-role fixtures are available.
|
||||
- Add runtime seeded scenario tests once dedicated security fixtures are available.
|
||||
- Introduce a first-class team hierarchy and production contact-sharing model if the business requires them.
|
||||
|
||||
@@ -56,6 +56,34 @@ Approval step handling must use resolved CRM role unions instead of a single leg
|
||||
- `organization`: organization-wide inside active tenant
|
||||
- `monitor`: read-oriented role with limited commercial visibility
|
||||
|
||||
## Customer Boundary
|
||||
|
||||
Customer visibility is now enforced from resolved CRM access.
|
||||
|
||||
- `organization` and admin scopes can see organization customers within branch scope
|
||||
- `team` currently behaves as branch/product-scoped broad visibility until a first-class team graph exists
|
||||
- `own` can see customers they created or customers linked to enquiries/quotations they can already access
|
||||
- `monitor` can see customers only when those customers are related to visible lead/enquiry records
|
||||
|
||||
## Contact Boundary
|
||||
|
||||
Contact visibility is enforced through customer visibility plus contact ownership rules.
|
||||
|
||||
- if a user can access the parent customer, they can access its contact list within their role boundary
|
||||
- contact creators retain access to their own contacts
|
||||
- admin/organization scope retains access
|
||||
- production shared-contact persistence does not exist yet, so legacy demo sharing is not part of the live authorization boundary
|
||||
|
||||
## Approval Boundary
|
||||
|
||||
Approval visibility is now narrower than generic approval-read permission.
|
||||
|
||||
A user can see approval records only when at least one of these is true:
|
||||
|
||||
1. they can access the related quotation through resolved CRM scope
|
||||
2. they are the current approval actor by resolved CRM role
|
||||
3. they are admin / organization-wide CRM authority
|
||||
|
||||
## Artifact Boundary
|
||||
|
||||
Artifact permission alone is not enough for approved quotation PDFs.
|
||||
@@ -74,6 +102,8 @@ Dashboard access is split:
|
||||
- approval widgets: approval read permission
|
||||
- export visibility: same scope as dashboard, with extra revenue export restriction
|
||||
|
||||
Approval widgets additionally inherit approval-request visibility rules, not just a raw approval permission.
|
||||
|
||||
## Audit Events
|
||||
|
||||
Security-sensitive denials are recorded under entity type `crm_security_access` with actions:
|
||||
|
||||
41
docs/security/team-scope-limitations.md
Normal file
41
docs/security/team-scope-limitations.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Team Scope Limitations
|
||||
|
||||
## Current Team Scope Logic
|
||||
|
||||
Current CRM authorization resolves the most permissive ownership scope from active CRM role assignments.
|
||||
|
||||
For `team` scope today:
|
||||
|
||||
- quotation visibility is effectively broader than `own`, but still constrained by branch scope and product scope
|
||||
- customer visibility is approximated from branch scope plus related enquiry/quotation relationships
|
||||
- approval visibility inherits quotation visibility or current approval-actor visibility
|
||||
|
||||
## Known Limitations
|
||||
|
||||
There is not yet a first-class team hierarchy model in the domain.
|
||||
|
||||
That means the system does **not** currently know:
|
||||
|
||||
- who reports to whom
|
||||
- which sales users belong to the same explicit team
|
||||
- whether a manager should see one subset of sales users but not another inside the same branch
|
||||
|
||||
Because of that, `team` currently behaves as an approximate operational scope rather than a strict org-chart scope.
|
||||
|
||||
## Contact Sharing Limitation
|
||||
|
||||
The production schema currently has no persisted contact-sharing table.
|
||||
|
||||
Result:
|
||||
|
||||
- contact visibility is enforced through customer ownership plus contact creator visibility
|
||||
- mock/demo shared-contact behavior from legacy demo data is not part of the production boundary yet
|
||||
|
||||
## Future Team Hierarchy Model
|
||||
|
||||
Recommended future direction:
|
||||
|
||||
1. add a first-class manager/team relationship model
|
||||
2. resolve `team` scope from that relationship instead of permissive approximation
|
||||
3. add explicit contact-sharing persistence if the business still requires cross-owner contact access
|
||||
4. expand runtime security fixtures around manager-team boundaries after real hierarchy data exists
|
||||
Reference in New Issue
Block a user