@bluealba/platform-cli 1.0.1 → 1.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.
Files changed (52) hide show
  1. package/dist/index.js +278 -15
  2. package/docs/404.mdx +5 -0
  3. package/docs/architecture/api-explorer.mdx +478 -0
  4. package/docs/architecture/architecture-diagrams.mdx +12 -0
  5. package/docs/architecture/authentication-system.mdx +903 -0
  6. package/docs/architecture/authorization-system.mdx +886 -0
  7. package/docs/architecture/bootstrap.mdx +1442 -0
  8. package/docs/architecture/gateway-architecture.mdx +845 -0
  9. package/docs/architecture/multi-tenancy.mdx +1150 -0
  10. package/docs/architecture/overview.mdx +776 -0
  11. package/docs/architecture/scheduler.mdx +818 -0
  12. package/docs/architecture/shell.mdx +885 -0
  13. package/docs/architecture/ui-extension-points.mdx +781 -0
  14. package/docs/architecture/user-states.mdx +794 -0
  15. package/docs/development/overview.mdx +21 -0
  16. package/docs/development/workflow.mdx +914 -0
  17. package/docs/getting-started/core-concepts.mdx +892 -0
  18. package/docs/getting-started/installation.mdx +780 -0
  19. package/docs/getting-started/overview.mdx +83 -0
  20. package/docs/getting-started/quick-start.mdx +940 -0
  21. package/docs/guides/adding-documentation-sites.mdx +1367 -0
  22. package/docs/guides/creating-services.mdx +1736 -0
  23. package/docs/guides/creating-ui-modules.mdx +1860 -0
  24. package/docs/guides/identity-providers.mdx +1007 -0
  25. package/docs/guides/mermaid-diagrams.mdx +212 -0
  26. package/docs/guides/using-feature-flags.mdx +1059 -0
  27. package/docs/guides/working-with-rooms.mdx +566 -0
  28. package/docs/index.mdx +57 -0
  29. package/docs/platform-cli/commands.mdx +604 -0
  30. package/docs/platform-cli/overview.mdx +195 -0
  31. package/package.json +5 -2
  32. package/skills/ba-platform/platform-cli.skill.md +26 -0
  33. package/skills/ba-platform/platform.skill.md +35 -0
  34. package/templates/application-monorepo-template/gitignore +95 -0
  35. package/templates/bootstrap-service-template/Dockerfile.development +1 -1
  36. package/templates/bootstrap-service-template/gitignore +57 -0
  37. package/templates/bootstrap-service-template/package.json +1 -1
  38. package/templates/bootstrap-service-template/src/main.ts +6 -16
  39. package/templates/customization-ui-module-template/Dockerfile.development +1 -1
  40. package/templates/customization-ui-module-template/gitignore +73 -0
  41. package/templates/nestjs-service-module-template/Dockerfile.development +1 -1
  42. package/templates/nestjs-service-module-template/gitignore +56 -0
  43. package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
  44. package/templates/platform-init-template/{{platformName}}-core/local/.env.example +1 -1
  45. package/templates/platform-init-template/{{platformName}}-core/local/platform-docker-compose.yml +1 -1
  46. package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +0 -1
  47. package/templates/react-ui-module-template/Dockerfile +1 -1
  48. package/templates/react-ui-module-template/Dockerfile.development +1 -3
  49. package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
  50. package/templates/react-ui-module-template/gitignore +72 -0
  51. package/templates/react-ui-module-template/Dockerfile_nginx +0 -11
  52. package/templates/react-ui-module-template/nginx/default.conf +0 -23
@@ -0,0 +1,886 @@
1
+ ---
2
+ title: Authorization System
3
+ description: Deep dive into the Blue Alba Platform authorization architecture - RBAC model, operations, roles, rules, and permission checking
4
+ ---
5
+
6
+ import { Card, CardGrid, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
7
+
8
+ The Blue Alba Platform implements a comprehensive **Role-Based Access Control (RBAC)** system with fine-grained permissions, conditional rules, and resource-level authorization.
9
+
10
+ ## Authorization Model Overview
11
+
12
+ The authorization system is built on four core concepts that form a hierarchy:
13
+
14
+ ```
15
+ Applications
16
+
17
+ Operations (permissions)
18
+
19
+ Roles (collections of operations)
20
+
21
+ Rules (conditional allow/deny logic)
22
+
23
+ Users & Groups (assignment targets)
24
+ ```
25
+
26
+ <CardGrid stagger>
27
+ <Card title="Applications" icon="seti:folder">
28
+ Top-level boundary for authorization. Operations, roles, and rules are scoped to applications.
29
+ </Card>
30
+
31
+ <Card title="Operations" icon="approve-check">
32
+ Granular permissions representing specific actions (e.g., `users::read`, `orders::create`).
33
+ </Card>
34
+
35
+ <Card title="Roles" icon="seti:config">
36
+ Named collections of operations assigned to users and groups.
37
+ </Card>
38
+
39
+ <Card title="Rules" icon="seti:license">
40
+ Conditional logic that allows or denies access based on context.
41
+ </Card>
42
+ </CardGrid>
43
+
44
+ ---
45
+
46
+ ## Applications
47
+
48
+ Applications are the highest level of authorization scoping.
49
+
50
+ ### Application Entity
51
+
52
+ ```typescript
53
+ interface Application {
54
+ id: number;
55
+ name: string; // Unique identifier (e.g., "customer-portal")
56
+ displayName: string; // Human-readable name
57
+ description?: string;
58
+ allowedByDefault: boolean; // If true, users get access by default
59
+ createdAt: Date;
60
+ createdBy: string;
61
+ updatedAt: Date;
62
+ updatedBy: string;
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Operations
69
+
70
+ **Operations** represent the most granular level of permission in the system.
71
+
72
+ ### Operation Entity
73
+
74
+ ```typescript
75
+ interface Operation {
76
+ id: number;
77
+ name: string; // e.g., "users::read"
78
+ description?: string; // Human-readable description
79
+ applicationId: number; // Scoped to application
80
+ createdAt: Date;
81
+ createdBy: string;
82
+ updatedAt: Date;
83
+ updatedBy: string;
84
+ }
85
+ ```
86
+
87
+ ### Operation Naming Conventions
88
+
89
+ Operations follow a hierarchical naming pattern for consistency:
90
+
91
+ ```
92
+ {domain}::{resource}::{action}[.{modifier}]
93
+ ```
94
+
95
+ **Structure**
96
+
97
+ - **domain**: Functional area of the system (e.g., authorization, authentication, documentation)
98
+ - **resource**: Specific entity within the domain (e.g., role, auth-methods, impersonation)
99
+ - **action**: Operation to perform (e.g., delete, manage, read, config)
100
+ - **modifier (optional)**: Additional qualifier to specify scope or context
101
+
102
+ **Examples**:
103
+
104
+ <Tabs>
105
+ <TabItem label="Basic Operations">
106
+ ```
107
+ users::read → View users
108
+ users::write → Create/update users
109
+ users::delete → Delete users
110
+ orders::read → View orders
111
+ orders::create → Create new orders
112
+ orders::update → Modify existing orders
113
+ orders::delete → Delete orders
114
+ reports::read → View reports
115
+ reports::export → Export reports to file
116
+ ```
117
+ </TabItem>
118
+
119
+ <TabItem label="Complex Operations">
120
+ ```
121
+ authorization::role::delete → Delete roles from the authorization system
122
+ authorization::role::manage-operations → Manage operations assigned to specific roles
123
+ ```
124
+ </TabItem>
125
+ </Tabs>
126
+
127
+ ### Using Operations in Code
128
+
129
+ <Tabs>
130
+ <TabItem label="Frontend (React)">
131
+ ```typescript
132
+ import { Authorized, useAuth } from '@bluealba/pae-ui-react-core';
133
+
134
+ function OrderManagement() {
135
+ const { hasAccess } = useAuth();
136
+
137
+ // Check single operation
138
+ const canRead = hasAccess('orders::read');
139
+
140
+ // Check if user has ALL of the operations
141
+ const isAdmin = hasAccess(['orders::read', 'orders::write', 'orders::delete']);
142
+
143
+ return (
144
+ <div>
145
+ {/* Declarative authorization */}
146
+ <Authorized operations=["orders::read"]>
147
+ <OrderList />
148
+ </Authorized>
149
+
150
+ {/* Programmatic authorization */}
151
+ {canRead && <ViewOrderButton />}
152
+ {isAdmin && <AdminPanel />}
153
+ </div>
154
+ );
155
+ }
156
+ ```
157
+ </TabItem>
158
+ </Tabs>
159
+
160
+ ---
161
+
162
+ ## Roles
163
+
164
+ **Roles** are named collections of operations assigned to users and groups.
165
+
166
+ ### Role Entity
167
+
168
+ ```typescript
169
+ interface Role {
170
+ id: number;
171
+ name: string; // e.g., "admin", "viewer", "editor"
172
+ description?: string;
173
+ applicationId: number; // Scoped to application
174
+ operations: Operation[]; // Associated operations
175
+ createdAt: Date;
176
+ createdBy: string;
177
+ updatedAt: Date;
178
+ updatedBy: string;
179
+ }
180
+ ```
181
+
182
+ ### Standard Role Patterns
183
+
184
+ <CardGrid>
185
+ <Card title="Admin Role" icon="star">
186
+ **Full application access**
187
+
188
+ Operations:
189
+ - `{app}.*` (all operations for the application)
190
+
191
+ Use Case: System administrators
192
+ </Card>
193
+
194
+ <Card title="Viewer Role" icon="open-book">
195
+ **Read-only access**
196
+
197
+ Operations:
198
+ - `users::read`
199
+ - `orders::read`
200
+ - `reports::read`
201
+
202
+ Use Case: Users who need visibility without modification rights
203
+ </Card>
204
+
205
+ <Card title="Editor Role" icon="pencil">
206
+ **Read and write access**
207
+
208
+ Operations:
209
+ - `users::read`
210
+ - `users::write`
211
+ - `orders::read`
212
+ - `orders::write`
213
+
214
+ Use Case: Users who can create and update data
215
+ </Card>
216
+
217
+ <Card title="Approver Role" icon="approve-check">
218
+ **Specialized workflow role**
219
+
220
+ Operations:
221
+ - `orders::read`
222
+ - `orders::approve`
223
+ - `orders::reject`
224
+
225
+ Use Case: Users with approval authority
226
+ </Card>
227
+ </CardGrid>
228
+
229
+ ### Role Assignment
230
+
231
+ Roles can be assigned in two ways:
232
+
233
+ <Tabs>
234
+ <TabItem label="Direct Assignment">
235
+ ```typescript
236
+ // Assign role directly to a user
237
+ POST /applications/:appName/rules
238
+ {
239
+ "subjectType": "user",
240
+ "subject": "john@acme.com",
241
+ "resourceType": "role",
242
+ "resource": "editor",
243
+ "denied": false,
244
+ "tenantId": 1
245
+ }
246
+ ```
247
+
248
+ Direct assignment creates a rule granting the role to the specific user within the given tenant.
249
+ </TabItem>
250
+
251
+ <TabItem label="Group Assignment">
252
+ ```typescript
253
+ // Assign role to a group
254
+ POST /applications/:appName/rules
255
+ {
256
+ "subjectType": "group",
257
+ "subject": "editors-group",
258
+ "resourceType": "role",
259
+ "resource": "editor",
260
+ "denied": false,
261
+ "tenantId": 1
262
+ }
263
+
264
+ // Add user to group
265
+ POST /_/groups/:groupId/members/:username
266
+ ```
267
+
268
+ All users in the group automatically receive the role's operations.
269
+ </TabItem>
270
+ </Tabs>
271
+
272
+ <Aside type="tip">
273
+ **Best Practice**: Use group-based role assignment for easier management. Instead of assigning roles to 100 users individually, assign the role to a group and manage group membership.
274
+ </Aside>
275
+
276
+ ---
277
+
278
+ ## Rules
279
+
280
+ **Rules** add conditional logic to authorization, enabling fine-grained access control.
281
+
282
+ ### Rule Entity
283
+
284
+ ```typescript
285
+ interface Rule {
286
+ id: number;
287
+ subjectType: 'user' | 'group'; // Who the rule applies to
288
+ subject: string; // Username or group name
289
+ resourceType: 'operation' | 'role' | 'application';
290
+ resourceId: number; // ID of the resource
291
+ resourceName: string; // Name of the resource
292
+ denied: boolean; // Allow (false) or Deny (true)
293
+ applicationId?: number; // Optional application scope
294
+ tenantId?: number; // Tenant scope (null = global, applies to all tenants)
295
+ createdAt: Date;
296
+ createdBy: string;
297
+ updatedAt: Date;
298
+ updatedBy: string;
299
+ }
300
+ ```
301
+
302
+ ### Tenant-Scoped Rules
303
+
304
+ Rules can be scoped to a specific tenant or apply globally across all tenants:
305
+
306
+ - **Tenant-scoped rules** (`tenantId` set): Apply only within the specified tenant. All rules created from the Admin UI are automatically scoped to the current tenant.
307
+ - **Global rules** (`tenantId` is `null`): Apply across all tenants. These are typically legacy rules created before tenant scoping was introduced.
308
+
309
+ <Aside type="note">
310
+ In the Admin UI, global rules are highlighted with a **"Global"** badge. When deleting a global rule, the UI displays a confirmation warning that the deletion will affect all tenants.
311
+ </Aside>
312
+
313
+ ### Rule Types
314
+
315
+ <Tabs>
316
+ <TabItem label="Allow Rules">
317
+ Grant access to a resource.
318
+
319
+ ```typescript
320
+ {
321
+ subjectType: 'user',
322
+ subject: 'john@acme.com',
323
+ resourceType: 'operation',
324
+ resourceName: 'orders::read',
325
+ denied: false // ALLOW
326
+ }
327
+ ```
328
+
329
+ Result: User `john@acme.com` can perform `orders.read` operation.
330
+ </TabItem>
331
+
332
+ <TabItem label="Deny Rules">
333
+ Revoke access to a resource (takes precedence over allow).
334
+
335
+ ```typescript
336
+ {
337
+ subjectType: 'user',
338
+ subject: 'john@acme.com',
339
+ resourceType: 'operation',
340
+ resourceName: 'orders::delete',
341
+ denied: true // DENY
342
+ }
343
+ ```
344
+
345
+ Result: User `john@acme.com` cannot perform `orders.delete`, even if a role grants it.
346
+ </TabItem>
347
+
348
+ <TabItem label="Group Rules">
349
+ Apply rules to all members of a group.
350
+
351
+ ```typescript
352
+ {
353
+ subjectType: 'group',
354
+ subject: 'admins',
355
+ resourceType: 'role',
356
+ resourceName: 'admin',
357
+ denied: false // ALLOW
358
+ }
359
+ ```
360
+
361
+ Result: All users in the `admins` group receive the `admin` role.
362
+ </TabItem>
363
+
364
+ <TabItem label="Application Rules">
365
+ Grant access to entire applications.
366
+
367
+ ```typescript
368
+ {
369
+ subjectType: 'user',
370
+ subject: 'john@acme.com',
371
+ resourceType: 'application',
372
+ resourceName: 'customer-portal',
373
+ denied: false // ALLOW
374
+ }
375
+ ```
376
+
377
+ Result: User can access the `customer-portal` application.
378
+ </TabItem>
379
+ </Tabs>
380
+
381
+ ### Rule Evaluation Order
382
+
383
+ Rules are evaluated in a specific order to determine authorization:
384
+
385
+ ```
386
+ 1. Check DENY rules first (explicit denials take precedence)
387
+
388
+ 2. If denied, return DENIED
389
+
390
+ 3. Check ALLOW rules
391
+
392
+ 4. If allowed, return ALLOWED
393
+
394
+ 5. Default: DENIED (fail-safe)
395
+ ```
396
+
397
+ **Example**:
398
+
399
+ ```typescript
400
+ // User has these rules:
401
+ [
402
+ { subject: 'john', resource: 'orders::read', denied: false }, // ALLOW
403
+ { subject: 'john', resource: 'orders::write', denied: false }, // ALLOW
404
+ { subject: 'john', resource: 'orders::delete', denied: true } // DENY
405
+ ]
406
+
407
+ // Authorization checks:
408
+ isAuthorized('john', 'orders::read') → true (explicit allow)
409
+ isAuthorized('john', 'orders::write') → true (explicit allow)
410
+ isAuthorized('john', 'orders::delete') → false (explicit deny)
411
+ isAuthorized('john', 'orders::approve') → false (no rule, default deny)
412
+ ```
413
+
414
+ ---
415
+
416
+ ## Authorization Flow
417
+
418
+ ### High-Level Flow
419
+
420
+ ```
421
+ 1. User makes request
422
+
423
+ 2. Gateway authenticates user (JWT validation)
424
+
425
+ 3. Gateway extracts user's groups from JWT
426
+
427
+ 4. Gateway resolves tenant from request context
428
+
429
+ 5. Gateway queries authorization service for user's allowed resources
430
+ (filtered by tenant: includes tenant-specific + global rules)
431
+
432
+ 6. Authorization service evaluates rules and returns AllowedResources
433
+
434
+ 7. Gateway resolves target module from catalog
435
+
436
+ 8. Gateway checks if user is authorized for module + path + method
437
+
438
+ 9. If authorized, forward request; otherwise, return 403
439
+ ```
440
+
441
+ ### Allowed Resources Structure
442
+
443
+ The authorization service returns a consolidated view of user permissions:
444
+
445
+ ```typescript
446
+ interface AllowedResources {
447
+ applications: Array<{
448
+ id: number;
449
+ name: string;
450
+ displayName: string;
451
+ description: string;
452
+ createdAt: string;
453
+ createdBy: string;
454
+ updatedAt: string;
455
+ updatedBy: string;
456
+ operations: Array<{
457
+ id: number;
458
+ name: string;
459
+ }>;
460
+ }>;
461
+ }
462
+ ```
463
+
464
+ **Example**:
465
+
466
+ ```json
467
+ {
468
+ "applications": [
469
+ {
470
+ "id": 1,
471
+ "name": "customer-portal",
472
+ "displayName": "Customer Portal"
473
+ "description": "",
474
+ "createdAt": "2026-01-01T12:00:00.000Z",
475
+ "createdBy": "service",
476
+ "updatedAt": "2026-01-01T12:00:00.000Z",
477
+ "updatedBy": "service",
478
+ "operations": [
479
+ { "id": 101, "name": "users::read" },
480
+ { "id": 102, "name": "users::write" },
481
+ { "id": 201, "name": "orders::read" },
482
+ { "id": 202, "name": "orders::write" }
483
+ ]
484
+ },
485
+ {
486
+ "id": 2,
487
+ "name": "admin-console",
488
+ "displayName": "Admin Console"
489
+ "description": "",
490
+ "createdAt": "2026-01-01T12:00:00.000Z",
491
+ "createdBy": "service",
492
+ "updatedAt": "2026-01-01T12:00:00.000Z",
493
+ "updatedBy": "service",
494
+ "operations": [
495
+ { "id": 301, "name": "admin::read" },
496
+ { "id": 302, "name": "admin::write" }
497
+ ]
498
+ }
499
+ ]
500
+ }
501
+ ```
502
+
503
+ ### Authorization Check Implementation
504
+
505
+ The authorization check happens in multiple locations:
506
+
507
+ <Tabs>
508
+ <TabItem label="Gateway Level">
509
+ ```typescript
510
+ // apps/pae-nestjs-gateway-service/src/authorization/authorization.service.ts
511
+
512
+ async authorizeRequest(
513
+ context: ExecutionContext,
514
+ username: string,
515
+ tenantId?: number
516
+ ): Promise<boolean> {
517
+ const requestMethod = context.switchToHttp().getRequest().method;
518
+ const requestPath = context.switchToHttp().getRequest().url;
519
+
520
+ // 1. Get user's allowed resources (filtered by tenant)
521
+ const allowedResources = await this.authzService.getAuthorizedResourcesForUser(username, tenantId);
522
+
523
+ // 2. Get catalog
524
+ const catalog = await this.catalogService.getCatalog();
525
+
526
+ // 3. Resolve target module
527
+ const targetModule = this.catalogService.resolveTargetModule(catalog, requestPath);
528
+ if (!targetModule) {
529
+ return false; // No module found
530
+ }
531
+
532
+ // 4. Check authorization using pae-core-lib
533
+ const isAuthorized = paeInstance.isAuthorized(
534
+ targetModule,
535
+ catalog,
536
+ allowedResources,
537
+ requestPath,
538
+ requestMethod
539
+ );
540
+
541
+ // 5. Add authorization headers for downstream services
542
+ if (isAuthorized) {
543
+ this.addAuthzHeaders(context, allowedResources);
544
+ }
545
+
546
+ return isAuthorized;
547
+ }
548
+ ```
549
+ </TabItem>
550
+
551
+ <TabItem label="Core Authorization Logic">
552
+ ```typescript
553
+ // packages/pae-core-lib/src/authorization/isAuthorized/is-authorized.ts
554
+
555
+ export const isAuthorized = (
556
+ module: ModuleMetadata,
557
+ catalog: ModuleMetadata[],
558
+ allowedResources: AllowedResources,
559
+ requestPath?: string,
560
+ requestMethod: HttpMethod = 'GET'
561
+ ): boolean => {
562
+ // 1. Get module requirements (what operations are needed)
563
+ const moduleRequirements = getModuleRequirements(
564
+ module,
565
+ catalog,
566
+ requestPath,
567
+ requestMethod
568
+ );
569
+
570
+ // 2. Validate user has required operations
571
+ return validateRequirements(moduleRequirements, allowedResources);
572
+ };
573
+ ```
574
+ </TabItem>
575
+
576
+ <TabItem label="Frontend Level">
577
+ ```typescript
578
+ // React component with authorization
579
+
580
+ import { Authorized, useAuth } from '@bluealba/pae-ui-react-core';
581
+
582
+ function OrdersPage() {
583
+ const { hasAccess } = useAuth();
584
+
585
+ return (
586
+ <div>
587
+ <h1>Orders</h1>
588
+
589
+ {/* Declarative - component only renders if authorized */}
590
+ <Authorized operation={["orders::read"]}>
591
+ <OrdersList />
592
+ </Authorized>
593
+
594
+ {/* Programmatic - conditional rendering */}
595
+ {hasAccess('orders::create') && (
596
+ <Button onClick={handleCreate}>Create Order</Button>
597
+ )}
598
+
599
+ {/* Check for every of multiple operations */}
600
+ {hasAccess(['orders::approve', 'orders::admin']) && (
601
+ <ApprovalPanel />
602
+ )}
603
+ </div>
604
+ );
605
+ }
606
+ ```
607
+ </TabItem>
608
+ </Tabs>
609
+
610
+ ---
611
+
612
+ ## Module-Level Authorization
613
+
614
+ Modules in the catalog can specify required operations for access.
615
+
616
+ ### Module Authorization Configuration
617
+
618
+ ```typescript
619
+ interface CatalogModule {
620
+ id: string;
621
+ name: string;
622
+ baseUrl: string;
623
+
624
+ authorization: {
625
+
626
+ // Authorization configuration
627
+ operations?: string[]; // Required operations for module access
628
+ isPublic?: boolean; // If true, no authentication required
629
+
630
+ // Route-level operations
631
+ routes?: Array<{
632
+ pattern: string; // ex.: /cache/(.*)
633
+ methods: string[]; // ex.: ['POST', 'PUT']
634
+ operations: string[]; // Override operations for specific route
635
+ }>;
636
+ }
637
+
638
+ }
639
+ ```
640
+
641
+ **Example**:
642
+
643
+ ```typescript
644
+ {
645
+ name: 'orders-service',
646
+ baseUrl: '/api/orders',
647
+
648
+ authorization: {
649
+ operations: ['orders::read'], // Default: need orders.read to access
650
+
651
+ routes: [
652
+ {
653
+ pattern: '/api/orders',
654
+ methods: ['GET'],
655
+ operations: ['orders::read'] // Can be more specific
656
+ },
657
+ {
658
+ pattern: '/api/orders',
659
+ methods: ['POST'],
660
+ operations: ['orders::create', 'orders::write']
661
+ },
662
+ {
663
+ pattern: '/api/orders/:id',
664
+ methods: ['DELETE'],
665
+ operations: ['orders::delete', 'orders::admin']
666
+ }
667
+ ]
668
+ }
669
+ }
670
+ ```
671
+
672
+ ---
673
+
674
+ ## Authorization Service Architecture
675
+
676
+ The authorization system consists of multiple services working together:
677
+
678
+ ```
679
+ ┌─────────────────────────────────────────────────────────────────┐
680
+ │ Authorization Service │
681
+ │ (NestJS microservice) │
682
+ │ │
683
+ │ ┌────────────────────────────────────────────────────────────┐ │
684
+ │ │ Applications API │ │
685
+ │ │ POST /applications - Create application │ │
686
+ │ │ GET /applications - List applications │ │
687
+ │ │ PATCH /applications/:applicationName - Update application │ │
688
+ │ │ DELETE /applications/:applicationName - Delete application │ │
689
+ │ └────────────────────────────────────────────────────────────┘ │
690
+ │ │
691
+ │ ┌────────────────────────────────────────────────────────────┐ │
692
+ │ │ Operations API │ │
693
+ │ │ POST /applications/:applicationName/operations - Create operation │ │
694
+ │ │ GET /applications/:applicationName/operations - List operations │ │
695
+ │ │ PATCH /applications/:applicationName/operations/:operationName - Update operation │ │
696
+ │ │ DELETE /applications/:applicationName/operations/:operationName - Delete operation │ │
697
+ │ └────────────────────────────────────────────────────────────┘ │
698
+ │ │
699
+ │ ┌────────────────────────────────────────────────────────────┐ │
700
+ │ │ Roles API │ │
701
+ │ │ POST /applications/:applicationName/roles - Create role │ │
702
+ │ │ GET /applications/:applicationName/roles - List roles │ │
703
+ │ │ PATCH /applications/:applicationName/roles/:roleName - Update role │ │
704
+ │ │ DELETE /applications/:applicationName/roles/:roleName - Delete role │ │
705
+ │ │ POST /roles/:roleName/operations/:operationName - Assign operation │ │
706
+ │ │ DELETE /roles/:roleName/operations/:operationName - Remove operation │ │
707
+ │ └────────────────────────────────────────────────────────────┘ │
708
+ │ │
709
+ │ ┌────────────────────────────────────────────────────────────┐ │
710
+ │ │ Rules API │ │
711
+ │ │ POST /applications/:applicationName/rules - Create rule │ │
712
+ │ │ GET /applications/:applicationName/rules - List rules │ │
713
+ │ │ PATCH /applications/:applicationName/rules/:id - Update rule │ │
714
+ │ │ DELETE /applications/:applicationName/rules/:id - Delete rule │ │
715
+ │ └────────────────────────────────────────────────────────────┘ │
716
+ │ │
717
+ │ ┌────────────────────────────────────────────────────────────┐ │
718
+ │ │ Authorization Query API │ │
719
+ │ │ GET /users/:username/allowed-resources │ │
720
+ │ │ → Returns AllowedResources for user │ │
721
+ │ │ GET /users/:username/allowed-applications │ │
722
+ │ │ → Returns applications user can access │ │
723
+ │ │ GET /users/:username/allowed-operations │ │
724
+ │ │ → Returns operations user can perform │ │
725
+ │ └────────────────────────────────────────────────────────────┘ │
726
+ └─────────────────────────────────────────────────────────────────┘
727
+ ```
728
+
729
+ ---
730
+
731
+ ## Database Schema
732
+
733
+ The authorization system uses the following database tables:
734
+
735
+ ```sql
736
+ -- Applications
737
+ CREATE TABLE applications (
738
+ id SERIAL PRIMARY KEY,
739
+ name VARCHAR(255) UNIQUE NOT NULL,
740
+ display_name VARCHAR(255) NOT NULL,
741
+ description TEXT,
742
+ allowed_by_default BOOLEAN DEFAULT FALSE,
743
+ created_at TIMESTAMP DEFAULT NOW(),
744
+ created_by VARCHAR(255) NOT NULL,
745
+ updated_at TIMESTAMP DEFAULT NOW(),
746
+ updated_by VARCHAR(255) NOT NULL
747
+ );
748
+
749
+ -- Operations
750
+ CREATE TABLE operations (
751
+ id SERIAL PRIMARY KEY,
752
+ name VARCHAR(255) NOT NULL,
753
+ description TEXT,
754
+ application_id INTEGER REFERENCES applications(id) ON DELETE CASCADE,
755
+ created_at TIMESTAMP DEFAULT NOW(),
756
+ created_by VARCHAR(255) NOT NULL,
757
+ updated_at TIMESTAMP DEFAULT NOW(),
758
+ updated_by VARCHAR(255) NOT NULL,
759
+ UNIQUE(application_id, name)
760
+ );
761
+
762
+ -- Roles
763
+ CREATE TABLE roles (
764
+ id SERIAL PRIMARY KEY,
765
+ name VARCHAR(255) NOT NULL,
766
+ description TEXT,
767
+ application_id INTEGER REFERENCES applications(id) ON DELETE CASCADE,
768
+ created_at TIMESTAMP DEFAULT NOW(),
769
+ created_by VARCHAR(255) NOT NULL,
770
+ updated_at TIMESTAMP DEFAULT NOW(),
771
+ updated_by VARCHAR(255) NOT NULL,
772
+ UNIQUE(application_id, name)
773
+ );
774
+
775
+ -- Role-Operation assignments
776
+ CREATE TABLE role_operations (
777
+ role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
778
+ operation_id INTEGER REFERENCES operations(id) ON DELETE CASCADE,
779
+ PRIMARY KEY (role_id, operation_id)
780
+ );
781
+
782
+ -- Rules
783
+ CREATE TABLE rules (
784
+ id SERIAL PRIMARY KEY,
785
+ subject_type VARCHAR(50) NOT NULL, -- 'user' or 'group'
786
+ subject VARCHAR(255) NOT NULL, -- username or group name
787
+ resource_type VARCHAR(50) NOT NULL, -- 'operation', 'role', 'application'
788
+ resource_id INTEGER NOT NULL,
789
+ resource_name VARCHAR(255) NOT NULL,
790
+ denied BOOLEAN DEFAULT FALSE,
791
+ application_id INTEGER REFERENCES applications(id) ON DELETE CASCADE,
792
+ tenant_id INTEGER REFERENCES tenants(id) ON DELETE CASCADE, -- NULL = global (all tenants)
793
+ created_at TIMESTAMP DEFAULT NOW(),
794
+ created_by VARCHAR(255) NOT NULL,
795
+ updated_at TIMESTAMP DEFAULT NOW(),
796
+ updated_by VARCHAR(255) NOT NULL
797
+ );
798
+
799
+ -- Indexes for performance
800
+ CREATE INDEX idx_rules_subject ON rules(subject);
801
+ CREATE INDEX idx_rules_resource ON rules(resource_type, resource_id);
802
+ CREATE INDEX idx_rules_tenant ON rules(tenant_id);
803
+ CREATE INDEX idx_operations_app ON operations(application_id);
804
+ CREATE INDEX idx_roles_app ON roles(application_id);
805
+ ```
806
+
807
+ ---
808
+
809
+ ## Performance Considerations
810
+
811
+ ### Caching
812
+
813
+ Authorization data is cached to reduce database queries:
814
+
815
+ ```typescript
816
+ // Cache user's allowed resources for 5 minutes
817
+ const cacheKey = `authz:user:${username}:resources`;
818
+ let allowedResources = await cache.get(cacheKey);
819
+
820
+ if (!allowedResources) {
821
+ allowedResources = await authzService.getAllowedResources(username);
822
+ await cache.set(cacheKey, allowedResources, 300); // 5 minutes TTL
823
+ }
824
+ ```
825
+
826
+ ### Optimization Strategies
827
+
828
+ <CardGrid>
829
+ <Card title="Batch Queries" icon="rocket">
830
+ Load all rules for a user in one query instead of multiple queries
831
+ </Card>
832
+
833
+ <Card title="Denormalization" icon="seti:db">
834
+ Store flattened permission data for faster lookups
835
+ </Card>
836
+
837
+ <Card title="Index Optimization" icon="magnifier">
838
+ Database indexes on subject, resource_type, and resource_id columns
839
+ </Card>
840
+
841
+ <Card title="Early Termination" icon="approve-check">
842
+ Return immediately on first DENY rule (no need to check further)
843
+ </Card>
844
+ </CardGrid>
845
+
846
+ ---
847
+
848
+ ## Security Considerations
849
+
850
+ <Aside type="caution" title="Authorization Security Best Practices">
851
+
852
+ **Principle of Least Privilege**:
853
+ - Grant users only the minimum permissions needed
854
+ - Prefer deny-by-default (explicit allow required)
855
+ - Regularly audit and review permissions
856
+
857
+ **Rule Management**:
858
+ - DENY rules take precedence over ALLOW rules
859
+ - Avoid wildcard operations (`*.*`) except for admins
860
+ - Document why DENY rules exist
861
+
862
+ **Context Validation**:
863
+ - Always validate tenant context
864
+ - Never trust client-provided authorization headers
865
+ - Verify user still has permissions (don't rely on stale tokens)
866
+
867
+ **Audit Trail**:
868
+ - Log all authorization failures
869
+ - Track who grants/revokes permissions
870
+ - Monitor for unusual permission changes
871
+
872
+ **Testing**:
873
+ - Unit test authorization logic extensively
874
+ - Test both positive and negative cases
875
+ - Verify DENY rules work correctly
876
+ - Test permission inheritance through groups
877
+
878
+ </Aside>
879
+
880
+ ---
881
+
882
+ ## Next Steps
883
+
884
+ - **[Gateway Architecture](/_/docs/architecture/gateway-architecture/)** - How gateway enforces authorization
885
+ - **[Authentication System](/_/docs/architecture/authentication-system/)** - User authentication and JWT
886
+ - **[Multi-Tenancy](/_/docs/architecture/multi-tenancy/)** - Tenant-scoped authorization