## Summary - introduce a shared tenant-owned query and record-resolution canon for first-slice Filament resources - harden direct views, row actions, bulk actions, relation managers, and workspace-admin canonical viewers against wrong-tenant access - add registry-backed rollout metadata, search posture handling, architectural guards, and focused Pest coverage for scope parity and 404/403 semantics ## Included - Spec 150 package under `specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/` - shared support classes: `TenantOwnedModelFamilies`, `TenantOwnedQueryScope`, `TenantOwnedRecordResolver` - shared Filament concern: `InteractsWithTenantOwnedRecords` - resource/page/policy hardening across findings, policies, policy versions, backup schedules, backup sets, restore runs, inventory items, and Entra groups - additional regression coverage for canonical tenant state, wrong-tenant record resolution, relation-manager congruence, and action-surface guardrails ## Validation - `vendor/bin/sail artisan test --compact` passed - full suite result: `2733 passed, 8 skipped` - formatting applied with `vendor/bin/sail bin pint --dirty --format agent` ## Notes - Livewire v4.0+ compliant via existing Filament v5 stack - provider registration remains in `bootstrap/providers.php` - globally searchable first-slice posture: Entra groups scoped; policies and policy versions explicitly disabled - destructive actions continue to use confirmation and policy authorization - no new Filament assets added; existing deployment flow remains unchanged, including `php artisan filament:assets` when registered assets are used Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #180
140 lines
4.6 KiB
YAML
140 lines
4.6 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Tenant-Owned Query Canon Contract
|
|
version: 0.1.0
|
|
summary: Internal behavioral contract for tenant-owned list, detail, search, and action paths.
|
|
servers:
|
|
- url: http://localhost
|
|
paths:
|
|
/admin/t/{tenant}/{resource}:
|
|
get:
|
|
summary: List tenant-owned records within the route tenant scope
|
|
operationId: listTenantOwnedRecords
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- $ref: '#/components/parameters/Resource'
|
|
responses:
|
|
'200':
|
|
description: Returns only records owned by the entitled route tenant.
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/admin/t/{tenant}/{resource}/{record}:
|
|
get:
|
|
summary: View a tenant-owned record using the same scope rule as the list
|
|
operationId: viewTenantOwnedRecord
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- $ref: '#/components/parameters/Resource'
|
|
- $ref: '#/components/parameters/Record'
|
|
responses:
|
|
'200':
|
|
description: The record belongs to the entitled route tenant and is viewable.
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/admin/t/{tenant}/{resource}/{record}/actions/{action}:
|
|
post:
|
|
summary: Execute a protected row action against a tenant-owned record
|
|
operationId: actOnTenantOwnedRecord
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- $ref: '#/components/parameters/Resource'
|
|
- $ref: '#/components/parameters/Record'
|
|
- $ref: '#/components/parameters/Action'
|
|
responses:
|
|
'204':
|
|
description: The action executed against an in-scope record.
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/admin/t/{tenant}/{resource}/bulk-actions/{action}:
|
|
post:
|
|
summary: Execute a protected bulk action against tenant-owned records
|
|
operationId: bulkActOnTenantOwnedRecords
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- $ref: '#/components/parameters/Resource'
|
|
- $ref: '#/components/parameters/Action'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [record_ids]
|
|
properties:
|
|
record_ids:
|
|
type: array
|
|
items:
|
|
type: string
|
|
responses:
|
|
'204':
|
|
description: All submitted record IDs belong to the entitled tenant scope and the action executed.
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/admin/{resource}/{record}:
|
|
get:
|
|
summary: View a tenant-owned record from a workspace-admin canonical viewer
|
|
operationId: viewTenantOwnedRecordFromCanonicalViewer
|
|
parameters:
|
|
- $ref: '#/components/parameters/Resource'
|
|
- $ref: '#/components/parameters/Record'
|
|
responses:
|
|
'200':
|
|
description: The record is tenant-owned, and explicit record-owner entitlement succeeded.
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/admin/search:
|
|
get:
|
|
summary: Search tenant-owned resources only when safe search parity is enabled
|
|
operationId: searchTenantOwnedRecords
|
|
parameters:
|
|
- name: q
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: resource
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Search results are limited to tenant-owned families whose search posture is scoped.
|
|
'404':
|
|
description: Used only when the search destination would reveal an inaccessible record.
|
|
components:
|
|
parameters:
|
|
Tenant:
|
|
name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
description: Route tenant external identifier for tenant-bound surfaces.
|
|
Resource:
|
|
name: resource
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
Record:
|
|
name: record
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
Action:
|
|
name: action
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
NotFound:
|
|
description: The actor is not entitled to the workspace or tenant scope, or the target record does not belong to the resolved tenant scope.
|
|
Forbidden:
|
|
description: The actor is entitled to the tenant scope, but lacks the required capability for the protected action. |