@i4e/invest4edu-access-core 0.5.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i4e/invest4edu-access-core",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Shared access-control primitives for NeoFindesk: tenant keystone, role capabilities, reportee tree, feature flags, and the unified access engine (registry schema, snapshot resolver, visibleWhen).",
5
5
  "type": "module",
6
6
  "exports": {
@@ -173,9 +173,16 @@ export function resolveAccessSnapshot({
173
173
  : mergeGrants(grants, { mappedProducts, now });
174
174
 
175
175
  const byCode = new Map(features.map((f) => [f.feature_code, f]));
176
- const productScope = bypass
176
+ // Product scope comes from the IDENTITY, for admins too. The loader already resolves an
177
+ // unmapped identity (internal staff, platform admins) to every product code, so this is "all"
178
+ // for them without any special case. Deriving a bypass admin's scope from the products declared
179
+ // ON FEATURES — as this used to — yields [] whenever no feature declares one, which is the state
180
+ // of the registry today: a system admin resolved to NO products, and every feature then inherited
181
+ // `products: []`. Kept only as a fallback for the case where identity scope is genuinely empty.
182
+ const identityScope = [...new Set(mappedProducts.map(String))];
183
+ const productScope = bypass && !identityScope.length
177
184
  ? [...new Set(features.flatMap((f) => f.products || []))]
178
- : [...new Set(mappedProducts.map(String))];
185
+ : identityScope;
179
186
 
180
187
  const featureMap = {};
181
188
  const allowedScreens = [];
@@ -30,6 +30,10 @@ export const CAPABILITY_FLAGS = Object.freeze([
30
30
  "canCrossAccount",
31
31
  "productScoped",
32
32
  "bypassAllGates",
33
+ // Edit the GLOBAL feature registry (modules/screens/actions + their flags). Deliberately
34
+ // narrower than canGovernDelegation: that includes per-account BROKER_ADMINs, and the registry
35
+ // is cross-account — one edit changes what every tenant sees.
36
+ "canManageRegistry",
33
37
  ]);
34
38
 
35
39
  /**
@@ -42,8 +46,8 @@ export const CAPABILITY_FLAGS = Object.freeze([
42
46
  export const DEFAULT_ROLE_CAPABILITIES = Object.freeze({
43
47
  // bypassAllGates (D27) defaults to the two platform-admin roles. Inert until the access
44
48
  // engine reads it, and revocable from the `rolecapabilities` collection with no deploy.
45
- SUPER_ADMIN: { isFullAccess: true, canGovernDelegation: true, canCorrectE1: true, bypassAllGates: true },
46
- SYSTEM_ADMIN: { isFullAccess: true, canGovernDelegation: true, canCorrectE1: true, bypassAllGates: true },
49
+ SUPER_ADMIN: { isFullAccess: true, canGovernDelegation: true, canCorrectE1: true, bypassAllGates: true, canManageRegistry: true },
50
+ SYSTEM_ADMIN: { isFullAccess: true, canGovernDelegation: true, canCorrectE1: true, bypassAllGates: true, canManageRegistry: true },
47
51
  BROKER_ADMIN: { isFullAccess: true, canGovernDelegation: true, canCorrectE1: true },
48
52
  BACKOFFICE_USER: { isFullAccess: true, canCorrectE1: true },
49
53
  HR_USER: { isFullAccess: true },