@venizia/ignis-docs 0.2.0 → 0.2.1-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 +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -18,91 +18,97 @@ flowchart TD
|
|
|
18
18
|
|
|
19
19
|
S5 -->|No enforcers| OK3([No error - skip])
|
|
20
20
|
S5 -->|Yes| S5b{"Resolve enforcer"}
|
|
21
|
-
S5b -->|Name not found|
|
|
22
|
-
S5b -->|DI fails|
|
|
21
|
+
S5b -->|Name not found| E400b[/"400: Descriptor not found"/]
|
|
22
|
+
S5b -->|DI fails| E400c[/"400: Failed to resolve"/]
|
|
23
23
|
S5b -->|OK| S6{"Step 6: Build rules"}
|
|
24
24
|
|
|
25
|
-
S6 -->|Not configured|
|
|
26
|
-
S6 -->|No FilteredAdapter|
|
|
27
|
-
S6 -->|Empty Redis key|
|
|
25
|
+
S6 -->|Not configured| E400d[/"400: Not configured. Call configure() first."/]
|
|
26
|
+
S6 -->|No FilteredAdapter| E400e[/"400: Adapter does not support loadFilteredPolicy"/]
|
|
27
|
+
S6 -->|Empty Redis key| E400f[/"400: keyFn returned an empty cache key"/]
|
|
28
|
+
S6 -->|No principalType| E400g[/"400: principalType is required"/]
|
|
28
29
|
S6 -->|OK| S7{"Step 7: Evaluate"}
|
|
29
30
|
|
|
30
|
-
S7 -->|No action/resource|
|
|
31
|
+
S7 -->|No action/resource| E400h[/"400: action and resource required"/]
|
|
31
32
|
S7 -->|ALLOW| OK4([Authorized])
|
|
32
|
-
S7 -->|DENY| E403b[/"403: Authorization denied"/]
|
|
33
|
+
S7 -->|DENY / ABSTAIN-as-deny| E403b[/"403: Authorization denied"/]
|
|
34
|
+
|
|
35
|
+
style E401 fill:#f8d7da,stroke:#dc3545
|
|
36
|
+
style E403a fill:#f8d7da,stroke:#dc3545
|
|
37
|
+
style E403b fill:#f8d7da,stroke:#dc3545
|
|
38
|
+
style E400b fill:#fff3cd,stroke:#ffc107
|
|
39
|
+
style E400c fill:#fff3cd,stroke:#ffc107
|
|
40
|
+
style E400d fill:#fff3cd,stroke:#ffc107
|
|
41
|
+
style E400e fill:#fff3cd,stroke:#ffc107
|
|
42
|
+
style E400f fill:#fff3cd,stroke:#ffc107
|
|
43
|
+
style E400g fill:#fff3cd,stroke:#ffc107
|
|
44
|
+
style E400h fill:#fff3cd,stroke:#ffc107
|
|
33
45
|
```
|
|
34
46
|
|
|
35
47
|
## Complete Error Reference
|
|
36
48
|
|
|
37
|
-
All error messages from the authorization module, organized by source
|
|
49
|
+
All error messages from the authorization module, organized by source. **Status** is what `error.statusCode` holds - most calls use `getError({ message })` with no explicit `statusCode`, which the shared `ApplicationError` defaults to **400**. Only the request-pipeline errors below (steps 2, 4, 7) set an explicit `401`/`403`.
|
|
38
50
|
|
|
39
|
-
### Component Errors (AuthorizeComponent)
|
|
51
|
+
### Component Errors (`AuthorizeComponent`)
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|---------------|--------|--------|
|
|
43
|
-
| `[AuthorizeComponent] No authorize options found. Bind options to AuthorizeBindingKeys.OPTIONS before registering the component.` | 500 | `binding` |
|
|
53
|
+
Thrown during `binding()`, at application startup.
|
|
44
54
|
|
|
45
|
-
|
|
55
|
+
| Error Message | Status | Method | Cause |
|
|
56
|
+
|---------------|--------|--------|-------|
|
|
57
|
+
| `[AuthorizeComponent] No authorize options found. Bind options to AuthorizeBindingKeys.OPTIONS before registering the component.` | 400 | `binding` | `IAuthorizeOptions` was never bound before `this.component(AuthorizeComponent)` |
|
|
46
58
|
|
|
47
|
-
|
|
48
|
-
|---------------|--------|------|
|
|
49
|
-
| `Authorization failed: No authenticated user found` | 401 | Step 2 -- User check |
|
|
50
|
-
| `Authorization failed: user.principalType is required for enforcer-based authorization` | 400 | Step 6 -- Build rules |
|
|
51
|
-
| <code v-pre>Authorization denied by voter | action: {{action}} | resource: {{resource}}</code> | 403 | Step 4 -- Voter DENY |
|
|
52
|
-
| <code v-pre>Authorization denied | action: {{action}} | resource: {{resource}}</code> | 403 | Step 7 -- Enforcer denied |
|
|
59
|
+
### Enforcer Registry Errors (`AuthorizationEnforcerRegistry` + inherited `AbstractAuthRegistry`)
|
|
53
60
|
|
|
54
|
-
|
|
61
|
+
| Error Message | Status | Method | Cause |
|
|
62
|
+
|---------------|--------|--------|-------|
|
|
63
|
+
| <code v-pre>[getKey] Invalid name | name: {{name}}</code> | 400 | `getKey` | Enforcer name is empty or falsy |
|
|
64
|
+
| `[AuthorizationEnforcerRegistry] No items registered` | 400 | `getDefaultName` | `getDefaultEnforcerName()` called with zero enforcers registered |
|
|
65
|
+
| <code v-pre>[AuthorizationEnforcerRegistry] Duplicate enforcer name(s): {{names}}</code> | 400 | `register` | Two or more enforcers in the same `register()` call share a name |
|
|
66
|
+
| <code v-pre>[AuthorizationEnforcerRegistry] Enforcer already registered: {{name}}</code> | 400 | `register` | An enforcer with this name was already registered in a previous call |
|
|
67
|
+
| <code v-pre>[AuthorizationEnforcerRegistry] Descriptor not found: {{name}}</code> | 400 | `resolveDescriptor` | `enforcerName` doesn't match any registered enforcer |
|
|
68
|
+
| <code v-pre>[AuthorizationEnforcerRegistry] Failed to resolve: {{name}}</code> | 400 | `resolveDescriptor` | The registered class has unsatisfied `@inject` dependencies |
|
|
69
|
+
| <code v-pre>[AuthorizationEnforcerRegistry] Enforcer "{{name}}" does not support cache invalidation</code> | 400 | `invalidateUserCache` / `rebuildUserCache` | The resolved enforcer doesn't implement the optional cache-management methods |
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
|
71
|
+
> [!NOTE]
|
|
72
|
+
> `[AuthorizationEnforcerRegistry] No items registered` can only surface if `getDefaultEnforcerName()` is called directly. During normal middleware execution, the provider checks `registry.hasEnforcers()` first and skips authorization when no enforcers exist - it never reaches this throw.
|
|
73
|
+
|
|
74
|
+
### Authorization Provider Errors (`AuthorizationProvider` - the request pipeline)
|
|
75
|
+
|
|
76
|
+
The only errors in the module with an **explicit** `statusCode`.
|
|
77
|
+
|
|
78
|
+
| Error Message | Status | Pipeline step | Cause |
|
|
79
|
+
|---------------|--------|------|-------|
|
|
80
|
+
| `Authorization failed: No authenticated user found` | 401 | 2 - User check | `Authentication.CURRENT_USER` is missing from the Hono context |
|
|
81
|
+
| <code v-pre>Authorization denied by voter | action: {{action}} | resource: {{resource}}</code> | 403 | 4 - Voters | A voter function returned `AuthorizationDecisions.DENY` |
|
|
82
|
+
| `Authorization failed: user.principalType is required for enforcer-based authorization` | 400 | 6 - Build rules | The authenticated user object has no `principalType` field |
|
|
83
|
+
| <code v-pre>Authorization denied | action: {{action}} | resource: {{resource}}</code> | 403 | 7 - Evaluate | The enforcer returned `DENY`, or `ABSTAIN` and `defaultDecision` resolved to `'deny'` |
|
|
84
|
+
|
|
85
|
+
### Casbin Enforcer Errors - Startup (`CasbinAuthorizationEnforcer.configure()`)
|
|
86
|
+
|
|
87
|
+
| Error Message | Status | Method | Cause |
|
|
88
|
+
|---------------|--------|--------|-------|
|
|
89
|
+
| `[CasbinAuthorizationEnforcer] "casbin" is not installed` | 400 | `configure` | The optional `casbin` peer dependency isn't installed |
|
|
90
|
+
| `[CasbinAuthorizationEnforcer] options.model is required.` | 400 | `configure` | `model` is missing from the enforcer options |
|
|
91
|
+
| <code v-pre>[registerMatchers] Role definition "{{name}}" is not declared in the Casbin model. Declare it under [role_definition] (e.g. `g = _, _, _`) before enabling domainMatching.</code> | 400 | `configure` (via `registerMatchers`) | `domainMatching.roleDefinition` isn't declared in `[role_definition]` (only checked when `domainMatching` is set) |
|
|
92
|
+
| <code v-pre>[CasbinAuthorizationEnforcer] Matcher smoke test failed at warmup - the model matcher did not compile ... {{error}}</code> | 400 | `configure` (via `assertMatcherCompilesSync`) | The matcher expression doesn't compile: syntax error, an unregistered function, or an arity mismatch |
|
|
93
|
+
| <code v-pre>[CasbinAuthorizationEnforcer] cached.options.expiresIn must be >= 10000 (ms) | Received: {{value}}</code> | 400 | `configure` (via `validateExpiresIn`) | `cached.options.expiresIn` is below `MIN_EXPIRES_IN` (10,000 ms) |
|
|
94
|
+
| <code v-pre>[resolveDomainMatchingFn] Unsupported func: {{name}} | Valids: [...]</code> | 400 | `configure` (via `registerMatchers`) | `domainMatching.fn` isn't a `CasbinDomainMatchingFunctions` value |
|
|
95
|
+
| <code v-pre>[resolveModel] Invalid model.driver | Valids: [file, text]</code> | 400 | `configure` (via `resolveModel`) | `model.driver` isn't `'file'` or `'text'` |
|
|
64
96
|
|
|
65
97
|
> [!NOTE]
|
|
66
|
-
>
|
|
67
|
-
|
|
68
|
-
### Casbin Enforcer Errors (
|
|
69
|
-
|
|
70
|
-
| Error Message | Status | Method |
|
|
71
|
-
|
|
72
|
-
| `[CasbinAuthorizationEnforcer]
|
|
73
|
-
| `[CasbinAuthorizationEnforcer]
|
|
74
|
-
| `[CasbinAuthorizationEnforcer]
|
|
75
|
-
| `[
|
|
76
|
-
| `[
|
|
77
|
-
| `[
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
| <code v-pre>[resolveDomainMatchingFn] Unsupported func: {{name}} | Valids: [...]</code> | 500 | `configure` (via `registerMatchers`) |
|
|
81
|
-
| <code v-pre>[registerMatchers] Role definition "{{name}}" is not declared in the Casbin model. ...</code> | 500 | `configure` (via `registerMatchers`, only when `domainMatching` is set) |
|
|
82
|
-
| <code v-pre>[resolveModel] Invalid model.driver | Valids: [file, text]</code> | 500 | `configure` (via `resolveModel`) |
|
|
83
|
-
| `[CasbinAuthorizationEnforcer] Cache management requires the redis cache driver, but caching is disabled.` | 500 | `invalidateUserCache`/`rebuildUserCache` (via `requireRedisCache`) |
|
|
84
|
-
|
|
85
|
-
> The `Invalid cached.driver` errors were removed - `cached` is now a typed union
|
|
86
|
-
> (`{ use: false } | { use: true, driver: 'redis', ... }`), so an invalid driver is a compile-time
|
|
87
|
-
> error, not a runtime one.
|
|
88
|
-
|
|
89
|
-
### Policy Loading Errors (CasbinAuthorizationEnforcer internals)
|
|
90
|
-
|
|
91
|
-
| Error Message | Status | Method |
|
|
92
|
-
|---------------|--------|--------|
|
|
93
|
-
| `[CasbinAuthorizationEnforcer] keyFn returned an empty cache key.` | 400 | `resolveCacheKey` (read + cache-management paths) |
|
|
94
|
-
| `[extractUserLines] Adapter does not support loadFilteredPolicy.` | 500 | `extractUserLines` |
|
|
95
|
-
| `[loadPolicyLinesIntoModel] Not configured. Call configure() first.` | 500 | `loadPolicyLinesIntoModel` |
|
|
96
|
-
| `[CasbinAuthorizationEnforcer] Cached payload is not an array of policy lines.` | - (logged, not thrown) | `parseCachedPolicyLines` - corrupt entry is discarded + refetched |
|
|
97
|
-
|
|
98
|
-
> A corrupted Redis cache entry does **not** raise an error - it is logged and discarded, and the
|
|
99
|
-
> lines are refetched from the adapter (the request never 500s on cache corruption).
|
|
100
|
-
|
|
101
|
-
### Registry Errors (AuthorizationEnforcerRegistry)
|
|
102
|
-
|
|
103
|
-
| Error Message | Status | Method |
|
|
104
|
-
|---------------|--------|--------|
|
|
105
|
-
| `[AuthorizationEnforcerRegistry] Enforcer "{{name}}" does not support cache invalidation` | 500 | `invalidateUserCache` / `rebuildUserCache` (the resolved enforcer lacks the optional method) |
|
|
98
|
+
> There is no `Invalid cached.driver` runtime error - `cached` is a typed discriminated union (`{ use: false } | { use: true; driver: 'redis'; ... }`), so an unsupported cache driver is a compile-time error. Caching is **Redis-only**.
|
|
99
|
+
|
|
100
|
+
### Casbin Enforcer Errors - Runtime (`buildRules` / `evaluate` / cache management)
|
|
101
|
+
|
|
102
|
+
| Error Message | Status | Method | Cause |
|
|
103
|
+
|---------------|--------|--------|-------|
|
|
104
|
+
| `[CasbinAuthorizationEnforcer] Not configured. Call configure() first.` | 400 | `evaluate` | `evaluate()` ran before `configure()` built the enforcer pool - should not happen via `resolveEnforcer()` |
|
|
105
|
+
| `[CasbinAuthorizationEnforcer] request.action and request.resource are required.` | 400 | `evaluate` | Malformed `IAuthorizationRequest` passed to `evaluate()` |
|
|
106
|
+
| `[CasbinAuthorizationEnforcer] keyFn returned an empty cache key.` | 400 | `resolveCacheKey` (via `buildRules`, `invalidateUserCache`, `rebuildUserCache`) | `cached.options.keyFn` returned a falsy value for this user |
|
|
107
|
+
| `[CasbinAuthorizationEnforcer] Cache management requires the redis cache driver, but caching is disabled.` | 400 | `invalidateUserCache` / `rebuildUserCache` (via `requireRedisCache`) | Called with `cached: { use: false }` |
|
|
108
|
+
| `[extractUserLines] Adapter does not support loadFilteredPolicy.` | 400 | `buildRules` (via `extractUserLines`) | `options.adapter` doesn't implement casbin's `FilteredAdapter.loadFilteredPolicy` |
|
|
109
|
+
| `[loadPolicyLinesIntoModel] Not configured. Call configure() first.` | 400 | `evaluate` (via `loadPolicyLinesIntoModel`) | Same root cause as the `evaluate` "Not configured" row, different call site |
|
|
110
|
+
|
|
111
|
+
**Not an error** - `[CasbinAuthorizationEnforcer] Cached payload is not an array of policy lines.` is logged at `warn` level by `parseCachedPolicyLines` and never thrown up to the caller: a corrupted or legacy Redis entry is discarded and the lines are refetched from the adapter, so the request never 500s (or 400s) on cache corruption.
|
|
106
112
|
|
|
107
113
|
## Troubleshooting
|
|
108
114
|
|
|
@@ -110,85 +116,73 @@ All error messages from the authorization module, organized by source:
|
|
|
110
116
|
|
|
111
117
|
**Cause:** `AuthorizeComponent` was registered but `IAuthorizeOptions` was not bound to the container.
|
|
112
118
|
|
|
113
|
-
**Fix:** Bind options **before** registering the component
|
|
119
|
+
**Fix:** Bind options **before** registering the component.
|
|
114
120
|
|
|
115
121
|
```typescript
|
|
116
|
-
// 1. Bind options first
|
|
117
122
|
this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({
|
|
118
123
|
defaultDecision: 'deny',
|
|
119
124
|
alwaysAllowRoles: ['999_super-admin'],
|
|
120
125
|
});
|
|
121
126
|
|
|
122
|
-
// 2. Then register the component
|
|
123
127
|
this.component(AuthorizeComponent);
|
|
124
128
|
```
|
|
125
129
|
|
|
126
130
|
### "Authorization failed: No authenticated user found"
|
|
127
131
|
|
|
128
|
-
**Cause:** The authorization middleware runs after authentication, but no user was found on the context (`Authentication.CURRENT_USER` is undefined).
|
|
129
|
-
- The route has `authorize` but no `authenticate` config
|
|
130
|
-
- Authentication middleware failed silently
|
|
131
|
-
- Authentication was skipped but authorization was not
|
|
132
|
+
**Cause:** The authorization middleware runs after authentication, but no user was found on the context (`Authentication.CURRENT_USER` is `undefined`). Common triggers: the route has `authorize` but no `authenticate`, authentication was skipped but authorization wasn't, or the request genuinely has no valid credentials.
|
|
132
133
|
|
|
133
|
-
**Fix:**
|
|
134
|
+
**Fix:** Every route with `authorize` needs a matching `authenticate` config.
|
|
134
135
|
|
|
135
136
|
```typescript
|
|
136
137
|
this.defineRoute({
|
|
137
138
|
configs: {
|
|
138
139
|
path: '/',
|
|
139
140
|
method: 'get',
|
|
140
|
-
authenticate: { strategies: [Authentication.STRATEGY_JWT] }, //
|
|
141
|
+
authenticate: { strategies: [Authentication.STRATEGY_JWT] }, // must be present
|
|
141
142
|
authorize: { action: AuthorizationActions.READ, resource: 'Article' },
|
|
142
143
|
// ...
|
|
143
144
|
},
|
|
144
|
-
handler: async
|
|
145
|
+
handler: async context => { /* ... */ },
|
|
145
146
|
});
|
|
146
147
|
```
|
|
147
148
|
|
|
148
149
|
### "Authorization failed: user.principalType is required"
|
|
149
150
|
|
|
150
|
-
**Cause:** The authenticated user object
|
|
151
|
+
**Cause:** The authenticated user object has no `principalType` field. Enforcer-based authorization uses it to build the casbin subject (e.g. `User_123`).
|
|
151
152
|
|
|
152
|
-
**Fix:**
|
|
153
|
+
**Fix:** Set `principalType` when you build the user in your authentication service or token payload.
|
|
153
154
|
|
|
154
155
|
```typescript
|
|
155
|
-
// In your JWT token service or authentication service:
|
|
156
156
|
return {
|
|
157
157
|
userId: '123',
|
|
158
|
-
principalType: '
|
|
158
|
+
principalType: 'User', // required for authorization
|
|
159
159
|
roles: [...],
|
|
160
160
|
};
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
> [!NOTE]
|
|
164
|
-
> `principalType` is
|
|
164
|
+
> `principalType` is read via the `IAuthUser` index signature, not a dedicated field - it must be a real property on the returned user object.
|
|
165
165
|
|
|
166
166
|
### "Authorization denied by voter | action: ... | resource: ..."
|
|
167
167
|
|
|
168
|
-
**Cause:** A voter function explicitly returned `AuthorizationDecisions.DENY
|
|
168
|
+
**Cause:** A voter function explicitly returned `AuthorizationDecisions.DENY`.
|
|
169
169
|
|
|
170
|
-
**Fix:**
|
|
171
|
-
- Voter checking ownership and user is not the owner
|
|
172
|
-
- Voter checking time window and request is outside allowed hours
|
|
173
|
-
- Voter checking resource state (e.g., locked, archived)
|
|
170
|
+
**Fix:** Inspect the voter named in your logs. Common causes: ownership check failed, a time-window check rejected the request, or a resource-state check (locked/archived) blocked it.
|
|
174
171
|
|
|
175
172
|
### "Authorization denied | action: ... | resource: ..."
|
|
176
173
|
|
|
177
|
-
**Cause:**
|
|
178
|
-
- No matching policies were found for the user
|
|
179
|
-
- Matching policies exist but deny the action
|
|
180
|
-
- The casbin model does not cover the requested action/resource combination
|
|
174
|
+
**Cause:** `enforcer.evaluate()` returned `DENY`, or `ABSTAIN` and it fell back to `defaultDecision`. Usually: no matching policy for the user, a matching `deny` policy, or the model doesn't cover the requested domain/resource/action combination.
|
|
181
175
|
|
|
182
|
-
**Fix:**
|
|
176
|
+
**Fix:** Work through this checklist:
|
|
177
|
+
|
|
178
|
+
1. **Policies are loaded correctly** - verify the adapter returns the right rows for this user (`ScopedCasbinAdapter` reads `PolicyDefinition` filtered by `subject_type`/`subject_id`).
|
|
179
|
+
2. **Subject format matches.** `normalizePayloadFn` (or the default scoped payload) must produce a subject that matches what's stored, e.g. `User_123`.
|
|
180
|
+
3. **The model covers the request.** For a custom (non-scoped) `.conf`, confirm the matcher handles the action/resource/domain shape you're sending.
|
|
181
|
+
4. **Set `defaultDecision` explicitly** - don't rely on an implicit fallback:
|
|
183
182
|
|
|
184
|
-
1. **Policies are loaded correctly** -- verify your adapter is returning the right policy definitions for the user
|
|
185
|
-
2. **Subject format matches** -- the `normalizePayloadFn` must produce subjects matching your policy definitions (e.g., `user_123` must match what's in the database)
|
|
186
|
-
3. **Casbin model covers the request** -- your `.conf` file must define matchers for the action/resource/domain pattern you're using
|
|
187
|
-
4. **Set defaultDecision explicitly:**
|
|
188
183
|
```typescript
|
|
189
184
|
this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({
|
|
190
|
-
defaultDecision: 'deny',
|
|
191
|
-
// ...
|
|
185
|
+
defaultDecision: 'deny', // explicit is better than implicit
|
|
192
186
|
});
|
|
193
187
|
```
|
|
194
188
|
|
|
@@ -196,108 +190,82 @@ this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({
|
|
|
196
190
|
|
|
197
191
|
**Cause:** Two or more enforcers in the same `register()` call have the same name.
|
|
198
192
|
|
|
199
|
-
**Fix:**
|
|
193
|
+
**Fix:** Give every enforcer in the call a unique name.
|
|
200
194
|
|
|
201
195
|
```typescript
|
|
202
196
|
AuthorizationEnforcerRegistry.getInstance().register({
|
|
203
197
|
container: this,
|
|
204
198
|
enforcers: [
|
|
205
|
-
{ enforcer:
|
|
206
|
-
{ enforcer:
|
|
199
|
+
{ enforcer: CasbinAuthorizationEnforcer, name: 'casbin', type: 'casbin', options: { /* ... */ } },
|
|
200
|
+
{ enforcer: MyCustomEnforcer, name: 'custom', type: 'custom' }, // different name
|
|
207
201
|
],
|
|
208
202
|
});
|
|
209
203
|
```
|
|
210
204
|
|
|
211
205
|
### "[AuthorizationEnforcerRegistry] Enforcer already registered: ..."
|
|
212
206
|
|
|
213
|
-
**Cause:** An enforcer with this name was already registered in a previous `register()` call
|
|
214
|
-
|
|
215
|
-
**Fix:** Ensure you only register each enforcer name once. If you need to re-register, call `registry.reset()` first (typically only in tests).
|
|
216
|
-
|
|
217
|
-
### "[AuthorizationEnforcerRegistry] No items registered"
|
|
207
|
+
**Cause:** An enforcer with this name was already registered in a previous `register()` call - the registry doesn't allow overwriting.
|
|
218
208
|
|
|
219
|
-
**
|
|
220
|
-
|
|
221
|
-
**Fix:** Register enforcers after registering the component:
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
// 1. Bind options and register component
|
|
225
|
-
this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({ ... });
|
|
226
|
-
this.component(AuthorizeComponent);
|
|
227
|
-
|
|
228
|
-
// 2. Register enforcers
|
|
229
|
-
AuthorizationEnforcerRegistry.getInstance().register({
|
|
230
|
-
container: this,
|
|
231
|
-
enforcers: [{ enforcer: CasbinAuthorizationEnforcer, name: 'casbin', type: 'casbin', options: { ... } }],
|
|
232
|
-
});
|
|
233
|
-
```
|
|
209
|
+
**Fix:** Register each name once. Call `AuthorizationEnforcerRegistry.getInstance().reset()` first if you genuinely need to re-register (typically only in tests).
|
|
234
210
|
|
|
235
211
|
### "[AuthorizationEnforcerRegistry] Descriptor not found: ..."
|
|
236
212
|
|
|
237
|
-
**Cause:**
|
|
213
|
+
**Cause:** `enforcerName` in an `authorize()` call doesn't match any registered enforcer name.
|
|
238
214
|
|
|
239
|
-
**Fix:**
|
|
215
|
+
**Fix:** Match the name used in `register()`. The default (when `enforcerName` is omitted) is the first one registered.
|
|
240
216
|
|
|
241
217
|
### "[AuthorizationEnforcerRegistry] Failed to resolve: ..."
|
|
242
218
|
|
|
243
|
-
**Cause:** The enforcer class
|
|
219
|
+
**Cause:** The enforcer class is registered, but DI resolution returned `null` - typically an unsatisfied `@inject` dependency in its constructor.
|
|
244
220
|
|
|
245
|
-
**Fix:**
|
|
221
|
+
**Fix:** Confirm every `@inject` in the enforcer's constructor is bound in the same container before `resolveEnforcer()` runs.
|
|
246
222
|
|
|
247
|
-
### "[CasbinAuthorizationEnforcer] casbin is not installed"
|
|
223
|
+
### "[CasbinAuthorizationEnforcer] "casbin" is not installed"
|
|
248
224
|
|
|
249
|
-
**Cause:** The Casbin enforcer dynamically imports `casbin`
|
|
225
|
+
**Cause:** The Casbin enforcer dynamically imports `casbin` in `configure()`, but the package isn't installed.
|
|
250
226
|
|
|
251
|
-
**Fix:**
|
|
227
|
+
**Fix:**
|
|
252
228
|
|
|
253
229
|
```bash
|
|
254
230
|
bun add casbin
|
|
255
231
|
```
|
|
256
232
|
|
|
257
|
-
### "[CasbinAuthorizationEnforcer] options.model is required"
|
|
233
|
+
### "[CasbinAuthorizationEnforcer] options.model is required."
|
|
258
234
|
|
|
259
|
-
**Cause:** The
|
|
235
|
+
**Cause:** The enforcer options have no `model`.
|
|
260
236
|
|
|
261
|
-
**Fix:** Provide
|
|
237
|
+
**Fix:** Provide a model - inline text (recommended: `CASBIN_RBAC_DOMAIN_SCOPED_MODEL`) or a file path.
|
|
262
238
|
|
|
263
239
|
```typescript
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
options: {
|
|
271
|
-
model: {
|
|
272
|
-
driver: 'file',
|
|
273
|
-
definition: path.resolve(__dirname, './security/model.conf'),
|
|
274
|
-
},
|
|
275
|
-
cached: { use: false },
|
|
276
|
-
},
|
|
277
|
-
}],
|
|
278
|
-
});
|
|
240
|
+
options: {
|
|
241
|
+
model: { driver: CasbinEnforcerModelDrivers.TEXT, definition: CASBIN_RBAC_DOMAIN_SCOPED_MODEL },
|
|
242
|
+
isScoped: true,
|
|
243
|
+
adapter,
|
|
244
|
+
cached: { use: false },
|
|
245
|
+
}
|
|
279
246
|
```
|
|
280
247
|
|
|
281
|
-
### "[
|
|
248
|
+
### "[registerMatchers] Role definition "g2" is not declared in the Casbin model..."
|
|
282
249
|
|
|
283
|
-
**Cause:**
|
|
250
|
+
**Cause:** `domainMatching.roleDefinition` references a relation the model doesn't declare under `[role_definition]`. Casbin would otherwise register the function as a silent no-op, leaving wildcard domains permanently unmatched (global roles silently denied) - the enforcer throws at boot instead.
|
|
284
251
|
|
|
285
|
-
**Fix:**
|
|
252
|
+
**Fix:** Point `roleDefinition` at a relation the model actually declares.
|
|
286
253
|
|
|
287
254
|
```typescript
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const adapter = new ScopedCasbinAdapter({
|
|
291
|
-
dataSource,
|
|
292
|
-
entities: { /* IScopedCasbinEntities */ },
|
|
293
|
-
});
|
|
255
|
+
domainMatching: { roleDefinition: 'g', fn: CasbinDomainMatchingFunctions.KEY_MATCH } // model must declare `g = _, _, _`
|
|
294
256
|
```
|
|
295
257
|
|
|
258
|
+
### "[CasbinAuthorizationEnforcer] Matcher smoke test failed at warmup..."
|
|
259
|
+
|
|
260
|
+
**Cause:** `assertMatcherCompilesSync()` runs a dummy `enforceSync()` at warmup to force casbin's lazy matcher compile. It failed - a matcher syntax error, a function referenced in the matcher that was never registered, or a request-arity mismatch (3 vs. 4 tokens).
|
|
261
|
+
|
|
262
|
+
**Fix:** Check the `[matchers]` section of your `.conf`. If scoped, confirm `isScoped: true` is set (registers `objectMatch` + `keyMatch` automatically); if using a custom flat model, confirm every function the matcher calls is registered via `domainMatching`.
|
|
263
|
+
|
|
296
264
|
### "[CasbinAuthorizationEnforcer] cached.options.expiresIn must be >= 10000"
|
|
297
265
|
|
|
298
|
-
**Cause:**
|
|
266
|
+
**Cause:** `expiresIn` is below the 10,000 ms minimum (`MIN_EXPIRES_IN`).
|
|
299
267
|
|
|
300
|
-
**Fix:**
|
|
268
|
+
**Fix:**
|
|
301
269
|
|
|
302
270
|
```typescript
|
|
303
271
|
cached: {
|
|
@@ -305,53 +273,42 @@ cached: {
|
|
|
305
273
|
driver: 'redis',
|
|
306
274
|
options: {
|
|
307
275
|
connection: redisHelper,
|
|
308
|
-
expiresIn: 5 * 60 * 1000,
|
|
309
|
-
keyFn: ({ user }) => `authz:policies:${user.userId}`,
|
|
276
|
+
expiresIn: 5 * 60 * 1000, // 5 minutes (minimum: 10,000 ms)
|
|
277
|
+
keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
|
|
310
278
|
},
|
|
311
279
|
},
|
|
312
280
|
```
|
|
313
281
|
|
|
314
282
|
### "[CasbinAuthorizationEnforcer] Not configured. Call configure() first."
|
|
315
283
|
|
|
316
|
-
**Cause:**
|
|
284
|
+
**Cause:** `evaluate()` (or its internal `loadPolicyLinesIntoModel()`) ran before `configure()` built the enforcer pool. This shouldn't happen through `resolveEnforcer()` (which auto-configures), but can occur if the enforcer is resolved manually from the DI container.
|
|
317
285
|
|
|
318
|
-
**Fix:** Always
|
|
286
|
+
**Fix:** Always go through the registry, which handles configure-once automatically.
|
|
319
287
|
|
|
320
288
|
```typescript
|
|
321
289
|
const enforcer = await AuthorizationEnforcerRegistry.getInstance().resolveEnforcer({ name: 'casbin' });
|
|
322
|
-
// configure() is called automatically on first resolve
|
|
323
290
|
```
|
|
324
291
|
|
|
325
|
-
### "[
|
|
292
|
+
### "[CasbinAuthorizationEnforcer] keyFn returned an empty cache key."
|
|
326
293
|
|
|
327
|
-
**Cause:**
|
|
294
|
+
**Cause:** `cached.options.keyFn` returned a falsy value for a user.
|
|
328
295
|
|
|
329
|
-
**Fix:**
|
|
296
|
+
**Fix:** Always return a stable, non-empty key.
|
|
330
297
|
|
|
331
298
|
```typescript
|
|
332
|
-
keyFn: ({ user }) => {
|
|
333
|
-
if (!user.userId) {
|
|
334
|
-
throw new Error('User ID is required for cache key');
|
|
335
|
-
}
|
|
336
|
-
return `authz:policies:${user.userId}`;
|
|
337
|
-
},
|
|
299
|
+
keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
|
|
338
300
|
```
|
|
339
301
|
|
|
340
|
-
### "[
|
|
341
|
-
|
|
342
|
-
**Cause:** `evaluate()` or `loadPolicyLinesIntoModel()` ran before the enforcer pool was built.
|
|
343
|
-
|
|
344
|
-
**Fix:** Ensure `configure()` completed successfully (the registry calls it on first use) before any
|
|
345
|
-
evaluation. The enforcer pool is created in `configure()`.
|
|
346
|
-
|
|
347
|
-
### "[CasbinAuthorizationEnforcer] keyFn returned an empty cache key."
|
|
302
|
+
### "[extractUserLines] Adapter does not support loadFilteredPolicy"
|
|
348
303
|
|
|
349
|
-
**Cause:**
|
|
304
|
+
**Cause:** `options.adapter` doesn't implement casbin's `FilteredAdapter.loadFilteredPolicy`. Authorization always loads policies filtered by principal.
|
|
350
305
|
|
|
351
|
-
**Fix:**
|
|
306
|
+
**Fix:** Use `ScopedCasbinAdapter`, or extend `BaseFilteredAdapter` and implement `loadFilteredPolicy`.
|
|
352
307
|
|
|
353
308
|
```typescript
|
|
354
|
-
|
|
309
|
+
import { ScopedCasbinAdapter } from '@venizia/ignis';
|
|
310
|
+
|
|
311
|
+
const adapter = new ScopedCasbinAdapter({ dataSource, entities: { /* IScopedCasbinEntities */ } });
|
|
355
312
|
```
|
|
356
313
|
|
|
357
314
|
### "[resolveModel] Invalid model.driver | Valids: [file, text]"
|
|
@@ -360,39 +317,44 @@ keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
|
|
|
360
317
|
|
|
361
318
|
**Fix:** Use `CasbinEnforcerModelDrivers.FILE` (`'file'`) or `CasbinEnforcerModelDrivers.TEXT` (`'text'`).
|
|
362
319
|
|
|
363
|
-
|
|
364
|
-
|
|
320
|
+
### "[CasbinAuthorizationEnforcer] Cache management requires the redis cache driver, but caching is disabled."
|
|
321
|
+
|
|
322
|
+
**Cause:** `invalidateUserCache()` or `rebuildUserCache()` was called on an enforcer configured with `cached: { use: false }`.
|
|
323
|
+
|
|
324
|
+
**Fix:** Either configure Redis caching, or don't call the cache-management methods for a non-cached enforcer.
|
|
365
325
|
|
|
366
326
|
## Common Patterns
|
|
367
327
|
|
|
368
|
-
### Authorization
|
|
328
|
+
### Authorization is not running
|
|
329
|
+
|
|
330
|
+
Check, in order:
|
|
331
|
+
|
|
332
|
+
1. `authorize` is actually set on the route config (not just `authenticate`).
|
|
333
|
+
2. `authenticate` isn't `{ skip: true }` - that skips authorization too, in both raw route configs and the CRUD factory.
|
|
334
|
+
3. The component is registered AND at least one enforcer is registered via `AuthorizationEnforcerRegistry`.
|
|
335
|
+
4. If no enforcers are registered, the middleware skips authorization silently (`next()`) rather than throwing - this is often the real cause of "authorization does nothing" during rollout.
|
|
369
336
|
|
|
370
|
-
|
|
371
|
-
1. Verify `authorize` is set on the route config (not just `authenticate`)
|
|
372
|
-
2. Verify `authenticate` is not set to `{ skip: true }` (which also skips authorization in CRUD factory)
|
|
373
|
-
3. Verify the component is registered and enforcers are registered via the registry
|
|
374
|
-
4. Note: if no enforcers are registered, the middleware skips authorization silently (calls `next()`) rather than throwing an error
|
|
337
|
+
### Rules are rebuilt on every request
|
|
375
338
|
|
|
376
|
-
|
|
339
|
+
Rules cache on `Authorization.RULES`, but only **within one request**:
|
|
377
340
|
|
|
378
|
-
|
|
379
|
-
-
|
|
380
|
-
-
|
|
381
|
-
- If rules are `undefined` or `null`, they will be rebuilt
|
|
341
|
+
- A new HTTP request always starts with an empty cache.
|
|
342
|
+
- Multiple `authorize` specs on the same route share the cache - only the first builds.
|
|
343
|
+
- `undefined`/`null` on `Authorization.RULES` triggers a rebuild - `c.set(Authorization.RULES, null)` forces one mid-request.
|
|
382
344
|
|
|
383
|
-
### Casbin
|
|
345
|
+
### Casbin policies not loading
|
|
384
346
|
|
|
385
|
-
1. **
|
|
386
|
-
2. **
|
|
387
|
-
3. **
|
|
388
|
-
4. **
|
|
347
|
+
1. **Adapter entities** - `IScopedCasbinEntities` (`policyDefinition`/`permission` table + schema names, `principals`, `domainTypes`) must match your actual database schema.
|
|
348
|
+
2. **Variant column** - `PolicyDefinition.variant` must use `AuthorizationPolicyVariants.*.action` values: `grant`, `assign_role`, `join_domain`, `role_inherits`, `resource_inherits`, `action_inherits`, `domain_inherits`.
|
|
349
|
+
3. **Subject/target types** - the adapter's SQL filters by `subject_type`/`target_type` against `principals` and `domainTypes`; a mismatch silently returns zero rows.
|
|
350
|
+
4. **Model** - scoped RBAC needs `CASBIN_RBAC_DOMAIN_SCOPED_MODEL` with `isScoped: true` together; one without the other misfires.
|
|
389
351
|
|
|
390
|
-
### Redis
|
|
352
|
+
### Redis cache not working
|
|
391
353
|
|
|
392
|
-
1. **
|
|
393
|
-
2.
|
|
394
|
-
3.
|
|
395
|
-
4. **
|
|
354
|
+
1. **Connection** - verify the `IRedisHelper` (`RedisSingleHelper`/`RedisClusterHelper`/`RedisSentinelHelper`) is actually connected.
|
|
355
|
+
2. **`keyFn`** - must return a unique, non-empty key per user.
|
|
356
|
+
3. **`expiresIn`** - must be `>= 10_000` (`MIN_EXPIRES_IN`).
|
|
357
|
+
4. **Corruption is silent** - a malformed cached payload is discarded and refetched (see the Casbin runtime error table above), so a "cache never seems to hit" symptom is more likely a `keyFn` mismatch than corruption.
|
|
396
358
|
|
|
397
359
|
## See Also
|
|
398
360
|
|