@aiquants/authz-react-router 0.2.6 → 0.4.0

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 CHANGED
@@ -89,8 +89,14 @@ WHERE r.role_key='viewer';
89
89
 
90
90
  ```tsx
91
91
  // loader: const myPermissions = await getMyPermissions(request, { resourceKeys: ["report"] })
92
- import { MyPermissionIndicator } from "@aiquants/authz-react-router"
93
- <MyPermissionIndicator permission={myPermissions.report} /> // → 読○ 書△ 削除×
92
+ import { MyPermissionIndicator, MyPermissionStatus } from "@aiquants/authz-react-router"
93
+
94
+ // 1. Single indicator badge group
95
+ <MyPermissionIndicator permission={myPermissions.report} resourceName="Monthly Report" />
96
+
97
+ // 2. Full "Your Permissions" status bar component for any page header/toolbar
98
+ <MyPermissionStatus permission={myPermissions.report} resourceName="Monthly Report" />
99
+ // → "Your Permissions: [Read ○][Write △][Delete ×]" with tooltip "Target Resource: Monthly Report (report)"
94
100
  ```
95
101
 
96
102
  ## Scope cheat-sheet (§3.2)
@@ -108,12 +114,16 @@ Multiple roles **union** (wider wins): rows OR'd (any `NULL` ⇒ all rows); colu
108
114
 
109
115
  ## API
110
116
 
111
- - `createAuthz({ appKey, resolveUserId, getEffectivePermissions, onDeny? })` → `{ requirePermission, getMyPermissions }`.
117
+ - `createAuthz({ appKey, resolveUserId, getEffectivePermissions, onDeny? })` → `{ requirePermission, getMyPermissions, requireAndGetPermission }`.
112
118
  - `requirePermission(request, {resourceKey, action}, options?)` → `{ userId, scope }` on allow (so loaders apply row WHERE / column mask — not just 403). Denies throw `AuthzDeniedError` (403), or `throw redirect(options.failureRedirect)` when set.
119
+ - `requireAndGetPermission(request, {resourceKey, action}, options?)` → `{ userId, scope, permission }`. Atomically performs guard and fetches matching UI `PermissionView` to prevent page-resource mismatches.
120
+ - `defineResources({ ... })` → Type-safe centralized resource registry helper for resource keys and display names.
113
121
  - `getMyPermissions(request, {resourceKeys})` → `Record<resourceKey, PermissionView>` for the UI.
114
122
  - `toPermissionView(resourceKey, perm)` → serializable view (loader → client).
115
123
  - `usePermission(view)` → ○△× display states (`readState`/`writeState`/`deleteState`); pure, safe in render.
116
- - `<MyPermissionIndicator permission={view} />` → read / write(create∪update) / delete as ○△×. `read` is △ when row/column-restricted; `write` is ○ only when both create & update are held, △ when one.
124
+ - `<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`.
125
+ - `<MyPermissionStatus permission={view} resourceName="Documents" expectedResourceKey="documents" prefixLabel="Your Permissions" />` → Reusable status component combining prefix label and permission indicator badges for any application page.
126
+ - `<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`).
117
127
 
118
128
  ## Demo App
119
129
 
@@ -124,7 +134,7 @@ This package contains an interactive React Router 7 SPA demo in the `demo/` subd
124
134
  - **Role-Based Access Control**: Demonstrates role switching (Admin, Manager, Operator, Reader) and how it affects UI elements.
125
135
  - **Route Guards**: Uses `authz.requirePermission` to protect routes and handle unauthorized access.
126
136
  - **Scope Verification**: Demonstrates `rowScope` (row filters) and `columnScope` (masked values) dynamically applied based on the active role's permissions.
127
- - **In-Browser Database Reset**: Includes a **「デモデータ初期化」 (Reset Demo Data)** button in the header of key views (Dashboard, Protected Documents, and Sales pages) to wipe customized roles, assignments, permissions, and CRUD data from `localStorage`, instantly restoring default test states.
137
+ - **In-Browser Database Reset**: Includes a **"Reset Demo Data"** button in the header of key views (Dashboard, Protected Documents, and Sales pages) to wipe customized roles, assignments, permissions, and CRUD data from `localStorage`, instantly restoring default test states.
128
138
 
129
139
  ### Running the Demo
130
140
 
package/dist/index.d.mts CHANGED
@@ -53,6 +53,12 @@ declare function createAuthz(config: AuthzConfig): {
53
53
  getMyPermissions: (request: Request, query: {
54
54
  resourceKeys: string[];
55
55
  }) => Promise<Record<string, PermissionView>>;
56
+ requireAndGetPermission: (request: Request, query: {
57
+ resourceKey: string;
58
+ action: AuthzAction;
59
+ }, options?: RequirePermissionOptions) => Promise<GuardResult<AuthzUserId> & {
60
+ permission: PermissionView;
61
+ }>;
56
62
  };
57
63
 
58
64
  /** Authz admin UI strings (i18n). Defaults are English; override per-section via `createAuthzAdminRoutes(..., { labels })`. */
@@ -443,8 +449,85 @@ declare function ErrorAlert({ message }: {
443
449
  message?: string;
444
450
  }): react.JSX.Element | null;
445
451
 
452
+ type MyPermissionGroupItem = {
453
+ /** Expected or actual resource key */
454
+ resourceKey?: string;
455
+ /** Human-readable resource display name (e.g. "NI", "KT", "オリジナル") */
456
+ resourceName: string;
457
+ /** Permission view data for this resource */
458
+ permission: PermissionView | null | undefined;
459
+ /** Expected resource key to detect mismatches with permission.resourceKey during development */
460
+ expectedResourceKey?: string;
461
+ /** Nesting level for hierarchical rendering (0: root, 1: child, etc.) */
462
+ level?: number;
463
+ /** Optional child items for hierarchical rendering */
464
+ children?: MyPermissionGroupItem[];
465
+ };
466
+ /**
467
+ * Flattens hierarchical items tree into a single array with computed nesting levels.
468
+ * 木構造またはフラット配列のアイテムに level 属性を付与してフラット化する処理。
469
+ */
470
+ declare function flattenGroupItems(items: MyPermissionGroupItem[], currentLevel?: number): MyPermissionGroupItem[];
471
+ type MyPermissionGroupStatusProps = {
472
+ /** List of resources and their permissions */
473
+ items: MyPermissionGroupItem[];
474
+ /** Prefix label shown before group items or inside popover header. Default: "あなたの権限" */
475
+ prefixLabel?: string;
476
+ /**
477
+ * Display style variant:
478
+ * - "pills": Inline parent label + compact status tags per item
479
+ * - "popover": Compact single button trigger opening detailed popup card
480
+ * Default: "pills"
481
+ */
482
+ variant?: "pills" | "popover";
483
+ /**
484
+ * Component size variant:
485
+ * - "2xs": Ultra micro size for extremely dense UI
486
+ * - "xs": Extra small size for compact headers
487
+ * - "sm": Small size
488
+ * - "md": Standard default size
489
+ * - "lg": Larger size
490
+ * Default: "md"
491
+ */
492
+ size?: "2xs" | "xs" | "sm" | "md" | "lg";
493
+ labels?: {
494
+ read?: string;
495
+ write?: string;
496
+ delete?: string;
497
+ noPermission?: string;
498
+ fullWrite?: string;
499
+ partialWrite?: string;
500
+ readOnly?: string;
501
+ partialRead?: string;
502
+ };
503
+ className?: string;
504
+ style?: CSSProperties;
505
+ };
506
+ /**
507
+ * Get human-readable summary text and style for a resource permission.
508
+ * 権限状態から簡潔なサマリテキストとスタイルを判定 (カスタムラベル対応)。
509
+ */
510
+ declare function getPermissionSummary(permission: PermissionView | null | undefined, customLabels?: MyPermissionGroupStatusProps["labels"]): {
511
+ text: string;
512
+ color: string;
513
+ bgColor: string;
514
+ borderColor: string;
515
+ };
516
+ /**
517
+ * Multi-resource "Your Permissions" status component.
518
+ * Supports inline compact pills (Proposal 1) and dropdown popover card (Proposal 2).
519
+ * 複数リソースの権限状態を共通レイアウト(pills/popover)で綺麗に整列・表示するコンポーネント。
520
+ */
521
+ declare function MyPermissionGroupStatus({ items, prefixLabel, variant, size, labels, className, style }: MyPermissionGroupStatusProps): react.JSX.Element;
522
+
446
523
  type MyPermissionIndicatorProps = {
447
524
  permission: PermissionView | null | undefined;
525
+ /** Human-readable resource display name (e.g. "文書データ"). Falls back to permission.resourceKey. */
526
+ resourceName?: string;
527
+ /** Expected resource key to detect mismatches with permission.resourceKey during development. */
528
+ expectedResourceKey?: string;
529
+ /** Whether to include resource name/key tooltip. Default true. */
530
+ showResourceTooltip?: boolean;
448
531
  labels?: {
449
532
  read?: string;
450
533
  write?: string;
@@ -454,7 +537,58 @@ type MyPermissionIndicatorProps = {
454
537
  showScopeSummary?: boolean;
455
538
  className?: string;
456
539
  };
457
- declare function MyPermissionIndicator({ permission, labels, showScopeSummary, className }: MyPermissionIndicatorProps): react.JSX.Element;
540
+ type MyPermissionStatusProps = MyPermissionIndicatorProps & {
541
+ /** Label prefix shown before the indicator badges. Default: "あなたの権限" */
542
+ prefixLabel?: string;
543
+ /** Whether to display the prefix label. Default: true */
544
+ showPrefixLabel?: boolean;
545
+ /** Additional inline styles for wrapper element. */
546
+ style?: React.CSSProperties;
547
+ };
548
+ /**
549
+ * Format human-readable resource tooltip text.
550
+ * リソース表示名およびキーからツールチップ用テキストを生成。
551
+ */
552
+ declare function formatResourceTooltip(resourceKey?: string, resourceName?: string): string | undefined;
553
+ /**
554
+ * Display permission indicator badges for read, write, and delete capabilities.
555
+ * 閲覧・編集・削除の権限状態バッジ群を描画。
556
+ */
557
+ declare function MyPermissionIndicator({ permission, resourceName, expectedResourceKey, showResourceTooltip, labels, showScopeSummary, className, }: MyPermissionIndicatorProps): react.JSX.Element;
558
+ /**
559
+ * Reusable "Your Permissions" status bar component suitable for any application page.
560
+ * 「あなたの権限」ラベルと権限状態インジケータを束ねた再利用可能なコンポーネント。
561
+ */
562
+ declare function MyPermissionStatus({ permission, resourceName, expectedResourceKey, prefixLabel, showPrefixLabel, showResourceTooltip, labels, showScopeSummary, className, style, }: MyPermissionStatusProps): react.JSX.Element;
563
+
564
+ /**
565
+ * Resource definition and registry utilities for preventing page-resource mismatches.
566
+ * ページとリソースの表示・権限食い違いを防止するためのリソースレジストリ定義。
567
+ */
568
+ type ResourceDefinition = {
569
+ /** Unique resource key used in authorization rules (e.g. "documents") */
570
+ key: string;
571
+ /** Human-readable display name (e.g. "文書管理") */
572
+ name: string;
573
+ /** Optional description for administration UI */
574
+ description?: string;
575
+ };
576
+ type ResourceDefinitionInput = {
577
+ /** Optional explicit resource key; defaults to the object property name */
578
+ key?: string;
579
+ /** Human-readable display name (e.g. "文書管理") */
580
+ name: string;
581
+ /** Optional description for administration UI */
582
+ description?: string;
583
+ };
584
+ type ResourceRegistry<T extends Record<string, ResourceDefinition>> = T;
585
+ /**
586
+ * Define type-safe resource registry for an application.
587
+ * アプリ全体のリソース定義(キー・表示名・説明)を一元管理するヘルパー。
588
+ */
589
+ declare function defineResources<T extends Record<string, ResourceDefinitionInput>>(resources: T): {
590
+ [K in keyof T & string]: ResourceDefinition;
591
+ };
458
592
 
459
593
  type PermState = "full" | "partial" | "none";
460
594
  type UsePermission = {
@@ -473,4 +607,4 @@ type UsePermission = {
473
607
  */
474
608
  declare function usePermission(view: PermissionView | null | undefined): UsePermission;
475
609
 
476
- export { AuthzAdminAppView, type AuthzAdminLabels, type AuthzConfig, AuthzErrorBoundary, type AuthzGrantsData, AuthzGrantsView, AuthzLayout, type AuthzLayoutData, type AuthzResourcesData, AuthzResourcesView, type AuthzRolesData, AuthzRolesView, type AuthzUserId, type AuthzUserRolesData, AuthzUserRolesView, type CreateAuthzAdminAppOptions, type CreateAuthzAdminServerOptions, ErrorAlert, Field, MyPermissionIndicator, type MyPermissionIndicatorProps, type PartialAuthzAdminLabels, type PermState, type PermissionView, type RequirePermissionOptions, type UsePermission, createAuthz, createAuthzAdminApp, createAuthzAdminServer, defaultAuthzAdminLabels, makeAuthzErrorBoundary, resolveLabels, toPermissionView, ui, usePermission };
610
+ export { AuthzAdminAppView, type AuthzAdminLabels, type AuthzConfig, AuthzErrorBoundary, type AuthzGrantsData, AuthzGrantsView, AuthzLayout, type AuthzLayoutData, 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 };
package/dist/index.d.ts CHANGED
@@ -53,6 +53,12 @@ declare function createAuthz(config: AuthzConfig): {
53
53
  getMyPermissions: (request: Request, query: {
54
54
  resourceKeys: string[];
55
55
  }) => Promise<Record<string, PermissionView>>;
56
+ requireAndGetPermission: (request: Request, query: {
57
+ resourceKey: string;
58
+ action: AuthzAction;
59
+ }, options?: RequirePermissionOptions) => Promise<GuardResult<AuthzUserId> & {
60
+ permission: PermissionView;
61
+ }>;
56
62
  };
57
63
 
58
64
  /** Authz admin UI strings (i18n). Defaults are English; override per-section via `createAuthzAdminRoutes(..., { labels })`. */
@@ -443,8 +449,85 @@ declare function ErrorAlert({ message }: {
443
449
  message?: string;
444
450
  }): react.JSX.Element | null;
445
451
 
452
+ type MyPermissionGroupItem = {
453
+ /** Expected or actual resource key */
454
+ resourceKey?: string;
455
+ /** Human-readable resource display name (e.g. "NI", "KT", "オリジナル") */
456
+ resourceName: string;
457
+ /** Permission view data for this resource */
458
+ permission: PermissionView | null | undefined;
459
+ /** Expected resource key to detect mismatches with permission.resourceKey during development */
460
+ expectedResourceKey?: string;
461
+ /** Nesting level for hierarchical rendering (0: root, 1: child, etc.) */
462
+ level?: number;
463
+ /** Optional child items for hierarchical rendering */
464
+ children?: MyPermissionGroupItem[];
465
+ };
466
+ /**
467
+ * Flattens hierarchical items tree into a single array with computed nesting levels.
468
+ * 木構造またはフラット配列のアイテムに level 属性を付与してフラット化する処理。
469
+ */
470
+ declare function flattenGroupItems(items: MyPermissionGroupItem[], currentLevel?: number): MyPermissionGroupItem[];
471
+ type MyPermissionGroupStatusProps = {
472
+ /** List of resources and their permissions */
473
+ items: MyPermissionGroupItem[];
474
+ /** Prefix label shown before group items or inside popover header. Default: "あなたの権限" */
475
+ prefixLabel?: string;
476
+ /**
477
+ * Display style variant:
478
+ * - "pills": Inline parent label + compact status tags per item
479
+ * - "popover": Compact single button trigger opening detailed popup card
480
+ * Default: "pills"
481
+ */
482
+ variant?: "pills" | "popover";
483
+ /**
484
+ * Component size variant:
485
+ * - "2xs": Ultra micro size for extremely dense UI
486
+ * - "xs": Extra small size for compact headers
487
+ * - "sm": Small size
488
+ * - "md": Standard default size
489
+ * - "lg": Larger size
490
+ * Default: "md"
491
+ */
492
+ size?: "2xs" | "xs" | "sm" | "md" | "lg";
493
+ labels?: {
494
+ read?: string;
495
+ write?: string;
496
+ delete?: string;
497
+ noPermission?: string;
498
+ fullWrite?: string;
499
+ partialWrite?: string;
500
+ readOnly?: string;
501
+ partialRead?: string;
502
+ };
503
+ className?: string;
504
+ style?: CSSProperties;
505
+ };
506
+ /**
507
+ * Get human-readable summary text and style for a resource permission.
508
+ * 権限状態から簡潔なサマリテキストとスタイルを判定 (カスタムラベル対応)。
509
+ */
510
+ declare function getPermissionSummary(permission: PermissionView | null | undefined, customLabels?: MyPermissionGroupStatusProps["labels"]): {
511
+ text: string;
512
+ color: string;
513
+ bgColor: string;
514
+ borderColor: string;
515
+ };
516
+ /**
517
+ * Multi-resource "Your Permissions" status component.
518
+ * Supports inline compact pills (Proposal 1) and dropdown popover card (Proposal 2).
519
+ * 複数リソースの権限状態を共通レイアウト(pills/popover)で綺麗に整列・表示するコンポーネント。
520
+ */
521
+ declare function MyPermissionGroupStatus({ items, prefixLabel, variant, size, labels, className, style }: MyPermissionGroupStatusProps): react.JSX.Element;
522
+
446
523
  type MyPermissionIndicatorProps = {
447
524
  permission: PermissionView | null | undefined;
525
+ /** Human-readable resource display name (e.g. "文書データ"). Falls back to permission.resourceKey. */
526
+ resourceName?: string;
527
+ /** Expected resource key to detect mismatches with permission.resourceKey during development. */
528
+ expectedResourceKey?: string;
529
+ /** Whether to include resource name/key tooltip. Default true. */
530
+ showResourceTooltip?: boolean;
448
531
  labels?: {
449
532
  read?: string;
450
533
  write?: string;
@@ -454,7 +537,58 @@ type MyPermissionIndicatorProps = {
454
537
  showScopeSummary?: boolean;
455
538
  className?: string;
456
539
  };
457
- declare function MyPermissionIndicator({ permission, labels, showScopeSummary, className }: MyPermissionIndicatorProps): react.JSX.Element;
540
+ type MyPermissionStatusProps = MyPermissionIndicatorProps & {
541
+ /** Label prefix shown before the indicator badges. Default: "あなたの権限" */
542
+ prefixLabel?: string;
543
+ /** Whether to display the prefix label. Default: true */
544
+ showPrefixLabel?: boolean;
545
+ /** Additional inline styles for wrapper element. */
546
+ style?: React.CSSProperties;
547
+ };
548
+ /**
549
+ * Format human-readable resource tooltip text.
550
+ * リソース表示名およびキーからツールチップ用テキストを生成。
551
+ */
552
+ declare function formatResourceTooltip(resourceKey?: string, resourceName?: string): string | undefined;
553
+ /**
554
+ * Display permission indicator badges for read, write, and delete capabilities.
555
+ * 閲覧・編集・削除の権限状態バッジ群を描画。
556
+ */
557
+ declare function MyPermissionIndicator({ permission, resourceName, expectedResourceKey, showResourceTooltip, labels, showScopeSummary, className, }: MyPermissionIndicatorProps): react.JSX.Element;
558
+ /**
559
+ * Reusable "Your Permissions" status bar component suitable for any application page.
560
+ * 「あなたの権限」ラベルと権限状態インジケータを束ねた再利用可能なコンポーネント。
561
+ */
562
+ declare function MyPermissionStatus({ permission, resourceName, expectedResourceKey, prefixLabel, showPrefixLabel, showResourceTooltip, labels, showScopeSummary, className, style, }: MyPermissionStatusProps): react.JSX.Element;
563
+
564
+ /**
565
+ * Resource definition and registry utilities for preventing page-resource mismatches.
566
+ * ページとリソースの表示・権限食い違いを防止するためのリソースレジストリ定義。
567
+ */
568
+ type ResourceDefinition = {
569
+ /** Unique resource key used in authorization rules (e.g. "documents") */
570
+ key: string;
571
+ /** Human-readable display name (e.g. "文書管理") */
572
+ name: string;
573
+ /** Optional description for administration UI */
574
+ description?: string;
575
+ };
576
+ type ResourceDefinitionInput = {
577
+ /** Optional explicit resource key; defaults to the object property name */
578
+ key?: string;
579
+ /** Human-readable display name (e.g. "文書管理") */
580
+ name: string;
581
+ /** Optional description for administration UI */
582
+ description?: string;
583
+ };
584
+ type ResourceRegistry<T extends Record<string, ResourceDefinition>> = T;
585
+ /**
586
+ * Define type-safe resource registry for an application.
587
+ * アプリ全体のリソース定義(キー・表示名・説明)を一元管理するヘルパー。
588
+ */
589
+ declare function defineResources<T extends Record<string, ResourceDefinitionInput>>(resources: T): {
590
+ [K in keyof T & string]: ResourceDefinition;
591
+ };
458
592
 
459
593
  type PermState = "full" | "partial" | "none";
460
594
  type UsePermission = {
@@ -473,4 +607,4 @@ type UsePermission = {
473
607
  */
474
608
  declare function usePermission(view: PermissionView | null | undefined): UsePermission;
475
609
 
476
- export { AuthzAdminAppView, type AuthzAdminLabels, type AuthzConfig, AuthzErrorBoundary, type AuthzGrantsData, AuthzGrantsView, AuthzLayout, type AuthzLayoutData, type AuthzResourcesData, AuthzResourcesView, type AuthzRolesData, AuthzRolesView, type AuthzUserId, type AuthzUserRolesData, AuthzUserRolesView, type CreateAuthzAdminAppOptions, type CreateAuthzAdminServerOptions, ErrorAlert, Field, MyPermissionIndicator, type MyPermissionIndicatorProps, type PartialAuthzAdminLabels, type PermState, type PermissionView, type RequirePermissionOptions, type UsePermission, createAuthz, createAuthzAdminApp, createAuthzAdminServer, defaultAuthzAdminLabels, makeAuthzErrorBoundary, resolveLabels, toPermissionView, ui, usePermission };
610
+ export { AuthzAdminAppView, type AuthzAdminLabels, type AuthzConfig, AuthzErrorBoundary, type AuthzGrantsData, AuthzGrantsView, AuthzLayout, type AuthzLayoutData, 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 };
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- "use strict";var E=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var ee=(t,n)=>{for(var s in n)E(t,s,{get:n[s],enumerable:!0})},te=(t,n,s,d)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of j(n))!X.call(t,o)&&o!==s&&E(t,o,{get:()=>n[o],enumerable:!(d=Z(n,o))||d.enumerable});return t};var re=t=>te(E({},"__esModule",{value:!0}),t);var pe={};ee(pe,{AuthzAdminAppView:()=>ce,AuthzErrorBoundary:()=>le,AuthzGrantsView:()=>M,AuthzLayout:()=>ae,AuthzResourcesView:()=>B,AuthzRolesView:()=>q,AuthzUserRolesView:()=>O,ErrorAlert:()=>k,Field:()=>w,MyPermissionIndicator:()=>F,createAuthz:()=>me,createAuthzAdminApp:()=>oe,createAuthzAdminServer:()=>N,defaultAuthzAdminLabels:()=>W,makeAuthzErrorBoundary:()=>G,resolveLabels:()=>L,toPermissionView:()=>Q,ui:()=>r,usePermission:()=>U});module.exports=re(pe);var _=require("react-router");var v=require("@aiquants/authz-core"),A=require("react-router");var W={title:"Authorization (RBAC)",myPermission:"Your permissions",tabs:{roles:"Roles",resources:"Resources",role_permissions:"Permissions",user_roles:"User assignments"},common:{add:"Add",save:"Save",delete:"Delete",remove:"Remove",assign:"Assign",grant:"Grant",select:"Select\u2026",active:"\u25CB Enabled",inactive:"\xD7 Disabled",optional:"optional"},roles:{heading:"Roles (TMRole)",note:"Edit name / description inline and click Save (role_key is an immutable identifier).",roleKey:"role_key",name:"name",description:"description",activeHeader:"Active",empty:"No roles",newHeading:"New role",hintRoleKey:"e.g. admin"},resources:{heading:"Resources (TMResource)",note:"Edit name / description inline and click Save (app_key / resource_key are immutable identifiers).",appKey:"app_key",resourceKey:"resource_key",name:"name",description:"description",empty:"No resources",newHeading:"New resource",hintAppKey:"e.g. quants",hintResourceKey:"e.g. daily_report",hintDailyReport:"optional"},grants:{heading:"Permissions (TDRolePermission)",note:"Empty row_scope / column_scope = all rows / all columns (NULL). JSON must satisfy the \xA73.2 contract.",role:"role",resource:"resource",action:"action",rowScope:"row_scope",columnScope:"column_scope",empty:"No permissions",newHeading:"Grant a permission",rowScopeHint:"JSON / empty = all rows",columnScopeHint:"JSON / empty = all columns",rowScopePlaceholder:"empty = all rows (NULL)",columnScopePlaceholder:"empty = all columns (NULL)",nullRows:"NULL (all rows)",nullCols:"NULL (all columns)",selectPlaceholder:"Select\u2026"},userRoles:{heading:"User assignments (TDUserRole)",note:"Assign active roles to specific users.",user:"user",role:"role",empty:"No assignments",newHeading:"Assign a role",selectPlaceholder:"Select\u2026"},error:{forbiddenTitle:"Access denied",forbiddenBody:"You don't have permission to view this screen. Ask an administrator to grant access.",genericTitlePrefix:"Error",genericTitle:"An error occurred",genericBody:"An unexpected error occurred. Please try again later."},warn:{selfRoleDisable:"This role backs your own authorization-admin access. Disabling it may lock you out of this screen. Continue?",selfRoleDelete:"This role backs your own authorization-admin access. Deleting it may lock you out of this screen. Continue?",selfAssignmentRemove:"This assignment backs your own authorization-admin access. Removing it may lock you out of this screen. Continue?"}};function L(t){let n=W;return t?{title:t.title??n.title,myPermission:t.myPermission??n.myPermission,tabs:{...n.tabs,...t.tabs},common:{...n.common,...t.common},roles:{...n.roles,...t.roles},resources:{...n.resources,...t.resources},grants:{...n.grants,...t.grants},userRoles:{...n.userRoles,...t.userRoles},error:{...n.error,...t.error},warn:{...n.warn,...t.warn}}:n}var ne=t=>t instanceof Response;function D(t){if(ne(t))throw t;if(t instanceof v.AuthzDeniedError)throw new Response(t.message,{status:403});return(0,A.data)({error:t instanceof Error?t.message:"\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"},{status:400})}function N(t){let n=t.store,s=t.resourceKey??"authz_admin",d=L(t.labels),o=async(u,a)=>{let{userId:h}=await t.requirePermission(u,a);if(h==null)throw new v.AuthzDeniedError({appKey:"",resourceKey:a.resourceKey,action:a.action,reason:"unresolved-user",userId:null});return{userId:h}};async function c(u){await t.requireUser(u);try{return await o(u,{resourceKey:s,action:"read"})}catch(a){throw a instanceof v.AuthzDeniedError?new Response(`Forbidden: ${s}`,{status:403}):a}}let l=u=>t.getMyPermissions(u,{resourceKeys:[s]}),y=async u=>{let[a,h]=await Promise.all([n.listRoles(),n.getSelfAdminContext(Number(u))]);return{roles:a,selfAdminRoleIds:h.selfAdminRoleIds,labels:d}},g=async()=>({resources:await n.listResources(),labels:d}),f=async()=>{let[u,a,h]=await Promise.all([n.listGrants(),n.listRoles(),n.listResources()]);return{grants:u,roles:a,resources:h,labels:d}},S=async u=>{let[a,h,p,i]=await Promise.all([n.listUserRoles(),n.listRoles(),n.listUsers(),n.getSelfAdminContext(Number(u))]);return{assignments:a,roles:h,users:p,actorUserId:i.actorUserId,selfAdminRoleIds:i.selfAdminRoleIds,labels:d}};return{labels:d,resourceKey:s,layoutGuard:c,loadMyPermissions:l,layout:{async loader({request:u}){return await c(u),{myPermissions:await l(u),labels:d,adminResourceKey:s}}},index:{loader(){throw(0,A.redirect)("/authz/roles")}},roles:{data:y,async loader({request:u}){let{userId:a}=await o(u,{resourceKey:s,action:"read"});return y(a)},async action({request:u}){let a=await u.formData(),h=String(a.get("_action"));try{switch(h){case"create":{let{userId:p}=await o(u,{resourceKey:s,action:"create"}),i=String(a.get("roleKey")??"").trim(),b=String(a.get("name")??"").trim();return i&&b?(await n.createRole({roleKey:i,name:b,description:String(a.get("description")??"").trim()||null},String(p)),{ok:!0}):(0,A.data)({error:"role_key \u3068 name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"save":{let{userId:p}=await o(u,{resourceKey:s,action:"update"}),i=String(a.get("roleKey")??"").trim(),b=String(a.get("name")??"").trim();return i&&b?(await n.updateRole(Number(a.get("id")),{roleKey:i,name:b,description:String(a.get("description")??"").trim()||null},String(p)),{ok:!0}):(0,A.data)({error:"roleKey \u3068 name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"toggle":{let{userId:p}=await o(u,{resourceKey:s,action:"update"});return await n.setRoleActive(Number(a.get("id")),a.get("isActive")==="true",String(p)),{ok:!0}}case"delete":return await o(u,{resourceKey:s,action:"delete"}),await n.deleteRole(Number(a.get("id"))),{ok:!0};default:return(0,A.data)({error:"unknown action"},{status:400})}}catch(p){return D(p)}}},resources:{data:g,async loader({request:u}){return await o(u,{resourceKey:s,action:"read"}),g()},async action({request:u}){let a=await u.formData(),h=String(a.get("_action"));try{switch(h){case"create":{let{userId:p}=await o(u,{resourceKey:s,action:"create"}),i=String(a.get("appKey")??"").trim(),b=String(a.get("resourceKey")??"").trim(),I=String(a.get("name")??"").trim();return i&&b&&I?(await n.createResource({appKey:i,resourceKey:b,name:I,description:String(a.get("description")??"").trim()||null},String(p)),{ok:!0}):(0,A.data)({error:"app_key / resource_key / name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"save":{let{userId:p}=await o(u,{resourceKey:s,action:"update"}),i=String(a.get("name")??"").trim();return i?(await n.updateResource(Number(a.get("id")),{name:i,description:String(a.get("description")??"").trim()||null},String(p)),{ok:!0}):(0,A.data)({error:"name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"delete":return await o(u,{resourceKey:s,action:"delete"}),await n.deleteResource(Number(a.get("id"))),{ok:!0};default:return(0,A.data)({error:"unknown action"},{status:400})}}catch(p){return D(p)}}},grants:{data:f,async loader({request:u}){return await o(u,{resourceKey:s,action:"read"}),f()},async action({request:u}){let a=await u.formData(),h=String(a.get("_action"));try{switch(h){case"create":{let{userId:p}=await o(u,{resourceKey:s,action:"create"}),i=Number(a.get("roleId")),b=Number(a.get("resourceId"));if(!(i&&b))return(0,A.data)({error:"\u30ED\u30FC\u30EB\u3068\u30EA\u30BD\u30FC\u30B9\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044"},{status:400});let I=String(a.get("action")??"");if(!v.AUTHZ_ACTIONS.includes(I))return(0,A.data)({error:"\u7121\u52B9\u306A action \u3067\u3059"},{status:400});let Y=I;return await n.createGrant({roleId:i,resourceId:b,action:Y,rowScope:String(a.get("rowScope")??""),columnScope:String(a.get("columnScope")??"")},String(p)),{ok:!0}}case"save":{let{userId:p}=await o(u,{resourceKey:s,action:"update"}),i=String(a.get("action")??"");if(!v.AUTHZ_ACTIONS.includes(i))return(0,A.data)({error:"\u7121\u52B9\u306A action \u3067\u3059"},{status:400});let b=i;return await n.updateGrant(Number(a.get("id")),{action:b,rowScope:String(a.get("rowScope")??""),columnScope:String(a.get("columnScope")??"")},String(p)),{ok:!0}}case"delete":return await o(u,{resourceKey:s,action:"delete"}),await n.deleteGrant(Number(a.get("id"))),{ok:!0};default:return(0,A.data)({error:"unknown action"},{status:400})}}catch(p){return D(p)}}},userRoles:{data:S,async loader({request:u}){let{userId:a}=await o(u,{resourceKey:s,action:"read"});return S(a)},async action({request:u}){let a=await u.formData(),h=String(a.get("_action"));try{switch(h){case"assign":{let{userId:p}=await o(u,{resourceKey:s,action:"create"}),i=Number(a.get("userId")),b=Number(a.get("roleId"));return i&&b?(await n.assignUserRole(i,b,String(p)),{ok:!0}):(0,A.data)({error:"\u30E6\u30FC\u30B6\u30FC\u3068\u30ED\u30FC\u30EB\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044"},{status:400})}case"remove":return await o(u,{resourceKey:s,action:"delete"}),await n.removeUserRole(Number(a.get("userId")),Number(a.get("roleId"))),{ok:!0};default:return(0,A.data)({error:"unknown action"},{status:400})}}catch(p){return D(p)}}}}}function V(t){return String(t["*"]??"").split("/")[0]}function oe(t){let n=N(t),s=t.basePath??"/authz",d=["roles","resources","role_permissions","user_roles"],o={roles:n.roles.data,resources:n.resources.data,role_permissions:n.grants.data,user_roles:n.userRoles.data},c={roles:n.roles.action,resources:n.resources.action,role_permissions:n.grants.action,user_roles:n.userRoles.action};async function l(g){let f=V(g.params);if(!d.includes(f))throw(0,_.redirect)(`${s}/roles`);let{userId:S}=await n.layoutGuard(g.request),[u,a]=await Promise.all([n.loadMyPermissions(g.request),o[f](S)]);return{segment:f,basePath:s,myPermissions:u,adminResourceKey:n.resourceKey,...a}}async function y(g){let f=V(g.params),S=c[f];return S?S(g):(0,_.data)({error:"unknown action"},{status:400})}return{loader:l,action:y,server:n}}var K=require("react/jsx-runtime"),r={page:{padding:"1.25rem",maxWidth:1280,margin:"0 auto",color:"#0f172a"},tableContainer:{width:"100%",overflowX:"auto",marginBottom:"1rem",WebkitOverflowScrolling:"touch"},table:{borderCollapse:"collapse",width:"100%",fontSize:"0.9rem",tableLayout:"fixed"},th:{textAlign:"left",borderBottom:"2px solid #e2e8f0",padding:"0.45rem 0.65rem",fontSize:"0.78rem",color:"#475569",fontWeight:600,textTransform:"uppercase",letterSpacing:"0.03em"},td:{borderBottom:"1px solid #f1f5f9",padding:"0.5rem 0.65rem",verticalAlign:"middle",wordBreak:"break-all"},input:{padding:"0.4rem 0.55rem",border:"1px solid #cbd5e1",borderRadius:6,fontSize:"0.9rem",background:"#fff",width:"100%",boxSizing:"border-box"},select:{padding:"0.4rem 0.55rem",border:"1px solid #cbd5e1",borderRadius:6,fontSize:"0.9rem",background:"#fff",width:"100%",boxSizing:"border-box"},textarea:{padding:"0.4rem 0.55rem",border:"1px solid #cbd5e1",borderRadius:6,fontSize:"0.82rem",fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",minHeight:"3.25rem",resize:"vertical",width:"100%",boxSizing:"border-box"},button:{padding:"0.3rem 0.7rem",border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",cursor:"pointer",fontSize:"0.82rem",color:"#334155"},save:{padding:"0.3rem 0.7rem",border:"1px solid #2563eb",borderRadius:6,background:"#eff6ff",cursor:"pointer",fontSize:"0.82rem",color:"#2563eb",fontWeight:600},danger:{padding:"0.3rem 0.7rem",border:"1px solid #fecaca",borderRadius:6,background:"#fff",cursor:"pointer",fontSize:"0.82rem",color:"#dc2626"},primary:{padding:"0.5rem 1.25rem",border:"1px solid #2563eb",borderRadius:6,background:"#2563eb",color:"#fff",cursor:"pointer",fontSize:"0.9rem",fontWeight:600,alignSelf:"flex-start"},card:{display:"flex",flexDirection:"column",gap:"0.85rem",maxWidth:600,marginTop:"0.75rem",padding:"1rem 1.1rem",border:"1px solid #e2e8f0",borderRadius:10,background:"#f8fafc"},code:{fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",fontSize:"0.78rem",color:"#334155",wordBreak:"break-all"},muted:{color:"#94a3b8"},h2:{fontSize:"1.05rem",margin:"0 0 0.35rem",fontWeight:700},h3:{fontSize:"0.92rem",margin:"1.25rem 0 0",color:"#334155",fontWeight:600},note:{color:"#64748b",fontSize:"0.82rem",margin:"0 0 0.75rem"},empty:{color:"#94a3b8",padding:"0.75rem 0.65rem"}};function w({label:t,hint:n,children:s}){return(0,K.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"0.3rem"},children:[(0,K.jsxs)("span",{style:{fontSize:"0.8rem",color:"#475569",fontWeight:600},children:[t,n?(0,K.jsxs)("span",{style:{fontWeight:400,color:"#94a3b8"},children:[" \u2014 ",n]}):null]}),s]})}function k({message:t}){return t?(0,K.jsx)("p",{role:"alert",style:{color:"#dc2626",background:"#fef2f2",border:"1px solid #fecaca",borderRadius:8,padding:"0.55rem 0.8rem",fontSize:"0.85rem",margin:"0.25rem 0"},children:t}):null}var $=require("@aiquants/authz-core"),z=require("@aiquants/select-box"),P=require("react"),m=require("react-router");function U(t){let n=t?.read?t.readPartial?"partial":"full":"none",s=t?.create&&t?.update?"full":t?.create||t?.update?"partial":"none",d=t?.delete?"full":"none";return{canRead:!!t?.read,canWrite:!!t?.write,canDelete:!!t?.delete,readState:n,writeState:s,deleteState:d}}var R=require("react/jsx-runtime"),se={full:{bg:"#ecfdf5",text:"#047857",border:"#a7f3d0"},partial:{bg:"#fffbeb",text:"#b45309",border:"#fde68a"},none:{bg:"#f8fafc",text:"#94a3b8",border:"#cbd5e1"}};function ie({state:t}){return t==="full"?(0,R.jsx)("svg",{style:{width:"0.85rem",height:"0.85rem",flexShrink:0},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:3,"aria-hidden":"true",children:(0,R.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})}):t==="partial"?(0,R.jsx)("svg",{style:{width:"0.85rem",height:"0.85rem",flexShrink:0},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2.5,"aria-hidden":"true",children:(0,R.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"})}):(0,R.jsx)("svg",{style:{width:"0.85rem",height:"0.85rem",flexShrink:0},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2.5,"aria-hidden":"true",children:(0,R.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"})})}function F({permission:t,labels:n,showScopeSummary:s=!0,className:d="authz-permission-indicator"}){let o=U(t),c={read:n?.read??"\u95B2\u89A7",write:n?.write??"\u7DE8\u96C6",delete:n?.delete??"\u524A\u9664"},l=(y,g,f)=>{let S=se[g];return(0,R.jsxs)("span",{"data-perm":y,"data-state":g,title:`${f}: ${g}`,style:{display:"inline-flex",alignItems:"center",gap:"0.25rem",padding:"0.2rem 0.5rem",borderRadius:"100px",fontSize:"0.75rem",fontWeight:600,backgroundColor:S.bg,color:S.text,border:`1px solid ${S.border}`,lineHeight:1,userSelect:"none"},children:[(0,R.jsx)(ie,{state:g}),(0,R.jsx)("span",{children:f})]})};return(0,R.jsxs)("span",{className:d,"data-resource":t?.resourceKey??"",style:{display:"inline-flex",gap:"0.35rem",alignItems:"center",verticalAlign:"middle"},children:[l("read",o.readState,c.read),l("write",o.writeState,c.write),l("delete",o.deleteState,c.delete),s&&o.readState==="partial"?(0,R.jsx)("span",{"data-perm":"scope",style:{fontSize:"0.7rem",color:"#b45309",backgroundColor:"#fef3c7",padding:"0.15rem 0.35rem",borderRadius:"4px",fontWeight:600},children:"\u90E8\u5206\u5236\u9650"}):null]})}var e=require("react/jsx-runtime"),C=t=>t?.error;function H({children:t}){let{myPermissions:n,labels:s,adminResourceKey:d,basePath:o}=(0,m.useLoaderData)(),c=o??"/authz",l=[["roles",s.tabs.roles],["resources",s.tabs.resources],["role_permissions",s.tabs.role_permissions],["user_roles",s.tabs.user_roles]];return(0,e.jsxs)("div",{className:"authz-admin",style:r.page,children:[(0,e.jsxs)("header",{style:{display:"flex",alignItems:"baseline",justifyContent:"space-between",gap:"1rem",flexWrap:"wrap"},children:[(0,e.jsx)("h1",{style:{fontSize:"1.3rem",margin:0,fontWeight:700},children:s.title}),(0,e.jsxs)("span",{style:{fontSize:"0.82rem",color:"#475569",display:"inline-flex",alignItems:"center",gap:"0.4rem"},children:[s.myPermission,": ",(0,e.jsx)(F,{permission:n[d]})]})]}),(0,e.jsx)("nav",{style:{display:"flex",gap:"0.25rem",borderBottom:"1px solid #e2e8f0",margin:"0.9rem 0 1.25rem"},children:l.map(([y,g])=>(0,e.jsx)(m.NavLink,{to:`${c}/${y}`,style:({isActive:f})=>({padding:"0.5rem 0.9rem",textDecoration:"none",color:f?"#2563eb":"#475569",borderBottom:f?"2px solid #2563eb":"2px solid transparent",marginBottom:-1,fontWeight:f?700:500,fontSize:"0.92rem"}),children:g},y))}),t]})}function ae(){return(0,e.jsx)(H,{children:(0,e.jsx)(m.Outlet,{})})}function G(t){let n=L(t).error;return function(){let d=(0,m.useRouteError)(),o=(0,m.isRouteErrorResponse)(d)?d:null,c=o?.status===403,l=c?n.forbiddenTitle:o?`${n.genericTitlePrefix} (${o.status})`:n.genericTitle,y=c?n.forbiddenBody:o?typeof o.data=="string"&&o.data?o.data:o.statusText:n.genericBody;return(0,e.jsx)("div",{style:r.page,children:(0,e.jsxs)("div",{role:"alert",style:{maxWidth:480,margin:"3rem auto",padding:"1.5rem",border:"1px solid #fecaca",borderRadius:12,background:"#fef2f2"},children:[(0,e.jsx)("h1",{style:{fontSize:"1.1rem",fontWeight:700,color:"#b91c1c",margin:"0 0 0.5rem"},children:l}),(0,e.jsx)("p",{style:{color:"#7f1d1d",fontSize:"0.9rem",margin:0,lineHeight:1.7},children:y})]})})}}var le=G(),T=(t,n)=>s=>{t&&!window.confirm(n)&&s.preventDefault()};function q(){let{roles:t,selfAdminRoleIds:n,labels:s}=(0,m.useLoaderData)(),d=s.roles,o=s.common,c=new Set(n??[]);return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:r.h2,children:d.heading}),(0,e.jsx)(k,{message:C((0,m.useActionData)())}),(0,e.jsx)("p",{style:r.note,children:d.note}),(0,e.jsx)("div",{style:r.tableContainer,children:(0,e.jsxs)("table",{style:r.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...r.th,width:"20%"},children:d.roleKey}),(0,e.jsx)("th",{style:{...r.th,width:"25%"},children:d.name}),(0,e.jsx)("th",{style:{...r.th,width:"35%"},children:d.description}),(0,e.jsx)("th",{style:{...r.th,width:"10%"},children:d.activeHeader}),(0,e.jsx)("th",{style:{...r.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(l=>(0,e.jsxs)("tr",{children:[(0,e.jsx)("td",{style:r.td,children:l.roleKey==="@authenticated"||l.roleKey==="@anonymous"?(0,e.jsx)("span",{style:r.code,children:l.roleKey}):(0,e.jsx)("input",{name:"roleKey",defaultValue:l.roleKey,form:`save-role-${l.id}`,"aria-label":`roleKey-${l.id}`,required:!0,style:r.input})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("input",{name:"name",defaultValue:l.name,form:`save-role-${l.id}`,"aria-label":`name-${l.id}`,required:!0,style:r.input})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("input",{name:"description",defaultValue:l.description??"",form:`save-role-${l.id}`,"aria-label":`description-${l.id}`,style:r.input})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsxs)(m.Form,{method:"post",style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"toggle"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:l.id}),(0,e.jsx)("input",{type:"hidden",name:"isActive",value:String(!l.isActive)}),(0,e.jsx)("button",{type:"submit",style:r.button,onClick:l.isActive?T(c.has(l.id),s.warn.selfRoleDisable):void 0,children:l.isActive?o.active:o.inactive})]})}),(0,e.jsxs)("td",{style:{...r.td,whiteSpace:"nowrap"},children:[(0,e.jsxs)(m.Form,{method:"post",id:`save-role-${l.id}`,style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"save"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:l.id}),(0,e.jsx)("button",{type:"submit",style:r.save,children:o.save})]}),(0,e.jsxs)(m.Form,{method:"post",style:{display:"inline",marginLeft:"0.4rem"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"delete"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:l.id}),(0,e.jsx)("button",{type:"submit",style:r.danger,onClick:T(c.has(l.id),s.warn.selfRoleDelete),children:o.delete})]})]})]},l.id)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:r.empty,colSpan:5,children:d.empty})}):null]})]})}),(0,e.jsx)("h3",{style:r.h3,children:d.newHeading}),(0,e.jsxs)(m.Form,{method:"post",style:r.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"create"}),(0,e.jsx)(w,{label:d.roleKey,hint:d.hintRoleKey,children:(0,e.jsx)("input",{name:"roleKey","aria-label":"role_key",required:!0,style:r.input})}),(0,e.jsx)(w,{label:d.name,children:(0,e.jsx)("input",{name:"name","aria-label":"name",required:!0,style:r.input})}),(0,e.jsx)(w,{label:d.description,hint:o.optional,children:(0,e.jsx)("input",{name:"description","aria-label":"description",style:r.input})}),(0,e.jsx)("button",{type:"submit",style:r.primary,children:o.add})]})]})}function B(){let{resources:t,labels:n}=(0,m.useLoaderData)(),s=n.resources,d=n.common;return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:r.h2,children:s.heading}),(0,e.jsx)(k,{message:C((0,m.useActionData)())}),(0,e.jsx)("p",{style:r.note,children:s.note}),(0,e.jsx)("div",{style:r.tableContainer,children:(0,e.jsxs)("table",{style:r.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...r.th,width:"15%"},children:s.appKey}),(0,e.jsx)("th",{style:{...r.th,width:"20%"},children:s.resourceKey}),(0,e.jsx)("th",{style:{...r.th,width:"25%"},children:s.name}),(0,e.jsx)("th",{style:{...r.th,width:"30%"},children:s.description}),(0,e.jsx)("th",{style:{...r.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(o=>(0,e.jsxs)("tr",{children:[(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("span",{style:r.code,children:o.appKey})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("span",{style:r.code,children:o.resourceKey})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("input",{name:"name",defaultValue:o.name,form:`save-res-${o.id}`,"aria-label":`name-${o.id}`,required:!0,style:r.input})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("input",{name:"description",defaultValue:o.description??"",form:`save-res-${o.id}`,"aria-label":`description-${o.id}`,style:r.input})}),(0,e.jsxs)("td",{style:{...r.td,whiteSpace:"nowrap"},children:[(0,e.jsxs)(m.Form,{method:"post",id:`save-res-${o.id}`,style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"save"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:o.id}),(0,e.jsx)("button",{type:"submit",style:r.save,children:d.save})]}),(0,e.jsxs)(m.Form,{method:"post",style:{display:"inline",marginLeft:"0.4rem"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"delete"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:o.id}),(0,e.jsx)("button",{type:"submit",style:r.danger,children:d.delete})]})]})]},o.id)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:r.empty,colSpan:5,children:s.empty})}):null]})]})}),(0,e.jsx)("h3",{style:r.h3,children:s.newHeading}),(0,e.jsxs)(m.Form,{method:"post",style:r.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"create"}),(0,e.jsx)(w,{label:s.appKey,hint:s.hintAppKey,children:(0,e.jsx)("input",{name:"appKey","aria-label":"app_key",required:!0,style:r.input})}),(0,e.jsx)(w,{label:s.resourceKey,hint:s.hintResourceKey,children:(0,e.jsx)("input",{name:"resourceKey","aria-label":"resource_key",required:!0,style:r.input})}),(0,e.jsx)(w,{label:s.name,children:(0,e.jsx)("input",{name:"name","aria-label":"name",required:!0,style:r.input})}),(0,e.jsx)(w,{label:s.description,hint:d.optional,children:(0,e.jsx)("input",{name:"description","aria-label":"description",style:r.input})}),(0,e.jsx)("button",{type:"submit",style:r.primary,children:d.add})]})]})}function de({g:t,labels:n}){let s=n.common,[d,o]=(0,P.useState)({label:t.action,value:t.action});return(0,e.jsxs)("tr",{children:[(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("span",{style:r.code,children:t.roleKey})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsxs)("span",{style:r.code,children:[t.appKey,":",t.resourceKey]})}),(0,e.jsx)("td",{style:r.td,children:t.roleKey==="@anonymous"?(0,e.jsx)("span",{style:r.code,children:t.action}):(0,e.jsxs)(e.Fragment,{children:[(0,e.jsx)("input",{type:"hidden",name:"action",value:d.value,form:`save-grant-${t.id}`}),(0,e.jsx)(z.SelectBox,{options:$.AUTHZ_ACTIONS.map(c=>({label:c,value:c})),value:d,onChange:c=>{c&&o(c)}})]})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("textarea",{name:"rowScope",defaultValue:t.rowScope??"",form:`save-grant-${t.id}`,"aria-label":`row_scope-${t.id}`,rows:2,placeholder:n.grants.rowScopePlaceholder,style:r.textarea})}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsx)("textarea",{name:"columnScope",defaultValue:t.columnScope??"",form:`save-grant-${t.id}`,"aria-label":`column_scope-${t.id}`,rows:2,placeholder:n.grants.columnScopePlaceholder,style:r.textarea})}),(0,e.jsxs)("td",{style:{...r.td,whiteSpace:"nowrap"},children:[(0,e.jsxs)(m.Form,{method:"post",id:`save-grant-${t.id}`,style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"save"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:t.id}),(0,e.jsx)("button",{type:"submit",style:r.save,children:s.save})]}),(0,e.jsxs)(m.Form,{method:"post",style:{display:"inline",marginLeft:"0.4rem"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"delete"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:t.id}),(0,e.jsx)("button",{type:"submit",style:r.danger,children:s.delete})]})]})]})}function M(){let{grants:t,roles:n,resources:s,labels:d}=(0,m.useLoaderData)(),o=d.grants,c=d.common,l=n.map(i=>({label:i.roleKey,value:i.id})),y=s.map(i=>({label:`${i.appKey}:${i.resourceKey}`,value:i.id})),g=$.AUTHZ_ACTIONS.map(i=>({label:i,value:i})),[f,S]=(0,P.useState)(void 0),[u,a]=(0,P.useState)(void 0),[h,p]=(0,P.useState)({label:"read",value:"read"});return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:r.h2,children:o.heading}),(0,e.jsx)("p",{style:r.note,children:o.note}),(0,e.jsx)(k,{message:C((0,m.useActionData)())}),(0,e.jsx)("div",{style:r.tableContainer,children:(0,e.jsxs)("table",{style:r.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...r.th,width:"15%"},children:o.role}),(0,e.jsx)("th",{style:{...r.th,width:"20%"},children:o.resource}),(0,e.jsx)("th",{style:{...r.th,width:"15%"},children:o.action}),(0,e.jsx)("th",{style:{...r.th,width:"20%"},children:o.rowScope}),(0,e.jsx)("th",{style:{...r.th,width:"20%"},children:o.columnScope}),(0,e.jsx)("th",{style:{...r.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(i=>(0,e.jsx)(de,{g:i,labels:d},i.id)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:r.empty,colSpan:6,children:o.empty})}):null]})]})}),(0,e.jsx)("h3",{style:r.h3,children:o.newHeading}),(0,e.jsxs)(m.Form,{method:"post",style:r.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"create"}),(0,e.jsx)("input",{type:"hidden",name:"roleId",value:f?.value??""}),(0,e.jsx)("input",{type:"hidden",name:"resourceId",value:u?.value??""}),(0,e.jsx)("input",{type:"hidden",name:"action",value:h?.value??""}),(0,e.jsx)(w,{label:o.role,children:(0,e.jsx)(z.SelectBox,{options:l,value:f,onChange:i=>S(i||void 0),placeholder:o.selectPlaceholder})}),(0,e.jsx)(w,{label:o.resource,children:(0,e.jsx)(z.SelectBox,{options:y,value:u,onChange:i=>a(i||void 0),placeholder:o.selectPlaceholder})}),(0,e.jsx)(w,{label:o.action,children:(0,e.jsx)(z.SelectBox,{options:g,value:h,onChange:i=>p(i||void 0)})}),(0,e.jsx)(w,{label:o.rowScope,hint:o.rowScopeHint,children:(0,e.jsx)("textarea",{name:"rowScope","aria-label":"row_scope",rows:2,style:r.textarea,placeholder:'{"filters":[{"field":"\u90E8\u9580CD","op":"in","values":["A"]}]}'})}),(0,e.jsx)(w,{label:o.columnScope,hint:o.columnScopeHint,children:(0,e.jsx)("textarea",{name:"columnScope","aria-label":"column_scope",rows:2,style:r.textarea,placeholder:'{"mode":"deny","columns":["\u91D1\u984D"]}'})}),(0,e.jsx)("button",{type:"submit",style:r.primary,children:c.grant})]})]})}function O(){let{assignments:t,roles:n,users:s,actorUserId:d,selfAdminRoleIds:o,labels:c}=(0,m.useLoaderData)(),l=c.userRoles,y=c.common,g=new Set(o??[]),f=s.map(i=>({label:`${i.email}\uFF08${i.displayName}\uFF09`,value:i.id})),S=n.filter(i=>i.roleKey!=="@authenticated"&&i.roleKey!=="@anonymous").map(i=>({label:i.roleKey,value:i.id})),[u,a]=(0,P.useState)(void 0),[h,p]=(0,P.useState)(void 0);return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:r.h2,children:l.heading}),(0,e.jsx)(k,{message:C((0,m.useActionData)())}),(0,e.jsx)("p",{style:r.note,children:l.note}),(0,e.jsx)("div",{style:r.tableContainer,children:(0,e.jsxs)("table",{style:r.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...r.th,width:"45%"},children:l.user}),(0,e.jsx)("th",{style:{...r.th,width:"45%"},children:l.role}),(0,e.jsx)("th",{style:{...r.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(i=>(0,e.jsxs)("tr",{children:[(0,e.jsxs)("td",{style:r.td,children:[i.email,(0,e.jsxs)("span",{style:r.muted,children:["\uFF08",i.displayName,"\uFF09"]})]}),(0,e.jsx)("td",{style:r.td,children:i.roleKey}),(0,e.jsx)("td",{style:r.td,children:(0,e.jsxs)(m.Form,{method:"post",style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"remove"}),(0,e.jsx)("input",{type:"hidden",name:"userId",value:i.userId}),(0,e.jsx)("input",{type:"hidden",name:"roleId",value:i.roleId}),(0,e.jsx)("button",{type:"submit",style:r.danger,onClick:T(i.userId===d&&g.has(i.roleId),c.warn.selfAssignmentRemove),children:y.remove})]})})]},`${i.userId}-${i.roleId}`)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:r.empty,colSpan:3,children:l.empty})}):null]})]})}),(0,e.jsx)("h3",{style:r.h3,children:l.newHeading}),(0,e.jsxs)(m.Form,{method:"post",style:r.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"assign"}),(0,e.jsx)("input",{type:"hidden",name:"userId",value:u?.value??""}),(0,e.jsx)("input",{type:"hidden",name:"roleId",value:h?.value??""}),(0,e.jsx)(w,{label:l.user,children:(0,e.jsx)(z.SelectBox,{options:f,value:u,onChange:i=>a(i||void 0),placeholder:l.selectPlaceholder})}),(0,e.jsx)(w,{label:l.role,children:(0,e.jsx)(z.SelectBox,{options:S,value:h,onChange:i=>p(i||void 0),placeholder:l.selectPlaceholder})}),(0,e.jsx)("button",{type:"submit",style:r.primary,children:y.assign})]})]})}var ue={roles:q,resources:B,role_permissions:M,user_roles:O};function ce(){let{segment:t}=(0,m.useLoaderData)(),n=ue[t]??q;return(0,e.jsx)(H,{children:(0,e.jsx)(n,{})})}var x=require("@aiquants/authz-core"),J=require("react-router");function Q(t,n){let s=(0,x.can)(n,"read"),d=(0,x.can)(n,"create"),o=(0,x.can)(n,"update"),c=(0,x.can)(n,"delete"),l=n?.scopeByAction.read,y=s&&!!l&&(l.rowScope!==null||l.columnScope!==null);return{resourceKey:t,read:s,create:d,update:o,delete:c,write:d||o,readPartial:y,actions:n?Array.from(n.actions):[]}}function me(t){async function n(d,o,c){let l=c?.failureRedirect?()=>{throw(0,J.redirect)(c.failureRedirect)}:t.onDeny;return(0,x.createRequirePermission)({resolveUserId:t.resolveUserId,getEffectivePermissions:t.getEffectivePermissions,onDeny:l})(d,{appKey:t.appKey,resourceKey:o.resourceKey,action:o.action})}async function s(d,o){let c=await t.resolveUserId(d),l={};for(let y of o.resourceKeys){let g=await t.getEffectivePermissions({userId:c??null,appKey:t.appKey,resourceKey:y});l[y]=Q(y,g)}return l}return{requirePermission:n,getMyPermissions:s}}0&&(module.exports={AuthzAdminAppView,AuthzErrorBoundary,AuthzGrantsView,AuthzLayout,AuthzResourcesView,AuthzRolesView,AuthzUserRolesView,ErrorAlert,Field,MyPermissionIndicator,createAuthz,createAuthzAdminApp,createAuthzAdminServer,defaultAuthzAdminLabels,makeAuthzErrorBoundary,resolveLabels,toPermissionView,ui,usePermission});
1
+ "use strict";var F=Object.defineProperty;var oe=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ie=Object.prototype.hasOwnProperty;var ae=(t,r)=>{for(var n in r)F(t,n,{get:r[n],enumerable:!0})},le=(t,r,n,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of se(r))!ie.call(t,o)&&o!==n&&F(t,o,{get:()=>r[o],enumerable:!(i=oe(r,o))||i.enumerable});return t};var de=t=>le(F({},"__esModule",{value:!0}),t);var xe={};ae(xe,{AuthzAdminAppView:()=>Se,AuthzErrorBoundary:()=>he,AuthzGrantsView:()=>ee,AuthzLayout:()=>fe,AuthzResourcesView:()=>X,AuthzRolesView:()=>B,AuthzUserRolesView:()=>te,ErrorAlert:()=>C,Field:()=>x,MyPermissionGroupStatus:()=>q,MyPermissionIndicator:()=>_,MyPermissionStatus:()=>ye,createAuthz:()=>Re,createAuthzAdminApp:()=>ce,createAuthzAdminServer:()=>T,defaultAuthzAdminLabels:()=>H,defineResources:()=>Ae,flattenGroupItems:()=>U,formatResourceTooltip:()=>W,getPermissionSummary:()=>Q,makeAuthzErrorBoundary:()=>Z,resolveLabels:()=>D,toPermissionView:()=>ne,ui:()=>s,usePermission:()=>M});module.exports=de(xe);var G=require("react-router");var z=require("@aiquants/authz-core"),R=require("react-router");var H={title:"Authorization (RBAC)",myPermission:"Your permissions",tabs:{roles:"Roles",resources:"Resources",role_permissions:"Permissions",user_roles:"User assignments"},common:{add:"Add",save:"Save",delete:"Delete",remove:"Remove",assign:"Assign",grant:"Grant",select:"Select\u2026",active:"\u25CB Enabled",inactive:"\xD7 Disabled",optional:"optional"},roles:{heading:"Roles (TMRole)",note:"Edit name / description inline and click Save (role_key is an immutable identifier).",roleKey:"role_key",name:"name",description:"description",activeHeader:"Active",empty:"No roles",newHeading:"New role",hintRoleKey:"e.g. admin"},resources:{heading:"Resources (TMResource)",note:"Edit name / description inline and click Save (app_key / resource_key are immutable identifiers).",appKey:"app_key",resourceKey:"resource_key",name:"name",description:"description",empty:"No resources",newHeading:"New resource",hintAppKey:"e.g. quants",hintResourceKey:"e.g. daily_report",hintDailyReport:"optional"},grants:{heading:"Permissions (TDRolePermission)",note:"Empty row_scope / column_scope = all rows / all columns (NULL). JSON must satisfy the \xA73.2 contract.",role:"role",resource:"resource",action:"action",rowScope:"row_scope",columnScope:"column_scope",empty:"No permissions",newHeading:"Grant a permission",rowScopeHint:"JSON / empty = all rows",columnScopeHint:"JSON / empty = all columns",rowScopePlaceholder:"empty = all rows (NULL)",columnScopePlaceholder:"empty = all columns (NULL)",nullRows:"NULL (all rows)",nullCols:"NULL (all columns)",selectPlaceholder:"Select\u2026"},userRoles:{heading:"User assignments (TDUserRole)",note:"Assign active roles to specific users.",user:"user",role:"role",empty:"No assignments",newHeading:"Assign a role",selectPlaceholder:"Select\u2026"},error:{forbiddenTitle:"Access denied",forbiddenBody:"You don't have permission to view this screen. Ask an administrator to grant access.",genericTitlePrefix:"Error",genericTitle:"An error occurred",genericBody:"An unexpected error occurred. Please try again later."},warn:{selfRoleDisable:"This role backs your own authorization-admin access. Disabling it may lock you out of this screen. Continue?",selfRoleDelete:"This role backs your own authorization-admin access. Deleting it may lock you out of this screen. Continue?",selfAssignmentRemove:"This assignment backs your own authorization-admin access. Removing it may lock you out of this screen. Continue?"}};function D(t){let r=H;return t?{title:t.title??r.title,myPermission:t.myPermission??r.myPermission,tabs:{...r.tabs,...t.tabs},common:{...r.common,...t.common},roles:{...r.roles,...t.roles},resources:{...r.resources,...t.resources},grants:{...r.grants,...t.grants},userRoles:{...r.userRoles,...t.userRoles},error:{...r.error,...t.error},warn:{...r.warn,...t.warn}}:r}var ue=t=>t instanceof Response;function N(t){if(ue(t))throw t;if(t instanceof z.AuthzDeniedError)throw new Response(t.message,{status:403});return(0,R.data)({error:t instanceof Error?t.message:"\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"},{status:400})}function T(t){let r=t.store,n=t.resourceKey??"authz_admin",i=D(t.labels),o=async(u,a)=>{let{userId:y}=await t.requirePermission(u,a);if(y==null)throw new z.AuthzDeniedError({appKey:"",resourceKey:a.resourceKey,action:a.action,reason:"unresolved-user",userId:null});return{userId:y}};async function c(u){await t.requireUser(u);try{return await o(u,{resourceKey:n,action:"read"})}catch(a){throw a instanceof z.AuthzDeniedError?new Response(`Forbidden: ${n}`,{status:403}):a}}let l=u=>t.getMyPermissions(u,{resourceKeys:[n]}),p=async u=>{let[a,y]=await Promise.all([r.listRoles(),r.getSelfAdminContext(Number(u))]);return{roles:a,selfAdminRoleIds:y.selfAdminRoleIds,labels:i}},g=async()=>({resources:await r.listResources(),labels:i}),f=async()=>{let[u,a,y]=await Promise.all([r.listGrants(),r.listRoles(),r.listResources()]);return{grants:u,roles:a,resources:y,labels:i}},b=async u=>{let[a,y,m,d]=await Promise.all([r.listUserRoles(),r.listRoles(),r.listUsers(),r.getSelfAdminContext(Number(u))]);return{assignments:a,roles:y,users:m,actorUserId:d.actorUserId,selfAdminRoleIds:d.selfAdminRoleIds,labels:i}};return{labels:i,resourceKey:n,layoutGuard:c,loadMyPermissions:l,layout:{async loader({request:u}){return await c(u),{myPermissions:await l(u),labels:i,adminResourceKey:n}}},index:{loader(){throw(0,R.redirect)("/authz/roles")}},roles:{data:p,async loader({request:u}){let{userId:a}=await o(u,{resourceKey:n,action:"read"});return p(a)},async action({request:u}){let a=await u.formData(),y=String(a.get("_action"));try{switch(y){case"create":{let{userId:m}=await o(u,{resourceKey:n,action:"create"}),d=String(a.get("roleKey")??"").trim(),w=String(a.get("name")??"").trim();return d&&w?(await r.createRole({roleKey:d,name:w,description:String(a.get("description")??"").trim()||null},String(m)),{ok:!0}):(0,R.data)({error:"role_key \u3068 name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"save":{let{userId:m}=await o(u,{resourceKey:n,action:"update"}),d=String(a.get("roleKey")??"").trim(),w=String(a.get("name")??"").trim();return d&&w?(await r.updateRole(Number(a.get("id")),{roleKey:d,name:w,description:String(a.get("description")??"").trim()||null},String(m)),{ok:!0}):(0,R.data)({error:"roleKey \u3068 name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"toggle":{let{userId:m}=await o(u,{resourceKey:n,action:"update"});return await r.setRoleActive(Number(a.get("id")),a.get("isActive")==="true",String(m)),{ok:!0}}case"delete":return await o(u,{resourceKey:n,action:"delete"}),await r.deleteRole(Number(a.get("id"))),{ok:!0};default:return(0,R.data)({error:"unknown action"},{status:400})}}catch(m){return N(m)}}},resources:{data:g,async loader({request:u}){return await o(u,{resourceKey:n,action:"read"}),g()},async action({request:u}){let a=await u.formData(),y=String(a.get("_action"));try{switch(y){case"create":{let{userId:m}=await o(u,{resourceKey:n,action:"create"}),d=String(a.get("appKey")??"").trim(),w=String(a.get("resourceKey")??"").trim(),v=String(a.get("name")??"").trim();return d&&w&&v?(await r.createResource({appKey:d,resourceKey:w,name:v,description:String(a.get("description")??"").trim()||null},String(m)),{ok:!0}):(0,R.data)({error:"app_key / resource_key / name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"save":{let{userId:m}=await o(u,{resourceKey:n,action:"update"}),d=String(a.get("name")??"").trim();return d?(await r.updateResource(Number(a.get("id")),{name:d,description:String(a.get("description")??"").trim()||null},String(m)),{ok:!0}):(0,R.data)({error:"name \u306F\u5FC5\u9808\u3067\u3059"},{status:400})}case"delete":return await o(u,{resourceKey:n,action:"delete"}),await r.deleteResource(Number(a.get("id"))),{ok:!0};default:return(0,R.data)({error:"unknown action"},{status:400})}}catch(m){return N(m)}}},grants:{data:f,async loader({request:u}){return await o(u,{resourceKey:n,action:"read"}),f()},async action({request:u}){let a=await u.formData(),y=String(a.get("_action"));try{switch(y){case"create":{let{userId:m}=await o(u,{resourceKey:n,action:"create"}),d=Number(a.get("roleId")),w=Number(a.get("resourceId"));if(!(d&&w))return(0,R.data)({error:"\u30ED\u30FC\u30EB\u3068\u30EA\u30BD\u30FC\u30B9\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044"},{status:400});let v=String(a.get("action")??"");if(!z.AUTHZ_ACTIONS.includes(v))return(0,R.data)({error:"\u7121\u52B9\u306A action \u3067\u3059"},{status:400});let E=v;return await r.createGrant({roleId:d,resourceId:w,action:E,rowScope:String(a.get("rowScope")??""),columnScope:String(a.get("columnScope")??"")},String(m)),{ok:!0}}case"save":{let{userId:m}=await o(u,{resourceKey:n,action:"update"}),d=String(a.get("action")??"");if(!z.AUTHZ_ACTIONS.includes(d))return(0,R.data)({error:"\u7121\u52B9\u306A action \u3067\u3059"},{status:400});let w=d;return await r.updateGrant(Number(a.get("id")),{action:w,rowScope:String(a.get("rowScope")??""),columnScope:String(a.get("columnScope")??"")},String(m)),{ok:!0}}case"delete":return await o(u,{resourceKey:n,action:"delete"}),await r.deleteGrant(Number(a.get("id"))),{ok:!0};default:return(0,R.data)({error:"unknown action"},{status:400})}}catch(m){return N(m)}}},userRoles:{data:b,async loader({request:u}){let{userId:a}=await o(u,{resourceKey:n,action:"read"});return b(a)},async action({request:u}){let a=await u.formData(),y=String(a.get("_action"));try{switch(y){case"assign":{let{userId:m}=await o(u,{resourceKey:n,action:"create"}),d=Number(a.get("userId")),w=Number(a.get("roleId"));return d&&w?(await r.assignUserRole(d,w,String(m)),{ok:!0}):(0,R.data)({error:"\u30E6\u30FC\u30B6\u30FC\u3068\u30ED\u30FC\u30EB\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044"},{status:400})}case"remove":return await o(u,{resourceKey:n,action:"delete"}),await r.removeUserRole(Number(a.get("userId")),Number(a.get("roleId"))),{ok:!0};default:return(0,R.data)({error:"unknown action"},{status:400})}}catch(m){return N(m)}}}}}function j(t){return String(t["*"]??"").split("/")[0]}function ce(t){let r=T(t),n=t.basePath??"/authz",i=["roles","resources","role_permissions","user_roles"],o={roles:r.roles.data,resources:r.resources.data,role_permissions:r.grants.data,user_roles:r.userRoles.data},c={roles:r.roles.action,resources:r.resources.action,role_permissions:r.grants.action,user_roles:r.userRoles.action};async function l(g){let f=j(g.params);if(!i.includes(f))throw(0,G.redirect)(`${n}/roles`);let{userId:b}=await r.layoutGuard(g.request),[u,a]=await Promise.all([r.loadMyPermissions(g.request),o[f](b)]);return{segment:f,basePath:n,myPermissions:u,adminResourceKey:r.resourceKey,...a}}async function p(g){let f=j(g.params),b=c[f];return b?b(g):(0,G.data)({error:"unknown action"},{status:400})}return{loader:l,action:p,server:r}}var I=require("react/jsx-runtime"),s={page:{padding:"1.25rem",maxWidth:1280,margin:"0 auto",color:"#0f172a"},tableContainer:{width:"100%",overflowX:"auto",marginBottom:"1rem",WebkitOverflowScrolling:"touch"},table:{borderCollapse:"collapse",width:"100%",fontSize:"0.9rem",tableLayout:"fixed"},th:{textAlign:"left",borderBottom:"2px solid #e2e8f0",padding:"0.45rem 0.65rem",fontSize:"0.78rem",color:"#475569",fontWeight:600,textTransform:"uppercase",letterSpacing:"0.03em"},td:{borderBottom:"1px solid #f1f5f9",padding:"0.5rem 0.65rem",verticalAlign:"middle",wordBreak:"break-all"},input:{padding:"0.4rem 0.55rem",border:"1px solid #cbd5e1",borderRadius:6,fontSize:"0.9rem",background:"#fff",width:"100%",boxSizing:"border-box"},select:{padding:"0.4rem 0.55rem",border:"1px solid #cbd5e1",borderRadius:6,fontSize:"0.9rem",background:"#fff",width:"100%",boxSizing:"border-box"},textarea:{padding:"0.4rem 0.55rem",border:"1px solid #cbd5e1",borderRadius:6,fontSize:"0.82rem",fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",minHeight:"3.25rem",resize:"vertical",width:"100%",boxSizing:"border-box"},button:{padding:"0.3rem 0.7rem",border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",cursor:"pointer",fontSize:"0.82rem",color:"#334155"},save:{padding:"0.3rem 0.7rem",border:"1px solid #2563eb",borderRadius:6,background:"#eff6ff",cursor:"pointer",fontSize:"0.82rem",color:"#2563eb",fontWeight:600},danger:{padding:"0.3rem 0.7rem",border:"1px solid #fecaca",borderRadius:6,background:"#fff",cursor:"pointer",fontSize:"0.82rem",color:"#dc2626"},primary:{padding:"0.5rem 1.25rem",border:"1px solid #2563eb",borderRadius:6,background:"#2563eb",color:"#fff",cursor:"pointer",fontSize:"0.9rem",fontWeight:600,alignSelf:"flex-start"},card:{display:"flex",flexDirection:"column",gap:"0.85rem",maxWidth:600,marginTop:"0.75rem",padding:"1rem 1.1rem",border:"1px solid #e2e8f0",borderRadius:10,background:"#f8fafc"},code:{fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",fontSize:"0.78rem",color:"#334155",wordBreak:"break-all"},muted:{color:"#94a3b8"},h2:{fontSize:"1.05rem",margin:"0 0 0.35rem",fontWeight:700},h3:{fontSize:"0.92rem",margin:"1.25rem 0 0",color:"#334155",fontWeight:600},note:{color:"#64748b",fontSize:"0.82rem",margin:"0 0 0.75rem"},empty:{color:"#94a3b8",padding:"0.75rem 0.65rem"}};function x({label:t,hint:r,children:n}){return(0,I.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"0.3rem"},children:[(0,I.jsxs)("span",{style:{fontSize:"0.8rem",color:"#475569",fontWeight:600},children:[t,r?(0,I.jsxs)("span",{style:{fontWeight:400,color:"#94a3b8"},children:[" \u2014 ",r]}):null]}),n]})}function C({message:t}){return t?(0,I.jsx)("p",{role:"alert",style:{color:"#dc2626",background:"#fef2f2",border:"1px solid #fecaca",borderRadius:8,padding:"0.55rem 0.8rem",fontSize:"0.85rem",margin:"0.25rem 0"},children:t}):null}var V=require("@aiquants/authz-core"),K=require("@aiquants/select-box"),P=require("react"),h=require("react-router");var L=require("react");function M(t){let r=t?.read?t.readPartial?"partial":"full":"none",n=t?.create&&t?.update?"full":t?.create||t?.update?"partial":"none",i=t?.delete?"full":"none";return{canRead:!!t?.read,canWrite:!!t?.write,canDelete:!!t?.delete,readState:r,writeState:n,deleteState:i}}var A=require("react/jsx-runtime"),pe={full:{bg:"#ecfdf5",text:"#047857",border:"#a7f3d0"},partial:{bg:"#fffbeb",text:"#b45309",border:"#fde68a"},none:{bg:"#f8fafc",text:"#94a3b8",border:"#cbd5e1"}};function W(t,r){let n=t?.trim(),i=r?.trim();if(i&&n&&i!==n)return`\u5BFE\u8C61\u30EA\u30BD\u30FC\u30B9: ${i} (${n})`;if(i||n)return`\u5BFE\u8C61\u30EA\u30BD\u30FC\u30B9: ${i||n}`}function me({state:t}){return t==="full"?(0,A.jsx)("svg",{style:{width:"0.85rem",height:"0.85rem",flexShrink:0},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:3,"aria-hidden":"true",children:(0,A.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})}):t==="partial"?(0,A.jsx)("svg",{style:{width:"0.85rem",height:"0.85rem",flexShrink:0},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2.5,"aria-hidden":"true",children:(0,A.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"})}):(0,A.jsx)("svg",{style:{width:"0.85rem",height:"0.85rem",flexShrink:0},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2.5,"aria-hidden":"true",children:(0,A.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"})})}function _({permission:t,resourceName:r,expectedResourceKey:n,showResourceTooltip:i=!0,labels:o,showScopeSummary:c=!0,className:l="authz-permission-indicator"}){let p=M(t),g={read:o?.read??"\u95B2\u89A7",write:o?.write??"\u7DE8\u96C6",delete:o?.delete??"\u524A\u9664"},f=t?.resourceKey,b=r||f,u=i?W(f,r):void 0,a=!!(n&&f&&f!==n);a&&typeof console<"u"&&typeof console.error=="function"&&console.error(`[Authz Warning] Resource key mismatch in MyPermissionIndicator! Expected: "${n}", but received permission for: "${f}".`);let y=(m,d,w)=>{let v=pe[d],E=u?`${w}: ${d}
2
+ ${u}`:`${w}: ${d}`;return(0,A.jsxs)("span",{"data-perm":m,"data-state":d,title:E,style:{display:"inline-flex",alignItems:"center",gap:"0.25rem",padding:"0.2rem 0.5rem",borderRadius:"100px",fontSize:"0.75rem",fontWeight:600,backgroundColor:v.bg,color:v.text,border:`1px solid ${v.border}`,lineHeight:1,userSelect:"none"},children:[(0,A.jsx)(me,{state:d}),(0,A.jsx)("span",{children:w})]})};return(0,A.jsxs)("span",{className:l,title:u,"data-resource":f??"","data-resource-name":b??"","data-mismatch":a?"true":void 0,style:{display:"inline-flex",gap:"0.35rem",alignItems:"center",verticalAlign:"middle"},children:[y("read",p.readState,g.read),y("write",p.writeState,g.write),y("delete",p.deleteState,g.delete),c&&p.readState==="partial"?(0,A.jsx)("span",{"data-perm":"scope",title:u,style:{fontSize:"0.7rem",color:"#b45309",backgroundColor:"#fef3c7",padding:"0.15rem 0.35rem",borderRadius:"4px",fontWeight:600},children:"\u90E8\u5206\u5236\u9650"}):null,a?(0,A.jsxs)("span",{"data-testid":"resource-mismatch-warning",title:`expected: "${n}", actual: "${f}"`,style:{fontSize:"0.7rem",color:"#991b1b",backgroundColor:"#fee2e2",border:"1px solid #fca5a5",padding:"0.15rem 0.35rem",borderRadius:"4px",fontWeight:700},children:["\u26A0\uFE0F \u30EA\u30BD\u30FC\u30B9\u4E0D\u4E00\u81F4 (",f," \u2260 ",n,")"]}):null]})}function ye({permission:t,resourceName:r,expectedResourceKey:n,prefixLabel:i="\u3042\u306A\u305F\u306E\u6A29\u9650",showPrefixLabel:o=!0,showResourceTooltip:c=!0,labels:l,showScopeSummary:p=!0,className:g="authz-permission-status",style:f}){let b=t?.resourceKey,u=r||b,a=c?W(b,r):void 0;return(0,A.jsxs)("span",{className:g,title:a,"data-testid":"my-permission-status","data-resource":b??"","data-resource-name":u??"",style:{fontSize:"0.82rem",color:"#475569",display:"inline-flex",alignItems:"center",gap:"0.4rem",...f},children:[o&&(0,A.jsxs)("span",{"data-testid":"prefix-label",children:[i,":"]}),(0,A.jsx)(_,{permission:t,resourceName:r,expectedResourceKey:n,showResourceTooltip:c,labels:l,showScopeSummary:p})]})}var S=require("react/jsx-runtime");function U(t,r=0){let n=[];for(let i of t){let o=i.level??r;n.push({...i,level:o}),i.children&&i.children.length>0&&n.push(...U(i.children,o+1))}return n}var J={"2xs":{fontSize:"0.6rem",pillPadding:"0.05rem 0.25rem",pillGap:"0.15rem",wrapperGap:"0.15rem",triggerPadding:"0.1rem 0.35rem",triggerFontSize:"0.62rem",badgeFontSize:"0.58rem"},xs:{fontSize:"0.65rem",pillPadding:"0.08rem 0.35rem",pillGap:"0.18rem",wrapperGap:"0.2rem",triggerPadding:"0.12rem 0.4rem",triggerFontSize:"0.68rem",badgeFontSize:"0.62rem"},sm:{fontSize:"0.72rem",pillPadding:"0.1rem 0.4rem",pillGap:"0.2rem",wrapperGap:"0.25rem",triggerPadding:"0.15rem 0.45rem",triggerFontSize:"0.72rem",badgeFontSize:"0.65rem"},md:{fontSize:"0.78rem",pillPadding:"0.15rem 0.5rem",pillGap:"0.3rem",wrapperGap:"0.35rem",triggerPadding:"0.2rem 0.6rem",triggerFontSize:"0.78rem",badgeFontSize:"0.7rem"},lg:{fontSize:"0.85rem",pillPadding:"0.25rem 0.65rem",pillGap:"0.4rem",wrapperGap:"0.45rem",triggerPadding:"0.25rem 0.75rem",triggerFontSize:"0.85rem",badgeFontSize:"0.75rem"}};function Q(t,r){let n=r?.noPermission??"\u6A29\u9650\u306A\u3057",i=r?.fullWrite??"\u7DE8\u96C6\u53EF",o=r?.partialRead??"\u4E00\u90E8\u95B2\u89A7",c=r?.readOnly??"\u95B2\u89A7\u306E\u307F";return t&&(t.read||t.write||t.delete)?t.write?{text:i,color:"#047857",bgColor:"#ecfdf5",borderColor:"#a7f3d0"}:t.read?t.readPartial?{text:o,color:"#b45309",bgColor:"#fffbeb",borderColor:"#fde68a"}:{text:c,color:"#1d4ed8",bgColor:"#eff6ff",borderColor:"#bfdbfe"}:{text:n,color:"#64748b",bgColor:"#f1f5f9",borderColor:"#cbd5e1"}:{text:n,color:"#64748b",bgColor:"#f1f5f9",borderColor:"#cbd5e1"}}function ge({item:t,size:r="md",labels:n}){let i=Q(t.permission,n),o=t.permission?.resourceKey||t.resourceKey||t.expectedResourceKey,c=t.resourceName||o,l=!!(t.expectedResourceKey&&t.permission?.resourceKey&&t.permission.resourceKey!==t.expectedResourceKey),p=J[r],g=`\u5BFE\u8C61: ${c}${o?` (${o})`:""}${t.permission?` | ${n?.read??"\u95B2\u89A7"}:${t.permission.read?"\u25CB":"\xD7"} ${n?.write??"\u7DE8\u96C6"}:${t.permission.write?"\u25CB":"\xD7"} ${n?.delete??"\u524A\u9664"}:${t.permission.delete?"\u25CB":"\xD7"}`:""}`;return(0,S.jsxs)("span",{title:g,"data-testid":`permission-pill-${t.resourceName}`,"data-mismatch":l?"true":"false",style:{display:"inline-flex",alignItems:"center",gap:p.pillGap,fontSize:p.fontSize,padding:p.pillPadding,borderRadius:"9999px",backgroundColor:l?"#fee2e2":i.bgColor,color:l?"#991b1b":i.color,border:`1px solid ${l?"#fca5a5":i.borderColor}`,fontWeight:600,lineHeight:"1.2"},children:[(0,S.jsxs)("span",{children:[c,":"]}),(0,S.jsx)("span",{children:i.text}),l&&(0,S.jsx)("span",{title:`expected "${t.expectedResourceKey}", actual "${t.permission?.resourceKey}"`,children:"\u26A0\uFE0F"})]})}function q({items:t,prefixLabel:r="\u3042\u306A\u305F\u306E\u6A29\u9650",variant:n="pills",size:i="md",labels:o,className:c="authz-permission-group-status",style:l}){let[p,g]=(0,L.useState)(!1),f=(0,L.useRef)(null),b=J[i];if((0,L.useEffect)(()=>{if(n!=="popover"||!p)return;function u(y){f.current&&!f.current.contains(y.target)&&g(!1)}function a(y){y.key==="Escape"&&g(!1)}return document.addEventListener("mousedown",u),document.addEventListener("keydown",a),()=>{document.removeEventListener("mousedown",u),document.removeEventListener("keydown",a)}},[n,p]),n==="popover"){let u=t.filter(m=>m.permission?.write).length,a=t.length,y=u===a?o?.fullWrite??"\u7DE8\u96C6\u53EF":u>0?o?.partialWrite??"\u4E00\u90E8\u7DE8\u96C6\u53EF":o?.readOnly??"\u95B2\u89A7\u306E\u307F";return(0,S.jsxs)("span",{ref:f,className:c,"data-testid":"my-permission-group-popover",style:{position:"relative",display:"inline-block",...l},children:[(0,S.jsxs)("button",{type:"button",onClick:()=>g(!p),"data-testid":"permission-popover-trigger",style:{display:"inline-flex",alignItems:"center",gap:b.wrapperGap,fontSize:b.triggerFontSize,padding:b.triggerPadding,borderRadius:"6px",backgroundColor:"#f8fafc",color:"#334155",border:"1px solid #cbd5e1",fontWeight:600,cursor:"pointer"},children:[(0,S.jsxs)("span",{children:["\u{1F512} ",r]}),(0,S.jsxs)("span",{style:{fontSize:b.badgeFontSize,color:"#64748b"},children:["(",y,")"]}),(0,S.jsx)("span",{style:{fontSize:"0.65rem",marginLeft:"0.1rem"},children:p?"\u25B2":"\u25BE"})]}),p&&(0,S.jsxs)("div",{"data-testid":"permission-popover-card",style:{position:"absolute",top:"calc(100% + 6px)",right:0,zIndex:50,minWidth:"280px",whiteSpace:"nowrap",padding:"0.75rem 0.85rem",backgroundColor:"#ffffff",borderRadius:"8px",boxShadow:"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",border:"1px solid #e2e8f0",fontSize:"0.82rem"},children:[(0,S.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"0.5rem",paddingBottom:"0.4rem",borderBottom:"1px solid #f1f5f9",fontWeight:700,color:"#1e293b",whiteSpace:"nowrap"},children:[(0,S.jsxs)("span",{children:["\u{1F512} ",r]}),(0,S.jsx)("button",{type:"button",onClick:()=>g(!1),style:{border:"none",background:"none",cursor:"pointer",color:"#94a3b8",fontSize:"0.85rem",padding:"0 0.2rem"},children:"\u2715"})]}),(0,S.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:"0.4rem"},children:U(t).map(m=>{let d=m.level??0,w=d>0;return(0,S.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"1.25rem",whiteSpace:"nowrap",paddingLeft:w?`${d*.85}rem`:"0"},children:[(0,S.jsxs)("div",{style:{display:"inline-flex",alignItems:"center",whiteSpace:"nowrap"},children:[w&&(0,S.jsx)("span",{"aria-hidden":"true",style:{display:"inline-block",width:"10px",height:"10px",borderLeft:"1.5px solid #cbd5e1",borderBottom:"1.5px solid #cbd5e1",borderBottomLeftRadius:"3px",marginRight:"6px",marginTop:"-4px",flexShrink:0}}),(0,S.jsxs)("span",{style:{fontWeight:w?500:600,color:w?"#64748b":"#1e293b",fontSize:w?"0.78rem":"0.82rem",whiteSpace:"nowrap"},children:[m.resourceName,":"]})]}),(0,S.jsx)(_,{permission:m.permission,resourceName:m.resourceName,expectedResourceKey:m.expectedResourceKey,labels:o})]},m.resourceName)})})]})]})}return(0,S.jsxs)("span",{className:c,"data-testid":"my-permission-group-pills",style:{display:"inline-flex",alignItems:"center",flexWrap:"wrap",gap:b.pillGap,fontSize:b.fontSize,color:"#475569",...l},children:[r&&(0,S.jsxs)("span",{style:{fontWeight:600,color:"#334155"},children:[r,":"]}),(0,S.jsx)("span",{style:{display:"inline-flex",alignItems:"center",flexWrap:"wrap",gap:b.pillGap},children:t.map(u=>(0,S.jsx)(ge,{item:u,size:i,labels:o},u.resourceName))})]})}var e=require("react/jsx-runtime"),$=t=>t?.error;function Y({children:t}){let{myPermissions:r,labels:n,adminResourceKey:i,basePath:o}=(0,h.useLoaderData)(),c=o??"/authz",[l,p]=(0,P.useState)("pills"),g=[["roles",n.tabs.roles],["resources",n.tabs.resources],["role_permissions",n.tabs.role_permissions],["user_roles",n.tabs.user_roles]],f=Object.entries(r||{}).map(([u,a])=>({resourceKey:u,resourceName:a?.resourceKey||u,permission:a,expectedResourceKey:u})),b=f.length>0?f:[{resourceKey:i,resourceName:i,permission:r?.[i],expectedResourceKey:i}];return(0,e.jsxs)("div",{className:"authz-admin",style:s.page,children:[(0,e.jsxs)("header",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"0.75rem",flexWrap:"wrap",margin:"0 0 0.4rem"},children:[(0,e.jsx)("h1",{style:{fontSize:"1.15rem",margin:0,fontWeight:700,color:"#1e293b"},children:n.title}),(0,e.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"0.4rem"},children:[(0,e.jsx)(q,{prefixLabel:n.myPermission,items:b,variant:l,size:"xs"}),(0,e.jsxs)("div",{style:{display:"inline-flex",borderRadius:"4px",border:"1px solid #cbd5e1",padding:"1px",backgroundColor:"#f8fafc",fontSize:"0.7rem"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>p("pills"),"data-testid":"switch-variant-pills",style:{padding:"1px 5px",borderRadius:"3px",border:"none",cursor:"pointer",fontSize:"0.7rem",lineHeight:"1.2",fontWeight:l==="pills"?600:400,backgroundColor:l==="pills"?"#ffffff":"transparent",color:l==="pills"?"#1e293b":"#64748b",boxShadow:l==="pills"?"0 1px 2px rgba(0,0,0,0.05)":"none"},children:"Pills"}),(0,e.jsx)("button",{type:"button",onClick:()=>p("popover"),"data-testid":"switch-variant-popover",style:{padding:"1px 5px",borderRadius:"3px",border:"none",cursor:"pointer",fontSize:"0.7rem",lineHeight:"1.2",fontWeight:l==="popover"?600:400,backgroundColor:l==="popover"?"#ffffff":"transparent",color:l==="popover"?"#1e293b":"#64748b",boxShadow:l==="popover"?"0 1px 2px rgba(0,0,0,0.05)":"none"},children:"Popover"})]})]})]}),(0,e.jsx)("nav",{style:{display:"flex",gap:"0.25rem",borderBottom:"1px solid #e2e8f0",margin:"0.6rem 0 1rem"},children:g.map(([u,a])=>(0,e.jsx)(h.NavLink,{to:`${c}/${u}`,style:({isActive:y})=>({padding:"0.5rem 0.9rem",textDecoration:"none",color:y?"#2563eb":"#475569",borderBottom:y?"2px solid #2563eb":"2px solid transparent",marginBottom:-1,fontWeight:y?700:500,fontSize:"0.92rem"}),children:a},u))}),t]})}function fe(){return(0,e.jsx)(Y,{children:(0,e.jsx)(h.Outlet,{})})}function Z(t){let r=D(t).error;return function(){let i=(0,h.useRouteError)(),o=(0,h.isRouteErrorResponse)(i)?i:null,c=o?.status===403,l=c?r.forbiddenTitle:o?`${r.genericTitlePrefix} (${o.status})`:r.genericTitle,p=c?r.forbiddenBody:o?typeof o.data=="string"&&o.data?o.data:o.statusText:r.genericBody;return(0,e.jsx)("div",{style:s.page,children:(0,e.jsxs)("div",{role:"alert",style:{maxWidth:480,margin:"3rem auto",padding:"1.5rem",border:"1px solid #fecaca",borderRadius:12,background:"#fef2f2"},children:[(0,e.jsx)("h1",{style:{fontSize:"1.1rem",fontWeight:700,color:"#b91c1c",margin:"0 0 0.5rem"},children:l}),(0,e.jsx)("p",{style:{color:"#7f1d1d",fontSize:"0.9rem",margin:0,lineHeight:1.7},children:p})]})})}}var he=Z(),O=(t,r)=>n=>{t&&!window.confirm(r)&&n.preventDefault()};function B(){let{roles:t,selfAdminRoleIds:r,labels:n}=(0,h.useLoaderData)(),i=n.roles,o=n.common,c=new Set(r??[]);return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:s.h2,children:i.heading}),(0,e.jsx)(C,{message:$((0,h.useActionData)())}),(0,e.jsx)("p",{style:s.note,children:i.note}),(0,e.jsx)("div",{style:s.tableContainer,children:(0,e.jsxs)("table",{style:s.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...s.th,width:"20%"},children:i.roleKey}),(0,e.jsx)("th",{style:{...s.th,width:"25%"},children:i.name}),(0,e.jsx)("th",{style:{...s.th,width:"35%"},children:i.description}),(0,e.jsx)("th",{style:{...s.th,width:"10%"},children:i.activeHeader}),(0,e.jsx)("th",{style:{...s.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(l=>(0,e.jsxs)("tr",{children:[(0,e.jsx)("td",{style:s.td,children:l.roleKey==="@authenticated"||l.roleKey==="@anonymous"?(0,e.jsx)("span",{style:s.code,children:l.roleKey}):(0,e.jsx)("input",{name:"roleKey",defaultValue:l.roleKey,form:`save-role-${l.id}`,"aria-label":`roleKey-${l.id}`,required:!0,style:s.input})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("input",{name:"name",defaultValue:l.name,form:`save-role-${l.id}`,"aria-label":`name-${l.id}`,required:!0,style:s.input})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("input",{name:"description",defaultValue:l.description??"",form:`save-role-${l.id}`,"aria-label":`description-${l.id}`,style:s.input})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsxs)(h.Form,{method:"post",style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"toggle"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:l.id}),(0,e.jsx)("input",{type:"hidden",name:"isActive",value:String(!l.isActive)}),(0,e.jsx)("button",{type:"submit",style:s.button,onClick:l.isActive?O(c.has(l.id),n.warn.selfRoleDisable):void 0,children:l.isActive?o.active:o.inactive})]})}),(0,e.jsxs)("td",{style:{...s.td,whiteSpace:"nowrap"},children:[(0,e.jsxs)(h.Form,{method:"post",id:`save-role-${l.id}`,style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"save"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:l.id}),(0,e.jsx)("button",{type:"submit",style:s.save,children:o.save})]}),(0,e.jsxs)(h.Form,{method:"post",style:{display:"inline",marginLeft:"0.4rem"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"delete"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:l.id}),(0,e.jsx)("button",{type:"submit",style:s.danger,onClick:O(c.has(l.id),n.warn.selfRoleDelete),children:o.delete})]})]})]},l.id)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:s.empty,colSpan:5,children:i.empty})}):null]})]})}),(0,e.jsx)("h3",{style:s.h3,children:i.newHeading}),(0,e.jsxs)(h.Form,{method:"post",style:s.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"create"}),(0,e.jsx)(x,{label:i.roleKey,hint:i.hintRoleKey,children:(0,e.jsx)("input",{name:"roleKey","aria-label":"role_key",required:!0,style:s.input})}),(0,e.jsx)(x,{label:i.name,children:(0,e.jsx)("input",{name:"name","aria-label":"name",required:!0,style:s.input})}),(0,e.jsx)(x,{label:i.description,hint:o.optional,children:(0,e.jsx)("input",{name:"description","aria-label":"description",style:s.input})}),(0,e.jsx)("button",{type:"submit",style:s.primary,children:o.add})]})]})}function X(){let{resources:t,labels:r}=(0,h.useLoaderData)(),n=r.resources,i=r.common;return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:s.h2,children:n.heading}),(0,e.jsx)(C,{message:$((0,h.useActionData)())}),(0,e.jsx)("p",{style:s.note,children:n.note}),(0,e.jsx)("div",{style:s.tableContainer,children:(0,e.jsxs)("table",{style:s.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...s.th,width:"15%"},children:n.appKey}),(0,e.jsx)("th",{style:{...s.th,width:"20%"},children:n.resourceKey}),(0,e.jsx)("th",{style:{...s.th,width:"25%"},children:n.name}),(0,e.jsx)("th",{style:{...s.th,width:"30%"},children:n.description}),(0,e.jsx)("th",{style:{...s.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(o=>(0,e.jsxs)("tr",{children:[(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("span",{style:s.code,children:o.appKey})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("span",{style:s.code,children:o.resourceKey})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("input",{name:"name",defaultValue:o.name,form:`save-res-${o.id}`,"aria-label":`name-${o.id}`,required:!0,style:s.input})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("input",{name:"description",defaultValue:o.description??"",form:`save-res-${o.id}`,"aria-label":`description-${o.id}`,style:s.input})}),(0,e.jsxs)("td",{style:{...s.td,whiteSpace:"nowrap"},children:[(0,e.jsxs)(h.Form,{method:"post",id:`save-res-${o.id}`,style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"save"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:o.id}),(0,e.jsx)("button",{type:"submit",style:s.save,children:i.save})]}),(0,e.jsxs)(h.Form,{method:"post",style:{display:"inline",marginLeft:"0.4rem"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"delete"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:o.id}),(0,e.jsx)("button",{type:"submit",style:s.danger,children:i.delete})]})]})]},o.id)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:s.empty,colSpan:5,children:n.empty})}):null]})]})}),(0,e.jsx)("h3",{style:s.h3,children:n.newHeading}),(0,e.jsxs)(h.Form,{method:"post",style:s.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"create"}),(0,e.jsx)(x,{label:n.appKey,hint:n.hintAppKey,children:(0,e.jsx)("input",{name:"appKey","aria-label":"app_key",required:!0,style:s.input})}),(0,e.jsx)(x,{label:n.resourceKey,hint:n.hintResourceKey,children:(0,e.jsx)("input",{name:"resourceKey","aria-label":"resource_key",required:!0,style:s.input})}),(0,e.jsx)(x,{label:n.name,children:(0,e.jsx)("input",{name:"name","aria-label":"name",required:!0,style:s.input})}),(0,e.jsx)(x,{label:n.description,hint:i.optional,children:(0,e.jsx)("input",{name:"description","aria-label":"description",style:s.input})}),(0,e.jsx)("button",{type:"submit",style:s.primary,children:i.add})]})]})}function be({g:t,labels:r}){let n=r.common,[i,o]=(0,P.useState)({label:t.action,value:t.action});return(0,e.jsxs)("tr",{children:[(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("span",{style:s.code,children:t.roleKey})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsxs)("span",{style:s.code,children:[t.appKey,":",t.resourceKey]})}),(0,e.jsx)("td",{style:s.td,children:t.roleKey==="@anonymous"?(0,e.jsx)("span",{style:s.code,children:t.action}):(0,e.jsxs)(e.Fragment,{children:[(0,e.jsx)("input",{type:"hidden",name:"action",value:i.value,form:`save-grant-${t.id}`}),(0,e.jsx)(K.SelectBox,{options:V.AUTHZ_ACTIONS.map(c=>({label:c,value:c})),value:i,onChange:c=>{c&&o(c)}})]})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("textarea",{name:"rowScope",defaultValue:t.rowScope??"",form:`save-grant-${t.id}`,"aria-label":`row_scope-${t.id}`,rows:2,placeholder:r.grants.rowScopePlaceholder,style:s.textarea})}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsx)("textarea",{name:"columnScope",defaultValue:t.columnScope??"",form:`save-grant-${t.id}`,"aria-label":`column_scope-${t.id}`,rows:2,placeholder:r.grants.columnScopePlaceholder,style:s.textarea})}),(0,e.jsxs)("td",{style:{...s.td,whiteSpace:"nowrap"},children:[(0,e.jsxs)(h.Form,{method:"post",id:`save-grant-${t.id}`,style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"save"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:t.id}),(0,e.jsx)("button",{type:"submit",style:s.save,children:n.save})]}),(0,e.jsxs)(h.Form,{method:"post",style:{display:"inline",marginLeft:"0.4rem"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"delete"}),(0,e.jsx)("input",{type:"hidden",name:"id",value:t.id}),(0,e.jsx)("button",{type:"submit",style:s.danger,children:n.delete})]})]})]})}function ee(){let{grants:t,roles:r,resources:n,labels:i}=(0,h.useLoaderData)(),o=i.grants,c=i.common,l=r.map(d=>({label:d.roleKey,value:d.id})),p=n.map(d=>({label:`${d.appKey}:${d.resourceKey}`,value:d.id})),g=V.AUTHZ_ACTIONS.map(d=>({label:d,value:d})),[f,b]=(0,P.useState)(void 0),[u,a]=(0,P.useState)(void 0),[y,m]=(0,P.useState)({label:"read",value:"read"});return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:s.h2,children:o.heading}),(0,e.jsx)("p",{style:s.note,children:o.note}),(0,e.jsx)(C,{message:$((0,h.useActionData)())}),(0,e.jsx)("div",{style:s.tableContainer,children:(0,e.jsxs)("table",{style:s.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...s.th,width:"15%"},children:o.role}),(0,e.jsx)("th",{style:{...s.th,width:"20%"},children:o.resource}),(0,e.jsx)("th",{style:{...s.th,width:"15%"},children:o.action}),(0,e.jsx)("th",{style:{...s.th,width:"20%"},children:o.rowScope}),(0,e.jsx)("th",{style:{...s.th,width:"20%"},children:o.columnScope}),(0,e.jsx)("th",{style:{...s.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(d=>(0,e.jsx)(be,{g:d,labels:i},d.id)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:s.empty,colSpan:6,children:o.empty})}):null]})]})}),(0,e.jsx)("h3",{style:s.h3,children:o.newHeading}),(0,e.jsxs)(h.Form,{method:"post",style:s.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"create"}),(0,e.jsx)("input",{type:"hidden",name:"roleId",value:f?.value??""}),(0,e.jsx)("input",{type:"hidden",name:"resourceId",value:u?.value??""}),(0,e.jsx)("input",{type:"hidden",name:"action",value:y?.value??""}),(0,e.jsx)(x,{label:o.role,children:(0,e.jsx)(K.SelectBox,{options:l,value:f,onChange:d=>b(d||void 0),placeholder:o.selectPlaceholder})}),(0,e.jsx)(x,{label:o.resource,children:(0,e.jsx)(K.SelectBox,{options:p,value:u,onChange:d=>a(d||void 0),placeholder:o.selectPlaceholder})}),(0,e.jsx)(x,{label:o.action,children:(0,e.jsx)(K.SelectBox,{options:g,value:y,onChange:d=>m(d||void 0)})}),(0,e.jsx)(x,{label:o.rowScope,hint:o.rowScopeHint,children:(0,e.jsx)("textarea",{name:"rowScope","aria-label":"row_scope",rows:2,style:s.textarea,placeholder:'{"filters":[{"field":"\u90E8\u9580CD","op":"in","values":["A"]}]}'})}),(0,e.jsx)(x,{label:o.columnScope,hint:o.columnScopeHint,children:(0,e.jsx)("textarea",{name:"columnScope","aria-label":"column_scope",rows:2,style:s.textarea,placeholder:'{"mode":"deny","columns":["\u91D1\u984D"]}'})}),(0,e.jsx)("button",{type:"submit",style:s.primary,children:c.grant})]})]})}function te(){let{assignments:t,roles:r,users:n,actorUserId:i,selfAdminRoleIds:o,labels:c}=(0,h.useLoaderData)(),l=c.userRoles,p=c.common,g=new Set(o??[]),f=n.map(d=>({label:`${d.email}\uFF08${d.displayName}\uFF09`,value:d.id})),b=r.filter(d=>d.roleKey!=="@authenticated"&&d.roleKey!=="@anonymous").map(d=>({label:d.roleKey,value:d.id})),[u,a]=(0,P.useState)(void 0),[y,m]=(0,P.useState)(void 0);return(0,e.jsxs)("section",{children:[(0,e.jsx)("h2",{style:s.h2,children:l.heading}),(0,e.jsx)(C,{message:$((0,h.useActionData)())}),(0,e.jsx)("p",{style:s.note,children:l.note}),(0,e.jsx)("div",{style:s.tableContainer,children:(0,e.jsxs)("table",{style:s.table,children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{children:[(0,e.jsx)("th",{style:{...s.th,width:"45%"},children:l.user}),(0,e.jsx)("th",{style:{...s.th,width:"45%"},children:l.role}),(0,e.jsx)("th",{style:{...s.th,width:"10%"}})]})}),(0,e.jsxs)("tbody",{children:[t.map(d=>(0,e.jsxs)("tr",{children:[(0,e.jsxs)("td",{style:s.td,children:[d.email,(0,e.jsxs)("span",{style:s.muted,children:["\uFF08",d.displayName,"\uFF09"]})]}),(0,e.jsx)("td",{style:s.td,children:d.roleKey}),(0,e.jsx)("td",{style:s.td,children:(0,e.jsxs)(h.Form,{method:"post",style:{display:"inline"},children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"remove"}),(0,e.jsx)("input",{type:"hidden",name:"userId",value:d.userId}),(0,e.jsx)("input",{type:"hidden",name:"roleId",value:d.roleId}),(0,e.jsx)("button",{type:"submit",style:s.danger,onClick:O(d.userId===i&&g.has(d.roleId),c.warn.selfAssignmentRemove),children:p.remove})]})})]},`${d.userId}-${d.roleId}`)),t.length===0?(0,e.jsx)("tr",{children:(0,e.jsx)("td",{style:s.empty,colSpan:3,children:l.empty})}):null]})]})}),(0,e.jsx)("h3",{style:s.h3,children:l.newHeading}),(0,e.jsxs)(h.Form,{method:"post",style:s.card,children:[(0,e.jsx)("input",{type:"hidden",name:"_action",value:"assign"}),(0,e.jsx)("input",{type:"hidden",name:"userId",value:u?.value??""}),(0,e.jsx)("input",{type:"hidden",name:"roleId",value:y?.value??""}),(0,e.jsx)(x,{label:l.user,children:(0,e.jsx)(K.SelectBox,{options:f,value:u,onChange:d=>a(d||void 0),placeholder:l.selectPlaceholder})}),(0,e.jsx)(x,{label:l.role,children:(0,e.jsx)(K.SelectBox,{options:b,value:y,onChange:d=>m(d||void 0),placeholder:l.selectPlaceholder})}),(0,e.jsx)("button",{type:"submit",style:s.primary,children:p.assign})]})]})}var we={roles:B,resources:X,role_permissions:ee,user_roles:te};function Se(){let{segment:t}=(0,h.useLoaderData)(),r=we[t]??B;return(0,e.jsx)(Y,{children:(0,e.jsx)(r,{})})}function Ae(t){let r={};for(let n of Object.keys(t)){let i=t[n];r[n]={key:i.key??n,name:i.name,description:i.description}}return r}var k=require("@aiquants/authz-core"),re=require("react-router");function ne(t,r){let n=(0,k.can)(r,"read"),i=(0,k.can)(r,"create"),o=(0,k.can)(r,"update"),c=(0,k.can)(r,"delete"),l=r?.scopeByAction.read,p=n&&!!l&&(l.rowScope!==null||l.columnScope!==null);return{resourceKey:t,read:n,create:i,update:o,delete:c,write:i||o,readPartial:p,actions:r?Array.from(r.actions):[]}}function Re(t){async function r(o,c,l){let p=l?.failureRedirect?()=>{throw(0,re.redirect)(l.failureRedirect)}:t.onDeny;return(0,k.createRequirePermission)({resolveUserId:t.resolveUserId,getEffectivePermissions:t.getEffectivePermissions,onDeny:p})(o,{appKey:t.appKey,resourceKey:c.resourceKey,action:c.action})}async function n(o,c){let l=await t.resolveUserId(o),p={};for(let g of c.resourceKeys){let f=await t.getEffectivePermissions({userId:l??null,appKey:t.appKey,resourceKey:g});p[g]=ne(g,f)}return p}async function i(o,c,l){let p=await r(o,c,l),g=await n(o,{resourceKeys:[c.resourceKey]});return{...p,permission:g[c.resourceKey]}}return{requirePermission:r,getMyPermissions:n,requireAndGetPermission:i}}0&&(module.exports={AuthzAdminAppView,AuthzErrorBoundary,AuthzGrantsView,AuthzLayout,AuthzResourcesView,AuthzRolesView,AuthzUserRolesView,ErrorAlert,Field,MyPermissionGroupStatus,MyPermissionIndicator,MyPermissionStatus,createAuthz,createAuthzAdminApp,createAuthzAdminServer,defaultAuthzAdminLabels,defineResources,flattenGroupItems,formatResourceTooltip,getPermissionSummary,makeAuthzErrorBoundary,resolveLabels,toPermissionView,ui,usePermission});
2
3
  //# sourceMappingURL=index.js.map