@nextage/era-lib 1.0.191 → 1.0.193
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/build/constants.d.ts +48 -15
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +49 -16
- package/build/constants.js.map +1 -1
- package/build/enums.d.ts +26 -2
- package/build/enums.d.ts.map +1 -1
- package/build/enums.js +27 -2
- package/build/enums.js.map +1 -1
- package/build/events/base/base-event.controller.d.ts +1 -1
- package/build/events/base/base-event.controller.js +1 -1
- package/build/events/base/base-msg-listener.d.ts +1 -1
- package/build/events/base/base-msg-listener.js +1 -1
- package/build/events/publisher-registry.d.ts +1 -1
- package/build/events/publisher-registry.js +3 -3
- package/build/events/revocation-denylist.js +1 -1
- package/build/events/transport/nats-client-manager.d.ts +1 -1
- package/build/events/transport/nats-client-manager.js +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/interfaces.d.ts +50 -1
- package/build/interfaces.d.ts.map +1 -1
- package/build/types/base-user/index.d.ts +2 -1
- package/build/types/base-user/index.d.ts.map +1 -1
- package/build/types/nx-context.d.ts +18 -0
- package/build/types/nx-context.d.ts.map +1 -0
- package/build/types/nx-context.js +14 -0
- package/build/types/nx-context.js.map +1 -0
- package/build/utils/claims.d.ts +42 -0
- package/build/utils/claims.d.ts.map +1 -0
- package/build/utils/claims.interfaces.d.ts +36 -0
- package/build/utils/claims.interfaces.d.ts.map +1 -0
- package/build/utils/claims.interfaces.js +9 -0
- package/build/utils/claims.interfaces.js.map +1 -0
- package/build/utils/claims.js +64 -0
- package/build/utils/claims.js.map +1 -0
- package/build/utils/express.js +2 -2
- package/build/utils/functs.d.ts +3 -3
- package/build/utils/functs.js +3 -3
- package/build/utils/index.d.ts +4 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +4 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/scope.d.ts +75 -0
- package/build/utils/scope.d.ts.map +1 -0
- package/build/utils/scope.interfaces.d.ts +20 -0
- package/build/utils/scope.interfaces.d.ts.map +1 -0
- package/build/utils/scope.interfaces.js +10 -0
- package/build/utils/scope.interfaces.js.map +1 -0
- package/build/utils/scope.js +121 -0
- package/build/utils/scope.js.map +1 -0
- package/build/utils/start-service.js +1 -1
- package/build/utils/tenant.d.ts +71 -38
- package/build/utils/tenant.d.ts.map +1 -1
- package/build/utils/tenant.interfaces.d.ts +5 -5
- package/build/utils/tenant.js +83 -54
- package/build/utils/tenant.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*********************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* Identity claims - the SINGLE definition of what identity travels inside an access token. *
|
|
4
|
+
* *
|
|
5
|
+
* Two operations, kept together on purpose because they MUST stay each other's inverse: *
|
|
6
|
+
* *
|
|
7
|
+
* buildAuthClaims - auth-srv, when signing a token (login and refresh); *
|
|
8
|
+
* identityFromClaims - every resource service, when rebuilding the caller per request. *
|
|
9
|
+
* *
|
|
10
|
+
* They used to be four hand-written literals across two services. A drift between them is *
|
|
11
|
+
* not a cosmetic bug: a claim that is written but not read means the authorization data *
|
|
12
|
+
* silently disappears, and the checks that depend on it fail OPEN or CLOSED at random. *
|
|
13
|
+
* *
|
|
14
|
+
* NOTE: resource services do NOT re-read the user from the database - they trust these *
|
|
15
|
+
* claims (ADR-0005 section 5.1). Revoking a grant therefore takes effect only after the *
|
|
16
|
+
* token is refreshed: a declared property, bounded by the access-token TTL. *
|
|
17
|
+
* *
|
|
18
|
+
*********************************************************************************************/
|
|
19
|
+
import { BaseUser } from '@nextage/nx-frame-be';
|
|
20
|
+
import { AuthClaims, ClaimIdentity } from './claims.interfaces';
|
|
21
|
+
/**
|
|
22
|
+
* Builds the identity claims to embed in an access token.
|
|
23
|
+
*
|
|
24
|
+
* `grn` is omitted entirely when the user holds no grant, so the common case adds
|
|
25
|
+
* nothing to the token.
|
|
26
|
+
*
|
|
27
|
+
* @param user the user the token is being issued for, as read from the database
|
|
28
|
+
* @returns the identity claims to sign
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildAuthClaims(user: Partial<BaseUser>): AuthClaims;
|
|
31
|
+
/**
|
|
32
|
+
* Rebuilds the caller's identity from the claims of a verified token.
|
|
33
|
+
*
|
|
34
|
+
* Tolerant of a MISSING key but never of a malformed one: an older token issued
|
|
35
|
+
* before grants existed simply yields an empty list, whereas an entry without an
|
|
36
|
+
* `id` or a `role` is dropped rather than trusted half-formed.
|
|
37
|
+
*
|
|
38
|
+
* @param data the `data` payload of the verified access token
|
|
39
|
+
* @returns roles, memberships and grants, shaped as the `BaseUser` augmentation
|
|
40
|
+
*/
|
|
41
|
+
export declare function identityFromClaims(data?: Partial<AuthClaims>): ClaimIdentity;
|
|
42
|
+
//# sourceMappingURL=claims.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/utils/claims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;+FAiB+F;AAE/F,OAAO,EAAE,QAAQ,EAAE,MAAuB,sBAAsB,CAAC;AAGjE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEhE;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,UAAU,CAcnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,aAAa,CAS5E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT identity-claim types (ADR-0005 section 5.1).
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the logic in `claims.ts` (house-style: types live in a
|
|
5
|
+
* dedicated `*.interfaces.ts`, never mixed with the logic that consumes them).
|
|
6
|
+
*/
|
|
7
|
+
import { BaseEntity, OrgGrant } from '../interfaces';
|
|
8
|
+
/**
|
|
9
|
+
* The identity portion of the access-token payload.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately terse: these keys are repeated in every request. `rls` and `orgs`
|
|
12
|
+
* predate this ADR and keep their historic names and shapes.
|
|
13
|
+
*
|
|
14
|
+
* `grn` carries `OrgGrant` VERBATIM, not a token-specific shape: the pair
|
|
15
|
+
* `{ id, role }` is the whole of authorization, so a separate claim type could
|
|
16
|
+
* only ever drift from it. Provenance (`OrgGrantRecord.grantedBy`/`grantedAt`)
|
|
17
|
+
* stays out - it is audit data, and the token is neither the place to repeat it
|
|
18
|
+
* on every request nor a place anything can be revoked from before expiry.
|
|
19
|
+
*/
|
|
20
|
+
export interface AuthClaims {
|
|
21
|
+
rls: string[];
|
|
22
|
+
orgs: string[];
|
|
23
|
+
grn?: OrgGrant[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The identity rebuilt from a token by a resource service.
|
|
27
|
+
*
|
|
28
|
+
* Mirrors the fields the `BaseUser` augmentation adds, so the result can be spread
|
|
29
|
+
* straight onto the user stand-in a resource service builds per request.
|
|
30
|
+
*/
|
|
31
|
+
export interface ClaimIdentity {
|
|
32
|
+
roles: string[];
|
|
33
|
+
organizations: BaseEntity[];
|
|
34
|
+
grants: OrgGrant[];
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=claims.interfaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.interfaces.d.ts","sourceRoot":"","sources":["../../src/utils/claims.interfaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAG,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAU,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,MAAM,EAAS,QAAQ,EAAE,CAAC;CAC3B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JWT identity-claim types (ADR-0005 section 5.1).
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from the logic in `claims.ts` (house-style: types live in a
|
|
6
|
+
* dedicated `*.interfaces.ts`, never mixed with the logic that consumes them).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
//# sourceMappingURL=claims.interfaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.interfaces.js","sourceRoot":"","sources":["../../src/utils/claims.interfaces.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*********************************************************************************************
|
|
3
|
+
* *
|
|
4
|
+
* Identity claims - the SINGLE definition of what identity travels inside an access token. *
|
|
5
|
+
* *
|
|
6
|
+
* Two operations, kept together on purpose because they MUST stay each other's inverse: *
|
|
7
|
+
* *
|
|
8
|
+
* buildAuthClaims - auth-srv, when signing a token (login and refresh); *
|
|
9
|
+
* identityFromClaims - every resource service, when rebuilding the caller per request. *
|
|
10
|
+
* *
|
|
11
|
+
* They used to be four hand-written literals across two services. A drift between them is *
|
|
12
|
+
* not a cosmetic bug: a claim that is written but not read means the authorization data *
|
|
13
|
+
* silently disappears, and the checks that depend on it fail OPEN or CLOSED at random. *
|
|
14
|
+
* *
|
|
15
|
+
* NOTE: resource services do NOT re-read the user from the database - they trust these *
|
|
16
|
+
* claims (ADR-0005 section 5.1). Revoking a grant therefore takes effect only after the *
|
|
17
|
+
* token is refreshed: a declared property, bounded by the access-token TTL. *
|
|
18
|
+
* *
|
|
19
|
+
*********************************************************************************************/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.buildAuthClaims = buildAuthClaims;
|
|
22
|
+
exports.identityFromClaims = identityFromClaims;
|
|
23
|
+
/**
|
|
24
|
+
* Builds the identity claims to embed in an access token.
|
|
25
|
+
*
|
|
26
|
+
* `grn` is omitted entirely when the user holds no grant, so the common case adds
|
|
27
|
+
* nothing to the token.
|
|
28
|
+
*
|
|
29
|
+
* @param user the user the token is being issued for, as read from the database
|
|
30
|
+
* @returns the identity claims to sign
|
|
31
|
+
*/
|
|
32
|
+
function buildAuthClaims(user) {
|
|
33
|
+
// rebuilt entry by entry, never spread: `user.grants` is an `OrgGrantRecord[]`
|
|
34
|
+
// read from the database, and spreading it would carry `grantedBy`/`grantedAt`
|
|
35
|
+
// into every request's token. The pair below is the whole of authorization.
|
|
36
|
+
const grants = (user.grants ?? [])
|
|
37
|
+
.filter(g => g.id && g.role)
|
|
38
|
+
.map(g => ({ id: String(g.id), role: g.role }));
|
|
39
|
+
return {
|
|
40
|
+
rls: user.roles ?? [],
|
|
41
|
+
orgs: (user.organizations ?? []).map(o => o.id).filter(Boolean),
|
|
42
|
+
...(grants.length && { grn: grants })
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Rebuilds the caller's identity from the claims of a verified token.
|
|
47
|
+
*
|
|
48
|
+
* Tolerant of a MISSING key but never of a malformed one: an older token issued
|
|
49
|
+
* before grants existed simply yields an empty list, whereas an entry without an
|
|
50
|
+
* `id` or a `role` is dropped rather than trusted half-formed.
|
|
51
|
+
*
|
|
52
|
+
* @param data the `data` payload of the verified access token
|
|
53
|
+
* @returns roles, memberships and grants, shaped as the `BaseUser` augmentation
|
|
54
|
+
*/
|
|
55
|
+
function identityFromClaims(data) {
|
|
56
|
+
return {
|
|
57
|
+
roles: data?.rls ?? [],
|
|
58
|
+
organizations: (data?.orgs ?? []).map(id => ({ id })),
|
|
59
|
+
grants: (data?.grn ?? [])
|
|
60
|
+
.filter(g => g?.id && g?.role)
|
|
61
|
+
.map(g => ({ id: g.id, role: g.role }))
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=claims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/utils/claims.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;+FAiB+F;;AAgB/F,0CAcC;AAYD,gDASC;AA5CD;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,IAAuB;IAErD,+EAA+E;IAC/E,+EAA+E;IAC/E,4EAA4E;IAC5E,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAElD,OAAO;QACL,GAAG,EAAG,IAAI,CAAC,KAAK,IAAI,EAAE;QACtB,IAAI,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAChE,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,kBAAkB,CAAC,IAA0B;IAE3D,OAAO;QACL,KAAK,EAAU,IAAI,EAAE,GAAG,IAAI,EAAE;QAC9B,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACrD,MAAM,EAAS,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;aAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAiB,EAAE,CAAC,CAAC;KACvD,CAAC;AACJ,CAAC"}
|
package/build/utils/express.js
CHANGED
|
@@ -63,11 +63,11 @@ function allowMethods(methods = [], message = 'Method Not Allowed') {
|
|
|
63
63
|
}
|
|
64
64
|
;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Normalize an array of HTTP methods.
|
|
67
67
|
*
|
|
68
68
|
* @access private
|
|
69
69
|
* @param {Array<string>} methods
|
|
70
|
-
* The HTTP methods to
|
|
70
|
+
* The HTTP methods to normalize.
|
|
71
71
|
* @returns {Array<string>}
|
|
72
72
|
* Returns an array of capitalised HTTP methods.
|
|
73
73
|
*/
|
package/build/utils/functs.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare function initConfig(dir: string): Promise<AppData>;
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function buildMongoDBConfig(): Promise<AppMongoDB>;
|
|
20
20
|
/**
|
|
21
|
-
* Reads and
|
|
21
|
+
* Reads and normalizes the application environment.
|
|
22
22
|
*
|
|
23
23
|
* Loads the dotenv file for the active `NODE_ENV`, applies the {@link ENV_DEFAULTS}
|
|
24
24
|
* table for any unset variable, resolves the few defaults that depend on other
|
|
@@ -120,13 +120,13 @@ export declare function getEntityI18nString(input: NxTypeObject<string> | string
|
|
|
120
120
|
*/
|
|
121
121
|
export declare function setEntityI18nString(input: NxTypeObject<string> | string, lang: string): NxTypeObject<string>;
|
|
122
122
|
/**
|
|
123
|
-
* Validates and
|
|
123
|
+
* Validates and normalizes an absolute http(s) URL (SSRF guard).
|
|
124
124
|
*
|
|
125
125
|
* Requires an explicit `http`/`https` protocol and rejects loopback hosts
|
|
126
126
|
* (`localhost`/`127.0.0.1`) to prevent server-side request forgery.
|
|
127
127
|
*
|
|
128
128
|
* @param input the URL to validate
|
|
129
|
-
* @returns the parsed,
|
|
129
|
+
* @returns the parsed, normalized URL string
|
|
130
130
|
* @throws `Invalid URL` when malformed, `Forbidden URL` when pointing at loopback
|
|
131
131
|
*/
|
|
132
132
|
export declare function validateUrl(input: string): string;
|
package/build/utils/functs.js
CHANGED
|
@@ -258,7 +258,7 @@ function validateEnv() {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
|
-
* Reads and
|
|
261
|
+
* Reads and normalizes the application environment.
|
|
262
262
|
*
|
|
263
263
|
* Loads the dotenv file for the active `NODE_ENV`, applies the {@link ENV_DEFAULTS}
|
|
264
264
|
* table for any unset variable, resolves the few defaults that depend on other
|
|
@@ -500,13 +500,13 @@ function setEntityI18nString(input, lang) {
|
|
|
500
500
|
return res;
|
|
501
501
|
}
|
|
502
502
|
/**
|
|
503
|
-
* Validates and
|
|
503
|
+
* Validates and normalizes an absolute http(s) URL (SSRF guard).
|
|
504
504
|
*
|
|
505
505
|
* Requires an explicit `http`/`https` protocol and rejects loopback hosts
|
|
506
506
|
* (`localhost`/`127.0.0.1`) to prevent server-side request forgery.
|
|
507
507
|
*
|
|
508
508
|
* @param input the URL to validate
|
|
509
|
-
* @returns the parsed,
|
|
509
|
+
* @returns the parsed, normalized URL string
|
|
510
510
|
* @throws `Invalid URL` when malformed, `Forbidden URL` when pointing at loopback
|
|
511
511
|
*/
|
|
512
512
|
function validateUrl(input) {
|
package/build/utils/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
export * from './claims';
|
|
2
|
+
export * from './claims.interfaces';
|
|
1
3
|
export * from './express';
|
|
2
4
|
export * from './functs';
|
|
3
5
|
export * from './jwt';
|
|
4
6
|
export * from './jwt.constants';
|
|
5
7
|
export * from './jwt.interfaces';
|
|
8
|
+
export * from './scope';
|
|
9
|
+
export * from './scope.interfaces';
|
|
6
10
|
export * from './start-service';
|
|
7
11
|
export * from './tenant';
|
|
8
12
|
export * from './tenant.interfaces';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC"}
|
package/build/utils/index.js
CHANGED
|
@@ -14,11 +14,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./claims"), exports);
|
|
18
|
+
__exportStar(require("./claims.interfaces"), exports);
|
|
17
19
|
__exportStar(require("./express"), exports);
|
|
18
20
|
__exportStar(require("./functs"), exports);
|
|
19
21
|
__exportStar(require("./jwt"), exports);
|
|
20
22
|
__exportStar(require("./jwt.constants"), exports);
|
|
21
23
|
__exportStar(require("./jwt.interfaces"), exports);
|
|
24
|
+
__exportStar(require("./scope"), exports);
|
|
25
|
+
__exportStar(require("./scope.interfaces"), exports);
|
|
22
26
|
__exportStar(require("./start-service"), exports);
|
|
23
27
|
__exportStar(require("./tenant"), exports);
|
|
24
28
|
__exportStar(require("./tenant.interfaces"), exports);
|
package/build/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,2CAAyB;AACzB,wCAAsB;AACtB,kDAAgC;AAChC,mDAAiC;AACjC,kDAAgC;AAChC,2CAAyB;AACzB,sDAAoC;AACpC,4DAA0C"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,sDAAoC;AACpC,4CAA0B;AAC1B,2CAAyB;AACzB,wCAAsB;AACtB,kDAAgC;AAChC,mDAAiC;AACjC,0CAAwB;AACxB,qDAAmC;AACnC,kDAAgC;AAChC,2CAAyB;AACzB,sDAAoC;AACpC,4DAA0C"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*********************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* Organization scope - THE single answer to "may this user operate on THIS organization?" *
|
|
4
|
+
* *
|
|
5
|
+
* The organization arrives EXPLICITLY from the request (an `oid`/`uid` on a mutation). *
|
|
6
|
+
* The sibling question - "which organization is this user operating on?", where the tenant *
|
|
7
|
+
* must be DEDUCED - is answered by `resolveTenant` in `tenant.ts`. Two questions, two *
|
|
8
|
+
* functions, no overlap. *
|
|
9
|
+
* *
|
|
10
|
+
* SINGLE SOURCE OF TRUTH: every caller imports from here and NEVER re-implements the *
|
|
11
|
+
* check. `@auth(requires:)` alone is never sufficient - it verifies the ROLE, never the *
|
|
12
|
+
* ORGANIZATION (ADR-0005 section 5). *
|
|
13
|
+
* *
|
|
14
|
+
*********************************************************************************************/
|
|
15
|
+
import { BaseUser } from '@nextage/nx-frame-be';
|
|
16
|
+
import { OrgScope } from './scope.interfaces';
|
|
17
|
+
/**
|
|
18
|
+
* Resolves how `user` reaches the organization `orgId`, or `null` if they cannot.
|
|
19
|
+
*
|
|
20
|
+
* Resolution order (ADR-0005 section 5) - the first match wins:
|
|
21
|
+
*
|
|
22
|
+
* 1. cross-tenant level -> { tier: PROVIDER } // no organization constraint
|
|
23
|
+
* 2. membership on `orgId` -> { tier: MEMBER, role } // role from `roles[]`
|
|
24
|
+
* 3. grant on `orgId` -> { tier: GRANTED, role } // role from the grant
|
|
25
|
+
* 4. otherwise -> null // denied
|
|
26
|
+
*
|
|
27
|
+
* Membership is checked BEFORE grants because it is identity: a user who belongs to
|
|
28
|
+
* the organization already reaches it, and a grant on one's own organization would
|
|
29
|
+
* be redundant. Note the two carry the role differently - membership entries hold no
|
|
30
|
+
* role (it lives in `users.roles[]`, one organization-tier role per user), whereas a
|
|
31
|
+
* grant carries its own, which is what allows a different role per organization.
|
|
32
|
+
*
|
|
33
|
+
* FAIL-CLOSED: a missing user, a missing `orgId` or no match at all all yield `null`.
|
|
34
|
+
* Callers must treat `null` as "denied" and never as "unknown".
|
|
35
|
+
*
|
|
36
|
+
* @param user the authenticated user (`NxContext.user`)
|
|
37
|
+
* @param orgId the target organization id, as received from the request
|
|
38
|
+
* @returns the scope through which the user reaches the organization, or `null`
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveOrgScope(user?: BaseUser, orgId?: string): OrgScope | null;
|
|
41
|
+
/**
|
|
42
|
+
* Throwing variant of `resolveOrgScope`, for the call sites that must simply deny.
|
|
43
|
+
*
|
|
44
|
+
* Raises the same error whether the organization is out of scope or does not exist:
|
|
45
|
+
* distinguishing the two would confirm the existence of somebody else's organization.
|
|
46
|
+
*
|
|
47
|
+
* @param user the authenticated user (`NxContext.user`)
|
|
48
|
+
* @param orgId the target organization id, as received from the request
|
|
49
|
+
* @returns the resolved scope (never `null`)
|
|
50
|
+
* @throws `AppError('messages.authorization.missingRole')` when out of scope
|
|
51
|
+
*/
|
|
52
|
+
export declare function assertOrgScope(user?: BaseUser, orgId?: string): OrgScope;
|
|
53
|
+
/**
|
|
54
|
+
* Every organization the user reaches, for the LIST case.
|
|
55
|
+
*
|
|
56
|
+
* `resolveOrgScope` answers about ONE organization named by the request; this answers
|
|
57
|
+
* "which ones may appear at all?", which is what a model's `updateFilters` needs. The
|
|
58
|
+
* two share the same rules deliberately: a list that showed an organization the point
|
|
59
|
+
* check would then deny would be a leak by itself.
|
|
60
|
+
*
|
|
61
|
+
* Three outcomes, and the caller MUST tell them apart:
|
|
62
|
+
*
|
|
63
|
+
* `undefined` -> no constraint: a cross-tenant user, every organization is visible
|
|
64
|
+
* `string[]` -> exactly these ids (memberships UNION grants), never any other
|
|
65
|
+
* `[]` -> nothing is visible; the caller must yield an EMPTY result, never all
|
|
66
|
+
*
|
|
67
|
+
* The union is what makes a grant visible in the organization switcher: an
|
|
68
|
+
* `ORG_EDITOR` granted on another organization must find it in the list, or the
|
|
69
|
+
* grant would be unusable (ADR-0005 section 3.2).
|
|
70
|
+
*
|
|
71
|
+
* @param user the authenticated user (`NxContext.user`)
|
|
72
|
+
* @returns `undefined` for no constraint, otherwise the exact set of reachable ids
|
|
73
|
+
*/
|
|
74
|
+
export declare function reachableOrgIds(user?: BaseUser): string[] | undefined;
|
|
75
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/utils/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;+FAa+F;AAE/F,OAAO,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAM1D,OAAO,EAAE,QAAQ,EAAE,MAAgB,oBAAoB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAqBhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAQxE;AAcD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,GAAG,SAAS,CAcrE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Organization-scope types & interfaces (ADR-0005 section 5).
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the logic in `scope.ts` (house-style: types live in a
|
|
5
|
+
* dedicated `*.interfaces.ts`, never mixed with the logic that consumes them).
|
|
6
|
+
* `OrgScopeTier` itself lives in `enums.ts`, next to `BaseRoles`/`RolesType`.
|
|
7
|
+
*/
|
|
8
|
+
import { BaseRoles, OrgScopeTier } from '../enums';
|
|
9
|
+
/**
|
|
10
|
+
* Outcome of `resolveOrgScope`: the tier through which the user reaches the
|
|
11
|
+
* organization, plus the organization-tier role that applies THERE.
|
|
12
|
+
*
|
|
13
|
+
* `role` is undefined only for the `PROVIDER` tier, whose capabilities come from
|
|
14
|
+
* the capability matrix (ADR-0005 section 4) and not from an organization role.
|
|
15
|
+
*/
|
|
16
|
+
export interface OrgScope {
|
|
17
|
+
tier: OrgScopeTier;
|
|
18
|
+
role?: BaseRoles;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=scope.interfaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.interfaces.d.ts","sourceRoot":"","sources":["../../src/utils/scope.interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAG,YAAY,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Organization-scope types & interfaces (ADR-0005 section 5).
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from the logic in `scope.ts` (house-style: types live in a
|
|
6
|
+
* dedicated `*.interfaces.ts`, never mixed with the logic that consumes them).
|
|
7
|
+
* `OrgScopeTier` itself lives in `enums.ts`, next to `BaseRoles`/`RolesType`.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=scope.interfaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.interfaces.js","sourceRoot":"","sources":["../../src/utils/scope.interfaces.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*********************************************************************************************
|
|
3
|
+
* *
|
|
4
|
+
* Organization scope - THE single answer to "may this user operate on THIS organization?" *
|
|
5
|
+
* *
|
|
6
|
+
* The organization arrives EXPLICITLY from the request (an `oid`/`uid` on a mutation). *
|
|
7
|
+
* The sibling question - "which organization is this user operating on?", where the tenant *
|
|
8
|
+
* must be DEDUCED - is answered by `resolveTenant` in `tenant.ts`. Two questions, two *
|
|
9
|
+
* functions, no overlap. *
|
|
10
|
+
* *
|
|
11
|
+
* SINGLE SOURCE OF TRUTH: every caller imports from here and NEVER re-implements the *
|
|
12
|
+
* check. `@auth(requires:)` alone is never sufficient - it verifies the ROLE, never the *
|
|
13
|
+
* ORGANIZATION (ADR-0005 section 5). *
|
|
14
|
+
* *
|
|
15
|
+
*********************************************************************************************/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.resolveOrgScope = resolveOrgScope;
|
|
18
|
+
exports.assertOrgScope = assertOrgScope;
|
|
19
|
+
exports.reachableOrgIds = reachableOrgIds;
|
|
20
|
+
const nx_frame_be_1 = require("@nextage/nx-frame-be");
|
|
21
|
+
const constants_1 = require("../constants");
|
|
22
|
+
const constants_2 = require("../constants");
|
|
23
|
+
const enums_1 = require("../enums");
|
|
24
|
+
/**
|
|
25
|
+
* Resolves how `user` reaches the organization `orgId`, or `null` if they cannot.
|
|
26
|
+
*
|
|
27
|
+
* Resolution order (ADR-0005 section 5) - the first match wins:
|
|
28
|
+
*
|
|
29
|
+
* 1. cross-tenant level -> { tier: PROVIDER } // no organization constraint
|
|
30
|
+
* 2. membership on `orgId` -> { tier: MEMBER, role } // role from `roles[]`
|
|
31
|
+
* 3. grant on `orgId` -> { tier: GRANTED, role } // role from the grant
|
|
32
|
+
* 4. otherwise -> null // denied
|
|
33
|
+
*
|
|
34
|
+
* Membership is checked BEFORE grants because it is identity: a user who belongs to
|
|
35
|
+
* the organization already reaches it, and a grant on one's own organization would
|
|
36
|
+
* be redundant. Note the two carry the role differently - membership entries hold no
|
|
37
|
+
* role (it lives in `users.roles[]`, one organization-tier role per user), whereas a
|
|
38
|
+
* grant carries its own, which is what allows a different role per organization.
|
|
39
|
+
*
|
|
40
|
+
* FAIL-CLOSED: a missing user, a missing `orgId` or no match at all all yield `null`.
|
|
41
|
+
* Callers must treat `null` as "denied" and never as "unknown".
|
|
42
|
+
*
|
|
43
|
+
* @param user the authenticated user (`NxContext.user`)
|
|
44
|
+
* @param orgId the target organization id, as received from the request
|
|
45
|
+
* @returns the scope through which the user reaches the organization, or `null`
|
|
46
|
+
*/
|
|
47
|
+
function resolveOrgScope(user, orgId) {
|
|
48
|
+
if (!user || !orgId)
|
|
49
|
+
return null;
|
|
50
|
+
// 1. cross-tenant level: reaches every organization by definition of level.
|
|
51
|
+
if (user.hasRole?.(constants_1.CROSS_TENANT_ROLES))
|
|
52
|
+
return { tier: enums_1.OrgScopeTier.PROVIDER };
|
|
53
|
+
// 2. membership (identity): the role is the highest organization-tier one held.
|
|
54
|
+
if (user.organizations?.some(o => o.id === orgId))
|
|
55
|
+
return { tier: enums_1.OrgScopeTier.MEMBER, role: highestOrgRole(user) };
|
|
56
|
+
// 3. grant (authorization): the role travels WITH the grant.
|
|
57
|
+
const grant = user.grants?.find(g => g.id === orgId);
|
|
58
|
+
if (grant)
|
|
59
|
+
return { tier: enums_1.OrgScopeTier.GRANTED, role: grant.role };
|
|
60
|
+
// 4. no path to this organization.
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Throwing variant of `resolveOrgScope`, for the call sites that must simply deny.
|
|
65
|
+
*
|
|
66
|
+
* Raises the same error whether the organization is out of scope or does not exist:
|
|
67
|
+
* distinguishing the two would confirm the existence of somebody else's organization.
|
|
68
|
+
*
|
|
69
|
+
* @param user the authenticated user (`NxContext.user`)
|
|
70
|
+
* @param orgId the target organization id, as received from the request
|
|
71
|
+
* @returns the resolved scope (never `null`)
|
|
72
|
+
* @throws `AppError('messages.authorization.missingRole')` when out of scope
|
|
73
|
+
*/
|
|
74
|
+
function assertOrgScope(user, orgId) {
|
|
75
|
+
const scope = resolveOrgScope(user, orgId);
|
|
76
|
+
if (!scope)
|
|
77
|
+
throw new nx_frame_be_1.AppError('messages.authorization.missingRole');
|
|
78
|
+
return scope;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The highest organization-tier role the user holds, if any.
|
|
82
|
+
*
|
|
83
|
+
* `ORG_MEMBER_ROLES` is ordered from the highest to the lowest privilege, so the
|
|
84
|
+
* first match IS the highest. Returns `undefined` for a user with no
|
|
85
|
+
* organization-tier role at all (possible for a pure provider-level account).
|
|
86
|
+
*/
|
|
87
|
+
function highestOrgRole(user) {
|
|
88
|
+
return constants_2.ORG_MEMBER_ROLES.find(r => user.hasRole?.(r));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Every organization the user reaches, for the LIST case.
|
|
92
|
+
*
|
|
93
|
+
* `resolveOrgScope` answers about ONE organization named by the request; this answers
|
|
94
|
+
* "which ones may appear at all?", which is what a model's `updateFilters` needs. The
|
|
95
|
+
* two share the same rules deliberately: a list that showed an organization the point
|
|
96
|
+
* check would then deny would be a leak by itself.
|
|
97
|
+
*
|
|
98
|
+
* Three outcomes, and the caller MUST tell them apart:
|
|
99
|
+
*
|
|
100
|
+
* `undefined` -> no constraint: a cross-tenant user, every organization is visible
|
|
101
|
+
* `string[]` -> exactly these ids (memberships UNION grants), never any other
|
|
102
|
+
* `[]` -> nothing is visible; the caller must yield an EMPTY result, never all
|
|
103
|
+
*
|
|
104
|
+
* The union is what makes a grant visible in the organization switcher: an
|
|
105
|
+
* `ORG_EDITOR` granted on another organization must find it in the list, or the
|
|
106
|
+
* grant would be unusable (ADR-0005 section 3.2).
|
|
107
|
+
*
|
|
108
|
+
* @param user the authenticated user (`NxContext.user`)
|
|
109
|
+
* @returns `undefined` for no constraint, otherwise the exact set of reachable ids
|
|
110
|
+
*/
|
|
111
|
+
function reachableOrgIds(user) {
|
|
112
|
+
if (!user)
|
|
113
|
+
return [];
|
|
114
|
+
if (user.hasRole?.(constants_1.CROSS_TENANT_ROLES))
|
|
115
|
+
return undefined;
|
|
116
|
+
const ids = new Set();
|
|
117
|
+
user.organizations?.forEach(o => o.id && ids.add(String(o.id)));
|
|
118
|
+
user.grants?.forEach(g => g.id && ids.add(String(g.id)));
|
|
119
|
+
return [...ids];
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/utils/scope.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;+FAa+F;;AAiC/F,0CAqBC;AAaD,wCAQC;AAmCD,0CAcC;AA1HD,sDAA0D;AAE1D,4CAAkD;AAClD,4CAAkD;AAElD,oCAA8C;AAG9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,eAAe,CAAC,IAAe,EAAE,KAAc;IAE7D,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK;QACjB,OAAO,IAAI,CAAC;IAEd,4EAA4E;IAC5E,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,8BAAkB,CAAC;QACpC,OAAO,EAAE,IAAI,EAAE,oBAAY,CAAC,QAAQ,EAAE,CAAC;IAEzC,gFAAgF;IAChF,IAAI,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,oBAAY,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;IAEnE,6DAA6D;IAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;IAErD,IAAI,KAAK;QACP,OAAO,EAAE,IAAI,EAAE,oBAAY,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;IAE1D,mCAAmC;IACnC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,cAAc,CAAC,IAAe,EAAE,KAAc;IAE5D,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE3C,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,sBAAQ,CAAC,oCAAoC,CAAC,CAAC;IAE3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,IAAc;IAEpC,OAAO,4BAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,eAAe,CAAC,IAAe;IAE7C,IAAI,CAAC,IAAI;QACP,OAAO,EAAE,CAAC;IAEZ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,8BAAkB,CAAC;QACpC,OAAO,SAAS,CAAC;IAEnB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAE9B,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEzD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -152,7 +152,7 @@ async function safeCheck(check) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
|
-
*
|
|
155
|
+
* Normalizes an unknown thrown value into a readable message.
|
|
156
156
|
*/
|
|
157
157
|
function errMessage(err) {
|
|
158
158
|
if (err instanceof Error)
|