@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.
Files changed (291) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/package.json +101 -0
  4. package/src/assets/logo.svg +6 -0
  5. package/src/auth/SessionAuthAdapter.ts +459 -0
  6. package/src/auth/index.ts +15 -0
  7. package/src/chain/ActiveStack.ts +154 -0
  8. package/src/chain/StackHydrator.ts +292 -0
  9. package/src/chain/index.ts +25 -0
  10. package/src/chain/useActiveStack.ts +120 -0
  11. package/src/chain/useStackHydrator.ts +155 -0
  12. package/src/components/BoolCell.vue +34 -0
  13. package/src/components/InfoBanner.vue +73 -0
  14. package/src/components/QdadmRoot.vue +52 -0
  15. package/src/components/SeverityTag.vue +86 -0
  16. package/src/components/dialogs/BulkStatusDialog.vue +60 -0
  17. package/src/components/dialogs/MultiStepDialog.vue +331 -0
  18. package/src/components/dialogs/SimpleDialog.vue +122 -0
  19. package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
  20. package/src/components/display/BannerZone.vue +65 -0
  21. package/src/components/display/CardsGrid.vue +72 -0
  22. package/src/components/display/CopyableId.vue +84 -0
  23. package/src/components/display/EmptyState.vue +62 -0
  24. package/src/components/display/IntensityBar.vue +190 -0
  25. package/src/components/display/RichCardsGrid.vue +101 -0
  26. package/src/components/edit/FormActions.vue +78 -0
  27. package/src/components/edit/FormField.vue +82 -0
  28. package/src/components/edit/FormInput.vue +158 -0
  29. package/src/components/edit/FormPage.vue +260 -0
  30. package/src/components/edit/FormTab.vue +217 -0
  31. package/src/components/edit/FormTabs.vue +120 -0
  32. package/src/components/edit/LookupField.vue +406 -0
  33. package/src/components/edit/LookupPickerDialog.vue +230 -0
  34. package/src/components/editors/JsonEditorFoldable.vue +470 -0
  35. package/src/components/editors/JsonStructuredField.vue +264 -0
  36. package/src/components/editors/JsonViewer.vue +51 -0
  37. package/src/components/editors/KeyValueEditor.vue +314 -0
  38. package/src/components/editors/LanguageEditor.vue +259 -0
  39. package/src/components/editors/PermissionEditor.vue +558 -0
  40. package/src/components/editors/ScopeEditor.vue +372 -0
  41. package/src/components/editors/VanillaJsonEditor.vue +183 -0
  42. package/src/components/index.ts +83 -0
  43. package/src/components/item/FieldGroups.vue +392 -0
  44. package/src/components/layout/AppLayout.vue +1032 -0
  45. package/src/components/layout/BaseLayout.vue +190 -0
  46. package/src/components/layout/Breadcrumb.vue +55 -0
  47. package/src/components/layout/DashboardLayout.vue +104 -0
  48. package/src/components/layout/FormLayout.vue +256 -0
  49. package/src/components/layout/ListLayout.vue +278 -0
  50. package/src/components/layout/PageHeader.vue +156 -0
  51. package/src/components/layout/PageLayout.vue +126 -0
  52. package/src/components/layout/PageNav.vue +192 -0
  53. package/src/components/layout/SidebarBox.vue +87 -0
  54. package/src/components/layout/Zone.vue +182 -0
  55. package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
  56. package/src/components/layout/defaults/DefaultFooter.vue +89 -0
  57. package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
  58. package/src/components/layout/defaults/DefaultHeader.vue +78 -0
  59. package/src/components/layout/defaults/DefaultMenu.vue +188 -0
  60. package/src/components/layout/defaults/DefaultPagination.vue +75 -0
  61. package/src/components/layout/defaults/DefaultTable.vue +137 -0
  62. package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
  63. package/src/components/layout/defaults/index.ts +16 -0
  64. package/src/components/lists/ActionButtons.vue +49 -0
  65. package/src/components/lists/ActionColumn.vue +40 -0
  66. package/src/components/lists/FilterBar.vue +54 -0
  67. package/src/components/lists/ListPage.vue +467 -0
  68. package/src/components/pages/LoginPage.vue +334 -0
  69. package/src/components/pages/NotFoundPage.vue +98 -0
  70. package/src/components/show/ShowDisplay.vue +343 -0
  71. package/src/components/show/ShowField.vue +67 -0
  72. package/src/components/show/ShowPage.vue +431 -0
  73. package/src/composables/index.ts +155 -0
  74. package/src/composables/useApp.ts +67 -0
  75. package/src/composables/useAuth.ts +98 -0
  76. package/src/composables/useBareForm.ts +319 -0
  77. package/src/composables/useBreadcrumb.ts +252 -0
  78. package/src/composables/useChildPage.ts +113 -0
  79. package/src/composables/useCurrentEntity.ts +60 -0
  80. package/src/composables/useDeferred.ts +94 -0
  81. package/src/composables/useDirtyState.ts +138 -0
  82. package/src/composables/useEntityItemFormPage.ts +942 -0
  83. package/src/composables/useEntityItemFormPage.types.ts +451 -0
  84. package/src/composables/useEntityItemPage.ts +438 -0
  85. package/src/composables/useEntityItemShowPage.ts +722 -0
  86. package/src/composables/useFieldManager.ts +692 -0
  87. package/src/composables/useForm.ts +544 -0
  88. package/src/composables/useGuardStore.ts +53 -0
  89. package/src/composables/useHooks.ts +56 -0
  90. package/src/composables/useInfoBanner.ts +217 -0
  91. package/src/composables/useJsonSyntax.ts +122 -0
  92. package/src/composables/useLayoutResolver.ts +329 -0
  93. package/src/composables/useListPage.ts +1535 -0
  94. package/src/composables/useListPage.types.ts +437 -0
  95. package/src/composables/useListPage.utils.ts +101 -0
  96. package/src/composables/useNavContext.ts +439 -0
  97. package/src/composables/useNavigation.ts +216 -0
  98. package/src/composables/useOptionsLookup.ts +323 -0
  99. package/src/composables/usePageTitle.ts +84 -0
  100. package/src/composables/useSSEBridge.ts +142 -0
  101. package/src/composables/useSemanticBreadcrumb.ts +222 -0
  102. package/src/composables/useSidebarState.ts +24 -0
  103. package/src/composables/useSignals.ts +52 -0
  104. package/src/composables/useUnsavedChangesGuard.ts +155 -0
  105. package/src/composables/useUserImpersonator.ts +317 -0
  106. package/src/composables/useZoneRegistry.ts +252 -0
  107. package/src/core/bundles.ts +558 -0
  108. package/src/core/decorator.ts +420 -0
  109. package/src/core/extension.ts +470 -0
  110. package/src/core/index.ts +59 -0
  111. package/src/deferred/DeferredRegistry.ts +353 -0
  112. package/src/deferred/index.ts +15 -0
  113. package/src/editors/index.ts +12 -0
  114. package/src/entity/EntityManager.cache.ts +530 -0
  115. package/src/entity/EntityManager.crud.ts +615 -0
  116. package/src/entity/EntityManager.interface.ts +78 -0
  117. package/src/entity/EntityManager.query.ts +221 -0
  118. package/src/entity/EntityManager.relations.ts +151 -0
  119. package/src/entity/EntityManager.ts +944 -0
  120. package/src/entity/EntityManager.types.ts +288 -0
  121. package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
  122. package/src/entity/auth/EntityAuthAdapter.ts +119 -0
  123. package/src/entity/auth/PermissiveAdapter.ts +43 -0
  124. package/src/entity/auth/RoleHierarchy.ts +128 -0
  125. package/src/entity/auth/SecurityChecker.ts +202 -0
  126. package/src/entity/auth/factory.ts +203 -0
  127. package/src/entity/auth/index.ts +47 -0
  128. package/src/entity/factory.test.js +189 -0
  129. package/src/entity/factory.ts +146 -0
  130. package/src/entity/index.ts +49 -0
  131. package/src/entity/storage/ApiStorage.ts +228 -0
  132. package/src/entity/storage/IStorage.ts +100 -0
  133. package/src/entity/storage/LocalStorage.ts +200 -0
  134. package/src/entity/storage/MemoryStorage.ts +184 -0
  135. package/src/entity/storage/MockApiStorage.ts +318 -0
  136. package/src/entity/storage/SdkStorage.ts +377 -0
  137. package/src/entity/storage/factory.test.js +159 -0
  138. package/src/entity/storage/factory.ts +209 -0
  139. package/src/entity/storage/index.ts +75 -0
  140. package/src/gen/FieldMapper.ts +122 -0
  141. package/src/gen/StorageProfileFactory.ts +93 -0
  142. package/src/gen/connectors/BaseConnector.ts +150 -0
  143. package/src/gen/connectors/ManualConnector.test.js +499 -0
  144. package/src/gen/connectors/ManualConnector.ts +414 -0
  145. package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
  146. package/src/gen/connectors/OpenAPIConnector.ts +693 -0
  147. package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
  148. package/src/gen/connectors/index.ts +27 -0
  149. package/src/gen/createManagers.ts +245 -0
  150. package/src/gen/decorators.ts +134 -0
  151. package/src/gen/generateManagers.test.js +540 -0
  152. package/src/gen/generateManagers.ts +398 -0
  153. package/src/gen/index.ts +66 -0
  154. package/src/gen/schema.ts +248 -0
  155. package/src/gen/vite-plugin.ts +114 -0
  156. package/src/generated/managers/testManager.js +45 -0
  157. package/src/generated/managers/testManager.ts +46 -0
  158. package/src/hooks/HookRegistry.ts +16 -0
  159. package/src/hooks/index.ts +15 -0
  160. package/src/i18n/I18n.ts +344 -0
  161. package/src/i18n/IncrementalDomainProvider.ts +153 -0
  162. package/src/i18n/InlineTranslationProvider.ts +4 -0
  163. package/src/i18n/LazyTranslationProvider.ts +102 -0
  164. package/src/i18n/MessagesRegistry.ts +4 -0
  165. package/src/i18n/Resolver.ts +4 -0
  166. package/src/i18n/__tests__/I18n.test.ts +169 -0
  167. package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
  168. package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
  169. package/src/i18n/__tests__/Resolver.test.ts +271 -0
  170. package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
  171. package/src/i18n/defaults/core.en.yml +55 -0
  172. package/src/i18n/defaults/core.fr.yml +55 -0
  173. package/src/i18n/index.ts +55 -0
  174. package/src/i18n/loaders/raw-modules.d.ts +15 -0
  175. package/src/i18n/loaders/yaml.ts +35 -0
  176. package/src/i18n/strategies.ts +4 -0
  177. package/src/i18n/types.ts +34 -0
  178. package/src/i18n/useI18n.ts +34 -0
  179. package/src/index.ts +453 -0
  180. package/src/kernel/EventRouter.ts +23 -0
  181. package/src/kernel/Kernel.api.ts +79 -0
  182. package/src/kernel/Kernel.auth.ts +120 -0
  183. package/src/kernel/Kernel.i18n.ts +29 -0
  184. package/src/kernel/Kernel.modules.ts +170 -0
  185. package/src/kernel/Kernel.registries.ts +237 -0
  186. package/src/kernel/Kernel.routing.ts +334 -0
  187. package/src/kernel/Kernel.ts +460 -0
  188. package/src/kernel/Kernel.types.ts +263 -0
  189. package/src/kernel/Kernel.vue.ts +304 -0
  190. package/src/kernel/KernelContext.entities.ts +80 -0
  191. package/src/kernel/KernelContext.events.ts +57 -0
  192. package/src/kernel/KernelContext.i18n.ts +37 -0
  193. package/src/kernel/KernelContext.permissions.ts +38 -0
  194. package/src/kernel/KernelContext.routing.ts +280 -0
  195. package/src/kernel/KernelContext.ts +252 -0
  196. package/src/kernel/KernelContext.types.ts +173 -0
  197. package/src/kernel/KernelContext.zones.ts +54 -0
  198. package/src/kernel/Module.ts +169 -0
  199. package/src/kernel/ModuleLoader.ts +645 -0
  200. package/src/kernel/SSEBridge.ts +11 -0
  201. package/src/kernel/SignalBus.ts +48 -0
  202. package/src/kernel/index.ts +56 -0
  203. package/src/module/index.ts +33 -0
  204. package/src/module/moduleRegistry.ts +492 -0
  205. package/src/modules/debug/AuthCollector.ts +501 -0
  206. package/src/modules/debug/Collector.ts +20 -0
  207. package/src/modules/debug/DebugBridge.ts +14 -0
  208. package/src/modules/debug/DebugModule.ts +220 -0
  209. package/src/modules/debug/EntitiesCollector.ts +839 -0
  210. package/src/modules/debug/ErrorCollector.ts +4 -0
  211. package/src/modules/debug/I18nCollector.ts +9 -0
  212. package/src/modules/debug/LocalStorageAdapter.ts +7 -0
  213. package/src/modules/debug/RouterCollector.ts +449 -0
  214. package/src/modules/debug/SignalCollector.ts +4 -0
  215. package/src/modules/debug/ToastCollector.ts +4 -0
  216. package/src/modules/debug/ZonesCollector.ts +447 -0
  217. package/src/modules/debug/components/DebugBar.vue +31 -0
  218. package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
  219. package/src/modules/debug/components/index.ts +9 -0
  220. package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
  221. package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
  222. package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
  223. package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
  224. package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
  225. package/src/modules/debug/components/panels/index.ts +16 -0
  226. package/src/modules/debug/index.ts +58 -0
  227. package/src/modules/debug/styles.scss +2671 -0
  228. package/src/notifications/NotificationBadge.vue +35 -0
  229. package/src/notifications/NotificationListener.vue +71 -0
  230. package/src/notifications/NotificationModule.ts +76 -0
  231. package/src/notifications/NotificationPanel.vue +478 -0
  232. package/src/notifications/NotificationStore.ts +288 -0
  233. package/src/notifications/index.ts +42 -0
  234. package/src/notifications/styles.scss +32 -0
  235. package/src/orchestrator/Orchestrator.ts +427 -0
  236. package/src/orchestrator/index.ts +8 -0
  237. package/src/orchestrator/useOrchestrator.ts +80 -0
  238. package/src/plugin.ts +251 -0
  239. package/src/query/FilterQuery.ts +306 -0
  240. package/src/query/QueryExecutor.ts +341 -0
  241. package/src/query/index.ts +16 -0
  242. package/src/security/EntityRolesProvider.ts +385 -0
  243. package/src/security/PermissionMatcher.ts +152 -0
  244. package/src/security/PermissionRegistry.ts +273 -0
  245. package/src/security/PersistableRolesProvider.ts +584 -0
  246. package/src/security/RolesManager.ts +136 -0
  247. package/src/security/RolesProvider.ts +167 -0
  248. package/src/security/RolesProviderStorage.ts +211 -0
  249. package/src/security/SecurityModule.ts +94 -0
  250. package/src/security/StaticRolesProvider.ts +117 -0
  251. package/src/security/UsersManager.ts +163 -0
  252. package/src/security/index.ts +77 -0
  253. package/src/security/pages/RoleForm.vue +276 -0
  254. package/src/security/pages/RoleList.vue +123 -0
  255. package/src/styles/_alerts.scss +95 -0
  256. package/src/styles/_buttons.scss +391 -0
  257. package/src/styles/_cards.scss +241 -0
  258. package/src/styles/_code.scss +78 -0
  259. package/src/styles/_dialogs.scss +55 -0
  260. package/src/styles/_filter-bar.scss +70 -0
  261. package/src/styles/_forms.scss +434 -0
  262. package/src/styles/_helpers.scss +153 -0
  263. package/src/styles/_lists.scss +225 -0
  264. package/src/styles/_main.scss +356 -0
  265. package/src/styles/_markdown.scss +88 -0
  266. package/src/styles/_responsive.scss +255 -0
  267. package/src/styles/_show-pages.scss +186 -0
  268. package/src/styles/_sidebar.scss +380 -0
  269. package/src/styles/_states.scss +115 -0
  270. package/src/styles/_stats.scss +47 -0
  271. package/src/styles/_variables.scss +224 -0
  272. package/src/styles/index.scss +38 -0
  273. package/src/styles/theme/_components.scss +331 -0
  274. package/src/styles/theme/_index.scss +10 -0
  275. package/src/styles/theme/_tokens.scss +147 -0
  276. package/src/styles/theme/_utilities.scss +175 -0
  277. package/src/toast/ToastBridgeModule.ts +68 -0
  278. package/src/toast/ToastListener.vue +55 -0
  279. package/src/toast/index.ts +26 -0
  280. package/src/toast/useSignalToast.ts +154 -0
  281. package/src/types/index.ts +285 -0
  282. package/src/types/pluralize.d.ts +19 -0
  283. package/src/types/quarkernel.d.ts +50 -0
  284. package/src/utils/formatters.ts +174 -0
  285. package/src/utils/index.ts +35 -0
  286. package/src/utils/transformers.ts +131 -0
  287. package/src/vite/qdadmDebugPlugin.ts +401 -0
  288. package/src/vite-env.d.ts +50 -0
  289. package/src/zones/ZoneRegistry.ts +934 -0
  290. package/src/zones/index.ts +29 -0
  291. 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
+ }