qdadm 0.31.0 → 0.32.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 +1 -1
- package/src/composables/useAuth.js +2 -1
- package/src/debug/AuthCollector.js +104 -18
- package/src/debug/EntitiesCollector.js +28 -1
- package/src/debug/components/panels/AuthPanel.vue +81 -10
- package/src/debug/components/panels/EntitiesPanel.vue +100 -4
- package/src/entity/auth/CompositeAuthAdapter.js +212 -0
- package/src/entity/auth/factory.js +207 -0
- package/src/entity/auth/factory.test.js +257 -0
- package/src/entity/auth/index.js +14 -0
- package/src/entity/storage/MockApiStorage.js +34 -1
- package/src/kernel/Kernel.js +32 -0
|
@@ -65,13 +65,26 @@ export class MockApiStorage extends IStorage {
|
|
|
65
65
|
return MockApiStorage.capabilities.supportsCaching
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Instance capabilities getter.
|
|
70
|
+
* Merges static capabilities with instance-specific ones like requiresAuth.
|
|
71
|
+
* @returns {object}
|
|
72
|
+
*/
|
|
73
|
+
get capabilities() {
|
|
74
|
+
return {
|
|
75
|
+
...MockApiStorage.capabilities,
|
|
76
|
+
requiresAuth: !!this._authCheck
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
constructor(options = {}) {
|
|
69
81
|
super()
|
|
70
82
|
const {
|
|
71
83
|
entityName,
|
|
72
84
|
idField = 'id',
|
|
73
85
|
generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2),
|
|
74
|
-
initialData = null
|
|
86
|
+
initialData = null,
|
|
87
|
+
authCheck = null // Optional: () => boolean - throws 401 if returns false
|
|
75
88
|
} = options
|
|
76
89
|
|
|
77
90
|
if (!entityName) {
|
|
@@ -83,11 +96,24 @@ export class MockApiStorage extends IStorage {
|
|
|
83
96
|
this.generateId = generateId
|
|
84
97
|
this._storageKey = `mockapi_${entityName}_data`
|
|
85
98
|
this._data = new Map()
|
|
99
|
+
this._authCheck = authCheck
|
|
86
100
|
|
|
87
101
|
// Load from localStorage or seed with initialData
|
|
88
102
|
this._loadFromStorage(initialData)
|
|
89
103
|
}
|
|
90
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Check authentication if authCheck is configured
|
|
107
|
+
* @throws {Error} 401 error if not authenticated
|
|
108
|
+
*/
|
|
109
|
+
_checkAuth() {
|
|
110
|
+
if (this._authCheck && !this._authCheck()) {
|
|
111
|
+
const error = new Error(`Unauthorized: Authentication required to access ${this.entityName}`)
|
|
112
|
+
error.status = 401
|
|
113
|
+
throw error
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
91
117
|
/**
|
|
92
118
|
* Load data from localStorage, seed with initialData if empty
|
|
93
119
|
* @param {Array|null} initialData - Data to seed if storage is empty
|
|
@@ -158,6 +184,7 @@ export class MockApiStorage extends IStorage {
|
|
|
158
184
|
* @returns {Promise<{ items: Array, total: number }>}
|
|
159
185
|
*/
|
|
160
186
|
async list(params = {}) {
|
|
187
|
+
this._checkAuth()
|
|
161
188
|
const { page = 1, page_size = 20, sort_by, sort_order = 'asc', filters = {}, search } = params
|
|
162
189
|
|
|
163
190
|
let items = this._getAll()
|
|
@@ -213,6 +240,7 @@ export class MockApiStorage extends IStorage {
|
|
|
213
240
|
* @returns {Promise<object>}
|
|
214
241
|
*/
|
|
215
242
|
async get(id) {
|
|
243
|
+
this._checkAuth()
|
|
216
244
|
const item = this._data.get(String(id))
|
|
217
245
|
if (!item) {
|
|
218
246
|
const error = new Error(`Entity not found: ${id}`)
|
|
@@ -228,6 +256,7 @@ export class MockApiStorage extends IStorage {
|
|
|
228
256
|
* @returns {Promise<Array<object>>}
|
|
229
257
|
*/
|
|
230
258
|
async getMany(ids) {
|
|
259
|
+
this._checkAuth()
|
|
231
260
|
if (!ids || ids.length === 0) return []
|
|
232
261
|
const results = []
|
|
233
262
|
for (const id of ids) {
|
|
@@ -282,6 +311,7 @@ export class MockApiStorage extends IStorage {
|
|
|
282
311
|
* @returns {Promise<object>}
|
|
283
312
|
*/
|
|
284
313
|
async create(data) {
|
|
314
|
+
this._checkAuth()
|
|
285
315
|
const id = data[this.idField] || this.generateId()
|
|
286
316
|
const newItem = {
|
|
287
317
|
...data,
|
|
@@ -300,6 +330,7 @@ export class MockApiStorage extends IStorage {
|
|
|
300
330
|
* @returns {Promise<object>}
|
|
301
331
|
*/
|
|
302
332
|
async update(id, data) {
|
|
333
|
+
this._checkAuth()
|
|
303
334
|
if (!this._data.has(String(id))) {
|
|
304
335
|
const error = new Error(`Entity not found: ${id}`)
|
|
305
336
|
error.status = 404
|
|
@@ -322,6 +353,7 @@ export class MockApiStorage extends IStorage {
|
|
|
322
353
|
* @returns {Promise<object>}
|
|
323
354
|
*/
|
|
324
355
|
async patch(id, data) {
|
|
356
|
+
this._checkAuth()
|
|
325
357
|
const existing = this._data.get(String(id))
|
|
326
358
|
if (!existing) {
|
|
327
359
|
const error = new Error(`Entity not found: ${id}`)
|
|
@@ -344,6 +376,7 @@ export class MockApiStorage extends IStorage {
|
|
|
344
376
|
* @returns {Promise<void>}
|
|
345
377
|
*/
|
|
346
378
|
async delete(id) {
|
|
379
|
+
this._checkAuth()
|
|
347
380
|
if (!this._data.has(String(id))) {
|
|
348
381
|
const error = new Error(`Entity not found: ${id}`)
|
|
349
382
|
error.status = 404
|
package/src/kernel/Kernel.js
CHANGED
|
@@ -54,6 +54,7 @@ import { createZoneRegistry } from '../zones/ZoneRegistry.js'
|
|
|
54
54
|
import { registerStandardZones } from '../zones/zones.js'
|
|
55
55
|
import { createHookRegistry } from '../hooks/HookRegistry.js'
|
|
56
56
|
import { createSecurityChecker } from '../entity/auth/SecurityChecker.js'
|
|
57
|
+
import { authFactory, CompositeAuthAdapter } from '../entity/auth/index.js'
|
|
57
58
|
import { createManagers } from '../entity/factory.js'
|
|
58
59
|
import { defaultStorageResolver } from '../entity/storage/factory.js'
|
|
59
60
|
import { createDeferredRegistry } from '../deferred/DeferredRegistry.js'
|
|
@@ -139,6 +140,33 @@ export class Kernel {
|
|
|
139
140
|
this._pendingComponents = new Map()
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Resolve entityAuthAdapter through authFactory
|
|
145
|
+
*
|
|
146
|
+
* Enables multiple configuration styles:
|
|
147
|
+
* - Instance passthrough (backward compatible): authAdapter instance
|
|
148
|
+
* - String pattern: 'permissive', 'jwt' (if registered in authTypes)
|
|
149
|
+
* - Config object: { type: 'jwt', ... }
|
|
150
|
+
* - Composite: { default: adapter, mapping: { 'entity': adapter } }
|
|
151
|
+
*
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
_resolveEntityAuthAdapter() {
|
|
155
|
+
const { entityAuthAdapter, authTypes } = this.options
|
|
156
|
+
|
|
157
|
+
// No adapter configured → nothing to resolve
|
|
158
|
+
if (entityAuthAdapter == null) return
|
|
159
|
+
|
|
160
|
+
// Build factory context with CompositeAuthAdapter available
|
|
161
|
+
const context = {
|
|
162
|
+
authTypes: authTypes || {},
|
|
163
|
+
CompositeAuthAdapter
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Resolve through factory (handles all input types)
|
|
167
|
+
this.options.entityAuthAdapter = authFactory(entityAuthAdapter, context)
|
|
168
|
+
}
|
|
169
|
+
|
|
142
170
|
/**
|
|
143
171
|
* Create and configure the Vue app
|
|
144
172
|
*
|
|
@@ -149,6 +177,8 @@ export class Kernel {
|
|
|
149
177
|
* @returns {App} Vue app instance ready to mount
|
|
150
178
|
*/
|
|
151
179
|
createApp() {
|
|
180
|
+
// 0. Resolve entityAuthAdapter (supports instance, string, config, composite)
|
|
181
|
+
this._resolveEntityAuthAdapter()
|
|
152
182
|
// 1. Create services first (modules need them)
|
|
153
183
|
this._createSignalBus()
|
|
154
184
|
this._createHookRegistry()
|
|
@@ -192,6 +222,8 @@ export class Kernel {
|
|
|
192
222
|
* @returns {Promise<App>} Vue app instance ready to mount
|
|
193
223
|
*/
|
|
194
224
|
async createAppAsync() {
|
|
225
|
+
// 0. Resolve entityAuthAdapter (supports instance, string, config, composite)
|
|
226
|
+
this._resolveEntityAuthAdapter()
|
|
195
227
|
// 1. Create services first (modules need them)
|
|
196
228
|
this._createSignalBus()
|
|
197
229
|
this._createHookRegistry()
|