@happyvertical/smrt-users 0.51.15 → 0.51.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +6 -1
- package/README.md +66 -3
- package/dist/chunks/{TerminalAuthService-BynY2qhT.js → TerminalAuthService-BBxkYCOG.js} +353 -120
- package/dist/chunks/TerminalAuthService-BBxkYCOG.js.map +1 -0
- package/dist/chunks/types-DFqeph0R.js +39 -0
- package/dist/chunks/types-DFqeph0R.js.map +1 -0
- package/dist/collections/TenantCollection.d.ts +19 -19
- package/dist/collections/TenantCollection.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -5
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +15 -15
- package/dist/migrations/materializeTenantHierarchy.d.ts +52 -0
- package/dist/migrations/materializeTenantHierarchy.d.ts.map +1 -0
- package/dist/models/Tenant.d.ts +40 -8
- package/dist/models/Tenant.d.ts.map +1 -1
- package/dist/models/tenant-hierarchy.d.ts +105 -0
- package/dist/models/tenant-hierarchy.d.ts.map +1 -0
- package/dist/services/PermissionResolver.d.ts +22 -0
- package/dist/services/PermissionResolver.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +24 -17
- package/dist/sveltekit.js +1 -1
- package/package.json +10 -10
- package/dist/chunks/TerminalAuthService-BynY2qhT.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -21,7 +21,12 @@ are not prerequisites for unrelated user-package work.
|
|
|
21
21
|
`emailKey`. `profileId` is a unique cross-package reference: at most one User
|
|
22
22
|
owns each non-null Profile.
|
|
23
23
|
- Tenant uses STI and a materialized hierarchy, maximum depth 10.
|
|
24
|
-
`
|
|
24
|
+
`hierarchyPath`/`hierarchyLevel` are derived from `parentTenantId` by
|
|
25
|
+
`Tenant.save()` on every save (any caller-supplied value is recomputed) and
|
|
26
|
+
descendants are re-materialized when they change; never hand-maintain them.
|
|
27
|
+
Legacy rows are backfilled with `smrt db:materialize-tenant-hierarchy`
|
|
28
|
+
(`materializeTenantHierarchy()`). Tests that need a corrupt path must write it
|
|
29
|
+
with raw SQL (#3036).
|
|
25
30
|
- Role with `tenantId = null` is available to all tenants; `isSystem` prevents
|
|
26
31
|
deletion. `inheritsToDescendants` is opt-in. Seed owner/admin/member/viewer
|
|
27
32
|
through `RoleCollection.seedSystemRoles()` at application initialization.
|
package/README.md
CHANGED
|
@@ -663,6 +663,62 @@ immediately invalidates every child at the next guard check.
|
|
|
663
663
|
|
|
664
664
|
Tenants support parent-child trees (max depth 10). Two flags control inheritance: `cascadePermissions` (parent pushes down) and `inheritPermissions` (child accepts). Both must be true for permissions to flow.
|
|
665
665
|
|
|
666
|
+
The materialized `hierarchyPath` / `hierarchyLevel` are **derived by the
|
|
667
|
+
framework**: `Tenant.save()` recomputes them from the real `parentTenantId`
|
|
668
|
+
chain on every save — through the collection helpers, a plain
|
|
669
|
+
`tenant.parentTenantId = x; await tenant.save()`, or an STI subclass — and
|
|
670
|
+
re-materializes every descendant when they change. A missing parent, a cycle,
|
|
671
|
+
or a move that would push any descendant past the depth limit throws
|
|
672
|
+
`TenantHierarchyError` before anything is written. Never set them by hand.
|
|
673
|
+
Because a parent link carries authority down the tree, a new or changed
|
|
674
|
+
`parentTenantId` must also be visible under the caller's own tenancy scope
|
|
675
|
+
(the save loads it through `TenantCollection`); run platform-level reparenting
|
|
676
|
+
in `withSystemContext()`.
|
|
677
|
+
|
|
678
|
+
Both hierarchy features — `inheritsToDescendants` and the ancestor-read policy
|
|
679
|
+
below — walk that path and fail closed without it. Rows written before the
|
|
680
|
+
framework maintained it (a correct `parent_tenant_id` but an empty path at level
|
|
681
|
+
0) must be backfilled once, after `db:migrate`:
|
|
682
|
+
|
|
683
|
+
```sh
|
|
684
|
+
smrt db:materialize-tenant-hierarchy --dry-run # report what would change
|
|
685
|
+
smrt db:materialize-tenant-hierarchy # apply (idempotent)
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
Or programmatically: `await materializeTenantHierarchy(db, { dryRun })`. It
|
|
689
|
+
writes only rows that differ (a second run is a no-op), runs in one
|
|
690
|
+
transaction, touches only the two derived columns, and refuses — writing
|
|
691
|
+
nothing — when any tenant's parent chain is broken, listing the offenders.
|
|
692
|
+
|
|
693
|
+
**Upgrading an existing fleet.** Two things change the moment the new package
|
|
694
|
+
is deployed, before any backfill:
|
|
695
|
+
|
|
696
|
+
1. **Run `--dry-run` first, as a health check.** A tenant whose *real*
|
|
697
|
+
`parent_tenant_id` chain is broken — a missing parent, a cycle, or deeper
|
|
698
|
+
than 10 (older releases accepted explicit `hierarchyLevel`/`hierarchyPath`
|
|
699
|
+
create inputs without checking) — now makes permission resolution for that
|
|
700
|
+
tenant and its whole subtree throw `TenantHierarchyError`
|
|
701
|
+
(`code`: `PARENT_NOT_FOUND`, `CIRCULAR_REFERENCE`, `MAX_DEPTH_EXCEEDED`)
|
|
702
|
+
instead of guessing. The dry run lists exactly these rows; repair their
|
|
703
|
+
`parent_tenant_id` by hand (the backfill refuses to write while any exist),
|
|
704
|
+
then apply.
|
|
705
|
+
2. **The tenant-override cascade follows the real parent chain.** Resolution
|
|
706
|
+
no longer trusts a stored path it cannot verify, so a never-materialized
|
|
707
|
+
child now receives its real ancestors' `TenantPermissionOverride` rows in
|
|
708
|
+
both directions — DENYs *and* GRANTs (subject to `cascadePermissions` /
|
|
709
|
+
`inheritPermissions`). That is the intended cascade and what the backfill
|
|
710
|
+
produces anyway, but ancestor GRANT overrides that were previously inert on
|
|
711
|
+
such rows become live: audit them before rolling out. `inheritsToDescendants`
|
|
712
|
+
and the ancestor-read policy stay dormant on those rows until the backfill
|
|
713
|
+
runs.
|
|
714
|
+
|
|
715
|
+
Permission resolution works under the `@happyvertical/smrt-tenancy`
|
|
716
|
+
interceptor regardless of which users classes you register as tenant-scoped:
|
|
717
|
+
the resolver's own cross-tenant reads (ancestor tenant overrides, ancestor and
|
|
718
|
+
descendant memberships) run in the framework's system context, keyed by the
|
|
719
|
+
user and tenant being resolved. It returns permissions, never rows; your
|
|
720
|
+
application's reads stay filtered.
|
|
721
|
+
|
|
666
722
|
### Read-only ancestor visibility (opt-in, off by default)
|
|
667
723
|
|
|
668
724
|
Membership authority travels DOWN the hierarchy: a direct membership in the
|
|
@@ -748,8 +804,12 @@ pass `null` to force it off regardless of configuration.
|
|
|
748
804
|
|
|
749
805
|
#### Adoption note
|
|
750
806
|
|
|
751
|
-
This is additive and off by default — no migration is required, and no
|
|
752
|
-
deployment changes behavior until `permissions.ancestorRead` is
|
|
807
|
+
This is additive and off by default — no schema migration is required, and no
|
|
808
|
+
existing deployment changes behavior until `permissions.ancestorRead` is
|
|
809
|
+
declared. It does require materialized tenant paths: run
|
|
810
|
+
`smrt db:materialize-tenant-hierarchy` once if your tenants were created before
|
|
811
|
+
the framework maintained `hierarchyPath` (see
|
|
812
|
+
[Hierarchical tenants](#hierarchical-tenants)). When
|
|
753
813
|
adopting it, declare the **narrowest** role and collection lists that make the
|
|
754
814
|
ancestor-level list work, and keep `maxDepth` at the smallest value your
|
|
755
815
|
hierarchy needs. `roles` must name system-role slugs; a tenant-scoped role with
|
|
@@ -803,6 +863,8 @@ TenantService supports three modes: `flexible` (no auto-create), `personal` (aut
|
|
|
803
863
|
| `OidcLoginService` | Generic OIDC authorization-code login with PKCE for Kanidm, Dex, and other standards-compliant providers. |
|
|
804
864
|
| `backfillLegacyUserProfiles` | Transactionally create and link canonical global Person Profiles for legacy Users; never creates OIDC identities or infers ownership. |
|
|
805
865
|
| `backfillUserEmailKeys` | Idempotently populate durable normalized-email keys after migrating legacy Users; fails closed on duplicates. |
|
|
866
|
+
| `materializeTenantHierarchy` | Idempotently backfill `hierarchy_path`/`hierarchy_level` from `parent_tenant_id` (`{ dryRun }`); refuses a broken chain without writing. CLI: `smrt db:materialize-tenant-hierarchy`. |
|
|
867
|
+
| `planTenantHierarchy` | Pure planner behind the backfill: expected fields, changes, and broken-chain problems for a set of tenant rows. |
|
|
806
868
|
| `OidcProfileResolver` | Transaction-bound pre-provision hook for application identity reconciliation. |
|
|
807
869
|
| `OidcProfileOwnerAuthorizer` | Transaction-bound application authorization for binding a first identity to an existing canonical Profile and its sole approved User owner. |
|
|
808
870
|
| `NormalizedOidcClaims` | Frozen resolver claims with required normalized `email`. |
|
|
@@ -842,7 +904,8 @@ for authentication, deduplication, and status semantics.
|
|
|
842
904
|
| `ACCESS_REQUEST_CAPABILITIES`, `AccessRequestError` | Operator capability slugs; typed domain error (`error.code`) |
|
|
843
905
|
| `DEFAULT_ROLE_SLUGS`, `DEFAULT_ROLES`, `DEFAULT_TENANT_POLICY` | System role slugs, role configs, default tenant policy |
|
|
844
906
|
| `DEFAULT_SESSION_TTL`, `MAX_TENANT_HIERARCHY_DEPTH` | 604800 (7 days in seconds), 10 |
|
|
845
|
-
| `TenantHierarchyError` | Thrown
|
|
907
|
+
| `TenantHierarchyError` | Thrown on a missing parent, a cycle, or exceeding the hierarchy depth limit (`code`) |
|
|
908
|
+
| `TenantHierarchyMaterializationError` | Thrown by `materializeTenantHierarchy` when any tenant's parent chain is broken; lists `problems` |
|
|
846
909
|
|
|
847
910
|
## Dependencies
|
|
848
911
|
|