qdadm 0.35.0 → 0.36.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdadm",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -36,6 +36,7 @@ export class AuthCollector extends Collector {
36
36
  constructor(options = {}) {
37
37
  super(options)
38
38
  this._authAdapter = null
39
+ this._securityChecker = null
39
40
  this._ctx = null
40
41
  this._signalCleanups = []
41
42
  // Activity tracking for login/logout events
@@ -58,6 +59,7 @@ export class AuthCollector extends Collector {
58
59
  // Try alternate locations
59
60
  this._authAdapter = ctx.authAdapter
60
61
  }
62
+ this._securityChecker = ctx.security
61
63
  this._setupSignals()
62
64
  }
63
65
 
@@ -164,6 +166,7 @@ export class AuthCollector extends Collector {
164
166
  }
165
167
  this._signalCleanups = []
166
168
  this._authAdapter = null
169
+ this._securityChecker = null
167
170
  this._ctx = null
168
171
  }
169
172
 
@@ -292,6 +295,29 @@ export class AuthCollector extends Collector {
292
295
  // Permissions not available
293
296
  }
294
297
 
298
+ // Role hierarchy & permissions (lazy fetch - securityChecker created after module connect)
299
+ try {
300
+ const securityChecker = this._securityChecker || this._ctx?.security
301
+ const hierarchy = securityChecker?.roleHierarchy?.map
302
+ if (hierarchy && Object.keys(hierarchy).length > 0) {
303
+ entries.push({
304
+ type: 'hierarchy',
305
+ label: 'Role Hierarchy',
306
+ data: hierarchy
307
+ })
308
+ }
309
+ const rolePermissions = securityChecker?.rolePermissions
310
+ if (rolePermissions && Object.keys(rolePermissions).length > 0) {
311
+ entries.push({
312
+ type: 'role-permissions',
313
+ label: 'Role Permissions',
314
+ data: rolePermissions
315
+ })
316
+ }
317
+ } catch (e) {
318
+ // Security checker not available
319
+ }
320
+
295
321
  // Adapter info
296
322
  entries.push({
297
323
  type: 'adapter',
@@ -23,6 +23,8 @@ function getIcon(type) {
23
23
  user: 'pi-user',
24
24
  token: 'pi-key',
25
25
  permissions: 'pi-shield',
26
+ hierarchy: 'pi-sitemap',
27
+ 'role-permissions': 'pi-lock',
26
28
  adapter: 'pi-cog'
27
29
  }
28
30
  return icons[type] || 'pi-info-circle'
@@ -141,7 +141,7 @@ function getCapabilityLabel(cap) {
141
141
  <i
142
142
  v-if="entity.authSensitive"
143
143
  class="pi pi-shield perm-icon-auth-sensitive"
144
- title="Auth-sensitive (auto-invalidates on auth events)"
144
+ title="Auth-sensitive"
145
145
  />
146
146
  </div>
147
147
  <span class="entity-label">{{ entity.label }}</span>
@@ -59,7 +59,7 @@ export class EntityManager {
59
59
  localFilterThreshold = null, // Items threshold to switch to local filtering (null = use default)
60
60
  readOnly = false, // If true, canCreate/canUpdate/canDelete return false
61
61
  warmup = true, // If true, cache is preloaded at boot via DeferredRegistry
62
- authSensitive = false, // If true, auto-invalidate datalayer on auth events
62
+ authSensitive, // If true, auto-invalidate datalayer on auth events (auto-inferred from storage.requiresAuth if not set)
63
63
  // Scope control
64
64
  scopeWhitelist = null, // Array of scopes/modules that can bypass restrictions
65
65
  // Relations
@@ -86,7 +86,8 @@ export class EntityManager {
86
86
  this.localFilterThreshold = localFilterThreshold
87
87
  this._readOnly = readOnly
88
88
  this._warmup = warmup
89
- this._authSensitive = authSensitive
89
+ // Auto-infer authSensitive from storage.requiresAuth if not explicitly set
90
+ this._authSensitive = authSensitive ?? this._getStorageRequiresAuth()
90
91
 
91
92
  // Scope control
92
93
  this._scopeWhitelist = scopeWhitelist
@@ -1034,6 +1035,25 @@ export class EntityManager {
1034
1035
  return caps.supportsTotal ?? false
1035
1036
  }
1036
1037
 
1038
+ /**
1039
+ * Check if storage requires authentication
1040
+ *
1041
+ * Used to auto-infer authSensitive when not explicitly set.
1042
+ * Checks both instance capabilities and static capabilities.
1043
+ *
1044
+ * @returns {boolean} - true if storage requires auth
1045
+ * @private
1046
+ */
1047
+ _getStorageRequiresAuth() {
1048
+ // Check instance capabilities first (may have dynamic requiresAuth)
1049
+ if (this.storage?.capabilities?.requiresAuth !== undefined) {
1050
+ return this.storage.capabilities.requiresAuth
1051
+ }
1052
+ // Fallback to static capabilities
1053
+ const caps = this.storage?.constructor?.capabilities || {}
1054
+ return caps.requiresAuth ?? false
1055
+ }
1056
+
1037
1057
  /**
1038
1058
  * Get searchable fields declared by storage adapter
1039
1059
  *
@@ -39,7 +39,7 @@
39
39
 
40
40
  import { createApp, h } from 'vue'
41
41
  import { createPinia } from 'pinia'
42
- import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
42
+ import { createRouter, createWebHistory } from 'vue-router'
43
43
  import ToastService from 'primevue/toastservice'
44
44
  import ConfirmationService from 'primevue/confirmationservice'
45
45
  import Tooltip from 'primevue/tooltip'
@@ -86,7 +86,6 @@ export class Kernel {
86
86
  * @param {string} options.homeRoute - Route name for home redirect (or object { name, component })
87
87
  * @param {Array} options.coreRoutes - Additional routes as layout children (before module routes)
88
88
  * @param {string} options.basePath - Base path for router (e.g., '/dashboard/')
89
- * @param {boolean} options.hashMode - Use hash-based routing (/#/path) for static hosting
90
89
  * @param {object} options.app - App config { name, shortName, version, logo, theme }
91
90
  * @param {object} options.features - Feature toggles { auth, poweredBy }
92
91
  * @param {object} options.primevue - PrimeVue config { plugin, theme, options }
@@ -520,7 +519,7 @@ export class Kernel {
520
519
  * - **Layout-only mode**: Layout at root with all routes as children
521
520
  */
522
521
  _createRouter() {
523
- const { pages, homeRoute, coreRoutes, basePath, hashMode } = this.options
522
+ const { pages, homeRoute, coreRoutes, basePath } = this.options
524
523
 
525
524
  // Layout is required (shell is optional)
526
525
  if (!pages?.layout) {
@@ -598,7 +597,7 @@ export class Kernel {
598
597
  }
599
598
 
600
599
  this.router = createRouter({
601
- history: hashMode ? createWebHashHistory(basePath) : createWebHistory(basePath),
600
+ history: createWebHistory(basePath),
602
601
  routes
603
602
  })
604
603
  }
@@ -121,6 +121,14 @@ export class KernelContext {
121
121
  return this._kernel.options?.authAdapter ?? null
122
122
  }
123
123
 
124
+ /**
125
+ * Get security checker (role hierarchy, permissions)
126
+ * @returns {import('../entity/auth/SecurityChecker.js').SecurityChecker|null}
127
+ */
128
+ get security() {
129
+ return this._kernel.securityChecker
130
+ }
131
+
124
132
  // ─────────────────────────────────────────────────────────────────────────────
125
133
  // Fluent registration methods (return this for chaining)
126
134
  // ─────────────────────────────────────────────────────────────────────────────