PR Body Implements Spec 065 “Tenant RBAC v1” with capabilities-first RBAC, tenant membership scoping (Option 3), and consistent Filament action semantics. Key decisions / rules Tenancy Option 3: tenant switching is tenantless (ChooseTenant), tenant-scoped routes stay scoped, non-members get 404 (not 403). RBAC model: canonical capability registry + role→capability map + Gates for each capability (no role-string checks in UI logic). UX policy: for tenant members lacking permission → actions are visible but disabled + tooltip (avoid click→403). Security still enforced server-side. What’s included Capabilities foundation: Central capability registry (Capabilities::*) Role→capability mapping (RoleCapabilityMap) Gate registration + resolver/manager updates to support tenant-scoped authorization Filament enforcement hardening across the app: Tenant registration & tenant CRUD properly gated Backup/restore/policy flows aligned to “visible-but-disabled” where applicable Provider operations (health check / inventory sync / compliance snapshot) guarded and normalized Directory groups + inventory sync start surfaces normalized Policy version maintenance actions (archive/restore/prune/force delete) gated SpecKit artifacts for 065: spec.md, plan/tasks updates, checklists, enforcement hitlist Security guarantees Non-member → 404 via tenant scoping/membership guards. Member without capability → 403 on execution, even if UI is disabled. No destructive actions execute without proper authorization checks. Tests Adds/updates Pest coverage for: Tenant scoping & membership denial behavior Role matrix expectations (owner/manager/operator/readonly) Filament surface checks (visible/disabled actions, no side effects) Provider/Inventory/Groups run-start authorization Verified locally with targeted vendor/bin/sail artisan test --compact … Deployment / ops notes No new services required. Safe change: behavior is authorization + UI semantics; no breaking route changes intended. Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box> Reviewed-on: #79
9.5 KiB
9.5 KiB
TenantPilot Constitution
Core Principles
Inventory-first, Snapshots-second
- All modules MUST operate primarily on Inventory as “last observed” state.
- Inventory is the source of truth for what TenantPilot last observed; Microsoft Intune remains the external truth.
- Snapshots/Backups MUST be explicit actions (manual or scheduled) and MUST remain immutable.
Read/Write Separation by Default
- Analysis, reporting, and monitoring features MUST be read-only by default.
- Any write/change function (restore, remediation, promotion) MUST include preview/dry-run, explicit confirmation, audit logging, and tests.
- High-risk policy types default to
preview-onlyrestore unless explicitly enabled by a feature spec + tests.
Single Contract Path to Graph
- All Microsoft Graph calls MUST go through
GraphClientInterface. - Object types and endpoints MUST be modeled first in the contract registry (
config/graph_contracts.php). - Feature code MUST NOT hardcode “quick endpoints” or bypass contracts.
- Unknown/missing policy types MUST fail safe (preview-only / no Graph calls) rather than guessing endpoints.
Deterministic Capabilities
- Backup/restore/risk/support flags MUST be derived deterministically from config/contracts via a Capabilities Resolver.
- The resolver output MUST be programmatically testable (snapshot/golden tests) so config changes cannot silently break behavior.
Tenant Isolation is Non-negotiable
- Every read/write MUST be tenant-scoped.
- Cross-tenant views (MSP/Platform) MUST be explicit, access-checked, and aggregation-based (no ID-based shortcuts).
- Prefer least-privilege roles/scopes; surface warnings when higher privileges are selected.
- A non-member attempting to access a tenant route MUST be deny-as-not-found (404).
RBAC Standard (RBAC-001)
RBAC-001 Two Planes
- The platform MUST maintain two strictly separated authorization planes:
- Tenant plane (
/admin/t/{tenant}): authenticated Entra users (users), authorization is tenant-scoped. - Platform plane (
/system): authenticated platform users (platform_users), authorization is platform-scoped.
- Tenant plane (
- Cross-plane access MUST be deny-as-not-found (404) (not 403) to avoid route enumeration.
RBAC-002 Capabilities-first Authorization
- Feature code MUST NOT check raw roles directly (e.g. string role comparisons).
- Feature code MUST check capabilities via Gates/Policies only.
- A canonical capability registry MUST exist as the single source of truth (e.g.
TenantCapabilities/PlatformCapabilities). - Role → capability mapping MUST reference only registry entries.
RBAC-003 Least Privilege Role Semantics
- Tenant roles MUST follow least-privilege semantics:
- Readonly: view-only; MUST NOT start operations and MUST NOT mutate data.
- Operator: MAY start allowed tenant operations; MUST NOT manage credentials, settings, members, or perform destructive actions.
- Manager: MAY manage tenant configuration and start operations; MUST NOT manage tenant memberships (Owner-only).
- Owner: MAY manage memberships and all tenant configuration; Owner-only “danger zone” actions MUST remain Owner-only.
RBAC-004 UI is not Security
- Hiding UI elements is NOT sufficient.
- Every mutation endpoint and action MUST enforce authorization server-side (Policy/Gate).
RBAC-005 Destructive Actions Gate
- All destructive actions (delete / force delete / irreversible operations) MUST:
- require an explicit confirmation (e.g.,
requiresConfirmation()or equivalent), - be protected by a Policy/Gate,
- have at least one regression test asserting the action is forbidden for non-authorized roles.
- require an explicit confirmation (e.g.,
RBAC-006 Membership Safety Rule
- The system MUST prevent removing or demoting the last remaining Owner of a tenant.
RBAC-007 Tenant Isolation
- All tenant-plane queries MUST be tenant-scoped.
- A non-member attempting to access a tenant route MUST be deny-as-not-found (404).
RBAC-008 Auditing
- All access-control relevant changes MUST write
AuditLogentries with stable action IDs, including:- membership add / role change / remove
- provider credential rotation / connection disable
- break-glass enter / exit / expire (platform plane)
AuditLogentries MUST be redacted (no secrets/tokens, minimal identity fields).
RBAC-009 Testability Gate
- Any new feature that introduces or changes authorization MUST include:
- at least one positive test (authorized user can do it),
- at least one negative test (unauthorized user cannot do it),
- and MUST NOT introduce role-string checks outside the central mapping/registry.
Operations / Run Observability Standard
- Every long-running or operationally relevant action MUST be observable, deduplicated, and auditable via Monitoring → Operations.
- An action MUST create/reuse a canonical
OperationRunand execute asynchronously when any of the following applies:- It can take > 2 seconds under normal conditions.
- It performs remote/external calls (e.g., Microsoft Graph).
- It is queued or scheduled.
- It is operationally relevant for troubleshooting/audit (“what ran, who started it, did it succeed, what failed?”).
- Actions that are DB-only and typically complete in < 2 seconds MAY skip
OperationRun. - OPS-EX-AUTH-001 — Auth Handshake Exception:
- OIDC/SAML login handshakes MAY perform synchronous outbound HTTP (e.g., token exchange) without an
OperationRun. - Rationale: interactive, session-critical, and not a tenant-operational “background job”.
- Guardrail: outbound HTTP for auth handshakes is allowed only on
/auth/*endpoints and MUST NOT occur on Monitoring/Operations pages.
- OIDC/SAML login handshakes MAY perform synchronous outbound HTTP (e.g., token exchange) without an
- If an action is security-relevant or affects operational behavior (e.g., “Ignore policy”), it MUST write an
AuditLogentry including actor, tenant, action, target, before/after, and timestamp. - The
OperationRunrecord is the canonical source of truth for Monitoring (status, timestamps, counts, failures), even if implemented by multiple jobs/steps (“umbrella run”). - “Single-row” runs MUST still use consistent counters (e.g.,
total=1,processed=0|1) and outcome derived from success/failure. - Monitoring pages MUST be DB-only at render time (no external calls).
- Start surfaces MUST NOT perform remote work inline; they only: authorize, create/reuse run (dedupe), enqueue work, confirm + “View run”.
- Active-run dedupe MUST be enforced at DB level (partial unique index/constraint for active states).
- Failures MUST be stored as stable reason codes + sanitized messages; never persist secrets/tokens/PII/raw payload dumps in failures or notifications.
- Graph calls are allowed only via explicit user interaction and only when delegated auth is present; never as a render side-effect (restore group mapping is intentionally DB-only).
- Monitoring → Operations is reserved for
OperationRun-tracked operations. - Scheduled/queued operations MUST use locks + idempotency (no duplicates).
- Graph throttling and transient failures MUST be handled with backoff + jitter (e.g., 429/503).
Data Minimization & Safe Logging
- Inventory MUST store only metadata + whitelisted
meta_jsonb. - Payload-heavy content belongs in immutable snapshots/backup storage, not Inventory.
- Logs MUST not contain secrets/tokens; monitoring MUST rely on run records + error codes (not log parsing).
Badge Semantics Are Centralized (BADGE-001)
- Status-like badges (status/outcome/severity/risk/availability/boolean signals) MUST render via
BadgeCatalog/BadgeRenderer. - Filament resources/pages/widgets/views MUST NOT introduce ad-hoc status-like badge mappings (use a
BadgeDomaininstead). - Introducing or changing a status-like value MUST include updating the relevant badge mapper and adding/updating tests for the mapping.
- Tag/category chips (e.g., type/platform/environment) are not status-like and are not governed by BADGE-001.
Spec-First Workflow
- For any feature that changes runtime behavior, include or update
specs/<NNN>-<slug>/withspec.md,plan.md,tasks.md, andchecklists/requirements.md. - New work branches from
devusingfeat/<NNN>-<slug>(spec + code in the same PR).
Quality Gates
- Changes MUST be programmatically tested (Pest) and run via targeted
php artisan test .... - Run
./vendor/bin/sail bin pint --dirtybefore finalizing.
Governance
Scope & Compliance
- This constitution applies across the repo. Feature specs may add stricter constraints but not weaker ones.
- Restore semantics changes require: spec update, checklist update (if applicable), and tests proving safety.
Amendment Procedure
- Propose changes as a PR that updates
.specify/memory/constitution.md. - The PR MUST include a short rationale and list of impacted templates/specs.
- Amendments MUST update Last Amended date.
Versioning Policy (SemVer)
- PATCH: clarifications/typos/non-semantic refinements.
- MINOR: new principle/section or materially expanded guidance.
- MAJOR: removing/redefining principles in a backward-incompatible way.
Version: 1.5.0 | Ratified: 2026-01-03 | Last Amended: 2026-01-27