@spinajs/authz-queue 0.0.0-stage → 3.0.1
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 +22 -2
- package/lib/cjs/api.d.ts +9 -0
- package/lib/cjs/api.d.ts.map +1 -0
- package/lib/cjs/api.js +6 -0
- package/lib/cjs/api.js.map +1 -0
- package/lib/cjs/bus/change-bus.d.ts +44 -0
- package/lib/cjs/bus/change-bus.d.ts.map +1 -0
- package/lib/cjs/bus/change-bus.js +159 -0
- package/lib/cjs/bus/change-bus.js.map +1 -0
- package/lib/cjs/bus/index.d.ts +3 -0
- package/lib/cjs/bus/index.d.ts.map +1 -0
- package/lib/cjs/bus/index.js +19 -0
- package/lib/cjs/bus/index.js.map +1 -0
- package/lib/cjs/bus/route-source.d.ts +11 -0
- package/lib/cjs/bus/route-source.d.ts.map +1 -0
- package/lib/cjs/bus/route-source.js +32 -0
- package/lib/cjs/bus/route-source.js.map +1 -0
- package/lib/cjs/hook/hook.d.ts +21 -0
- package/lib/cjs/hook/hook.d.ts.map +1 -0
- package/lib/cjs/hook/hook.js +81 -0
- package/lib/cjs/hook/hook.js.map +1 -0
- package/lib/cjs/hook/index.d.ts +3 -0
- package/lib/cjs/hook/index.d.ts.map +1 -0
- package/lib/cjs/hook/index.js +19 -0
- package/lib/cjs/hook/index.js.map +1 -0
- package/lib/cjs/hook/initiator.d.ts +28 -0
- package/lib/cjs/hook/initiator.d.ts.map +1 -0
- package/lib/cjs/hook/initiator.js +128 -0
- package/lib/cjs/hook/initiator.js.map +1 -0
- package/lib/cjs/index.d.ts +5 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +21 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/job/index.d.ts +2 -0
- package/lib/cjs/job/index.d.ts.map +1 -0
- package/lib/cjs/job/index.js +18 -0
- package/lib/cjs/job/index.js.map +1 -0
- package/lib/cjs/job/job-grant.d.ts +12 -0
- package/lib/cjs/job/job-grant.d.ts.map +1 -0
- package/lib/cjs/job/job-grant.js +29 -0
- package/lib/cjs/job/job-grant.js.map +1 -0
- package/lib/cjs/job/job-resource.d.ts +10 -0
- package/lib/cjs/job/job-resource.d.ts.map +1 -0
- package/lib/cjs/job/job-resource.js +39 -0
- package/lib/cjs/job/job-resource.js.map +1 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/schema.d.ts +34 -0
- package/lib/cjs/schema.d.ts.map +1 -0
- package/lib/cjs/schema.js +45 -0
- package/lib/cjs/schema.js.map +1 -0
- package/lib/mjs/api.d.ts +9 -0
- package/lib/mjs/api.d.ts.map +1 -0
- package/lib/mjs/api.js +2 -0
- package/lib/mjs/api.js.map +1 -0
- package/lib/mjs/bus/change-bus.d.ts +44 -0
- package/lib/mjs/bus/change-bus.d.ts.map +1 -0
- package/lib/mjs/bus/change-bus.js +155 -0
- package/lib/mjs/bus/change-bus.js.map +1 -0
- package/lib/mjs/bus/index.d.ts +3 -0
- package/lib/mjs/bus/index.d.ts.map +1 -0
- package/lib/mjs/bus/index.js +3 -0
- package/lib/mjs/bus/index.js.map +1 -0
- package/lib/mjs/bus/route-source.d.ts +11 -0
- package/lib/mjs/bus/route-source.d.ts.map +1 -0
- package/lib/mjs/bus/route-source.js +29 -0
- package/lib/mjs/bus/route-source.js.map +1 -0
- package/lib/mjs/hook/hook.d.ts +21 -0
- package/lib/mjs/hook/hook.d.ts.map +1 -0
- package/lib/mjs/hook/hook.js +78 -0
- package/lib/mjs/hook/hook.js.map +1 -0
- package/lib/mjs/hook/index.d.ts +3 -0
- package/lib/mjs/hook/index.d.ts.map +1 -0
- package/lib/mjs/hook/index.js +3 -0
- package/lib/mjs/hook/index.js.map +1 -0
- package/lib/mjs/hook/initiator.d.ts +28 -0
- package/lib/mjs/hook/initiator.d.ts.map +1 -0
- package/lib/mjs/hook/initiator.js +123 -0
- package/lib/mjs/hook/initiator.js.map +1 -0
- package/lib/mjs/index.d.ts +5 -0
- package/lib/mjs/index.d.ts.map +1 -0
- package/lib/mjs/index.js +5 -0
- package/lib/mjs/index.js.map +1 -0
- package/lib/mjs/job/index.d.ts +2 -0
- package/lib/mjs/job/index.d.ts.map +1 -0
- package/lib/mjs/job/index.js +2 -0
- package/lib/mjs/job/index.js.map +1 -0
- package/lib/mjs/job/job-grant.d.ts +12 -0
- package/lib/mjs/job/job-grant.d.ts.map +1 -0
- package/lib/mjs/job/job-grant.js +26 -0
- package/lib/mjs/job/job-grant.js.map +1 -0
- package/lib/mjs/job/job-resource.d.ts +10 -0
- package/lib/mjs/job/job-resource.d.ts.map +1 -0
- package/lib/mjs/job/job-resource.js +36 -0
- package/lib/mjs/job/job-resource.js.map +1 -0
- package/lib/mjs/package.json +1 -0
- package/lib/mjs/schema.d.ts +34 -0
- package/lib/mjs/schema.d.ts.map +1 -0
- package/lib/mjs/schema.js +42 -0
- package/lib/mjs/schema.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -0
- package/lib/tsconfig.mjs.tsbuildinfo +1 -0
- package/package.json +87 -4
package/README.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@spinajs/authz-queue`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Queue integration for SpinaJS authorization.
|
|
4
|
+
|
|
5
|
+
Importing the package registers `AuthzQueueHook` as a `QueueMessageHook` of `@spinajs/queue`.
|
|
6
|
+
|
|
7
|
+
- On emit it writes `message.Initiator` (`{ type, id, ceiling? }`) from the principal in the current async context. Nothing is written when there is none (unattended), and an `Initiator` the caller set is removed.
|
|
8
|
+
- A handler runs as `UNATTENDED` unless its job says otherwise. Every check it makes is denied.
|
|
9
|
+
- `@Job({ runAs: 'initiator' })`: the handler runs as the principal that emitted the message. A message without a usable `Initiator` runs unattended.
|
|
10
|
+
- `@Job({ runAs: 'system' })`: the handler runs as `SYSTEM`.
|
|
11
|
+
- Every handler run is wrapped in `runAs`, with the reason `job:<name>`, so it shows in the audit when `authz.domains.<domain>.audit.runAs` is on.
|
|
12
|
+
|
|
13
|
+
The hook uses the authz domain named by `authz.queue.domain` (default `root`).
|
|
14
|
+
|
|
15
|
+
`Initiator` crosses the broker as plain data, so the broker is trusted to carry it unchanged. The default `InitiatorPrincipalFactory` builds the principal from the stamp alone (no attributes, `source: 'queue'`) and warns once about it through the `authz-queue` log (the console when no `Log` is registered). `authz.queue.stampedInitiatorWarning: false` silences the warning; a wrapper built with `new` that does reload the user passes `{ warn: false }` instead (or `{ warn: true }`), which decides without reading the config. Without the option the config is read, so a factory built with `new` needs a resolved `Configuration`. An app that must reload the user registers its own: `DI.register(MyFactory).as(InitiatorPrincipalFactory)`; the hook captures the factory when it is built, so register it before the queue starts. The stamp does not carry `onBehalfOf` or `authn` facts, so an agent or impersonating principal is rebuilt without them.
|
|
16
|
+
|
|
17
|
+
## Change bus
|
|
18
|
+
|
|
19
|
+
`QueueChangeBus` retries a failed listen every 5 s and then drops all caches. It carries `AuthzChange`s to the other nodes as `AuthzChangeEvent` queue events. Importing the package registers it as a `ChangeBus`, so it becomes the default bus of every domain without `authz.bus.service`; set that key to choose explicitly (`'QueueChangeBus'` or `'InProcessChangeBus'`). Subscribers on the publishing node hear a change at once and skip the copy that returns from the broker. A publish the queue refuses rejects. `authz.bus.topic`, when set and `queue.routing` has no route for `AuthzChangeEvent`, becomes that route's channel. `AuthzBusRouteSource`, a configuration source registered at import, adds the route when the `Configuration` loads, after the config files; a topic set at runtime does not change the routing.
|
|
20
|
+
|
|
21
|
+
## Job rows
|
|
22
|
+
|
|
23
|
+
`import '@spinajs/authz-queue/jobs'` registers `JobModel` (`queue_jobs`) as the `job` resource. The subpath needs `@spinajs/authz-orm`, an optional peer dependency; the package root does not load it, so an app that only wants the hooks or `QueueChangeBus` does not install authz-orm. The resource's `owner` is the initiator (`InitiatorType` and `InitiatorId`), so a principal owns a job only when both its type and its id match. The subpath also contributes, as a configuration source registered at import, the grant that gives the `user` role `job:read` on `owner` in `authz.rbac.roles`. The grant comes with the import, not with the install: a package that is installed but never imported adds nothing, so the boot does not fail on a permission nobody registered. The source loads before the config files, so the application's own `authz.rbac.roles.user` still adds grants and overrides scalar keys. It is read when the `Configuration` resolves its sources, so import the subpath before that; a later import takes effect on the next `Configuration` resolve, and `load()` alone reuses the sources already collected. The queue's own reads and writes of job rows (tracking, progress, retention) run as `SYSTEM` through `AuthzQueueTrackingHook` (a `QueueTrackingHook`, also from the subpath), with the reason `queue:tracking`. Any other multi-row select on `JobModel` follows the usual rules: it needs `.accessible(permission)`, `.unscoped(reason)` or `runAs(SYSTEM)`, or authz-orm's runtime guard flags it (an `AuthzConfigError` in the `test` env, a warning in development). `queue-http-progress`'s `GET jobs/v1/:jobId/status` checks `job:read` on the row and answers 404 for an unknown job and for one the caller may not read.
|
package/lib/cjs/api.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Ceiling, PrincipalType } from '@spinajs/authz';
|
|
2
|
+
export { QueueMessageHook } from '@spinajs/queue';
|
|
3
|
+
export type { JobOptions } from '@spinajs/queue';
|
|
4
|
+
export interface Initiator {
|
|
5
|
+
readonly type: PrincipalType;
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly ceiling?: Ceiling;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B"}
|
package/lib/cjs/api.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueueMessageHook = void 0;
|
|
4
|
+
var queue_1 = require("@spinajs/queue");
|
|
5
|
+
Object.defineProperty(exports, "QueueMessageHook", { enumerable: true, get: function () { return queue_1.QueueMessageHook; } });
|
|
6
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":";;;AAEA,wCAAkD;AAAzC,yGAAA,gBAAgB,OAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ChangeBus } from '@spinajs/authz';
|
|
2
|
+
import type { AuthzChange } from '@spinajs/authz';
|
|
3
|
+
import { QueueEvent, QueueService } from '@spinajs/queue';
|
|
4
|
+
interface WireChange {
|
|
5
|
+
domain?: unknown;
|
|
6
|
+
tags?: unknown;
|
|
7
|
+
actor?: unknown;
|
|
8
|
+
at?: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** What nodes send each other: the change, and the node that made it so that it can skip its own echo. */
|
|
11
|
+
export declare class AuthzChangeEvent extends QueueEvent {
|
|
12
|
+
Node: string;
|
|
13
|
+
Change: WireChange;
|
|
14
|
+
}
|
|
15
|
+
/** Rebuilds a change that went through a JSON broker; undefined when it cannot be one. */
|
|
16
|
+
export declare function reviveChange(raw: unknown): AuthzChange | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* A ChangeBus that carries changes to the other nodes over the queue (design §14.2).
|
|
19
|
+
* Select it with `authz.bus.service: 'QueueChangeBus'`. Subscribers on this node hear a change at once;
|
|
20
|
+
* the copy that comes back from the broker is skipped.
|
|
21
|
+
*/
|
|
22
|
+
export declare class QueueChangeBus extends ChangeBus {
|
|
23
|
+
protected readonly node: `${string}-${string}-${string}-${string}-${string}`;
|
|
24
|
+
private readonly subscribers;
|
|
25
|
+
private readonly log;
|
|
26
|
+
private queue?;
|
|
27
|
+
private retried;
|
|
28
|
+
private disposed;
|
|
29
|
+
private timer?;
|
|
30
|
+
protected retryMs: number;
|
|
31
|
+
publish(change: AuthzChange): Promise<void>;
|
|
32
|
+
subscribe(fn: (c: AuthzChange) => void): () => void;
|
|
33
|
+
/** AuthzService subscribes once, at boot: a queue that is not ready yet must not leave the node deaf. */
|
|
34
|
+
private listenWithRetry;
|
|
35
|
+
dispose(): Promise<void>;
|
|
36
|
+
/** Resolved per start, not injected: a queue that cannot start yet must not fail the bus, which retries. */
|
|
37
|
+
protected queueService(): Promise<QueueService>;
|
|
38
|
+
protected start(): Promise<QueueService>;
|
|
39
|
+
private listen;
|
|
40
|
+
private deliver;
|
|
41
|
+
private fail;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=change-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-bus.d.ts","sourceRoot":"","sources":["../../../src/bus/change-bus.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAA0B,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAS,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGjE,UAAU,UAAU;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,CAAC;CACd;AAED,0GAA0G;AAC1G,qBACa,gBAAiB,SAAQ,UAAU;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CAC3B;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CAoBlE;AAED;;;;GAIG;AACH,qBAEa,cAAe,SAAQ,SAAS;IAC3C,SAAS,CAAC,QAAQ,CAAC,IAAI,sDAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuC;IACnE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgC;IACpD,OAAO,CAAC,KAAK,CAAC,CAAwB;IACtC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAC,CAAiB;IAC/B,SAAS,CAAC,OAAO,SAAQ;IAEnB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhD;IAED,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,CAOlD;IAED,yGAAyG;IACzG,OAAO,CAAC,eAAe;IAsBjB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ7B;IAED,4GAA4G;IAC5G,UAAgB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,CAEpD;IAED,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,CASvC;YAEa,MAAM;IAcpB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,IAAI;CAGb"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.QueueChangeBus = exports.AuthzChangeEvent = void 0;
|
|
10
|
+
exports.reviveChange = reviveChange;
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const di_1 = require("@spinajs/di");
|
|
13
|
+
const authz_1 = require("@spinajs/authz");
|
|
14
|
+
const queue_1 = require("@spinajs/queue");
|
|
15
|
+
const util_1 = require("@spinajs/util");
|
|
16
|
+
/** What nodes send each other: the change, and the node that made it so that it can skip its own echo. */
|
|
17
|
+
let AuthzChangeEvent = class AuthzChangeEvent extends queue_1.QueueEvent {
|
|
18
|
+
};
|
|
19
|
+
exports.AuthzChangeEvent = AuthzChangeEvent;
|
|
20
|
+
exports.AuthzChangeEvent = AuthzChangeEvent = __decorate([
|
|
21
|
+
(0, queue_1.Event)()
|
|
22
|
+
], AuthzChangeEvent);
|
|
23
|
+
/** Rebuilds a change that went through a JSON broker; undefined when it cannot be one. */
|
|
24
|
+
function reviveChange(raw) {
|
|
25
|
+
if (raw === null || typeof raw !== 'object')
|
|
26
|
+
return undefined;
|
|
27
|
+
const c = raw;
|
|
28
|
+
if (!(0, util_1.isStringArray)(c.tags))
|
|
29
|
+
return undefined;
|
|
30
|
+
if (c.domain !== undefined && typeof c.domain !== 'string')
|
|
31
|
+
return undefined;
|
|
32
|
+
let actor;
|
|
33
|
+
if (c.actor !== undefined) {
|
|
34
|
+
const a = c.actor;
|
|
35
|
+
if (a === null || typeof a !== 'object' || typeof a.type !== 'string' || typeof a.id !== 'string')
|
|
36
|
+
return undefined;
|
|
37
|
+
actor = { type: a.type, id: a.id };
|
|
38
|
+
}
|
|
39
|
+
const at = new Date(typeof c.at === 'string' || typeof c.at === 'number' || c.at instanceof Date ? c.at : NaN);
|
|
40
|
+
return {
|
|
41
|
+
...(c.domain === undefined ? {} : { domain: c.domain }),
|
|
42
|
+
tags: [...c.tags],
|
|
43
|
+
...(actor ? { actor } : {}),
|
|
44
|
+
at: Number.isNaN(at.getTime()) ? new Date() : at,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A ChangeBus that carries changes to the other nodes over the queue (design §14.2).
|
|
49
|
+
* Select it with `authz.bus.service: 'QueueChangeBus'`. Subscribers on this node hear a change at once;
|
|
50
|
+
* the copy that comes back from the broker is skipped.
|
|
51
|
+
*/
|
|
52
|
+
let QueueChangeBus = class QueueChangeBus extends authz_1.ChangeBus {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(...arguments);
|
|
55
|
+
this.node = (0, node_crypto_1.randomUUID)();
|
|
56
|
+
this.subscribers = new Set();
|
|
57
|
+
this.log = (0, authz_1.engineLog)('authz-queue-bus');
|
|
58
|
+
this.retried = false;
|
|
59
|
+
this.disposed = false;
|
|
60
|
+
this.retryMs = 5000;
|
|
61
|
+
}
|
|
62
|
+
async publish(change) {
|
|
63
|
+
this.deliver(change);
|
|
64
|
+
const queue = await this.start();
|
|
65
|
+
const event = new AuthzChangeEvent();
|
|
66
|
+
event.Node = this.node;
|
|
67
|
+
event.Change = { domain: change.domain, tags: [...change.tags], actor: change.actor, at: change.at };
|
|
68
|
+
await queue.emit(event);
|
|
69
|
+
}
|
|
70
|
+
subscribe(fn) {
|
|
71
|
+
const entry = (c) => fn(c);
|
|
72
|
+
this.subscribers.add(entry);
|
|
73
|
+
this.listenWithRetry();
|
|
74
|
+
return () => {
|
|
75
|
+
this.subscribers.delete(entry);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** AuthzService subscribes once, at boot: a queue that is not ready yet must not leave the node deaf. */
|
|
79
|
+
listenWithRetry() {
|
|
80
|
+
this.start().then(() => {
|
|
81
|
+
// Changes made while the node was deaf are unknown: drop everything.
|
|
82
|
+
if (this.retried) {
|
|
83
|
+
this.retried = false;
|
|
84
|
+
this.deliver({ tags: ['*'], at: new Date() });
|
|
85
|
+
}
|
|
86
|
+
}, (err) => {
|
|
87
|
+
this.log.error({ error: (0, authz_1.thrownError)(err) }, 'authz change bus: cannot listen on the queue, retrying');
|
|
88
|
+
this.retried = true;
|
|
89
|
+
if (this.disposed || this.timer)
|
|
90
|
+
return;
|
|
91
|
+
this.timer = setTimeout(() => {
|
|
92
|
+
this.timer = undefined;
|
|
93
|
+
if (!this.disposed)
|
|
94
|
+
this.listenWithRetry();
|
|
95
|
+
}, this.retryMs);
|
|
96
|
+
this.timer.unref?.();
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async dispose() {
|
|
100
|
+
this.disposed = true;
|
|
101
|
+
if (this.timer)
|
|
102
|
+
clearTimeout(this.timer);
|
|
103
|
+
this.timer = undefined;
|
|
104
|
+
this.subscribers.clear();
|
|
105
|
+
const started = this.queue;
|
|
106
|
+
this.queue = undefined;
|
|
107
|
+
if (started)
|
|
108
|
+
await (await started.catch(() => undefined))?.stopConsuming(AuthzChangeEvent);
|
|
109
|
+
}
|
|
110
|
+
/** Resolved per start, not injected: a queue that cannot start yet must not fail the bus, which retries. */
|
|
111
|
+
async queueService() {
|
|
112
|
+
return di_1.DI.resolve(queue_1.QueueService);
|
|
113
|
+
}
|
|
114
|
+
start() {
|
|
115
|
+
if (this.queue === undefined) {
|
|
116
|
+
this.queue = this.listen();
|
|
117
|
+
// A failed start is retried by the next call.
|
|
118
|
+
this.queue.catch(() => {
|
|
119
|
+
this.queue = undefined;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return this.queue;
|
|
123
|
+
}
|
|
124
|
+
async listen() {
|
|
125
|
+
const queue = await this.queueService();
|
|
126
|
+
await queue.consume(AuthzChangeEvent, async (e) => {
|
|
127
|
+
if (e.Node === this.node)
|
|
128
|
+
return;
|
|
129
|
+
const change = reviveChange(e.Change);
|
|
130
|
+
if (change === undefined) {
|
|
131
|
+
this.log.warn('authz change bus: dropped a malformed change');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.deliver(change);
|
|
135
|
+
});
|
|
136
|
+
return queue;
|
|
137
|
+
}
|
|
138
|
+
deliver(change) {
|
|
139
|
+
for (const fn of [...this.subscribers]) {
|
|
140
|
+
try {
|
|
141
|
+
const r = fn(change);
|
|
142
|
+
if (r !== null && typeof r === 'object' && typeof r.then === 'function')
|
|
143
|
+
r.then(undefined, (err) => this.fail(err));
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
this.fail(err);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
fail(err) {
|
|
151
|
+
this.log.error({ error: (0, authz_1.thrownError)(err) }, 'authz change subscriber failed');
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
exports.QueueChangeBus = QueueChangeBus;
|
|
155
|
+
exports.QueueChangeBus = QueueChangeBus = __decorate([
|
|
156
|
+
(0, di_1.Singleton)(),
|
|
157
|
+
(0, di_1.Injectable)(authz_1.ChangeBus)
|
|
158
|
+
], QueueChangeBus);
|
|
159
|
+
//# sourceMappingURL=change-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-bus.js","sourceRoot":"","sources":["../../../src/bus/change-bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bus/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./change-bus.js"), exports);
|
|
18
|
+
__exportStar(require("./route-source.js"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/bus/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,oDAAkC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ConfigurationSource } from '@spinajs/configuration';
|
|
2
|
+
import type { Configuration, IConfigLike } from '@spinajs/configuration';
|
|
3
|
+
/**
|
|
4
|
+
* `authz.bus.topic` becomes the channel of `AuthzChangeEvent` unless `queue.routing` already routes it.
|
|
5
|
+
* Order 2 loads after the file sources (1), so it sees the application's topic and routing.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AuthzBusRouteSource extends ConfigurationSource {
|
|
8
|
+
get Order(): number;
|
|
9
|
+
Load(configuration: Configuration): Promise<IConfigLike>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=route-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-source.d.ts","sourceRoot":"","sources":["../../../src/bus/route-source.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAIzE;;;GAGG;AACH,qBACa,mBAAoB,SAAQ,mBAAmB;IAC1D,IAAW,KAAK,IAAI,MAAM,CAEzB;IAEM,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAK9D;CACF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AuthzBusRouteSource = void 0;
|
|
10
|
+
const di_1 = require("@spinajs/di");
|
|
11
|
+
const configuration_1 = require("@spinajs/configuration");
|
|
12
|
+
const change_bus_js_1 = require("./change-bus.js");
|
|
13
|
+
/**
|
|
14
|
+
* `authz.bus.topic` becomes the channel of `AuthzChangeEvent` unless `queue.routing` already routes it.
|
|
15
|
+
* Order 2 loads after the file sources (1), so it sees the application's topic and routing.
|
|
16
|
+
*/
|
|
17
|
+
let AuthzBusRouteSource = class AuthzBusRouteSource extends configuration_1.ConfigurationSource {
|
|
18
|
+
get Order() {
|
|
19
|
+
return 2;
|
|
20
|
+
}
|
|
21
|
+
Load(configuration) {
|
|
22
|
+
const route = ['queue', 'routing', change_bus_js_1.AuthzChangeEvent.name];
|
|
23
|
+
const topic = configuration.get(['authz', 'bus', 'topic']);
|
|
24
|
+
const skip = configuration.get(route) !== undefined || typeof topic !== 'string' || topic === '';
|
|
25
|
+
return Promise.resolve((skip ? {} : { queue: { routing: { [change_bus_js_1.AuthzChangeEvent.name]: { channel: topic } } } }));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.AuthzBusRouteSource = AuthzBusRouteSource;
|
|
29
|
+
exports.AuthzBusRouteSource = AuthzBusRouteSource = __decorate([
|
|
30
|
+
(0, di_1.Injectable)(configuration_1.ConfigurationSource)
|
|
31
|
+
], AuthzBusRouteSource);
|
|
32
|
+
//# sourceMappingURL=route-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-source.js","sourceRoot":"","sources":["../../../src/bus/route-source.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oCAAyC;AACzC,0DAA6D;AAG7D,mDAAmD;AAEnD;;;GAGG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,mCAAmB;IAC1D,IAAW,KAAK;QACd,OAAO,CAAC,CAAC;IACX,CAAC;IAEM,IAAI,CAAC,aAA4B;QACtC,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,gCAAgB,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAU,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAU,KAAK,CAAC,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1G,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,gCAAgB,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAA2B,CAAC,CAAC;IAC1I,CAAC;CACF,CAAA;;8BAXY,mBAAmB;IAD/B,IAAA,eAAU,EAAC,mCAAmB,CAAC;GACnB,mBAAmB,CAW/B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AuthzDomains } from '@spinajs/authz';
|
|
2
|
+
import type { AuthzService, Principal } from '@spinajs/authz';
|
|
3
|
+
import { QueueMessageHook } from '@spinajs/queue';
|
|
4
|
+
import type { IQueueMessage } from '@spinajs/queue';
|
|
5
|
+
import type { Initiator } from '../api.js';
|
|
6
|
+
import { InitiatorPrincipalFactory } from './initiator.js';
|
|
7
|
+
/**
|
|
8
|
+
* Stamps who emitted a message and decides whose authority its handler has (design §14.2, B30).
|
|
9
|
+
* A handler runs unattended unless its `@Job` says `runAs: 'initiator'` or `'system'`.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AuthzQueueHook extends QueueMessageHook {
|
|
12
|
+
/** The authz domain whose principal is stamped and whose `runAs` the handler uses. */
|
|
13
|
+
protected Domain: string;
|
|
14
|
+
protected Initiators: InitiatorPrincipalFactory;
|
|
15
|
+
protected Domains: AuthzDomains;
|
|
16
|
+
beforeEmit(message: IQueueMessage): Promise<void>;
|
|
17
|
+
aroundHandle<T>(message: IQueueMessage, next: () => Promise<T>): Promise<T>;
|
|
18
|
+
protected initiator(initiator: Initiator): Promise<Principal>;
|
|
19
|
+
protected service(): Promise<AuthzService>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/hook/hook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAsB,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAc,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,yBAAyB,EAAiB,MAAM,gBAAgB,CAAC;AAO1E;;;GAGG;AACH,qBACa,cAAe,SAAQ,gBAAgB;IAClD,sFAAsF;IAEtF,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IAGzB,SAAS,CAAC,UAAU,EAAE,yBAAyB,CAAC;IAGhD,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC;IAE1B,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBtD;IAEK,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAUhF;IAED,UAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAElE;IAED,UAAgB,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAE/C;CACF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AuthzQueueHook = void 0;
|
|
13
|
+
const di_1 = require("@spinajs/di");
|
|
14
|
+
const configuration_1 = require("@spinajs/configuration");
|
|
15
|
+
const authz_1 = require("@spinajs/authz");
|
|
16
|
+
const queue_1 = require("@spinajs/queue");
|
|
17
|
+
const initiator_js_1 = require("./initiator.js");
|
|
18
|
+
// The engine reads a ceiling whose expiresAt is not a Date, or whose reason is empty, as deny-all; revival must not repair it.
|
|
19
|
+
const isStrictCeiling = (c) => (c.expiresAt === undefined || c.expiresAt instanceof Date) && (c.reason === undefined || (typeof c.reason === 'string' && c.reason !== ''));
|
|
20
|
+
const isPlain = (v) => v !== null && typeof v === 'object' && Object.getPrototypeOf(v) === Object.prototype;
|
|
21
|
+
/**
|
|
22
|
+
* Stamps who emitted a message and decides whose authority its handler has (design §14.2, B30).
|
|
23
|
+
* A handler runs unattended unless its `@Job` says `runAs: 'initiator'` or `'system'`.
|
|
24
|
+
*/
|
|
25
|
+
let AuthzQueueHook = class AuthzQueueHook extends queue_1.QueueMessageHook {
|
|
26
|
+
async beforeEmit(message) {
|
|
27
|
+
// Whatever the caller put there is dropped: only the ambient principal may be named.
|
|
28
|
+
delete message.Initiator;
|
|
29
|
+
const principal = (await this.service()).current();
|
|
30
|
+
if (principal === authz_1.UNATTENDED || principal.type === 'unattended')
|
|
31
|
+
return;
|
|
32
|
+
// A system-typed principal that is not the singleton is forged; stamping it would later become SYSTEM.
|
|
33
|
+
if (principal.type === 'system' && principal !== authz_1.SYSTEM)
|
|
34
|
+
return;
|
|
35
|
+
// The stamp cannot carry these; rebuilding without them would widen the job's authority.
|
|
36
|
+
if (principal.onBehalfOf !== undefined || principal.delegation !== undefined || principal.authn?.impersonator !== undefined)
|
|
37
|
+
return;
|
|
38
|
+
const stamp = { type: principal.type, id: String(principal.id) };
|
|
39
|
+
if (principal.ceiling === undefined) {
|
|
40
|
+
message.Initiator = stamp;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// A ceiling that is not a plain, well-formed one is stamped as deny-all, never as no restriction.
|
|
44
|
+
const ceiling = isPlain(principal.ceiling) && isStrictCeiling(principal.ceiling) ? (0, initiator_js_1.reviveCeiling)(principal.ceiling) : undefined;
|
|
45
|
+
message.Initiator = { ...stamp, ceiling: ceiling ?? { permissions: [] } };
|
|
46
|
+
}
|
|
47
|
+
async aroundHandle(message, next) {
|
|
48
|
+
const service = await this.service();
|
|
49
|
+
const { runAs } = (0, queue_1.jobOptions)(message);
|
|
50
|
+
let principal = authz_1.UNATTENDED;
|
|
51
|
+
if (runAs === 'system')
|
|
52
|
+
principal = authz_1.SYSTEM;
|
|
53
|
+
else if (runAs === 'initiator' && message.Initiator)
|
|
54
|
+
principal = await this.initiator(message.Initiator);
|
|
55
|
+
// Also for unattended: a consumer running inside the emitter's async context must not inherit its principal.
|
|
56
|
+
return service.runAs(principal, `job:${message.Name}`, next);
|
|
57
|
+
}
|
|
58
|
+
async initiator(initiator) {
|
|
59
|
+
return this.Initiators.principalOf(initiator);
|
|
60
|
+
}
|
|
61
|
+
async service() {
|
|
62
|
+
return this.Domains.service(this.Domain);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
exports.AuthzQueueHook = AuthzQueueHook;
|
|
66
|
+
__decorate([
|
|
67
|
+
(0, configuration_1.Config)('authz.queue.domain'),
|
|
68
|
+
__metadata("design:type", String)
|
|
69
|
+
], AuthzQueueHook.prototype, "Domain", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
(0, di_1.Autoinject)(initiator_js_1.InitiatorPrincipalFactory),
|
|
72
|
+
__metadata("design:type", initiator_js_1.InitiatorPrincipalFactory)
|
|
73
|
+
], AuthzQueueHook.prototype, "Initiators", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, di_1.Autoinject)(authz_1.AuthzDomains),
|
|
76
|
+
__metadata("design:type", authz_1.AuthzDomains)
|
|
77
|
+
], AuthzQueueHook.prototype, "Domains", void 0);
|
|
78
|
+
exports.AuthzQueueHook = AuthzQueueHook = __decorate([
|
|
79
|
+
(0, di_1.Injectable)(queue_1.QueueMessageHook)
|
|
80
|
+
], AuthzQueueHook);
|
|
81
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../../src/hook/hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hook/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./hook.js"), exports);
|
|
18
|
+
__exportStar(require("./initiator.js"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hook/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,iDAA+B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Ceiling, HostLog, Principal } from '@spinajs/authz';
|
|
2
|
+
import type { Initiator } from '../api.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the principal a job runs as from the `Initiator` on its message. An app that must reload
|
|
5
|
+
* the user (attributes, deactivation) registers its own with `DI.register(X).as(InitiatorPrincipalFactory)`.
|
|
6
|
+
* Return `UNATTENDED` for an initiator that can no longer act; throw only for a failure worth a retry.
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class InitiatorPrincipalFactory {
|
|
9
|
+
abstract principalOf(initiator: Initiator): Promise<Principal>;
|
|
10
|
+
}
|
|
11
|
+
/** Rebuilds a Ceiling that went through a JSON broker (`expiresAt` is a string there); undefined when the shape is wrong. */
|
|
12
|
+
export declare function reviveCeiling(raw: unknown): Ceiling | undefined;
|
|
13
|
+
/** Trusts the stamp: type, id and ceiling are all the principal has. A malformed stamp yields `UNATTENDED`. */
|
|
14
|
+
export declare class StampedInitiatorPrincipalFactory extends InitiatorPrincipalFactory {
|
|
15
|
+
private warned;
|
|
16
|
+
private readonly warn;
|
|
17
|
+
protected Log: HostLog;
|
|
18
|
+
protected WarningSwitch: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* `warn` decides the one-time warning without reading the config, for a wrapper built with `new` that does reload the user.
|
|
21
|
+
* Without it, `authz.queue.stampedInitiatorWarning` decides, so a `new` instance needs a resolved Configuration.
|
|
22
|
+
*/
|
|
23
|
+
constructor(options?: {
|
|
24
|
+
readonly warn?: boolean;
|
|
25
|
+
});
|
|
26
|
+
principalOf(initiator: Initiator): Promise<Principal>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=initiator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initiator.d.ts","sourceRoot":"","sources":["../../../src/hook/initiator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;;;GAIG;AACH,8BAAsB,yBAAyB;IAC7C,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CAChE;AAQD,6HAA6H;AAC7H,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAiC/D;AAED,+GAA+G;AAC/G,qBACa,gCAAiC,SAAQ,yBAAyB;IAC7E,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAE3C,SAAS,CAAC,GAAG,EAAE,OAAO,CAA4B;IAGlD,SAAS,CAAC,aAAa,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,YAAY,OAAO,GAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,EAGpD;IAEK,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CA2B1D;CACF"}
|