@aiquants/authz-react-router 0.6.0 → 0.7.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/README.md +82 -18
- package/dist/index.d.mts +191 -60
- package/dist/index.d.ts +191 -60
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ This walkthrough takes a React Router v7 app from nothing to a permission-guarde
|
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
// app/services/authz/config.ts
|
|
36
|
-
import { defineAuthzSchema,
|
|
36
|
+
import { defineAuthzSchema, getEffectivePermissionsMany, listResourceKeys } from "@aiquants/authz-drizzle"
|
|
37
37
|
import { createAuthz } from "@aiquants/authz-react-router"
|
|
38
38
|
import { db } from "~/db.server" // your drizzle handle
|
|
39
39
|
import { Users } from "~/models/Users" // your user table (has numeric id)
|
|
@@ -45,8 +45,10 @@ const authz = createAuthz({
|
|
|
45
45
|
appKey: "myapp",
|
|
46
46
|
// request → your user id (or null → checked against "@anonymous" permissions)
|
|
47
47
|
resolveUserId: async (request) => (await getSessionUser(request))?.id ?? null,
|
|
48
|
-
// load + merge this user's grants for one
|
|
49
|
-
getEffectivePermissions: (args) =>
|
|
48
|
+
// load + merge this user's grants for MANY resources in one round-trip
|
|
49
|
+
getEffectivePermissions: (args) => getEffectivePermissionsMany(db, authzTables, args),
|
|
50
|
+
// optional: lets `getMyPermissions(request)` report on every registered resource
|
|
51
|
+
listResourceKeys: () => listResourceKeys(db, authzTables, "myapp"),
|
|
50
52
|
})
|
|
51
53
|
|
|
52
54
|
export const { requirePermission, getMyPermissions } = authz
|
|
@@ -54,25 +56,31 @@ export const { requirePermission, getMyPermissions } = authz
|
|
|
54
56
|
|
|
55
57
|
> If your DB pool can be cold at the first request (serverless / lazy connect), have `resolveUserId` and `getEffectivePermissions` await a connection-ready guard first — otherwise a cold-start DB error can surface as a misleading 403 instead of a 500.
|
|
56
58
|
|
|
57
|
-
**Step 2 — guard a loader/action and
|
|
59
|
+
**Step 2 — guard a loader/action, apply scope, and render the badge.** On allow you get `{ userId, scope, permission }` from a **single** evaluation, so you can authorize, narrow the data, *and* tell the user what they may do — without asking the database a second time.
|
|
58
60
|
|
|
59
61
|
```ts
|
|
60
62
|
// app/routes/report.tsx
|
|
61
|
-
import {
|
|
63
|
+
import { buildRowScopeWhere, maskColumns } from "@aiquants/authz-drizzle"
|
|
62
64
|
import { requirePermission } from "~/services/authz/config"
|
|
63
65
|
import { db } from "~/db.server"
|
|
64
66
|
import { Report } from "~/models/Report"
|
|
65
67
|
|
|
66
68
|
export async function loader({ request }: { request: Request }) {
|
|
67
|
-
const { scope } = await requirePermission(request, { resourceKey: "report", action: "read" })
|
|
69
|
+
const { scope, permission } = await requirePermission(request, { resourceKey: "report", action: "read" })
|
|
68
70
|
// map the scope's logical field names → real columns (per endpoint; the core layer doesn't know your table)
|
|
69
71
|
const columnMap = { dept: Report.dept, amount: Report.amount }
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
+
const where = buildRowScopeWhere(scope.rowScope, columnMap) // undefined = all rows; 1=0 = no rows
|
|
73
|
+
const rows = await (where ? db.select().from(Report).where(where) : db.select().from(Report))
|
|
74
|
+
return { rows: maskColumns(rows, scope.columnScope), permission } // deny disallowed columns before returning
|
|
72
75
|
}
|
|
73
76
|
// denied → AuthzDeniedError (throw a 403 Response in your error boundary, or pass options.failureRedirect)
|
|
74
77
|
```
|
|
75
78
|
|
|
79
|
+
> **Guarding a sub-resource endpoint? Use `requirePermission`, not a hand-rolled check.** Reading a boolean off a
|
|
80
|
+
> permission map and throwing your own 403 duplicates the decision — and a typo in the field name reads as `undefined`,
|
|
81
|
+
> i.e. a silent denial. `requirePermission` is the only thing that decides access; `getMyPermissions` exists to *display*
|
|
82
|
+
> permissions, not to enforce them.
|
|
83
|
+
|
|
76
84
|
**Step 3 — register the resource + grant.** Insert a `TMResource('myapp','report')` and a `TDRolePermission` for the role(s) that should see it. Either run SQL, or build a tiny admin screen with the CRUD helpers (see the host app's admin guide). Example grant: "role `viewer` may `read` report, rows where dept ∈ {A}, hiding the amount column":
|
|
77
85
|
|
|
78
86
|
```sql
|
|
@@ -85,20 +93,35 @@ FROM dbo_authz.TMRole r JOIN dbo_authz.TMResource res ON res.app_key='myapp' AND
|
|
|
85
93
|
WHERE r.role_key='viewer';
|
|
86
94
|
```
|
|
87
95
|
|
|
88
|
-
**Step 4 — show the user their
|
|
96
|
+
**Step 4 — show the user their permissions.** For the page's own resource, the badge data already came back from
|
|
97
|
+
`requirePermission` (Step 2). `getMyPermissions` is for the *other* resources a screen wants to describe:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// several named resources — one round-trip, and the returned keys are typed from the argument,
|
|
101
|
+
// so `myPermissions.reprot` is a compile error rather than an `undefined` at runtime
|
|
102
|
+
const myPermissions = await getMyPermissions(request, { resourceKeys: ["report", "report_comment"] })
|
|
103
|
+
|
|
104
|
+
// from a defineResources registry, keeping the keys literal-typed
|
|
105
|
+
const fromRegistry = await getMyPermissions(request, { resourceKeys: resourceKeysOf(REPORT_RESOURCES) })
|
|
106
|
+
|
|
107
|
+
// or every resource registered for the app (needs the `listResourceKeys` port)
|
|
108
|
+
const all = await getMyPermissions(request)
|
|
109
|
+
```
|
|
89
110
|
|
|
90
111
|
```tsx
|
|
91
|
-
// loader: const myPermissions = await getMyPermissions(request, { resourceKeys: ["report"] })
|
|
92
112
|
import { MyPermissionIndicator, MyPermissionStatus } from "@aiquants/authz-react-router"
|
|
93
113
|
|
|
94
114
|
// 1. Single indicator badge group
|
|
95
115
|
<MyPermissionIndicator permission={myPermissions.report} resourceName="Monthly Report" />
|
|
96
116
|
|
|
97
|
-
// 2. Full "
|
|
117
|
+
// 2. Full "your permissions" status bar component for any page header/toolbar
|
|
98
118
|
<MyPermissionStatus permission={myPermissions.report} resourceName="Monthly Report" />
|
|
99
|
-
// → "
|
|
119
|
+
// → "あなたの権限: [閲覧 ○][編集 △][削除 ×]" with the tooltip "対象リソース: Monthly Report (report)"
|
|
100
120
|
```
|
|
101
121
|
|
|
122
|
+
> ⚠️ Unlike the admin shell's `labels`, these two components ship **Japanese** defaults (`"あなたの権限"`, `"閲覧"` /
|
|
123
|
+
> `"編集"` / `"削除"`, `"部分制限"`, and the `対象リソース: …` tooltip). Pass `prefixLabel` and `labels` to localize them.
|
|
124
|
+
|
|
102
125
|
## Scope cheat-sheet (§3.2)
|
|
103
126
|
|
|
104
127
|
The JSON you put in `row_scope` / `column_scope` (full contract in `@aiquants/authz-core`):
|
|
@@ -114,19 +137,60 @@ Multiple roles **union** (wider wins): rows OR'd (any `NULL` ⇒ all rows); colu
|
|
|
114
137
|
|
|
115
138
|
## API
|
|
116
139
|
|
|
117
|
-
- `createAuthz({ appKey, resolveUserId, resolveGroupIds?, getEffectivePermissions, onDeny? })` → `{ requirePermission, getMyPermissions
|
|
140
|
+
- `createAuthz({ appKey, resolveUserId, resolveGroupIds?, getEffectivePermissions, listResourceKeys?, onDeny? })` → `{ requirePermission, getMyPermissions }`.
|
|
141
|
+
- `getEffectivePermissions({ userId, groupIds?, appKey, resourceKeys })` → `Record<resourceKey, EffectivePermission | null>`. **One call, many resources** — pair it with `getEffectivePermissionsMany` from `@aiquants/authz-drizzle` and N resources cost one round-trip. Keys the port omits are treated as "no permission" (fail-close).
|
|
142
|
+
- `listResourceKeys(request)` → every resource key registered for the app. Optional; configuring it is what makes the no-argument `getMyPermissions(request)` legal. Without it that call throws `AuthzUsageError` naming both remedies instead of guessing.
|
|
118
143
|
- `resolveGroupIds(request)` → the acting user's group ids. Forwarded automatically to `getEffectivePermissions` by both `requirePermission` and `getMyPermissions`, so group-granted roles count toward effective permissions. Returning `null`/`undefined` (or omitting the port) sends no `groupIds` at all.
|
|
119
144
|
⚠️ Without this port, roles assigned to groups in the admin UI have **no runtime effect** — the store writes succeed and the UI looks correct, so the failure is silent. Cover the wiring with a test.
|
|
120
|
-
- `requirePermission(request, {resourceKey, action}, options?)` → `{ userId, scope }` on allow
|
|
121
|
-
- `
|
|
122
|
-
-
|
|
123
|
-
- `
|
|
145
|
+
- `requirePermission(request, {resourceKey, action}, options?)` → `{ userId, scope, permission }` on allow: the scope for row WHERE / column mask, **and** the `PermissionView` derived from the very same evaluation (no second lookup, so the guard and the badge can never disagree). Denies throw `AuthzDeniedError` (403), or `throw redirect(options.failureRedirect)` when set. A malformed query (blank `resourceKey`, unknown `action`, or the derived `"write"`) throws `AuthzUsageError` before anything is loaded — a mistake surfaces as a 500 you can read, never as a mysterious 403.
|
|
146
|
+
- `getMyPermissions(request, {resourceKeys})` → `Record<K, PermissionView>` where `K` is the literal union of the keys you passed, so an unrequested key is a **compile error** rather than a runtime `undefined`. `getMyPermissions(request)` reports on every registered resource (requires `listResourceKeys`). Duplicate keys collapse; an empty list asks the database nothing.
|
|
147
|
+
⚠️ The no-argument form's result **keys are your app's resource inventory**. Every value is still evaluated fail-closed (an unauthenticated caller gets all-false views), but do not hand the whole map to an unauthenticated client if the resource names themselves are sensitive — name the resources you actually render instead.
|
|
148
|
+
- `AuthzUsageError` — thrown for API misuse (missing/ill-typed arguments, missing port). Deliberately distinct from `AuthzDeniedError`: misuse must not masquerade as a denial.
|
|
149
|
+
- `defineResources({ ... })` → Type-safe centralized resource registry helper for resource keys and display names. Keys stay **literal-typed**.
|
|
150
|
+
- `resourceKeysOf(registry)` → the registry's resource keys as a literal union array. Use it instead of `Object.keys(registry)`, which both widens to `string[]` (losing the key check on the permission map) and returns *property names* — wrong for entries that set an explicit `key`.
|
|
124
151
|
- `toPermissionView(resourceKey, perm)` → serializable view (loader → client).
|
|
125
|
-
- `usePermission(view)` → ○△× display states (`readState`/`writeState`/`deleteState`); pure, safe in render.
|
|
152
|
+
- `usePermission(view)` → the same `can*` booleans plus ○△× display states (`readState`/`writeState`/`deleteState`); pure, safe in render.
|
|
126
153
|
- `<MyPermissionIndicator permission={view} resourceName="Documents" expectedResourceKey="documents" />` → read / write(create∪update) / delete as ○△×. `read` is △ when row/column-restricted; `write` is ○ only when both create & update are held, △ when one. Hover shows resource name & key tooltip. Automatically flags mismatches if `permission.resourceKey !== expectedResourceKey`.
|
|
127
154
|
- `<MyPermissionStatus permission={view} resourceName="Documents" expectedResourceKey="documents" prefixLabel="Your Permissions" />` → Reusable status component combining prefix label and permission indicator badges for any application page.
|
|
128
155
|
- `<MyPermissionGroupStatus items={[{ resourceName: "Primary Resource", permission: primaryView }, { resourceName: "Sub Resource", permission: subView, level: 1 }]} variant="pills" | "popover" size="xs" labels={{ fullWrite: "Writable", readOnly: "Read-only" }} />` → Multi-resource permission widget for pages with multiple distinct resource scopes. Supports inline status tags (`variant="pills"`), single-button popover cards (`variant="popover"`), hierarchical tree indentation via `level` / `children`, and fully customizable status label mappings (`labels`).
|
|
129
156
|
|
|
157
|
+
## One vocabulary, one decision
|
|
158
|
+
|
|
159
|
+
Two rules make the whole surface predictable, and both exist because their absence produced real production bugs:
|
|
160
|
+
|
|
161
|
+
1. **Every permission boolean is `can*`, everywhere.** `PermissionView` and `usePermission()` expose the *same*
|
|
162
|
+
`canRead` / `canCreate` / `canUpdate` / `canDelete` / `canWrite` names. Previously the view said `read` while the hook
|
|
163
|
+
said `canRead`, so reading `view.canRead` yielded `undefined` — falsy, therefore a **silent denial** that looked like
|
|
164
|
+
an authorization bug. A verb-shaped field is also undestructurable (`const { delete } = view` is a syntax error).
|
|
165
|
+
2. **The guard decides once.** `requirePermission` returns the UI view built from the permission it just evaluated, so
|
|
166
|
+
there is no second query that could return a different answer. (The old `requireAndGetPermission` claimed to be
|
|
167
|
+
atomic but issued two independent lookups; it is gone — `requirePermission` always returns `permission`.)
|
|
168
|
+
|
|
169
|
+
### Migrating from 0.6.x
|
|
170
|
+
|
|
171
|
+
| Before | Now |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `permissionView.read` / `.create` / `.update` / `.delete` / `.write` | `.canRead` / `.canCreate` / `.canUpdate` / `.canDelete` / `.canWrite` |
|
|
174
|
+
| `requireAndGetPermission(request, query)` | `requirePermission(request, query)` — it already returns `permission` |
|
|
175
|
+
| `getEffectivePermissions: (args) => …(args.resourceKey)` (one resource) | `getEffectivePermissions: (args) => …(args.resourceKeys)` returning a record (many resources) |
|
|
176
|
+
| `createAuthzAdminApp({ …, getMyPermissions })` | port removed — the admin shell reads its badge off the guard |
|
|
177
|
+
| `createAuthzAdminApp({ requirePermission })` resolving to `{ userId }` | must now resolve to `{ userId, permission }` — pass `createAuthz`'s `requirePermission` directly, or add `permission` to a hand-rolled guard |
|
|
178
|
+
| `usePermission(view)` → `{ canRead, canWrite, canDelete, …States }` | adds `canCreate` / `canUpdate`, so it is a superset of the view's booleans |
|
|
179
|
+
| `expectedResourceKey?: string` on the indicator components | now `NoInfer<K>` — bound to the permission's own key, so a mismatch is a compile error (0.6.x code that deliberately passed a different key stops compiling) |
|
|
180
|
+
| `createAuthzAdminServer(...).loadMyPermissions` | removed — `layoutGuard` returns `{ userId, myPermissions }` from the guard it already runs |
|
|
181
|
+
| `getMyPermissions(request, { resourceKeys: someStringArray })` → total map | values are now `PermissionView \| undefined` whenever the keys are not literals (a runtime-built list cannot promise which keys exist). Pass literals or `resourceKeysOf(registry)` to keep the total map, or add the presence check |
|
|
182
|
+
| `getMyPermissions(request)` → `TypeError` | configure `listResourceKeys`, or pass `{ resourceKeys }` — otherwise a named `AuthzUsageError` |
|
|
183
|
+
|
|
184
|
+
**Requirements.** TypeScript ≥ 5.4 to consume the emitted `.d.ts` (the indicator props use the built-in `NoInfer`), and
|
|
185
|
+
an ES2022 runtime for `Object.hasOwn` (Node ≥ 16.9 — the package already declares `engines.node >= 18` — and any
|
|
186
|
+
browser from 2022 on).
|
|
187
|
+
|
|
188
|
+
> `AuthzUsageError` is a **programmer** error, so it travels like any other thrown error: React Router hands the
|
|
189
|
+
> boundary a sanitized error outside development, and the actionable message lands in your **server log**. Read it
|
|
190
|
+
> there — the browser will only show the generic 500 body.
|
|
191
|
+
|
|
192
|
+
`readPartial` and `actions` keep their names (they are not booleans about an action).
|
|
193
|
+
|
|
130
194
|
## Admin UI tabs
|
|
131
195
|
|
|
132
196
|
The splat-mounted admin app (`createAuthzAdminApp`) serves `roles`, `resources`, `role_permissions`, `user_roles`, plus:
|
package/dist/index.d.mts
CHANGED
|
@@ -2,10 +2,36 @@ import * as react_router from 'react-router';
|
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode, CSSProperties } from 'react';
|
|
4
4
|
import * as _aiquants_authz_core from '@aiquants/authz-core';
|
|
5
|
-
import { EffectivePermission, DenyInfo, AuthzAction,
|
|
5
|
+
import { EffectivePermission, DenyInfo, AuthzAction, MergedScope, AuthzGrant, AuthzUser, AuthzRole, AuthzResource, AuthzAssignment, AuthzGroupRoleAssignment, AuthzGroupSummary, AuthzAdminStore } from '@aiquants/authz-core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* React Router (v7) adapter: binds `appKey` + the DI ports and exposes the loader/action authorization API.
|
|
9
|
+
* React Router (v7) 向けアダプタ。`appKey` と DI ポートを束ね、loader/action 用の認可 API を公開するモジュール。
|
|
10
|
+
*
|
|
11
|
+
* 設計上の中心的な不変条件は **判定は 1 回だけ** ということ。ガード (`requirePermission`) と
|
|
12
|
+
* UI 表示用の権限ビューは同一の実効権限から導出されるため、「画面は開けるのに一覧では false」
|
|
13
|
+
* という乖離が構造的に起こり得ない。
|
|
14
|
+
*/
|
|
6
15
|
|
|
7
16
|
type Awaitable<T> = T | Promise<T>;
|
|
8
17
|
type AuthzUserId = string | number;
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when the API itself is called incorrectly (bad arguments, missing port) — a programmer error,
|
|
20
|
+
* never an authorization decision. It is deliberately NOT an `AuthzDeniedError`: a misuse must surface as a
|
|
21
|
+
* loud 500, not as a silent 403 that looks like a permission problem.
|
|
22
|
+
* API の使い方が誤っている場合に送出される例外。認可拒否 (403) とは明確に区別されるプログラマ向けエラー。
|
|
23
|
+
*/
|
|
24
|
+
declare class AuthzUsageError extends Error {
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
}
|
|
27
|
+
/** Arguments handed to the {@link AuthzConfig.getEffectivePermissions} port. 実効権限ポートへ渡す引数。 */
|
|
28
|
+
type EffectivePermissionsQuery = {
|
|
29
|
+
userId: AuthzUserId | null;
|
|
30
|
+
groupIds?: (string | number)[];
|
|
31
|
+
appKey: string;
|
|
32
|
+
/** Always a de-duplicated, non-empty list — the port may load them all in one round-trip. Read-only: the adapter reports on exactly these keys. */
|
|
33
|
+
resourceKeys: readonly string[];
|
|
34
|
+
};
|
|
9
35
|
type AuthzConfig = {
|
|
10
36
|
/** Stable app identifier (e.g. "quants"). Bound into every permission query. */
|
|
11
37
|
appKey: string;
|
|
@@ -13,13 +39,18 @@ type AuthzConfig = {
|
|
|
13
39
|
resolveUserId: (request: Request) => Awaitable<AuthzUserId | null | undefined>;
|
|
14
40
|
/** Optionally resolve acting user's group IDs from the request or session. */
|
|
15
41
|
resolveGroupIds?: (request: Request) => Awaitable<(string | number)[] | null | undefined>;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Load effective permissions for (user, app, resources) — **one call, many resources** (typically
|
|
44
|
+
* `getEffectivePermissionsMany` from `@aiquants/authz-drizzle`). A key with no grants may be omitted or
|
|
45
|
+
* mapped to `null`; both mean "no permission" (fail-close).
|
|
46
|
+
*/
|
|
47
|
+
getEffectivePermissions: (args: EffectivePermissionsQuery) => Awaitable<Record<string, EffectivePermission | null | undefined> | null | undefined>;
|
|
48
|
+
/**
|
|
49
|
+
* Optional: every resource key registered for this app (typically `listResourceKeys` from
|
|
50
|
+
* `@aiquants/authz-drizzle`). Configuring it is what makes `getMyPermissions(request)` — with no resource
|
|
51
|
+
* list — legal; without it that call throws {@link AuthzUsageError} instead of guessing.
|
|
52
|
+
*/
|
|
53
|
+
listResourceKeys?: (request: Request) => Awaitable<readonly string[] | null | undefined>;
|
|
23
54
|
/** Default deny handler. If omitted, denies throw AuthzDeniedError (403). */
|
|
24
55
|
onDeny?: (info: DenyInfo) => void;
|
|
25
56
|
};
|
|
@@ -27,41 +58,82 @@ type RequirePermissionOptions = {
|
|
|
27
58
|
/** When set, denials `throw redirect(failureRedirect)` instead of throwing 403. */
|
|
28
59
|
failureRedirect?: string;
|
|
29
60
|
};
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Serializable per-resource permission summary for UI (loader → component).
|
|
63
|
+
* UI 向けの直列化可能なリソース単位権限サマリ (loader からコンポーネントへ受け渡す形)。
|
|
64
|
+
*
|
|
65
|
+
* 真偽値は `can` 接頭辞で統一されている。`delete` のような素の動詞名は分割代入できず
|
|
66
|
+
* (`const { delete } = view` は構文エラー)、フックの戻り値との二重語彙も生むため採用しない。
|
|
67
|
+
*/
|
|
68
|
+
type PermissionView<K extends string = string> = {
|
|
69
|
+
resourceKey: K;
|
|
70
|
+
canRead: boolean;
|
|
71
|
+
canCreate: boolean;
|
|
72
|
+
canUpdate: boolean;
|
|
73
|
+
canDelete: boolean;
|
|
37
74
|
/** create ∪ update */
|
|
38
|
-
|
|
75
|
+
canWrite: boolean;
|
|
39
76
|
/** read granted but row and/or column restricted (→ △ partial). */
|
|
40
77
|
readPartial: boolean;
|
|
41
78
|
actions: AuthzAction[];
|
|
42
79
|
};
|
|
43
|
-
/**
|
|
44
|
-
|
|
80
|
+
/** Result of a successful {@link createAuthz} guard. ガード成功時の戻り値。 */
|
|
81
|
+
type RequirePermissionResult<K extends string = string> = {
|
|
82
|
+
userId: AuthzUserId | null;
|
|
83
|
+
/** Effective row/column scope for the guarded action — apply as WHERE / column mask. */
|
|
84
|
+
scope: MergedScope;
|
|
85
|
+
/** UI view derived from the **same** evaluation as the guard decision (no second lookup). */
|
|
86
|
+
permission: PermissionView<K>;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Permission views keyed by the exact resource keys that were requested.
|
|
90
|
+
* 要求したキーで型付けした権限ビュー群。
|
|
91
|
+
*
|
|
92
|
+
* リテラルのキーを渡した場合だけ「そのキーは必ず存在する」形になる。`string[]` のようにキーが実行時に
|
|
93
|
+
* しか分からない場合は `| undefined` を含む索引型へ落ち、存在チェックを強制する (総和型の索引を
|
|
94
|
+
* 名乗ると、要求していないキーが型の上では必ず存在することになり、元の不具合と同じ罠に戻る)。
|
|
95
|
+
*
|
|
96
|
+
* ⚠️ キーは配列の **要素型** から導かれる。`flag ? ["a"] : ["b"]` のように要素型が合併になる式では、
|
|
97
|
+
* 実行時に片方しか無くても型の上では両方が存在する扱いになる。リテラル配列を直接渡すか
|
|
98
|
+
* `resourceKeysOf(registry)` を使うこと。
|
|
99
|
+
* ⚠️ 戻り値を `Record<string, …>` 型の変数へ代入すると、その文脈から `K` が `string` へ広がり
|
|
100
|
+
* 索引型 (値は `| undefined`) に落ちる。リテラルキーの保証が要る箇所では代入先を広げないこと。
|
|
101
|
+
* ⚠️ 戻り値は素のオブジェクト。要求していない `toString` などのキーを読むと `Object.prototype` の
|
|
102
|
+
* メンバが返る (索引型の `| undefined` とは一致しない)。要求したキーだけを読むこと。
|
|
103
|
+
*/
|
|
104
|
+
type PermissionViewMap<K extends string> = string extends K ? Record<string, PermissionView | undefined> : {
|
|
105
|
+
[P in K]: PermissionView<P>;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Map an EffectivePermission to a serializable {@link PermissionView}.
|
|
109
|
+
* 実効権限を直列化可能な {@link PermissionView} へ変換する処理。
|
|
110
|
+
*
|
|
111
|
+
* @returns The UI-facing permission summary (all-false when `perm` is absent). UI 表示用の権限サマリ。
|
|
112
|
+
*/
|
|
113
|
+
declare function toPermissionView<K extends string>(resourceKey: K, perm: EffectivePermission | null | undefined): PermissionView<K>;
|
|
45
114
|
/**
|
|
46
115
|
* React Router adapter. Binds `appKey` and the DI deps, exposing:
|
|
47
|
-
* - `requirePermission(request, {resourceKey, action}, options?)` → `{ userId, scope }` on allow
|
|
48
|
-
* (apply row WHERE / column mask with the
|
|
49
|
-
* - `getMyPermissions(request, {resourceKeys})` → `Record<resourceKey, PermissionView>` for the UI
|
|
116
|
+
* - `requirePermission(request, {resourceKey, action}, options?)` → `{ userId, scope, permission }` on allow
|
|
117
|
+
* (apply row WHERE / column mask with `scope`; render `permission` in the UI); denies throw 403 or redirect.
|
|
118
|
+
* - `getMyPermissions(request, {resourceKeys})` → `Record<resourceKey, PermissionView>` for the UI,
|
|
119
|
+
* or `getMyPermissions(request)` for every registered resource when `listResourceKeys` is configured.
|
|
120
|
+
* `appKey` と DI ポートを束ね、ガードと自己権限取得の 2 面だけを公開するファクトリ。
|
|
121
|
+
*
|
|
122
|
+
* @returns The bound `requirePermission` / `getMyPermissions` pair. 結線済みの認可 API 一式。
|
|
50
123
|
*/
|
|
51
124
|
declare function createAuthz(config: AuthzConfig): {
|
|
52
|
-
requirePermission: (request: Request, query: {
|
|
53
|
-
resourceKey:
|
|
125
|
+
requirePermission: <K extends string>(request: Request, query: {
|
|
126
|
+
resourceKey: K;
|
|
54
127
|
action: AuthzAction;
|
|
55
|
-
}, options?: RequirePermissionOptions) => Promise<
|
|
56
|
-
getMyPermissions:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}>;
|
|
128
|
+
}, options?: RequirePermissionOptions) => Promise<RequirePermissionResult<K>>;
|
|
129
|
+
getMyPermissions: {
|
|
130
|
+
<K extends string>(request: Request, options: {
|
|
131
|
+
resourceKeys: readonly K[];
|
|
132
|
+
}): Promise<PermissionViewMap<K>>;
|
|
133
|
+
(request: Request, options?: {
|
|
134
|
+
resourceKeys?: readonly string[];
|
|
135
|
+
}): Promise<Record<string, PermissionView | undefined>>;
|
|
136
|
+
};
|
|
65
137
|
};
|
|
66
138
|
|
|
67
139
|
/** Authz admin UI strings (i18n). Defaults are English; override per-section via `createAuthzAdminRoutes(..., { labels })`. */
|
|
@@ -182,7 +254,7 @@ type WithLabels = {
|
|
|
182
254
|
labels: AuthzAdminLabels;
|
|
183
255
|
};
|
|
184
256
|
type AuthzLayoutData = WithLabels & {
|
|
185
|
-
myPermissions: Record<string, PermissionView>;
|
|
257
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
186
258
|
adminResourceKey: string;
|
|
187
259
|
basePath?: string;
|
|
188
260
|
};
|
|
@@ -286,18 +358,17 @@ type CreateAuthzAdminServerOptions = {
|
|
|
286
358
|
/** 未ログインをログインへ誘導する(例: throw redirect(...))。 */
|
|
287
359
|
requireUser: (request: Request) => Promise<unknown>;
|
|
288
360
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* userId
|
|
361
|
+
* Authorization guard supplied by the app (from `createAuthz`); allow → `{ userId, permission }`.
|
|
362
|
+
* 認可ガード (アプリの createAuthz 由来)。allow で { userId, permission } を返すポート。
|
|
363
|
+
* `userId` は nullable(未解決/万一の public 付与)であり、createAuthzAdminServer 内で deny する
|
|
292
364
|
* (管理経路は監査・自己権限計算に実 id が必須のため非 null を保証)。
|
|
365
|
+
* `permission` は **同一判定から導かれた** 自己権限ビューであり、表示用に再取得しない
|
|
366
|
+
* (再取得は「ガードは通るのにバッジは権限なし」という乖離の温床)。
|
|
293
367
|
*/
|
|
294
368
|
requirePermission: (request: Request, query: PermissionQuery) => Promise<{
|
|
295
369
|
userId: string | number | null;
|
|
370
|
+
permission: PermissionView;
|
|
296
371
|
}>;
|
|
297
|
-
/** 自己権限表示用(アプリの createAuthz 由来)。 */
|
|
298
|
-
getMyPermissions: (request: Request, args: {
|
|
299
|
-
resourceKeys: string[];
|
|
300
|
-
}) => Promise<Record<string, PermissionView>>;
|
|
301
372
|
/** 文言の部分上書き(既定は日本語)。 */
|
|
302
373
|
labels?: PartialAuthzAdminLabels;
|
|
303
374
|
};
|
|
@@ -307,9 +378,9 @@ declare function createAuthzAdminServer(opts: CreateAuthzAdminServerOptions): {
|
|
|
307
378
|
resourceKey: string;
|
|
308
379
|
layoutGuard: (request: Request) => Promise<{
|
|
309
380
|
userId: string | number;
|
|
381
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
310
382
|
}>;
|
|
311
|
-
|
|
312
|
-
/** /authz レイアウト: requireUser(302) → requirePermission(read, 403) → 自己権限取得。 */
|
|
383
|
+
/** /authz レイアウト: requireUser(302) → requirePermission(read, 403)。自己権限はガードの戻り値から得る。 */
|
|
313
384
|
layout: {
|
|
314
385
|
loader({ request }: Req): Promise<AuthzLayoutData>;
|
|
315
386
|
};
|
|
@@ -376,6 +447,12 @@ type CreateAuthzAdminAppOptions = CreateAuthzAdminServerOptions & {
|
|
|
376
447
|
/** マウントのベースパス(タブリンク/未指定セグメントのリダイレクト先)。既定 `/authz`。 */
|
|
377
448
|
basePath?: string;
|
|
378
449
|
};
|
|
450
|
+
/**
|
|
451
|
+
* Build the loader/action pair that serves the whole authorization admin UI from one splat route.
|
|
452
|
+
* 認可管理 UI 全体を 1 本のスプラットルートで提供する loader/action 対を生成するファクトリ。
|
|
453
|
+
*
|
|
454
|
+
* @returns The route `loader` / `action` plus the underlying server for direct use. ルートの loader/action と内部サーバ。
|
|
455
|
+
*/
|
|
379
456
|
declare function createAuthzAdminApp(opts: CreateAuthzAdminAppOptions): {
|
|
380
457
|
loader: (args: RouteArgs) => Promise<{
|
|
381
458
|
labels: AuthzAdminLabels;
|
|
@@ -383,14 +460,14 @@ declare function createAuthzAdminApp(opts: CreateAuthzAdminAppOptions): {
|
|
|
383
460
|
selfAdminRoleIds?: number[];
|
|
384
461
|
segment: string;
|
|
385
462
|
basePath: string;
|
|
386
|
-
myPermissions: Record<string, PermissionView>;
|
|
463
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
387
464
|
adminResourceKey: string;
|
|
388
465
|
} | {
|
|
389
466
|
labels: AuthzAdminLabels;
|
|
390
467
|
resources: _aiquants_authz_core.AuthzResource[];
|
|
391
468
|
segment: string;
|
|
392
469
|
basePath: string;
|
|
393
|
-
myPermissions: Record<string, PermissionView>;
|
|
470
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
394
471
|
adminResourceKey: string;
|
|
395
472
|
} | {
|
|
396
473
|
labels: AuthzAdminLabels;
|
|
@@ -406,7 +483,7 @@ declare function createAuthzAdminApp(opts: CreateAuthzAdminAppOptions): {
|
|
|
406
483
|
}>;
|
|
407
484
|
segment: string;
|
|
408
485
|
basePath: string;
|
|
409
|
-
myPermissions: Record<string, PermissionView>;
|
|
486
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
410
487
|
adminResourceKey: string;
|
|
411
488
|
} | {
|
|
412
489
|
labels: AuthzAdminLabels;
|
|
@@ -420,7 +497,7 @@ declare function createAuthzAdminApp(opts: CreateAuthzAdminAppOptions): {
|
|
|
420
497
|
selfAdminRoleIds?: number[];
|
|
421
498
|
segment: string;
|
|
422
499
|
basePath: string;
|
|
423
|
-
myPermissions: Record<string, PermissionView>;
|
|
500
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
424
501
|
adminResourceKey: string;
|
|
425
502
|
} | {
|
|
426
503
|
labels: AuthzAdminLabels;
|
|
@@ -429,7 +506,7 @@ declare function createAuthzAdminApp(opts: CreateAuthzAdminAppOptions): {
|
|
|
429
506
|
groups: _aiquants_authz_core.AuthzGroupSummary[];
|
|
430
507
|
segment: string;
|
|
431
508
|
basePath: string;
|
|
432
|
-
myPermissions: Record<string, PermissionView>;
|
|
509
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
433
510
|
adminResourceKey: string;
|
|
434
511
|
}>;
|
|
435
512
|
action: (args: RouteArgs) => Promise<unknown>;
|
|
@@ -438,8 +515,8 @@ declare function createAuthzAdminApp(opts: CreateAuthzAdminAppOptions): {
|
|
|
438
515
|
resourceKey: string;
|
|
439
516
|
layoutGuard: (request: Request) => Promise<{
|
|
440
517
|
userId: string | number;
|
|
518
|
+
myPermissions: Record<string, PermissionView | undefined>;
|
|
441
519
|
}>;
|
|
442
|
-
loadMyPermissions: (request: Request) => Promise<Record<string, PermissionView>>;
|
|
443
520
|
layout: {
|
|
444
521
|
loader({ request }: {
|
|
445
522
|
request: Request;
|
|
@@ -608,12 +685,16 @@ declare function getPermissionSummary(permission: PermissionView | null | undefi
|
|
|
608
685
|
*/
|
|
609
686
|
declare function MyPermissionGroupStatus({ items, prefixLabel, variant, size, labels, className, style }: MyPermissionGroupStatusProps): react.JSX.Element;
|
|
610
687
|
|
|
611
|
-
type MyPermissionIndicatorProps = {
|
|
612
|
-
permission: PermissionView | null | undefined;
|
|
688
|
+
type MyPermissionIndicatorProps<K extends string = string> = {
|
|
689
|
+
permission: PermissionView<K> | null | undefined;
|
|
613
690
|
/** Human-readable resource display name (e.g. "文書データ"). Falls back to permission.resourceKey. */
|
|
614
691
|
resourceName?: string;
|
|
615
|
-
/**
|
|
616
|
-
|
|
692
|
+
/**
|
|
693
|
+
* Expected resource key. Typed as `NoInfer<K>`, so when the permission carries a literal key
|
|
694
|
+
* (`getMyPermissions` / `requirePermission` both do) a mismatch is a **compile error**; the runtime
|
|
695
|
+
* detector below stays for dynamically-keyed and JavaScript callers.
|
|
696
|
+
*/
|
|
697
|
+
expectedResourceKey?: NoInfer<K>;
|
|
617
698
|
/** Whether to include resource name/key tooltip. Default true. */
|
|
618
699
|
showResourceTooltip?: boolean;
|
|
619
700
|
labels?: {
|
|
@@ -625,7 +706,7 @@ type MyPermissionIndicatorProps = {
|
|
|
625
706
|
showScopeSummary?: boolean;
|
|
626
707
|
className?: string;
|
|
627
708
|
};
|
|
628
|
-
type MyPermissionStatusProps = MyPermissionIndicatorProps & {
|
|
709
|
+
type MyPermissionStatusProps<K extends string = string> = MyPermissionIndicatorProps<K> & {
|
|
629
710
|
/** Label prefix shown before the indicator badges. Default: "あなたの権限" */
|
|
630
711
|
prefixLabel?: string;
|
|
631
712
|
/** Whether to display the prefix label. Default: true */
|
|
@@ -642,12 +723,12 @@ declare function formatResourceTooltip(resourceKey?: string, resourceName?: stri
|
|
|
642
723
|
* Display permission indicator badges for read, write, and delete capabilities.
|
|
643
724
|
* 閲覧・編集・削除の権限状態バッジ群を描画。
|
|
644
725
|
*/
|
|
645
|
-
declare function MyPermissionIndicator({ permission, resourceName, expectedResourceKey, showResourceTooltip, labels, showScopeSummary, className }: MyPermissionIndicatorProps): react.JSX.Element;
|
|
726
|
+
declare function MyPermissionIndicator<K extends string = string>({ permission, resourceName, expectedResourceKey, showResourceTooltip, labels, showScopeSummary, className }: MyPermissionIndicatorProps<K>): react.JSX.Element;
|
|
646
727
|
/**
|
|
647
728
|
* Reusable "Your Permissions" status bar component suitable for any application page.
|
|
648
729
|
* 「あなたの権限」ラベルと権限状態インジケータを束ねた再利用可能なコンポーネント。
|
|
649
730
|
*/
|
|
650
|
-
declare function MyPermissionStatus({ permission, resourceName, expectedResourceKey, prefixLabel, showPrefixLabel, showResourceTooltip, labels, showScopeSummary, className, style }: MyPermissionStatusProps): react.JSX.Element;
|
|
731
|
+
declare function MyPermissionStatus<K extends string = string>({ permission, resourceName, expectedResourceKey, prefixLabel, showPrefixLabel, showResourceTooltip, labels, showScopeSummary, className, style, }: MyPermissionStatusProps<K>): react.JSX.Element;
|
|
651
732
|
|
|
652
733
|
/**
|
|
653
734
|
* Resource definition and registry utilities for preventing page-resource mismatches.
|
|
@@ -670,19 +751,66 @@ type ResourceDefinitionInput = {
|
|
|
670
751
|
description?: string;
|
|
671
752
|
};
|
|
672
753
|
type ResourceRegistry<T extends Record<string, ResourceDefinition>> = T;
|
|
754
|
+
/**
|
|
755
|
+
* The resource key an entry resolves to: its explicit `key`, otherwise the property name.
|
|
756
|
+
* `key` を明示していればそれ、無ければプロパティ名。
|
|
757
|
+
*
|
|
758
|
+
* `Extract<…, string>` を挟むのは `key?: string` (省略可) が `string | undefined` になり、素の
|
|
759
|
+
* `extends string` では偽になってプロパティ名を名乗ってしまうため (実行時は `key` を使うので型が嘘になる)。
|
|
760
|
+
* `key` を書いていないエントリでは `Extract` が `never` になり、プロパティ名へ落ちる。
|
|
761
|
+
*/
|
|
762
|
+
type ResolvedResourceKey<PropertyName extends string, Input extends ResourceDefinitionInput> = [Extract<Input["key"], string>] extends [never] ? PropertyName : Extract<Input["key"], string>;
|
|
673
763
|
/**
|
|
674
764
|
* Define type-safe resource registry for an application.
|
|
675
765
|
* アプリ全体のリソース定義(キー・表示名・説明)を一元管理するヘルパー。
|
|
766
|
+
*
|
|
767
|
+
* 各エントリの `key` はリテラル型のまま保持する。`Record<string, …>` へ広げてしまうと
|
|
768
|
+
* {@link resourceKeysOf} が `string[]` に落ち、権限マップのキー型検査が効かなくなるため。
|
|
769
|
+
*
|
|
770
|
+
* @returns The registry with literal-typed resource keys. リソースキーをリテラル型で保持したレジストリ。
|
|
676
771
|
*/
|
|
677
|
-
declare function defineResources<T extends Record<string, ResourceDefinitionInput>>(resources: T): {
|
|
678
|
-
[K in keyof T & string]:
|
|
772
|
+
declare function defineResources<const T extends Record<string, ResourceDefinitionInput>>(resources: T): {
|
|
773
|
+
[K in keyof T & string]: {
|
|
774
|
+
key: ResolvedResourceKey<K, T[K]>;
|
|
775
|
+
name: string;
|
|
776
|
+
description?: string;
|
|
777
|
+
};
|
|
679
778
|
};
|
|
779
|
+
/**
|
|
780
|
+
* Extract a registry's resource keys with their literal types preserved.
|
|
781
|
+
* レジストリのリソースキーをリテラル型を保ったまま取り出す処理。
|
|
782
|
+
*
|
|
783
|
+
* `Object.keys(registry)` は `string[]` になり、`getMyPermissions` の戻り値も索引型へ落ちてキーの誤りを
|
|
784
|
+
* 検出できなくなる。加えて `Object.keys` は **プロパティ名** を返すため、`key` を明示したエントリでは
|
|
785
|
+
* 実際のリソースキーと食い違う。
|
|
786
|
+
*
|
|
787
|
+
* ⚠️ 1 エントリでもリテラルでない `key` (例: 変数由来の `string`) を持つと、合併の性質上レジストリ全体の
|
|
788
|
+
* キー型が `string` へ広がり、他のエントリ分のコンパイル時検査も無効になる。`key` は必ずリテラルで書くこと。
|
|
789
|
+
*
|
|
790
|
+
* @returns The registry's resource keys as a literal union array. リテラル合併型のリソースキー配列。
|
|
791
|
+
*/
|
|
792
|
+
declare function resourceKeysOf<T extends Record<string, {
|
|
793
|
+
key: string;
|
|
794
|
+
}>>(registry: T): Array<T[keyof T & string]["key"]>;
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Display-state derivation for a {@link PermissionView} (pure, no React state involved).
|
|
798
|
+
* {@link PermissionView} から表示状態を導出するモジュール (React の状態は一切持たない純粋関数)。
|
|
799
|
+
*/
|
|
680
800
|
|
|
681
801
|
type PermState = "full" | "partial" | "none";
|
|
802
|
+
/**
|
|
803
|
+
* Display-oriented reading of a {@link PermissionView}: the same `can*` booleans (safe defaults when the
|
|
804
|
+
* view is absent) plus the ○△× tri-state used by the indicator components.
|
|
805
|
+
* {@link PermissionView} の表示向け読み取り結果。同名の `can*` 真偽値と ○△× の三値状態を併せ持つ形。
|
|
806
|
+
*/
|
|
682
807
|
type UsePermission = {
|
|
683
808
|
canRead: boolean;
|
|
684
|
-
|
|
809
|
+
canCreate: boolean;
|
|
810
|
+
canUpdate: boolean;
|
|
685
811
|
canDelete: boolean;
|
|
812
|
+
/** create ∪ update */
|
|
813
|
+
canWrite: boolean;
|
|
686
814
|
/** read: none(×) / partial(△, row|col restricted) / full(○). */
|
|
687
815
|
readState: PermState;
|
|
688
816
|
/** write = create ∪ update: full(○ both) / partial(△ one) / none(×). */
|
|
@@ -692,7 +820,10 @@ type UsePermission = {
|
|
|
692
820
|
/**
|
|
693
821
|
* Derive ○△× display states from a {@link PermissionView}. Pure (safe to call in render);
|
|
694
822
|
* named `usePermission` for ergonomic call-sites.
|
|
823
|
+
* {@link PermissionView} から ○△× の表示状態を導出する純粋関数 (レンダー内から安全に呼べる形)。
|
|
824
|
+
*
|
|
825
|
+
* @returns The permission booleans plus their display states. 権限の真偽値と表示状態の組。
|
|
695
826
|
*/
|
|
696
827
|
declare function usePermission(view: PermissionView | null | undefined): UsePermission;
|
|
697
828
|
|
|
698
|
-
export { AuthzAdminAppView, type AuthzAdminAppViewProps, type AuthzAdminLabels, type AuthzConfig, AuthzErrorBoundary, type AuthzGrantsData, AuthzGrantsView, type AuthzGraphData, AuthzGraphView, type AuthzGroupRolesData, AuthzGroupRolesView, AuthzLayout, type AuthzLayoutData, type AuthzLayoutProps, type AuthzLayoutShellProps, type AuthzNodeType, type AuthzResourcesData, AuthzResourcesView, type AuthzRolesData, AuthzRolesView, type AuthzUserId, type AuthzUserRolesData, AuthzUserRolesView, type CreateAuthzAdminAppOptions, type CreateAuthzAdminServerOptions, ErrorAlert, Field, type MyPermissionGroupItem, MyPermissionGroupStatus, type MyPermissionGroupStatusProps, MyPermissionIndicator, type MyPermissionIndicatorProps, MyPermissionStatus, type MyPermissionStatusProps, type PartialAuthzAdminLabels, type PermState, type PermissionView, type RequirePermissionOptions, type ResourceDefinition, type ResourceDefinitionInput, type ResourceRegistry, type UsePermission, createAuthz, createAuthzAdminApp, createAuthzAdminServer, defaultAuthzAdminLabels, defineResources, flattenGroupItems, formatResourceTooltip, getPermissionSummary, makeAuthzErrorBoundary, resolveLabels, toPermissionView, ui, usePermission };
|
|
829
|
+
export { AuthzAdminAppView, type AuthzAdminAppViewProps, type AuthzAdminLabels, type AuthzConfig, AuthzErrorBoundary, type AuthzGrantsData, AuthzGrantsView, type AuthzGraphData, AuthzGraphView, type AuthzGroupRolesData, AuthzGroupRolesView, AuthzLayout, type AuthzLayoutData, type AuthzLayoutProps, type AuthzLayoutShellProps, type AuthzNodeType, type AuthzResourcesData, AuthzResourcesView, type AuthzRolesData, AuthzRolesView, AuthzUsageError, type AuthzUserId, type AuthzUserRolesData, AuthzUserRolesView, type CreateAuthzAdminAppOptions, type CreateAuthzAdminServerOptions, type EffectivePermissionsQuery, ErrorAlert, Field, type MyPermissionGroupItem, MyPermissionGroupStatus, type MyPermissionGroupStatusProps, MyPermissionIndicator, type MyPermissionIndicatorProps, MyPermissionStatus, type MyPermissionStatusProps, type PartialAuthzAdminLabels, type PermState, type PermissionView, type PermissionViewMap, type RequirePermissionOptions, type RequirePermissionResult, type ResourceDefinition, type ResourceDefinitionInput, type ResourceRegistry, type UsePermission, createAuthz, createAuthzAdminApp, createAuthzAdminServer, defaultAuthzAdminLabels, defineResources, flattenGroupItems, formatResourceTooltip, getPermissionSummary, makeAuthzErrorBoundary, resolveLabels, resourceKeysOf, toPermissionView, ui, usePermission };
|