@quazardous/qdadm 1.19.3
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/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +101 -0
- package/src/assets/logo.svg +6 -0
- package/src/auth/SessionAuthAdapter.ts +459 -0
- package/src/auth/index.ts +15 -0
- package/src/chain/ActiveStack.ts +154 -0
- package/src/chain/StackHydrator.ts +292 -0
- package/src/chain/index.ts +25 -0
- package/src/chain/useActiveStack.ts +120 -0
- package/src/chain/useStackHydrator.ts +155 -0
- package/src/components/BoolCell.vue +34 -0
- package/src/components/InfoBanner.vue +73 -0
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/SeverityTag.vue +86 -0
- package/src/components/dialogs/BulkStatusDialog.vue +60 -0
- package/src/components/dialogs/MultiStepDialog.vue +331 -0
- package/src/components/dialogs/SimpleDialog.vue +122 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
- package/src/components/display/BannerZone.vue +65 -0
- package/src/components/display/CardsGrid.vue +72 -0
- package/src/components/display/CopyableId.vue +84 -0
- package/src/components/display/EmptyState.vue +62 -0
- package/src/components/display/IntensityBar.vue +190 -0
- package/src/components/display/RichCardsGrid.vue +101 -0
- package/src/components/edit/FormActions.vue +78 -0
- package/src/components/edit/FormField.vue +82 -0
- package/src/components/edit/FormInput.vue +158 -0
- package/src/components/edit/FormPage.vue +260 -0
- package/src/components/edit/FormTab.vue +217 -0
- package/src/components/edit/FormTabs.vue +120 -0
- package/src/components/edit/LookupField.vue +406 -0
- package/src/components/edit/LookupPickerDialog.vue +230 -0
- package/src/components/editors/JsonEditorFoldable.vue +470 -0
- package/src/components/editors/JsonStructuredField.vue +264 -0
- package/src/components/editors/JsonViewer.vue +51 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +259 -0
- package/src/components/editors/PermissionEditor.vue +558 -0
- package/src/components/editors/ScopeEditor.vue +372 -0
- package/src/components/editors/VanillaJsonEditor.vue +183 -0
- package/src/components/index.ts +83 -0
- package/src/components/item/FieldGroups.vue +392 -0
- package/src/components/layout/AppLayout.vue +1032 -0
- package/src/components/layout/BaseLayout.vue +190 -0
- package/src/components/layout/Breadcrumb.vue +55 -0
- package/src/components/layout/DashboardLayout.vue +104 -0
- package/src/components/layout/FormLayout.vue +256 -0
- package/src/components/layout/ListLayout.vue +278 -0
- package/src/components/layout/PageHeader.vue +156 -0
- package/src/components/layout/PageLayout.vue +126 -0
- package/src/components/layout/PageNav.vue +192 -0
- package/src/components/layout/SidebarBox.vue +87 -0
- package/src/components/layout/Zone.vue +182 -0
- package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
- package/src/components/layout/defaults/DefaultFooter.vue +89 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
- package/src/components/layout/defaults/DefaultHeader.vue +78 -0
- package/src/components/layout/defaults/DefaultMenu.vue +188 -0
- package/src/components/layout/defaults/DefaultPagination.vue +75 -0
- package/src/components/layout/defaults/DefaultTable.vue +137 -0
- package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
- package/src/components/layout/defaults/index.ts +16 -0
- package/src/components/lists/ActionButtons.vue +49 -0
- package/src/components/lists/ActionColumn.vue +40 -0
- package/src/components/lists/FilterBar.vue +54 -0
- package/src/components/lists/ListPage.vue +467 -0
- package/src/components/pages/LoginPage.vue +334 -0
- package/src/components/pages/NotFoundPage.vue +98 -0
- package/src/components/show/ShowDisplay.vue +343 -0
- package/src/components/show/ShowField.vue +67 -0
- package/src/components/show/ShowPage.vue +431 -0
- package/src/composables/index.ts +155 -0
- package/src/composables/useApp.ts +67 -0
- package/src/composables/useAuth.ts +98 -0
- package/src/composables/useBareForm.ts +319 -0
- package/src/composables/useBreadcrumb.ts +252 -0
- package/src/composables/useChildPage.ts +113 -0
- package/src/composables/useCurrentEntity.ts +60 -0
- package/src/composables/useDeferred.ts +94 -0
- package/src/composables/useDirtyState.ts +138 -0
- package/src/composables/useEntityItemFormPage.ts +942 -0
- package/src/composables/useEntityItemFormPage.types.ts +451 -0
- package/src/composables/useEntityItemPage.ts +438 -0
- package/src/composables/useEntityItemShowPage.ts +722 -0
- package/src/composables/useFieldManager.ts +692 -0
- package/src/composables/useForm.ts +544 -0
- package/src/composables/useGuardStore.ts +53 -0
- package/src/composables/useHooks.ts +56 -0
- package/src/composables/useInfoBanner.ts +217 -0
- package/src/composables/useJsonSyntax.ts +122 -0
- package/src/composables/useLayoutResolver.ts +329 -0
- package/src/composables/useListPage.ts +1535 -0
- package/src/composables/useListPage.types.ts +437 -0
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavContext.ts +439 -0
- package/src/composables/useNavigation.ts +216 -0
- package/src/composables/useOptionsLookup.ts +323 -0
- package/src/composables/usePageTitle.ts +84 -0
- package/src/composables/useSSEBridge.ts +142 -0
- package/src/composables/useSemanticBreadcrumb.ts +222 -0
- package/src/composables/useSidebarState.ts +24 -0
- package/src/composables/useSignals.ts +52 -0
- package/src/composables/useUnsavedChangesGuard.ts +155 -0
- package/src/composables/useUserImpersonator.ts +317 -0
- package/src/composables/useZoneRegistry.ts +252 -0
- package/src/core/bundles.ts +558 -0
- package/src/core/decorator.ts +420 -0
- package/src/core/extension.ts +470 -0
- package/src/core/index.ts +59 -0
- package/src/deferred/DeferredRegistry.ts +353 -0
- package/src/deferred/index.ts +15 -0
- package/src/editors/index.ts +12 -0
- package/src/entity/EntityManager.cache.ts +530 -0
- package/src/entity/EntityManager.crud.ts +615 -0
- package/src/entity/EntityManager.interface.ts +78 -0
- package/src/entity/EntityManager.query.ts +221 -0
- package/src/entity/EntityManager.relations.ts +151 -0
- package/src/entity/EntityManager.ts +944 -0
- package/src/entity/EntityManager.types.ts +288 -0
- package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
- package/src/entity/auth/EntityAuthAdapter.ts +119 -0
- package/src/entity/auth/PermissiveAdapter.ts +43 -0
- package/src/entity/auth/RoleHierarchy.ts +128 -0
- package/src/entity/auth/SecurityChecker.ts +202 -0
- package/src/entity/auth/factory.ts +203 -0
- package/src/entity/auth/index.ts +47 -0
- package/src/entity/factory.test.js +189 -0
- package/src/entity/factory.ts +146 -0
- package/src/entity/index.ts +49 -0
- package/src/entity/storage/ApiStorage.ts +228 -0
- package/src/entity/storage/IStorage.ts +100 -0
- package/src/entity/storage/LocalStorage.ts +200 -0
- package/src/entity/storage/MemoryStorage.ts +184 -0
- package/src/entity/storage/MockApiStorage.ts +318 -0
- package/src/entity/storage/SdkStorage.ts +377 -0
- package/src/entity/storage/factory.test.js +159 -0
- package/src/entity/storage/factory.ts +209 -0
- package/src/entity/storage/index.ts +75 -0
- package/src/gen/FieldMapper.ts +122 -0
- package/src/gen/StorageProfileFactory.ts +93 -0
- package/src/gen/connectors/BaseConnector.ts +150 -0
- package/src/gen/connectors/ManualConnector.test.js +499 -0
- package/src/gen/connectors/ManualConnector.ts +414 -0
- package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
- package/src/gen/connectors/OpenAPIConnector.ts +693 -0
- package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
- package/src/gen/connectors/index.ts +27 -0
- package/src/gen/createManagers.ts +245 -0
- package/src/gen/decorators.ts +134 -0
- package/src/gen/generateManagers.test.js +540 -0
- package/src/gen/generateManagers.ts +398 -0
- package/src/gen/index.ts +66 -0
- package/src/gen/schema.ts +248 -0
- package/src/gen/vite-plugin.ts +114 -0
- package/src/generated/managers/testManager.js +45 -0
- package/src/generated/managers/testManager.ts +46 -0
- package/src/hooks/HookRegistry.ts +16 -0
- package/src/hooks/index.ts +15 -0
- package/src/i18n/I18n.ts +344 -0
- package/src/i18n/IncrementalDomainProvider.ts +153 -0
- package/src/i18n/InlineTranslationProvider.ts +4 -0
- package/src/i18n/LazyTranslationProvider.ts +102 -0
- package/src/i18n/MessagesRegistry.ts +4 -0
- package/src/i18n/Resolver.ts +4 -0
- package/src/i18n/__tests__/I18n.test.ts +169 -0
- package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
- package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
- package/src/i18n/__tests__/Resolver.test.ts +271 -0
- package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
- package/src/i18n/defaults/core.en.yml +55 -0
- package/src/i18n/defaults/core.fr.yml +55 -0
- package/src/i18n/index.ts +55 -0
- package/src/i18n/loaders/raw-modules.d.ts +15 -0
- package/src/i18n/loaders/yaml.ts +35 -0
- package/src/i18n/strategies.ts +4 -0
- package/src/i18n/types.ts +34 -0
- package/src/i18n/useI18n.ts +34 -0
- package/src/index.ts +453 -0
- package/src/kernel/EventRouter.ts +23 -0
- package/src/kernel/Kernel.api.ts +79 -0
- package/src/kernel/Kernel.auth.ts +120 -0
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +170 -0
- package/src/kernel/Kernel.registries.ts +237 -0
- package/src/kernel/Kernel.routing.ts +334 -0
- package/src/kernel/Kernel.ts +460 -0
- package/src/kernel/Kernel.types.ts +263 -0
- package/src/kernel/Kernel.vue.ts +304 -0
- package/src/kernel/KernelContext.entities.ts +80 -0
- package/src/kernel/KernelContext.events.ts +57 -0
- package/src/kernel/KernelContext.i18n.ts +37 -0
- package/src/kernel/KernelContext.permissions.ts +38 -0
- package/src/kernel/KernelContext.routing.ts +280 -0
- package/src/kernel/KernelContext.ts +252 -0
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/Module.ts +169 -0
- package/src/kernel/ModuleLoader.ts +645 -0
- package/src/kernel/SSEBridge.ts +11 -0
- package/src/kernel/SignalBus.ts +48 -0
- package/src/kernel/index.ts +56 -0
- package/src/module/index.ts +33 -0
- package/src/module/moduleRegistry.ts +492 -0
- package/src/modules/debug/AuthCollector.ts +501 -0
- package/src/modules/debug/Collector.ts +20 -0
- package/src/modules/debug/DebugBridge.ts +14 -0
- package/src/modules/debug/DebugModule.ts +220 -0
- package/src/modules/debug/EntitiesCollector.ts +839 -0
- package/src/modules/debug/ErrorCollector.ts +4 -0
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +7 -0
- package/src/modules/debug/RouterCollector.ts +449 -0
- package/src/modules/debug/SignalCollector.ts +4 -0
- package/src/modules/debug/ToastCollector.ts +4 -0
- package/src/modules/debug/ZonesCollector.ts +447 -0
- package/src/modules/debug/components/DebugBar.vue +31 -0
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +9 -0
- package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
- package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
- package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
- package/src/modules/debug/components/panels/index.ts +16 -0
- package/src/modules/debug/index.ts +58 -0
- package/src/modules/debug/styles.scss +2671 -0
- package/src/notifications/NotificationBadge.vue +35 -0
- package/src/notifications/NotificationListener.vue +71 -0
- package/src/notifications/NotificationModule.ts +76 -0
- package/src/notifications/NotificationPanel.vue +478 -0
- package/src/notifications/NotificationStore.ts +288 -0
- package/src/notifications/index.ts +42 -0
- package/src/notifications/styles.scss +32 -0
- package/src/orchestrator/Orchestrator.ts +427 -0
- package/src/orchestrator/index.ts +8 -0
- package/src/orchestrator/useOrchestrator.ts +80 -0
- package/src/plugin.ts +251 -0
- package/src/query/FilterQuery.ts +306 -0
- package/src/query/QueryExecutor.ts +341 -0
- package/src/query/index.ts +16 -0
- package/src/security/EntityRolesProvider.ts +385 -0
- package/src/security/PermissionMatcher.ts +152 -0
- package/src/security/PermissionRegistry.ts +273 -0
- package/src/security/PersistableRolesProvider.ts +584 -0
- package/src/security/RolesManager.ts +136 -0
- package/src/security/RolesProvider.ts +167 -0
- package/src/security/RolesProviderStorage.ts +211 -0
- package/src/security/SecurityModule.ts +94 -0
- package/src/security/StaticRolesProvider.ts +117 -0
- package/src/security/UsersManager.ts +163 -0
- package/src/security/index.ts +77 -0
- package/src/security/pages/RoleForm.vue +276 -0
- package/src/security/pages/RoleList.vue +123 -0
- package/src/styles/_alerts.scss +95 -0
- package/src/styles/_buttons.scss +391 -0
- package/src/styles/_cards.scss +241 -0
- package/src/styles/_code.scss +78 -0
- package/src/styles/_dialogs.scss +55 -0
- package/src/styles/_filter-bar.scss +70 -0
- package/src/styles/_forms.scss +434 -0
- package/src/styles/_helpers.scss +153 -0
- package/src/styles/_lists.scss +225 -0
- package/src/styles/_main.scss +356 -0
- package/src/styles/_markdown.scss +88 -0
- package/src/styles/_responsive.scss +255 -0
- package/src/styles/_show-pages.scss +186 -0
- package/src/styles/_sidebar.scss +380 -0
- package/src/styles/_states.scss +115 -0
- package/src/styles/_stats.scss +47 -0
- package/src/styles/_variables.scss +224 -0
- package/src/styles/index.scss +38 -0
- package/src/styles/theme/_components.scss +331 -0
- package/src/styles/theme/_index.scss +10 -0
- package/src/styles/theme/_tokens.scss +147 -0
- package/src/styles/theme/_utilities.scss +175 -0
- package/src/toast/ToastBridgeModule.ts +68 -0
- package/src/toast/ToastListener.vue +55 -0
- package/src/toast/index.ts +26 -0
- package/src/toast/useSignalToast.ts +154 -0
- package/src/types/index.ts +285 -0
- package/src/types/pluralize.d.ts +19 -0
- package/src/types/quarkernel.d.ts +50 -0
- package/src/utils/formatters.ts +174 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/transformers.ts +131 -0
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +50 -0
- package/src/zones/ZoneRegistry.ts +934 -0
- package/src/zones/index.ts +29 -0
- package/src/zones/zones.ts +191 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoleProvider - Interface for role → permissions mapping
|
|
3
|
+
*
|
|
4
|
+
* The framework provides StaticRoleProvider (from config object).
|
|
5
|
+
* Apps can implement custom adapters (entity-based, API-backed, etc.)
|
|
6
|
+
*
|
|
7
|
+
* This interface abstracts HOW roles and permissions are stored/retrieved,
|
|
8
|
+
* allowing apps to use:
|
|
9
|
+
* - Static config (simple apps, demos)
|
|
10
|
+
* - Database/entity storage (apps with role management UI)
|
|
11
|
+
* - External API (microservices architecture)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Static adapter (auto-created from config)
|
|
15
|
+
* security: {
|
|
16
|
+
* role_permissions: { ROLE_USER: ['entity:*:read'] }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* // Custom adapter
|
|
20
|
+
* security: {
|
|
21
|
+
* rolesProvider: new EntityRoleProvider({ entityName: 'roles' })
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Role metadata for display purposes
|
|
27
|
+
*/
|
|
28
|
+
export interface RoleMeta {
|
|
29
|
+
label?: string
|
|
30
|
+
description?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Complete role data object
|
|
35
|
+
*
|
|
36
|
+
* Note: `id` is optional and maps to `name` for EntityManager compatibility
|
|
37
|
+
* when using idField: 'name'
|
|
38
|
+
*/
|
|
39
|
+
export interface RoleData {
|
|
40
|
+
name: string
|
|
41
|
+
label: string
|
|
42
|
+
permissions: string[]
|
|
43
|
+
inherits: string[]
|
|
44
|
+
/** Virtual id field (equals name) for EntityRecord compatibility */
|
|
45
|
+
id?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Role hierarchy mapping
|
|
50
|
+
*/
|
|
51
|
+
export type RoleHierarchyMap = Record<string, string[]>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Role permissions mapping
|
|
55
|
+
*/
|
|
56
|
+
export type RolePermissionsMap = Record<string, string[]>
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Role labels mapping
|
|
60
|
+
*/
|
|
61
|
+
export type RoleLabelsMap = Record<string, string>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Kernel context interface (minimal for adapter needs)
|
|
65
|
+
*/
|
|
66
|
+
export interface RoleProviderContext {
|
|
67
|
+
signals?: {
|
|
68
|
+
on: (pattern: string, handler: () => void) => () => void
|
|
69
|
+
}
|
|
70
|
+
orchestrator?: {
|
|
71
|
+
get: (name: string) => unknown
|
|
72
|
+
}
|
|
73
|
+
[key: string]: unknown
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class RoleProvider {
|
|
77
|
+
/**
|
|
78
|
+
* Get permissions granted to a role
|
|
79
|
+
*
|
|
80
|
+
* @param role - Role name (e.g., 'ROLE_USER')
|
|
81
|
+
* @returns Array of permission strings (may include wildcards)
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* adapter.getPermissions('ROLE_ADMIN')
|
|
85
|
+
* // ['entity:**', 'admin:**']
|
|
86
|
+
*/
|
|
87
|
+
getPermissions(_role: string): string[] {
|
|
88
|
+
throw new Error('RoleProvider.getPermissions() must be implemented')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get all defined roles
|
|
93
|
+
*
|
|
94
|
+
* @returns Array of role names
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* adapter.getRoles()
|
|
98
|
+
* // ['ROLE_USER', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN']
|
|
99
|
+
*/
|
|
100
|
+
getRoles(): string[] {
|
|
101
|
+
throw new Error('RoleProvider.getRoles() must be implemented')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get role hierarchy map
|
|
106
|
+
*
|
|
107
|
+
* @returns Role → inherited roles
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* adapter.getHierarchy()
|
|
111
|
+
* // { ROLE_ADMIN: ['ROLE_USER'], ROLE_SUPER_ADMIN: ['ROLE_ADMIN'] }
|
|
112
|
+
*/
|
|
113
|
+
getHierarchy(): RoleHierarchyMap {
|
|
114
|
+
throw new Error('RoleProvider.getHierarchy() must be implemented')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the role for unauthenticated users
|
|
119
|
+
*
|
|
120
|
+
* Always returns 'ROLE_ANONYMOUS' (convention)
|
|
121
|
+
*
|
|
122
|
+
* @returns Anonymous role name
|
|
123
|
+
*/
|
|
124
|
+
getAnonymousRole(): string {
|
|
125
|
+
return 'ROLE_ANONYMOUS'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get role metadata (label, description)
|
|
130
|
+
* Optional - for display purposes
|
|
131
|
+
*
|
|
132
|
+
* @param role - Role name
|
|
133
|
+
* @returns Role metadata or null
|
|
134
|
+
*/
|
|
135
|
+
getRoleMeta(_role: string): RoleMeta | null {
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Install adapter (called by Kernel when context is ready)
|
|
141
|
+
* Override for adapters that need initialization
|
|
142
|
+
*
|
|
143
|
+
* @param ctx - Kernel context
|
|
144
|
+
*/
|
|
145
|
+
install(_ctx: RoleProviderContext): void {
|
|
146
|
+
// Override if needed
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Uninstall adapter (cleanup)
|
|
151
|
+
*/
|
|
152
|
+
uninstall(): void {
|
|
153
|
+
// Override if needed
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check if adapter supports persistence (editing + saving)
|
|
158
|
+
*
|
|
159
|
+
* When true, the adapter supports mutations (setRolePermissions, etc.)
|
|
160
|
+
* AND can persist changes. UI should show edit controls.
|
|
161
|
+
*
|
|
162
|
+
* When false, the adapter is read-only. UI should hide edit controls.
|
|
163
|
+
*/
|
|
164
|
+
get canPersist(): boolean {
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoleProviderStorage - Storage bridge for rolesProvider
|
|
3
|
+
*
|
|
4
|
+
* Implements Storage interface and delegates to rolesProvider.
|
|
5
|
+
* Allows RolesManager to use standard EntityManager patterns.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { StorageCapabilities } from '../types'
|
|
9
|
+
import type { RoleProvider, RoleData } from './RolesProvider'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* List parameters for role listing
|
|
13
|
+
*/
|
|
14
|
+
export interface RoleListParams {
|
|
15
|
+
search?: string
|
|
16
|
+
[key: string]: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* List result for roles
|
|
21
|
+
*/
|
|
22
|
+
export interface RoleListResult {
|
|
23
|
+
items: RoleData[]
|
|
24
|
+
total: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Role data for create/update operations
|
|
29
|
+
*/
|
|
30
|
+
export interface RoleInput {
|
|
31
|
+
name: string
|
|
32
|
+
label?: string
|
|
33
|
+
permissions?: string[]
|
|
34
|
+
inherits?: string[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extended RoleProvider with optional mutation methods
|
|
39
|
+
*/
|
|
40
|
+
interface MutableRoleProvider extends RoleProvider {
|
|
41
|
+
ensureReady?(): Promise<void>
|
|
42
|
+
getLabels?(): Record<string, string>
|
|
43
|
+
getRole?(name: string): RoleData | null
|
|
44
|
+
createRole?(
|
|
45
|
+
name: string,
|
|
46
|
+
options: { label?: string; permissions?: string[]; inherits?: string[] }
|
|
47
|
+
): Promise<RoleData>
|
|
48
|
+
updateRole?(
|
|
49
|
+
name: string,
|
|
50
|
+
options: { label?: string; permissions?: string[]; inherits?: string[] }
|
|
51
|
+
): Promise<RoleData>
|
|
52
|
+
deleteRole?(name: string): Promise<void>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class RoleProviderStorage {
|
|
56
|
+
/**
|
|
57
|
+
* Storage name for debug display (survives minification)
|
|
58
|
+
*/
|
|
59
|
+
static storageName = 'RoleProviderStorage'
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Static capabilities (standard storage pattern)
|
|
63
|
+
*/
|
|
64
|
+
static capabilities: StorageCapabilities = {
|
|
65
|
+
supportsTotal: true,
|
|
66
|
+
supportsFilters: false,
|
|
67
|
+
supportsPagination: false,
|
|
68
|
+
supportsCaching: false, // In-memory via rolesProvider, no need for EntityManager cache
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private _rolesProvider: MutableRoleProvider | null
|
|
72
|
+
|
|
73
|
+
constructor(rolesProvider: RoleProvider | null) {
|
|
74
|
+
this._rolesProvider = rolesProvider as MutableRoleProvider | null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Instance capabilities (merge static + dynamic)
|
|
79
|
+
*
|
|
80
|
+
* Dynamic properties:
|
|
81
|
+
* - requiresAuth: false (roles are system data)
|
|
82
|
+
* - readOnly: based on rolesProvider.canPersist
|
|
83
|
+
*/
|
|
84
|
+
get capabilities(): StorageCapabilities & { requiresAuth: boolean; readOnly: boolean } {
|
|
85
|
+
return {
|
|
86
|
+
...RoleProviderStorage.capabilities,
|
|
87
|
+
requiresAuth: false,
|
|
88
|
+
readOnly: !this._rolesProvider?.canPersist,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* List all roles
|
|
94
|
+
*/
|
|
95
|
+
async list(params: RoleListParams = {}): Promise<RoleListResult> {
|
|
96
|
+
if (!this._rolesProvider) {
|
|
97
|
+
return { items: [], total: 0 }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Ensure loaded for adapters that need it
|
|
101
|
+
if (this._rolesProvider.ensureReady) {
|
|
102
|
+
await this._rolesProvider.ensureReady()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const roleNames = this._rolesProvider.getRoles()
|
|
106
|
+
const roles = roleNames.map((name) => this._getRole(name)).filter((r): r is RoleData => r !== null)
|
|
107
|
+
|
|
108
|
+
// Simple search filter
|
|
109
|
+
let filtered = roles
|
|
110
|
+
if (params.search) {
|
|
111
|
+
const search = params.search.toLowerCase()
|
|
112
|
+
filtered = roles.filter(
|
|
113
|
+
(r) => r.name.toLowerCase().includes(search) || r.label?.toLowerCase().includes(search)
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
items: filtered,
|
|
119
|
+
total: filtered.length,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get a single role by name
|
|
125
|
+
*/
|
|
126
|
+
async get(name: string): Promise<RoleData | null> {
|
|
127
|
+
if (!this._rolesProvider) return null
|
|
128
|
+
|
|
129
|
+
if (this._rolesProvider.ensureReady) {
|
|
130
|
+
await this._rolesProvider.ensureReady()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this._getRole(name)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get many roles by names
|
|
138
|
+
*/
|
|
139
|
+
async getMany(names: string[]): Promise<RoleData[]> {
|
|
140
|
+
if (!this._rolesProvider) return []
|
|
141
|
+
|
|
142
|
+
if (this._rolesProvider.ensureReady) {
|
|
143
|
+
await this._rolesProvider.ensureReady()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return names.map((name) => this._getRole(name)).filter((r): r is RoleData => r !== null)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Create a new role
|
|
151
|
+
*/
|
|
152
|
+
async create(data: RoleInput): Promise<RoleData> {
|
|
153
|
+
if (!this._rolesProvider?.createRole) {
|
|
154
|
+
throw new Error('Role creation not supported by this adapter')
|
|
155
|
+
}
|
|
156
|
+
return this._rolesProvider.createRole(data.name, {
|
|
157
|
+
label: data.label,
|
|
158
|
+
permissions: data.permissions || [],
|
|
159
|
+
inherits: data.inherits || [],
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Update an existing role
|
|
165
|
+
*/
|
|
166
|
+
async update(name: string, data: Partial<RoleInput>): Promise<RoleData> {
|
|
167
|
+
if (!this._rolesProvider?.updateRole) {
|
|
168
|
+
throw new Error('Role update not supported by this adapter')
|
|
169
|
+
}
|
|
170
|
+
return this._rolesProvider.updateRole(name, {
|
|
171
|
+
label: data.label,
|
|
172
|
+
permissions: data.permissions,
|
|
173
|
+
inherits: data.inherits,
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Patch a role (partial update)
|
|
179
|
+
*/
|
|
180
|
+
async patch(name: string, data: Partial<RoleInput>): Promise<RoleData> {
|
|
181
|
+
return this.update(name, data)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Delete a role
|
|
186
|
+
*/
|
|
187
|
+
async delete(name: string): Promise<void> {
|
|
188
|
+
if (!this._rolesProvider?.deleteRole) {
|
|
189
|
+
throw new Error('Role deletion not supported by this adapter')
|
|
190
|
+
}
|
|
191
|
+
return this._rolesProvider.deleteRole(name)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get role object from rolesProvider
|
|
196
|
+
*/
|
|
197
|
+
private _getRole(name: string): RoleData | null {
|
|
198
|
+
if (!this._rolesProvider) return null
|
|
199
|
+
|
|
200
|
+
if (this._rolesProvider.getRole) {
|
|
201
|
+
return this._rolesProvider.getRole(name)
|
|
202
|
+
}
|
|
203
|
+
// Fallback for adapters without getRole
|
|
204
|
+
return {
|
|
205
|
+
name,
|
|
206
|
+
label: this._rolesProvider.getLabels?.()[name] || name,
|
|
207
|
+
permissions: this._rolesProvider.getPermissions(name),
|
|
208
|
+
inherits: this._rolesProvider.getHierarchy()[name] || [],
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecurityModule - Role management UI
|
|
3
|
+
*
|
|
4
|
+
* Provides a page to view/edit roles and permissions.
|
|
5
|
+
* Works with any RoleProvider:
|
|
6
|
+
* - StaticRoleProvider: read-only view
|
|
7
|
+
* - PersistableRoleProvider: full CRUD
|
|
8
|
+
* - EntityRoleProvider: full CRUD via entity
|
|
9
|
+
*
|
|
10
|
+
* Dynamically registers permissions from managers:
|
|
11
|
+
* - RolesManager.adminPermission (default: security:roles:manage)
|
|
12
|
+
* - security:users:manage (for UsersManager)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { SecurityModule } from '@quazardous/qdadm/security'
|
|
16
|
+
*
|
|
17
|
+
* const kernel = new Kernel({
|
|
18
|
+
* moduleDefs: [SecurityModule, ...],
|
|
19
|
+
* security: {
|
|
20
|
+
* rolesProvider: createLocalStorageRolesProvider({ ... })
|
|
21
|
+
* }
|
|
22
|
+
* })
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
|
+
import { Module } from '../kernel/Module'
|
|
27
|
+
import { RolesManager } from './RolesManager'
|
|
28
|
+
import type { RoleProvider } from './RolesProvider'
|
|
29
|
+
import type { PermissionRegistry } from './PermissionRegistry'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Kernel context interface for modules
|
|
33
|
+
*/
|
|
34
|
+
interface ModuleContext {
|
|
35
|
+
security?: {
|
|
36
|
+
rolesProvider?: RoleProvider
|
|
37
|
+
}
|
|
38
|
+
permissionRegistry?: PermissionRegistry
|
|
39
|
+
entity(name: string, manager: unknown): void
|
|
40
|
+
permissions(namespace: string, permissions: Record<string, string>): void
|
|
41
|
+
crud(
|
|
42
|
+
name: string,
|
|
43
|
+
pages: { list?: () => Promise<unknown>; form?: () => Promise<unknown> },
|
|
44
|
+
options?: { basePath?: string; nav?: { section?: string; icon?: string; permission?: string } }
|
|
45
|
+
): void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class SecurityModule extends Module {
|
|
49
|
+
static override moduleName = 'security'
|
|
50
|
+
static override requires: string[] = []
|
|
51
|
+
static override priority = 100 // Load late (after other modules register permissions)
|
|
52
|
+
|
|
53
|
+
async connect(ctx: ModuleContext): Promise<void> {
|
|
54
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
55
|
+
// ENTITY (wraps rolesProvider)
|
|
56
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
57
|
+
const rolesManager = new RolesManager({
|
|
58
|
+
rolesProvider: ctx.security?.rolesProvider ?? null,
|
|
59
|
+
permissionRegistry: ctx.permissionRegistry ?? null,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
ctx.entity('roles', rolesManager)
|
|
63
|
+
|
|
64
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
65
|
+
// PERMISSIONS (registered after entity so we can read adminPermission)
|
|
66
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
67
|
+
const rolesPerm = rolesManager.adminPermission?.replace('security:', '') || 'roles:manage'
|
|
68
|
+
ctx.permissions('security', {
|
|
69
|
+
[rolesPerm]: 'Manage roles (view, create, edit, delete)',
|
|
70
|
+
'users:manage': 'Manage users (view, create, edit, delete)',
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
74
|
+
// ROUTES (using ctx.crud helper)
|
|
75
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
76
|
+
ctx.crud(
|
|
77
|
+
'roles',
|
|
78
|
+
{
|
|
79
|
+
list: () => import('./pages/RoleList.vue'),
|
|
80
|
+
form: () => import('./pages/RoleForm.vue'),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
basePath: 'security/roles',
|
|
84
|
+
nav: {
|
|
85
|
+
section: 'Security',
|
|
86
|
+
icon: 'pi pi-shield',
|
|
87
|
+
permission: 'security:roles:read',
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default SecurityModule
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { RoleProvider } from './RolesProvider'
|
|
2
|
+
import type {
|
|
3
|
+
RoleMeta,
|
|
4
|
+
RoleHierarchyMap,
|
|
5
|
+
RolePermissionsMap,
|
|
6
|
+
RoleLabelsMap,
|
|
7
|
+
} from './RolesProvider'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Static roles provider configuration
|
|
11
|
+
*/
|
|
12
|
+
export interface StaticRolesProviderConfig {
|
|
13
|
+
role_hierarchy?: RoleHierarchyMap
|
|
14
|
+
role_permissions?: RolePermissionsMap
|
|
15
|
+
role_labels?: RoleLabelsMap
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* StaticRoleProvider - Roles provider from config object
|
|
20
|
+
*
|
|
21
|
+
* Default implementation for simple apps and demos.
|
|
22
|
+
* Auto-created when passing role_permissions object to Kernel.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Auto-created by Kernel
|
|
26
|
+
* const kernel = new Kernel({
|
|
27
|
+
* security: {
|
|
28
|
+
* role_hierarchy: { ROLE_ADMIN: ['ROLE_USER'] },
|
|
29
|
+
* role_permissions: {
|
|
30
|
+
* ROLE_USER: ['entity:*:read'],
|
|
31
|
+
* ROLE_ADMIN: ['**']
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* // Or explicit creation
|
|
37
|
+
* const provider = new StaticRoleProvider({
|
|
38
|
+
* role_hierarchy: { ROLE_ADMIN: ['ROLE_USER'] },
|
|
39
|
+
* role_permissions: {
|
|
40
|
+
* ROLE_USER: ['entity:*:read'],
|
|
41
|
+
* ROLE_ADMIN: ['**']
|
|
42
|
+
* },
|
|
43
|
+
* role_labels: {
|
|
44
|
+
* ROLE_USER: 'User',
|
|
45
|
+
* ROLE_ADMIN: 'Administrator'
|
|
46
|
+
* }
|
|
47
|
+
* })
|
|
48
|
+
*/
|
|
49
|
+
export class StaticRoleProvider extends RoleProvider {
|
|
50
|
+
protected _hierarchy: RoleHierarchyMap
|
|
51
|
+
protected _permissions: RolePermissionsMap
|
|
52
|
+
protected _labels: RoleLabelsMap
|
|
53
|
+
|
|
54
|
+
constructor(config: StaticRolesProviderConfig = {}) {
|
|
55
|
+
super()
|
|
56
|
+
this._hierarchy = config.role_hierarchy || {}
|
|
57
|
+
this._permissions = config.role_permissions || {}
|
|
58
|
+
this._labels = config.role_labels || {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get permissions for a role
|
|
63
|
+
*/
|
|
64
|
+
getPermissions(role: string): string[] {
|
|
65
|
+
return this._permissions[role] || []
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get all defined roles
|
|
70
|
+
*/
|
|
71
|
+
getRoles(): string[] {
|
|
72
|
+
// Combine roles from permissions and hierarchy
|
|
73
|
+
const roles = new Set<string>([
|
|
74
|
+
...Object.keys(this._permissions),
|
|
75
|
+
...Object.keys(this._hierarchy),
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
// Also include inherited roles
|
|
79
|
+
for (const inherits of Object.values(this._hierarchy)) {
|
|
80
|
+
for (const r of inherits) {
|
|
81
|
+
roles.add(r)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return [...roles]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get role hierarchy
|
|
90
|
+
*/
|
|
91
|
+
getHierarchy(): RoleHierarchyMap {
|
|
92
|
+
return this._hierarchy
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get role metadata
|
|
97
|
+
*/
|
|
98
|
+
getRoleMeta(role: string): RoleMeta | null {
|
|
99
|
+
const label = this._labels[role]
|
|
100
|
+
if (!label) return null
|
|
101
|
+
return { label }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Update role permissions at runtime (for testing or hot-reload)
|
|
106
|
+
*/
|
|
107
|
+
setPermissions(role: string, permissions: string[]): void {
|
|
108
|
+
this._permissions[role] = permissions
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Update role hierarchy at runtime
|
|
113
|
+
*/
|
|
114
|
+
setHierarchy(role: string, inherits: string[]): void {
|
|
115
|
+
this._hierarchy[role] = inherits
|
|
116
|
+
}
|
|
117
|
+
}
|