@dudousxd/nestjs-durable-dashboard 0.55.1 → 0.57.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/dist/client/durable-client.d.ts +11 -0
- package/dist/client/durable-client.d.ts.map +1 -1
- package/dist/client/durable-client.js +32 -16
- package/dist/client/durable-client.js.map +1 -1
- package/dist/server/index.cjs +698 -354
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +147 -43
- package/dist/server/index.d.ts +147 -43
- package/dist/server/index.js +612 -290
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/{bench-DCclNvcZ.js → bench-BXhQP2W7.js} +1 -1
- package/dist/spa/assets/index-C6Ih-K3x.js +1 -0
- package/dist/spa/assets/index-D_rIGnea.css +1 -0
- package/dist/spa/assets/{index-B-wtX4GV.js → index-DwGUovEa.js} +15 -15
- package/dist/spa/assets/{preview-CdsjvOrK.js → preview-ekPMzj7t.js} +1 -1
- package/dist/spa/bench.html +3 -3
- package/dist/spa/index.html +3 -3
- package/dist/spa/preview.html +3 -3
- package/package.json +7 -1
- package/dist/spa/assets/index-CJdk28vt.css +0 -1
- package/dist/spa/assets/index-ChjKRQZx.js +0 -1
package/dist/server/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile } from '@nestjs/common';
|
|
2
|
+
import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
|
|
1
3
|
import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
|
|
2
4
|
import { RunGateway, StateStore, WorkflowEngine, ScheduleListQuery, ScheduleDescription, RunResult, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RepublishLifecycleResult, WorkflowLifecycleLinks, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
|
|
3
5
|
export { RunDetail } from '@dudousxd/nestjs-durable-core';
|
|
@@ -5,7 +7,108 @@ import * as rxjs from 'rxjs';
|
|
|
5
7
|
import { Observable } from 'rxjs';
|
|
6
8
|
import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
|
|
7
9
|
import { FilterRunner } from '@dudousxd/nestjs-filter';
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
type ConsoleAuthorizeResult = boolean | {
|
|
12
|
+
allowed: true;
|
|
13
|
+
} | {
|
|
14
|
+
allowed: false;
|
|
15
|
+
reason?: 'unauthenticated' | 'forbidden';
|
|
16
|
+
} | {
|
|
17
|
+
redirectTo: string;
|
|
18
|
+
};
|
|
19
|
+
/** Class form: an `@Injectable()` provider (constructor injection works). */
|
|
20
|
+
interface ConsoleAuthorizer {
|
|
21
|
+
authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
|
|
22
|
+
}
|
|
23
|
+
type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
|
|
24
|
+
/** An `@nestjs/authorization` policy check: `policy[ability](user)`. */
|
|
25
|
+
interface ConsolePolicyCheck {
|
|
26
|
+
policy: Type<unknown>;
|
|
27
|
+
ability: string;
|
|
28
|
+
}
|
|
29
|
+
type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
|
|
30
|
+
default: ConsoleAuthorizerClass;
|
|
31
|
+
}>) | ConsolePolicyCheck | string;
|
|
32
|
+
type ConsoleDecision = {
|
|
33
|
+
allowed: true;
|
|
34
|
+
} | {
|
|
35
|
+
allowed: false;
|
|
36
|
+
reason: 'unauthenticated' | 'forbidden';
|
|
37
|
+
redirectTo?: string;
|
|
38
|
+
};
|
|
39
|
+
/** What a resolved `authorize` is: called once per request, never throws. */
|
|
40
|
+
type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
|
|
41
|
+
/**
|
|
42
|
+
* The provider `@dudousxd/nestjs-authz` registers to resolve gate names (`authorize: 'console.x'`).
|
|
43
|
+
* A registry symbol, so it is found without depending on that package.
|
|
44
|
+
*/
|
|
45
|
+
declare const CONSOLE_GATE: unique symbol;
|
|
46
|
+
interface ConsoleGate {
|
|
47
|
+
allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
|
|
48
|
+
}
|
|
49
|
+
/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
|
|
50
|
+
declare const CONSOLE_AUTHORIZE_MISSING_WARNING = "[@dudousxd/nestjs-durable-dashboard] console has no `authorize` configured: it is open outside production and denies every request in production. Set `authorize` in DurableDashboardModule.forRoot().";
|
|
51
|
+
/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
|
|
52
|
+
declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves `authorize` into one function the guard calls per request. Resolution errors that are
|
|
55
|
+
* configuration mistakes (a gate name without nestjs-authz, a policy without @nestjs/authorization)
|
|
56
|
+
* throw here, so call it at boot. The returned function never throws: an error is a `forbidden`.
|
|
57
|
+
*
|
|
58
|
+
* `authorize` omitted: open outside production, `forbidden` for every request in production (read
|
|
59
|
+
* when this runs), and one warning logged (unless `warnIfMissing: false`).
|
|
60
|
+
*/
|
|
61
|
+
declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
warnIfMissing?: boolean;
|
|
64
|
+
}): Promise<ConsoleDecide>;
|
|
65
|
+
/**
|
|
66
|
+
* Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
|
|
67
|
+
* is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
|
|
68
|
+
* empty, so a missing env var fails at config time instead of opening the console.
|
|
69
|
+
*/
|
|
70
|
+
declare function authorizeByToken(options: {
|
|
71
|
+
token: string | undefined;
|
|
72
|
+
}): ConsoleAuthorize;
|
|
73
|
+
/** DI token of the resolved {@link ConsoleDecide} the guard runs. */
|
|
74
|
+
declare const CONSOLE_AUTHORIZE_DECIDE: unique symbol;
|
|
75
|
+
/** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
|
|
76
|
+
declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-durable-dashboard:console-page";
|
|
77
|
+
declare const ConsolePage: () => ClassDecorator;
|
|
78
|
+
/**
|
|
79
|
+
* The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly
|
|
80
|
+
* where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.
|
|
81
|
+
*/
|
|
82
|
+
declare class ConsoleAccessDenied extends HttpException {
|
|
83
|
+
readonly decision: Extract<ConsoleDecision, {
|
|
84
|
+
allowed: false;
|
|
85
|
+
}>;
|
|
86
|
+
readonly page: boolean;
|
|
87
|
+
constructor(decision: Extract<ConsoleDecision, {
|
|
88
|
+
allowed: false;
|
|
89
|
+
}>, page: boolean);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Marks a console route that is not part of the console: `authorize` does not apply to it. Used on
|
|
93
|
+
* the `ctx.webhook()` callback, which third parties call with the token as the secret.
|
|
94
|
+
*/
|
|
95
|
+
declare const CONSOLE_PUBLIC_METADATA = "@dudousxd/nestjs-durable-dashboard:console-public";
|
|
96
|
+
declare const ConsolePublic: () => MethodDecorator;
|
|
97
|
+
declare class ConsoleAuthorizeGuard implements CanActivate {
|
|
98
|
+
private readonly decide;
|
|
99
|
+
constructor(decide: ConsoleDecide);
|
|
100
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
101
|
+
}
|
|
102
|
+
declare class ConsoleAuthorizeFilter implements ExceptionFilter {
|
|
103
|
+
private readonly adapterHost;
|
|
104
|
+
constructor(adapterHost: HttpAdapterHost);
|
|
105
|
+
catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The guard and its filter as one class decorator, for a console you mount yourself
|
|
109
|
+
* (`routes: false`). List it ABOVE your authentication guards, so it runs after them.
|
|
110
|
+
*/
|
|
111
|
+
declare const UseConsoleAuthorize: () => ClassDecorator & MethodDecorator;
|
|
9
112
|
|
|
10
113
|
/**
|
|
11
114
|
* Read-model and actions backing the control-plane UI. Run-facing ops (list/detail/retry/cancel/
|
|
@@ -325,9 +428,9 @@ declare class DurableApiControllerBase {
|
|
|
325
428
|
}>;
|
|
326
429
|
/**
|
|
327
430
|
* Public callback endpoint for `ctx.webhook()`: a third party POSTs here (the url handed to it),
|
|
328
|
-
* and the body resumes the waiting run.
|
|
329
|
-
*
|
|
330
|
-
* verification in your own middleware.
|
|
431
|
+
* and the body resumes the waiting run. Not a console route: the console's `authorize` does not
|
|
432
|
+
* apply, the token is the secret. The app's `decorators` still do. Treat the token as a secret
|
|
433
|
+
* and/or front it with signature verification in your own middleware.
|
|
331
434
|
*/
|
|
332
435
|
webhook(token: string, body: unknown): Promise<_dudousxd_nestjs_durable_core.RunResult>;
|
|
333
436
|
/** Live query: the latest value a run published for `key` via `ctx.setEvent` (no side effect). */
|
|
@@ -345,6 +448,25 @@ declare class DurableApiController extends DurableApiControllerBase {
|
|
|
345
448
|
}
|
|
346
449
|
|
|
347
450
|
interface DurableDashboardOptions {
|
|
451
|
+
/**
|
|
452
|
+
* Who may use the console (the SPA, its JSON API and the run stream). Not the `ctx.webhook()`
|
|
453
|
+
* callback (`POST <apiBasePath>/webhooks/:token`), which third parties call with the token as the
|
|
454
|
+
* secret; {@link decorators} still apply to it. One of:
|
|
455
|
+
*
|
|
456
|
+
* - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);
|
|
457
|
+
* - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;
|
|
458
|
+
* its dependencies resolve from {@link imports} or global modules);
|
|
459
|
+
* - a lazy import `() => import('./console-access.js')` whose default export is such a class;
|
|
460
|
+
* - `{ policy, ability }`, checked with `@nestjs/authorization`'s `AuthorizationService`;
|
|
461
|
+
* - a gate name, resolved by `@dudousxd/nestjs-authz`'s `AuthzModule`.
|
|
462
|
+
*
|
|
463
|
+
* `authorizeByToken({ token })` builds one for a bearer token. It runs after the guards in
|
|
464
|
+
* {@link decorators}, so `request.user` is already set.
|
|
465
|
+
*
|
|
466
|
+
* Omitted: the console is open outside production and answers `403` to every request when
|
|
467
|
+
* `NODE_ENV=production`, and one warning is logged at boot.
|
|
468
|
+
*/
|
|
469
|
+
authorize?: ConsoleAuthorize;
|
|
348
470
|
/**
|
|
349
471
|
* Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
|
|
350
472
|
* `/api` prefix so it reads as a UI, not an endpoint.
|
|
@@ -372,26 +494,26 @@ interface DurableDashboardOptions {
|
|
|
372
494
|
* another registration or onto the exported classes. A guard named in `UseGuards(...)` resolves
|
|
373
495
|
* its dependencies from {@link imports} (or a global module).
|
|
374
496
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* {@link DurableDashboardModule}. The module cannot tell a `Can(...)` from a decorator that
|
|
378
|
-
* protects nothing, so passing one is your statement that the console is protected.
|
|
497
|
+
* `decorators` is not access control on its own: {@link authorize} decides who gets in, and runs
|
|
498
|
+
* after these. Use `decorators` for what must run first, typically authentication.
|
|
379
499
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
500
|
+
* The controllers are marked `Can.Anyone()` for `@nestjs/authorization`, whose global guard
|
|
501
|
+
* otherwise refuses every route that declares no `@Can()`. A `@Can()` passed here is still
|
|
502
|
+
* enforced.
|
|
383
503
|
*/
|
|
384
504
|
decorators?: ClassDecorator[];
|
|
385
505
|
/**
|
|
386
506
|
* `false` mounts NO controllers: the SPA and its JSON API are not registered,
|
|
387
507
|
* while every provider still is (the run read/action service, the run filter, the SPA's base
|
|
388
|
-
* paths). Serve the console from controllers of your own instead,
|
|
389
|
-
*
|
|
508
|
+
* paths). Serve the console from controllers of your own instead, by extending
|
|
509
|
+
* {@link DurableUiControllerBase} and {@link DurableApiControllerBase}. Apply `authorize` to them
|
|
510
|
+
* with `@UseConsoleAuthorize()` (listed above your authentication guards, so it runs after them),
|
|
511
|
+
* or protect them with ordinary Nest decorators:
|
|
390
512
|
*
|
|
391
513
|
* ```ts
|
|
392
|
-
* @Controller('ops/durable') @
|
|
514
|
+
* @Controller('ops/durable') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
393
515
|
* class ConsoleUi extends DurableUiControllerBase {}
|
|
394
|
-
* @Controller('ops/durable/api') @
|
|
516
|
+
* @Controller('ops/durable/api') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
395
517
|
* class ConsoleApi extends DurableApiControllerBase {}
|
|
396
518
|
* // in the module that declares them:
|
|
397
519
|
* imports: [DurableDashboardModule.forRoot({ routes: false, basePath: '/ops/durable', apiBasePath: '/ops/durable/api' })]
|
|
@@ -400,8 +522,9 @@ interface DurableDashboardOptions {
|
|
|
400
522
|
* `basePath`/`apiBasePath` still matter here: they are where you mounted the two controllers, and
|
|
401
523
|
* the SPA is told to load its assets from `basePath` and fetch from `apiBasePath`.
|
|
402
524
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
525
|
+
* `authorize` omitted works as for mounted routes: `@UseConsoleAuthorize()` lets every request
|
|
526
|
+
* through outside production and answers `403` in production, and the boot warning is logged.
|
|
527
|
+
* The `*Base` classes carry no
|
|
405
528
|
* guards and no `Can.Anyone()` mark, so under `@nestjs/authorization`'s global guard a subclass
|
|
406
529
|
* without a `@Can()` is denied. `decorators` throws when combined with `routes: false` rather than
|
|
407
530
|
* being silently ignored.
|
|
@@ -411,10 +534,6 @@ interface DurableDashboardOptions {
|
|
|
411
534
|
*/
|
|
412
535
|
routes?: boolean;
|
|
413
536
|
}
|
|
414
|
-
/** DI token of the boot-time check that refuses an unprotected console in production. */
|
|
415
|
-
declare const DASHBOARD_PROTECTION_CHECK: unique symbol;
|
|
416
|
-
/** The boot error for a mounted console with no `decorators` under `NODE_ENV=production`. */
|
|
417
|
-
declare const UNPROTECTED_CONSOLE_IN_PRODUCTION: string;
|
|
418
537
|
/**
|
|
419
538
|
* Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own
|
|
420
539
|
* dynamic module because enhancers (a guard from `decorators`) DI-instantiate from their
|
|
@@ -427,34 +546,19 @@ declare class DurableApiModule {
|
|
|
427
546
|
routes?: boolean;
|
|
428
547
|
/** See {@link DurableDashboardOptions.decorators}. */
|
|
429
548
|
decorators?: ClassDecorator[];
|
|
549
|
+
/** See {@link DurableDashboardOptions.authorize}. */
|
|
550
|
+
authorize?: ConsoleAuthorize;
|
|
430
551
|
}): DynamicModule;
|
|
431
552
|
}
|
|
432
|
-
/**
|
|
433
|
-
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
434
|
-
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` (or `forRootAsync`
|
|
435
|
-
* when `dashboardAuth`'s `login` hook needs injected services) alongside `DurableModule` (global),
|
|
436
|
-
* so it resolves the engine and store. Front the routes with the first-class `guards` option
|
|
437
|
-
* (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
|
|
438
|
-
* or with the built-in `dashboardAuth` cookie login, or both.
|
|
439
|
-
* With none of those (and no `decorators` / `token` / `allowUnauthenticated`), the console fails
|
|
440
|
-
* closed when `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
|
|
441
|
-
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers — see
|
|
442
|
-
* {@link DurableDashboardOptions.routes}.
|
|
443
|
-
*/
|
|
444
553
|
/**
|
|
445
554
|
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
446
555
|
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` alongside
|
|
447
556
|
* `DurableModule` (global), so it resolves the engine and store.
|
|
448
557
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* 2. `routes: false`, and mount it yourself by extending `DurableUiControllerBase` /
|
|
454
|
-
* `DurableApiControllerBase` (see {@link DurableDashboardOptions.routes}).
|
|
455
|
-
*
|
|
456
|
-
* With `NODE_ENV=production`, a mounted console with no `decorators` fails the boot with
|
|
457
|
-
* {@link UNPROTECTED_CONSOLE_IN_PRODUCTION}. Outside production it is served open, with a warning.
|
|
558
|
+
* Access is decided by {@link DurableDashboardOptions.authorize}. Without it the console is open
|
|
559
|
+
* outside production and denies every request in production, with one warning at boot.
|
|
560
|
+
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers (see
|
|
561
|
+
* {@link DurableDashboardOptions.routes}).
|
|
458
562
|
*/
|
|
459
563
|
declare class DurableDashboardModule {
|
|
460
564
|
static forRoot(options?: DurableDashboardOptions): DynamicModule;
|
|
@@ -492,4 +596,4 @@ declare class DurableUiControllerBase {
|
|
|
492
596
|
declare class DurableUiController extends DurableUiControllerBase {
|
|
493
597
|
}
|
|
494
598
|
|
|
495
|
-
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH,
|
|
599
|
+
export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, CONSOLE_PUBLIC_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, ConsolePublic, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiControllerBase, UseConsoleAuthorize, type WorkflowLifecyclePage, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile } from '@nestjs/common';
|
|
2
|
+
import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
|
|
1
3
|
import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
|
|
2
4
|
import { RunGateway, StateStore, WorkflowEngine, ScheduleListQuery, ScheduleDescription, RunResult, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RepublishLifecycleResult, WorkflowLifecycleLinks, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
|
|
3
5
|
export { RunDetail } from '@dudousxd/nestjs-durable-core';
|
|
@@ -5,7 +7,108 @@ import * as rxjs from 'rxjs';
|
|
|
5
7
|
import { Observable } from 'rxjs';
|
|
6
8
|
import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
|
|
7
9
|
import { FilterRunner } from '@dudousxd/nestjs-filter';
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
type ConsoleAuthorizeResult = boolean | {
|
|
12
|
+
allowed: true;
|
|
13
|
+
} | {
|
|
14
|
+
allowed: false;
|
|
15
|
+
reason?: 'unauthenticated' | 'forbidden';
|
|
16
|
+
} | {
|
|
17
|
+
redirectTo: string;
|
|
18
|
+
};
|
|
19
|
+
/** Class form: an `@Injectable()` provider (constructor injection works). */
|
|
20
|
+
interface ConsoleAuthorizer {
|
|
21
|
+
authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
|
|
22
|
+
}
|
|
23
|
+
type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
|
|
24
|
+
/** An `@nestjs/authorization` policy check: `policy[ability](user)`. */
|
|
25
|
+
interface ConsolePolicyCheck {
|
|
26
|
+
policy: Type<unknown>;
|
|
27
|
+
ability: string;
|
|
28
|
+
}
|
|
29
|
+
type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
|
|
30
|
+
default: ConsoleAuthorizerClass;
|
|
31
|
+
}>) | ConsolePolicyCheck | string;
|
|
32
|
+
type ConsoleDecision = {
|
|
33
|
+
allowed: true;
|
|
34
|
+
} | {
|
|
35
|
+
allowed: false;
|
|
36
|
+
reason: 'unauthenticated' | 'forbidden';
|
|
37
|
+
redirectTo?: string;
|
|
38
|
+
};
|
|
39
|
+
/** What a resolved `authorize` is: called once per request, never throws. */
|
|
40
|
+
type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
|
|
41
|
+
/**
|
|
42
|
+
* The provider `@dudousxd/nestjs-authz` registers to resolve gate names (`authorize: 'console.x'`).
|
|
43
|
+
* A registry symbol, so it is found without depending on that package.
|
|
44
|
+
*/
|
|
45
|
+
declare const CONSOLE_GATE: unique symbol;
|
|
46
|
+
interface ConsoleGate {
|
|
47
|
+
allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
|
|
48
|
+
}
|
|
49
|
+
/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
|
|
50
|
+
declare const CONSOLE_AUTHORIZE_MISSING_WARNING = "[@dudousxd/nestjs-durable-dashboard] console has no `authorize` configured: it is open outside production and denies every request in production. Set `authorize` in DurableDashboardModule.forRoot().";
|
|
51
|
+
/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
|
|
52
|
+
declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves `authorize` into one function the guard calls per request. Resolution errors that are
|
|
55
|
+
* configuration mistakes (a gate name without nestjs-authz, a policy without @nestjs/authorization)
|
|
56
|
+
* throw here, so call it at boot. The returned function never throws: an error is a `forbidden`.
|
|
57
|
+
*
|
|
58
|
+
* `authorize` omitted: open outside production, `forbidden` for every request in production (read
|
|
59
|
+
* when this runs), and one warning logged (unless `warnIfMissing: false`).
|
|
60
|
+
*/
|
|
61
|
+
declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
warnIfMissing?: boolean;
|
|
64
|
+
}): Promise<ConsoleDecide>;
|
|
65
|
+
/**
|
|
66
|
+
* Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
|
|
67
|
+
* is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
|
|
68
|
+
* empty, so a missing env var fails at config time instead of opening the console.
|
|
69
|
+
*/
|
|
70
|
+
declare function authorizeByToken(options: {
|
|
71
|
+
token: string | undefined;
|
|
72
|
+
}): ConsoleAuthorize;
|
|
73
|
+
/** DI token of the resolved {@link ConsoleDecide} the guard runs. */
|
|
74
|
+
declare const CONSOLE_AUTHORIZE_DECIDE: unique symbol;
|
|
75
|
+
/** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
|
|
76
|
+
declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-durable-dashboard:console-page";
|
|
77
|
+
declare const ConsolePage: () => ClassDecorator;
|
|
78
|
+
/**
|
|
79
|
+
* The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly
|
|
80
|
+
* where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.
|
|
81
|
+
*/
|
|
82
|
+
declare class ConsoleAccessDenied extends HttpException {
|
|
83
|
+
readonly decision: Extract<ConsoleDecision, {
|
|
84
|
+
allowed: false;
|
|
85
|
+
}>;
|
|
86
|
+
readonly page: boolean;
|
|
87
|
+
constructor(decision: Extract<ConsoleDecision, {
|
|
88
|
+
allowed: false;
|
|
89
|
+
}>, page: boolean);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Marks a console route that is not part of the console: `authorize` does not apply to it. Used on
|
|
93
|
+
* the `ctx.webhook()` callback, which third parties call with the token as the secret.
|
|
94
|
+
*/
|
|
95
|
+
declare const CONSOLE_PUBLIC_METADATA = "@dudousxd/nestjs-durable-dashboard:console-public";
|
|
96
|
+
declare const ConsolePublic: () => MethodDecorator;
|
|
97
|
+
declare class ConsoleAuthorizeGuard implements CanActivate {
|
|
98
|
+
private readonly decide;
|
|
99
|
+
constructor(decide: ConsoleDecide);
|
|
100
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
101
|
+
}
|
|
102
|
+
declare class ConsoleAuthorizeFilter implements ExceptionFilter {
|
|
103
|
+
private readonly adapterHost;
|
|
104
|
+
constructor(adapterHost: HttpAdapterHost);
|
|
105
|
+
catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The guard and its filter as one class decorator, for a console you mount yourself
|
|
109
|
+
* (`routes: false`). List it ABOVE your authentication guards, so it runs after them.
|
|
110
|
+
*/
|
|
111
|
+
declare const UseConsoleAuthorize: () => ClassDecorator & MethodDecorator;
|
|
9
112
|
|
|
10
113
|
/**
|
|
11
114
|
* Read-model and actions backing the control-plane UI. Run-facing ops (list/detail/retry/cancel/
|
|
@@ -325,9 +428,9 @@ declare class DurableApiControllerBase {
|
|
|
325
428
|
}>;
|
|
326
429
|
/**
|
|
327
430
|
* Public callback endpoint for `ctx.webhook()`: a third party POSTs here (the url handed to it),
|
|
328
|
-
* and the body resumes the waiting run.
|
|
329
|
-
*
|
|
330
|
-
* verification in your own middleware.
|
|
431
|
+
* and the body resumes the waiting run. Not a console route: the console's `authorize` does not
|
|
432
|
+
* apply, the token is the secret. The app's `decorators` still do. Treat the token as a secret
|
|
433
|
+
* and/or front it with signature verification in your own middleware.
|
|
331
434
|
*/
|
|
332
435
|
webhook(token: string, body: unknown): Promise<_dudousxd_nestjs_durable_core.RunResult>;
|
|
333
436
|
/** Live query: the latest value a run published for `key` via `ctx.setEvent` (no side effect). */
|
|
@@ -345,6 +448,25 @@ declare class DurableApiController extends DurableApiControllerBase {
|
|
|
345
448
|
}
|
|
346
449
|
|
|
347
450
|
interface DurableDashboardOptions {
|
|
451
|
+
/**
|
|
452
|
+
* Who may use the console (the SPA, its JSON API and the run stream). Not the `ctx.webhook()`
|
|
453
|
+
* callback (`POST <apiBasePath>/webhooks/:token`), which third parties call with the token as the
|
|
454
|
+
* secret; {@link decorators} still apply to it. One of:
|
|
455
|
+
*
|
|
456
|
+
* - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);
|
|
457
|
+
* - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;
|
|
458
|
+
* its dependencies resolve from {@link imports} or global modules);
|
|
459
|
+
* - a lazy import `() => import('./console-access.js')` whose default export is such a class;
|
|
460
|
+
* - `{ policy, ability }`, checked with `@nestjs/authorization`'s `AuthorizationService`;
|
|
461
|
+
* - a gate name, resolved by `@dudousxd/nestjs-authz`'s `AuthzModule`.
|
|
462
|
+
*
|
|
463
|
+
* `authorizeByToken({ token })` builds one for a bearer token. It runs after the guards in
|
|
464
|
+
* {@link decorators}, so `request.user` is already set.
|
|
465
|
+
*
|
|
466
|
+
* Omitted: the console is open outside production and answers `403` to every request when
|
|
467
|
+
* `NODE_ENV=production`, and one warning is logged at boot.
|
|
468
|
+
*/
|
|
469
|
+
authorize?: ConsoleAuthorize;
|
|
348
470
|
/**
|
|
349
471
|
* Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
|
|
350
472
|
* `/api` prefix so it reads as a UI, not an endpoint.
|
|
@@ -372,26 +494,26 @@ interface DurableDashboardOptions {
|
|
|
372
494
|
* another registration or onto the exported classes. A guard named in `UseGuards(...)` resolves
|
|
373
495
|
* its dependencies from {@link imports} (or a global module).
|
|
374
496
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* {@link DurableDashboardModule}. The module cannot tell a `Can(...)` from a decorator that
|
|
378
|
-
* protects nothing, so passing one is your statement that the console is protected.
|
|
497
|
+
* `decorators` is not access control on its own: {@link authorize} decides who gets in, and runs
|
|
498
|
+
* after these. Use `decorators` for what must run first, typically authentication.
|
|
379
499
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
500
|
+
* The controllers are marked `Can.Anyone()` for `@nestjs/authorization`, whose global guard
|
|
501
|
+
* otherwise refuses every route that declares no `@Can()`. A `@Can()` passed here is still
|
|
502
|
+
* enforced.
|
|
383
503
|
*/
|
|
384
504
|
decorators?: ClassDecorator[];
|
|
385
505
|
/**
|
|
386
506
|
* `false` mounts NO controllers: the SPA and its JSON API are not registered,
|
|
387
507
|
* while every provider still is (the run read/action service, the run filter, the SPA's base
|
|
388
|
-
* paths). Serve the console from controllers of your own instead,
|
|
389
|
-
*
|
|
508
|
+
* paths). Serve the console from controllers of your own instead, by extending
|
|
509
|
+
* {@link DurableUiControllerBase} and {@link DurableApiControllerBase}. Apply `authorize` to them
|
|
510
|
+
* with `@UseConsoleAuthorize()` (listed above your authentication guards, so it runs after them),
|
|
511
|
+
* or protect them with ordinary Nest decorators:
|
|
390
512
|
*
|
|
391
513
|
* ```ts
|
|
392
|
-
* @Controller('ops/durable') @
|
|
514
|
+
* @Controller('ops/durable') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
393
515
|
* class ConsoleUi extends DurableUiControllerBase {}
|
|
394
|
-
* @Controller('ops/durable/api') @
|
|
516
|
+
* @Controller('ops/durable/api') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
395
517
|
* class ConsoleApi extends DurableApiControllerBase {}
|
|
396
518
|
* // in the module that declares them:
|
|
397
519
|
* imports: [DurableDashboardModule.forRoot({ routes: false, basePath: '/ops/durable', apiBasePath: '/ops/durable/api' })]
|
|
@@ -400,8 +522,9 @@ interface DurableDashboardOptions {
|
|
|
400
522
|
* `basePath`/`apiBasePath` still matter here: they are where you mounted the two controllers, and
|
|
401
523
|
* the SPA is told to load its assets from `basePath` and fetch from `apiBasePath`.
|
|
402
524
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
525
|
+
* `authorize` omitted works as for mounted routes: `@UseConsoleAuthorize()` lets every request
|
|
526
|
+
* through outside production and answers `403` in production, and the boot warning is logged.
|
|
527
|
+
* The `*Base` classes carry no
|
|
405
528
|
* guards and no `Can.Anyone()` mark, so under `@nestjs/authorization`'s global guard a subclass
|
|
406
529
|
* without a `@Can()` is denied. `decorators` throws when combined with `routes: false` rather than
|
|
407
530
|
* being silently ignored.
|
|
@@ -411,10 +534,6 @@ interface DurableDashboardOptions {
|
|
|
411
534
|
*/
|
|
412
535
|
routes?: boolean;
|
|
413
536
|
}
|
|
414
|
-
/** DI token of the boot-time check that refuses an unprotected console in production. */
|
|
415
|
-
declare const DASHBOARD_PROTECTION_CHECK: unique symbol;
|
|
416
|
-
/** The boot error for a mounted console with no `decorators` under `NODE_ENV=production`. */
|
|
417
|
-
declare const UNPROTECTED_CONSOLE_IN_PRODUCTION: string;
|
|
418
537
|
/**
|
|
419
538
|
* Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own
|
|
420
539
|
* dynamic module because enhancers (a guard from `decorators`) DI-instantiate from their
|
|
@@ -427,34 +546,19 @@ declare class DurableApiModule {
|
|
|
427
546
|
routes?: boolean;
|
|
428
547
|
/** See {@link DurableDashboardOptions.decorators}. */
|
|
429
548
|
decorators?: ClassDecorator[];
|
|
549
|
+
/** See {@link DurableDashboardOptions.authorize}. */
|
|
550
|
+
authorize?: ConsoleAuthorize;
|
|
430
551
|
}): DynamicModule;
|
|
431
552
|
}
|
|
432
|
-
/**
|
|
433
|
-
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
434
|
-
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` (or `forRootAsync`
|
|
435
|
-
* when `dashboardAuth`'s `login` hook needs injected services) alongside `DurableModule` (global),
|
|
436
|
-
* so it resolves the engine and store. Front the routes with the first-class `guards` option
|
|
437
|
-
* (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
|
|
438
|
-
* or with the built-in `dashboardAuth` cookie login, or both.
|
|
439
|
-
* With none of those (and no `decorators` / `token` / `allowUnauthenticated`), the console fails
|
|
440
|
-
* closed when `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
|
|
441
|
-
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers — see
|
|
442
|
-
* {@link DurableDashboardOptions.routes}.
|
|
443
|
-
*/
|
|
444
553
|
/**
|
|
445
554
|
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
446
555
|
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` alongside
|
|
447
556
|
* `DurableModule` (global), so it resolves the engine and store.
|
|
448
557
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* 2. `routes: false`, and mount it yourself by extending `DurableUiControllerBase` /
|
|
454
|
-
* `DurableApiControllerBase` (see {@link DurableDashboardOptions.routes}).
|
|
455
|
-
*
|
|
456
|
-
* With `NODE_ENV=production`, a mounted console with no `decorators` fails the boot with
|
|
457
|
-
* {@link UNPROTECTED_CONSOLE_IN_PRODUCTION}. Outside production it is served open, with a warning.
|
|
558
|
+
* Access is decided by {@link DurableDashboardOptions.authorize}. Without it the console is open
|
|
559
|
+
* outside production and denies every request in production, with one warning at boot.
|
|
560
|
+
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers (see
|
|
561
|
+
* {@link DurableDashboardOptions.routes}).
|
|
458
562
|
*/
|
|
459
563
|
declare class DurableDashboardModule {
|
|
460
564
|
static forRoot(options?: DurableDashboardOptions): DynamicModule;
|
|
@@ -492,4 +596,4 @@ declare class DurableUiControllerBase {
|
|
|
492
596
|
declare class DurableUiController extends DurableUiControllerBase {
|
|
493
597
|
}
|
|
494
598
|
|
|
495
|
-
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH,
|
|
599
|
+
export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, CONSOLE_PUBLIC_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, ConsolePublic, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiControllerBase, UseConsoleAuthorize, type WorkflowLifecyclePage, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };
|