nucleus-core-ts 0.10.36 → 0.10.38
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/dist/.build-ok
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.10.
|
|
1
|
+
0.10.38
|
|
@@ -1,2 +1,24 @@
|
|
|
1
1
|
import type { AuthActions, AuthState } from './types';
|
|
2
|
+
export declare const GODMIN_ROLE_NAME = "godmin";
|
|
3
|
+
export declare function hasGodminRole(roles: {
|
|
4
|
+
name: string;
|
|
5
|
+
}[]): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The one place that decides whether a claim lookup is even reached.
|
|
8
|
+
*
|
|
9
|
+
* `allow` — the actor bypasses claims entirely (matches the backend).
|
|
10
|
+
* `deny` — the hydrated state failed its integrity check; answer nothing.
|
|
11
|
+
* `claims` — no bypass; the caller must consult the claim list.
|
|
12
|
+
*
|
|
13
|
+
* Order matters and is asserted in the tests: `is_god` short-circuits before
|
|
14
|
+
* integrity (it always has), while the godmin ROLE is honoured only after
|
|
15
|
+
* integrity passes, because `roles` is part of the integrity hash and a
|
|
16
|
+
* tampered roles array must not buy a bypass.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveClaimGate(actor: {
|
|
19
|
+
isGod?: boolean | null;
|
|
20
|
+
roles: {
|
|
21
|
+
name: string;
|
|
22
|
+
}[];
|
|
23
|
+
}, integrityOk: boolean): 'allow' | 'deny' | 'claims';
|
|
2
24
|
export declare const useAuthGuardStore: () => import("h-state").StoreType<AuthState, AuthActions>;
|
|
@@ -16,6 +16,36 @@ function computeIntegrityHash(roles, claims) {
|
|
|
16
16
|
}
|
|
17
17
|
return hash.toString(36);
|
|
18
18
|
}
|
|
19
|
+
// ─── Godmin ─────────────────────────────────────────────────────────────────
|
|
20
|
+
// The backend treats the `godmin` ROLE as a full bypass
|
|
21
|
+
// (Services/Authorization/Middleware: `isGodmin` -> `authorized: true`), and it
|
|
22
|
+
// does so for the role alone — `users.is_god` is a separate, additional flag.
|
|
23
|
+
// The claim helpers below must agree with that, or a user the API authorises is
|
|
24
|
+
// shown an empty panel: every menu entry and card is hidden by `hasClaim`, and
|
|
25
|
+
// only the accounts that happen to carry `is_god` can see anything. Measured on
|
|
26
|
+
// DijitalKt: the `godmin` role owns zero rows in `role_claims`, so the whole
|
|
27
|
+
// admin panel rendered empty for it.
|
|
28
|
+
export const GODMIN_ROLE_NAME = 'godmin';
|
|
29
|
+
export function hasGodminRole(roles) {
|
|
30
|
+
return roles.some((r)=>r.name === GODMIN_ROLE_NAME);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The one place that decides whether a claim lookup is even reached.
|
|
34
|
+
*
|
|
35
|
+
* `allow` — the actor bypasses claims entirely (matches the backend).
|
|
36
|
+
* `deny` — the hydrated state failed its integrity check; answer nothing.
|
|
37
|
+
* `claims` — no bypass; the caller must consult the claim list.
|
|
38
|
+
*
|
|
39
|
+
* Order matters and is asserted in the tests: `is_god` short-circuits before
|
|
40
|
+
* integrity (it always has), while the godmin ROLE is honoured only after
|
|
41
|
+
* integrity passes, because `roles` is part of the integrity hash and a
|
|
42
|
+
* tampered roles array must not buy a bypass.
|
|
43
|
+
*/ export function resolveClaimGate(actor, integrityOk) {
|
|
44
|
+
if (actor.isGod) return 'allow';
|
|
45
|
+
if (!integrityOk) return 'deny';
|
|
46
|
+
if (hasGodminRole(actor.roles)) return 'allow';
|
|
47
|
+
return 'claims';
|
|
48
|
+
}
|
|
19
49
|
// ─── Claim Matching ─────────────────────────────────────────────────────────
|
|
20
50
|
// Mirrors nucleus-core backend's claimMatches logic:
|
|
21
51
|
// "get.users" matches "get.users.firstName" (prefix/hierarchical match)
|
|
@@ -99,7 +129,7 @@ export const { useStore: useAuthGuardStore } = createStore({
|
|
|
99
129
|
},
|
|
100
130
|
isGodmin: (store)=>()=>{
|
|
101
131
|
if (store.user?.isGod) return true;
|
|
102
|
-
return store.roles
|
|
132
|
+
return hasGodminRole(store.roles);
|
|
103
133
|
},
|
|
104
134
|
hasRole: (store)=>(roleName)=>{
|
|
105
135
|
if (store.user?.isGod) return true;
|
|
@@ -114,23 +144,35 @@ export const { useStore: useAuthGuardStore } = createStore({
|
|
|
114
144
|
return roleNames.every((name)=>store.roles.some((r)=>r.name === name));
|
|
115
145
|
},
|
|
116
146
|
hasClaim: (store)=>(claimAction, scope)=>{
|
|
117
|
-
|
|
118
|
-
|
|
147
|
+
const gate = resolveClaimGate({
|
|
148
|
+
isGod: store.user?.isGod,
|
|
149
|
+
roles: store.roles
|
|
150
|
+
}, store.verifyIntegrity());
|
|
151
|
+
if (gate !== 'claims') return gate === 'allow';
|
|
119
152
|
return userHasClaim(store.claims, claimAction, scope);
|
|
120
153
|
},
|
|
121
154
|
hasAnyClaim: (store)=>(claimActions, scope)=>{
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
const gate = resolveClaimGate({
|
|
156
|
+
isGod: store.user?.isGod,
|
|
157
|
+
roles: store.roles
|
|
158
|
+
}, store.verifyIntegrity());
|
|
159
|
+
if (gate !== 'claims') return gate === 'allow';
|
|
124
160
|
return claimActions.some((action)=>userHasClaim(store.claims, action, scope));
|
|
125
161
|
},
|
|
126
162
|
hasAllClaims: (store)=>(claimActions, scope)=>{
|
|
127
|
-
|
|
128
|
-
|
|
163
|
+
const gate = resolveClaimGate({
|
|
164
|
+
isGod: store.user?.isGod,
|
|
165
|
+
roles: store.roles
|
|
166
|
+
}, store.verifyIntegrity());
|
|
167
|
+
if (gate !== 'claims') return gate === 'allow';
|
|
129
168
|
return claimActions.every((action)=>userHasClaim(store.claims, action, scope));
|
|
130
169
|
},
|
|
131
170
|
checkPermission: (store)=>(roles, claims, options)=>{
|
|
132
|
-
|
|
133
|
-
|
|
171
|
+
const gate = resolveClaimGate({
|
|
172
|
+
isGod: store.user?.isGod,
|
|
173
|
+
roles: store.roles
|
|
174
|
+
}, store.verifyIntegrity());
|
|
175
|
+
if (gate !== 'claims') return gate === 'allow';
|
|
134
176
|
const mode = options?.mode ?? 'any';
|
|
135
177
|
const scope = options?.scope;
|
|
136
178
|
const rolePass = roles.length === 0 ? false : roles.some((name)=>store.roles.some((r)=>r.name === name));
|