@discover-cloud/shared 1.2.9 → 1.3.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/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/permission-cache.service.d.ts +104 -16
- package/dist/authorization/permission-cache.service.js +156 -143
- package/dist/authorization/permission-checker.d.ts +30 -0
- package/dist/authorization/permission-checker.js +40 -0
- package/dist/contracts/auth-service/account.dto.d.ts +30 -0
- package/dist/contracts/auth-service/account.dto.js +7 -0
- package/dist/contracts/auth-service/auth.dto.d.ts +21 -0
- package/dist/contracts/auth-service/auth.dto.js +10 -0
- package/dist/dtos/auth-service.dto.d.ts +72 -26
- package/dist/dtos/auth-service.dto.js +4 -0
- package/dist/dtos/cloud-service.dto.d.ts +51 -27
- package/dist/dtos/cloud-service.dto.js +5 -0
- package/dist/dtos/insights-service.dto.d.ts +18 -27
- package/dist/dtos/insights-service.dto.js +8 -0
- package/dist/dtos/response.dto.d.ts +7 -84
- package/dist/dtos/response.dto.js +2 -21
- package/dist/dtos/user-service.dto.d.ts +118 -13
- package/dist/enums/domain.enums.d.ts +246 -75
- package/dist/enums/domain.enums.js +305 -102
- package/dist/enums/permissions.enums.d.ts +142 -80
- package/dist/enums/permissions.enums.js +174 -133
- package/dist/errors/app-error.d.ts +13 -18
- package/dist/errors/app-error.js +14 -20
- package/dist/errors/http-errors.d.ts +105 -17
- package/dist/errors/http-errors.js +118 -33
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/http/request-context.d.ts +8 -0
- package/dist/http/request-context.js +37 -0
- package/dist/http/service-client.d.ts +91 -38
- package/dist/http/service-client.js +102 -53
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/jwt/index.d.ts +2 -1
- package/dist/jwt/index.js +2 -1
- package/dist/jwt/machine-jwt-verifier.d.ts +21 -0
- package/dist/jwt/machine-jwt-verifier.js +84 -0
- package/dist/jwt/machine-token-client.d.ts +35 -4
- package/dist/jwt/machine-token-client.js +57 -23
- package/dist/jwt/user-jwt-verifier.d.ts +31 -0
- package/dist/jwt/user-jwt-verifier.js +101 -0
- package/dist/messaging/index.d.ts +1 -0
- package/dist/{security → messaging}/index.js +1 -1
- package/dist/messaging/rabbitmq.client.d.ts +13 -0
- package/dist/messaging/rabbitmq.client.js +127 -0
- package/dist/middleware/error-handler.middleware.d.ts +8 -28
- package/dist/middleware/error-handler.middleware.js +61 -52
- package/dist/middleware/index.d.ts +7 -5
- package/dist/middleware/index.js +7 -5
- package/dist/middleware/request-context.middleware.d.ts +17 -0
- package/dist/middleware/request-context.middleware.js +73 -0
- package/dist/middleware/require-auth.middleware.d.ts +17 -70
- package/dist/middleware/require-auth.middleware.js +24 -103
- package/dist/middleware/require-machine.middleware.d.ts +38 -0
- package/dist/middleware/require-machine.middleware.js +123 -0
- package/dist/middleware/require-org-permission-from-body.middleware.d.ts +10 -0
- package/dist/middleware/require-org-permission-from-body.middleware.js +24 -0
- package/dist/middleware/require-org-permission.middleware.d.ts +19 -0
- package/dist/middleware/require-org-permission.middleware.js +115 -0
- package/dist/middleware/require-platform-permission.middleware.d.ts +19 -0
- package/dist/middleware/require-platform-permission.middleware.js +91 -0
- package/dist/middleware/require-user.middleware.d.ts +14 -0
- package/dist/middleware/require-user.middleware.js +30 -0
- package/dist/middleware/require-workspace-permission.middleware.d.ts +20 -0
- package/dist/middleware/require-workspace-permission.middleware.js +114 -0
- package/dist/middleware/validate.middleware.d.ts +29 -29
- package/dist/middleware/validate.middleware.js +30 -32
- package/dist/types/express.types.d.ts +83 -122
- package/dist/types/express.types.js +28 -47
- package/dist/utils/date.util.d.ts +6 -0
- package/dist/utils/date.util.js +11 -0
- package/dist/utils/env.util.d.ts +6 -0
- package/dist/utils/env.util.js +19 -0
- package/dist/utils/index.d.ts +5 -4
- package/dist/utils/index.js +5 -4
- package/dist/utils/logger.util.d.ts +26 -0
- package/dist/utils/logger.util.js +53 -0
- package/dist/utils/pagination.util.d.ts +6 -0
- package/dist/utils/pagination.util.js +20 -0
- package/dist/utils/response.util.d.ts +27 -0
- package/dist/utils/response.util.js +56 -0
- package/dist/utils/slug.util.d.ts +9 -0
- package/dist/utils/slug.util.js +32 -0
- package/dist/utils/url-safety.util.d.ts +6 -0
- package/dist/utils/url-safety.util.js +72 -0
- package/package.json +3 -1
- package/dist/authorization/permissions.d.ts +0 -78
- package/dist/authorization/permissions.js +0 -174
- package/dist/context/access-context.d.ts +0 -10
- package/dist/context/access-context.js +0 -2
- package/dist/context/index.d.ts +0 -1
- package/dist/context/index.js +0 -17
- package/dist/dto/auth-service.dtos.d.ts +0 -44
- package/dist/dto/auth-service.dtos.js +0 -2
- package/dist/dto/index.d.ts +0 -3
- package/dist/dto/index.js +0 -19
- package/dist/dto/response.dtos.d.ts +0 -55
- package/dist/dto/response.dtos.js +0 -6
- package/dist/dto/user-service.dtos.d.ts +0 -50
- package/dist/dto/user-service.dtos.js +0 -2
- package/dist/enums/auth-service.enums.d.ts +0 -12
- package/dist/enums/auth-service.enums.js +0 -17
- package/dist/enums/permissions.types.d.ts +0 -12
- package/dist/enums/permissions.types.js +0 -17
- package/dist/enums/user-service.enums.d.ts +0 -32
- package/dist/enums/user-service.enums.js +0 -41
- package/dist/internal/index.d.ts +0 -4
- package/dist/internal/index.js +0 -20
- package/dist/internal/internal-jwt.service.d.ts +0 -13
- package/dist/internal/internal-jwt.service.js +0 -88
- package/dist/internal/internal-jwt.types.d.ts +0 -7
- package/dist/internal/internal-jwt.types.js +0 -2
- package/dist/internal/internal-key-manager.d.ts +0 -16
- package/dist/internal/internal-key-manager.js +0 -67
- package/dist/internal/registry.d.ts +0 -8
- package/dist/internal/registry.js +0 -34
- package/dist/internal/service-client.d.ts +0 -9
- package/dist/internal/service-client.js +0 -94
- package/dist/jwt/internal-jwt-verifier.d.ts +0 -41
- package/dist/jwt/internal-jwt-verifier.js +0 -185
- package/dist/jwt/jwt-verifier.d.ts +0 -9
- package/dist/jwt/jwt-verifier.js +0 -36
- package/dist/jwt/service-client.d.ts +0 -7
- package/dist/jwt/service-client.js +0 -87
- package/dist/middleware/authorize.d.ts +0 -3
- package/dist/middleware/authorize.js +0 -24
- package/dist/middleware/authorize.middleware.d.ts +0 -54
- package/dist/middleware/authorize.middleware.js +0 -104
- package/dist/middleware/error-handler.d.ts +0 -4
- package/dist/middleware/error-handler.js +0 -23
- package/dist/middleware/request-id.d.ts +0 -2
- package/dist/middleware/request-id.js +0 -9
- package/dist/middleware/request-id.middleware.d.ts +0 -22
- package/dist/middleware/request-id.middleware.js +0 -34
- package/dist/middleware/require-auth.d.ts +0 -10
- package/dist/middleware/require-auth.js +0 -34
- package/dist/middleware/require-human.middleware.d.ts +0 -2
- package/dist/middleware/require-human.middleware.js +0 -18
- package/dist/middleware/require-internal.middleware.d.ts +0 -18
- package/dist/middleware/require-internal.middleware.js +0 -183
- package/dist/middleware/validate.d.ts +0 -5
- package/dist/middleware/validate.js +0 -18
- package/dist/middleware/validated-merge.middleware.d.ts +0 -20
- package/dist/middleware/validated-merge.middleware.js +0 -33
- package/dist/middleware/verify-internal-jwt.d.ts +0 -7
- package/dist/middleware/verify-internal-jwt.js +0 -25
- package/dist/security/guard.d.ts +0 -10
- package/dist/security/guard.js +0 -40
- package/dist/security/index.d.ts +0 -1
- package/dist/types/express.d.ts +0 -22
- package/dist/types/express.js +0 -3
- package/dist/utils/date.utils.d.ts +0 -25
- package/dist/utils/date.utils.js +0 -30
- package/dist/utils/env.d.ts +0 -46
- package/dist/utils/env.js +0 -61
- package/dist/utils/env.utils.d.ts +0 -46
- package/dist/utils/env.utils.js +0 -61
- package/dist/utils/logger.utils.d.ts +0 -66
- package/dist/utils/logger.utils.js +0 -97
- package/dist/utils/response.d.ts +0 -4
- package/dist/utils/response.js +0 -35
- package/dist/utils/response.utils.d.ts +0 -54
- package/dist/utils/response.utils.js +0 -85
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file require-workspace-permission.middleware.ts
|
|
3
|
+
* @description Express middleware factory to enforce workspace-scoped permission checks.
|
|
4
|
+
*/
|
|
5
|
+
import { RequestHandler } from "express";
|
|
6
|
+
import { Permission } from "../enums";
|
|
7
|
+
import { PermissionCacheService } from "../authorization/permission-cache.service";
|
|
8
|
+
/**
|
|
9
|
+
* Creates requireWorkspacePermission middleware factory.
|
|
10
|
+
*
|
|
11
|
+
* @param {PermissionCacheService} permissionCache - Shared Redis cache service.
|
|
12
|
+
* @param {(accountId: string, workspaceId: string) => Promise<string[]>} resolveFn
|
|
13
|
+
* - Function called on a cache miss to fetch the caller's permission strings for the workspace.
|
|
14
|
+
* @returns {object} Workspace permission middlewares.
|
|
15
|
+
*/
|
|
16
|
+
export declare const createRequireWorkspacePermission: (permissionCache: PermissionCacheService, resolveFn: (accountId: string, workspaceId: string) => Promise<string[]>) => {
|
|
17
|
+
requireWorkspacePermission: (permission: Permission) => RequestHandler;
|
|
18
|
+
requireAnyWorkspacePermission: (...permissions: Permission[]) => RequestHandler;
|
|
19
|
+
requireAllWorkspacePermissions: (...permissions: Permission[]) => RequestHandler;
|
|
20
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file require-workspace-permission.middleware.ts
|
|
4
|
+
* @description Express middleware factory to enforce workspace-scoped permission checks.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createRequireWorkspacePermission = void 0;
|
|
8
|
+
const permission_checker_1 = require("../authorization/permission-checker");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
/**
|
|
11
|
+
* Creates requireWorkspacePermission middleware factory.
|
|
12
|
+
*
|
|
13
|
+
* @param {PermissionCacheService} permissionCache - Shared Redis cache service.
|
|
14
|
+
* @param {(accountId: string, workspaceId: string) => Promise<string[]>} resolveFn
|
|
15
|
+
* - Function called on a cache miss to fetch the caller's permission strings for the workspace.
|
|
16
|
+
* @returns {object} Workspace permission middlewares.
|
|
17
|
+
*/
|
|
18
|
+
const createRequireWorkspacePermission = (permissionCache, resolveFn) => {
|
|
19
|
+
const requireWorkspacePermission = (permission) => {
|
|
20
|
+
return async (req, res, next) => {
|
|
21
|
+
const ctx = req.authContext;
|
|
22
|
+
if (!ctx || ctx.type !== "user") {
|
|
23
|
+
(0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Resolve workspaceId from request locations
|
|
27
|
+
const workspaceId = req.params["workspaceId"] ??
|
|
28
|
+
req.validated?.params?.["workspaceId"] ??
|
|
29
|
+
req.validated?.body?.["workspaceId"];
|
|
30
|
+
if (!workspaceId || typeof workspaceId !== "string") {
|
|
31
|
+
(0, utils_1.failure)(res, req, "Bad Request: Missing workspaceId", "BAD_REQUEST", 400);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const perms = await permissionCache.resolveWorkspacePermissions(ctx.accountId, workspaceId, async () => {
|
|
36
|
+
return resolveFn(ctx.accountId, workspaceId);
|
|
37
|
+
});
|
|
38
|
+
if (!permission_checker_1.PermissionChecker.has(perms, permission)) {
|
|
39
|
+
(0, utils_1.failure)(res, req, `Forbidden: Missing required workspace permission: ${permission}`, "FORBIDDEN", 403);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
next();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
next(err);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const requireAnyWorkspacePermission = (...permissions) => {
|
|
50
|
+
return async (req, res, next) => {
|
|
51
|
+
const ctx = req.authContext;
|
|
52
|
+
if (!ctx || ctx.type !== "user") {
|
|
53
|
+
(0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const workspaceId = req.params["workspaceId"] ??
|
|
57
|
+
req.validated?.params?.["workspaceId"] ??
|
|
58
|
+
req.validated?.body?.["workspaceId"];
|
|
59
|
+
if (!workspaceId || typeof workspaceId !== "string") {
|
|
60
|
+
(0, utils_1.failure)(res, req, "Bad Request: Missing workspaceId", "BAD_REQUEST", 400);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const perms = await permissionCache.resolveWorkspacePermissions(ctx.accountId, workspaceId, async () => {
|
|
65
|
+
return resolveFn(ctx.accountId, workspaceId);
|
|
66
|
+
});
|
|
67
|
+
if (!permission_checker_1.PermissionChecker.hasAny(perms, permissions)) {
|
|
68
|
+
(0, utils_1.failure)(res, req, `Forbidden: Missing one of required workspace permissions: [${permissions.join(", ")}]`, "FORBIDDEN", 403);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
next();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
next(err);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const requireAllWorkspacePermissions = (...permissions) => {
|
|
79
|
+
return async (req, res, next) => {
|
|
80
|
+
const ctx = req.authContext;
|
|
81
|
+
if (!ctx || ctx.type !== "user") {
|
|
82
|
+
(0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const workspaceId = req.params["workspaceId"] ??
|
|
86
|
+
req.validated?.params?.["workspaceId"] ??
|
|
87
|
+
req.validated?.body?.["workspaceId"];
|
|
88
|
+
if (!workspaceId || typeof workspaceId !== "string") {
|
|
89
|
+
(0, utils_1.failure)(res, req, "Bad Request: Missing workspaceId", "BAD_REQUEST", 400);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const perms = await permissionCache.resolveWorkspacePermissions(ctx.accountId, workspaceId, async () => {
|
|
94
|
+
return resolveFn(ctx.accountId, workspaceId);
|
|
95
|
+
});
|
|
96
|
+
if (!permission_checker_1.PermissionChecker.hasAll(perms, permissions)) {
|
|
97
|
+
const missing = permissions.filter(p => !permission_checker_1.PermissionChecker.has(perms, p));
|
|
98
|
+
(0, utils_1.failure)(res, req, `Forbidden: Missing required workspace permissions: [${missing.join(", ")}]`, "FORBIDDEN", 403);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
next();
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
next(err);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
requireWorkspacePermission,
|
|
110
|
+
requireAnyWorkspacePermission,
|
|
111
|
+
requireAllWorkspacePermissions,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
exports.createRequireWorkspacePermission = createRequireWorkspacePermission;
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file validate.middleware.ts
|
|
3
|
+
* @description Request validation middleware leveraging Zod schema definitions.
|
|
4
|
+
* Supports validating multiple request sources without overwriting previous results.
|
|
5
|
+
*/
|
|
1
6
|
import { Request, Response, NextFunction } from "express";
|
|
2
7
|
import { ZodType } from "zod";
|
|
3
8
|
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Source options:
|
|
11
|
-
* "body" — req.body (POST/PUT/PATCH payloads)
|
|
12
|
-
* "params" — req.params (URL path parameters)
|
|
13
|
-
* "query" — req.query (URL query string)
|
|
14
|
-
* "headers" — req.headers (custom header validation, e.g. API keys)
|
|
15
|
-
*
|
|
16
|
-
* Narrowing req.validated in a controller:
|
|
17
|
-
* req.validated is typed as unknown in express.d.ts — consumers must
|
|
18
|
-
* narrow it to the schema's inferred type at the call site:
|
|
19
|
-
*
|
|
20
|
-
* const body = req.validated as z.infer<typeof CreateOrderSchema>;
|
|
21
|
-
*
|
|
22
|
-
* This is intentional: the middleware is schema-agnostic and cannot
|
|
23
|
-
* carry the generic type through Express's untyped req object.
|
|
24
|
-
*
|
|
25
|
-
* Usage:
|
|
26
|
-
* router.post(
|
|
27
|
-
* "/orders",
|
|
28
|
-
* requireAuth,
|
|
29
|
-
* Validator.validate(CreateOrderSchema, "body"),
|
|
30
|
-
* controller.create,
|
|
31
|
-
* );
|
|
9
|
+
* Supported Express request validation sources.
|
|
10
|
+
*/
|
|
11
|
+
type ValidationSource = "body" | "params" | "query" | "headers";
|
|
12
|
+
/**
|
|
13
|
+
* Validator utility class containing Express middleware generation methods.
|
|
32
14
|
*/
|
|
33
15
|
export declare class Validator {
|
|
34
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Generates middleware to parse and validate request parts.
|
|
18
|
+
*
|
|
19
|
+
* Validated data is stored by source:
|
|
20
|
+
*
|
|
21
|
+
* req.validated = {
|
|
22
|
+
* params: {},
|
|
23
|
+
* query: {},
|
|
24
|
+
* body: {},
|
|
25
|
+
* headers: {}
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* Multiple validators can safely run on the same request.
|
|
29
|
+
*
|
|
30
|
+
* @param {ZodType<T>} schema - Zod validation schema.
|
|
31
|
+
* @param {ValidationSource} source - Request source.
|
|
32
|
+
*/
|
|
33
|
+
static validate<T>(schema: ZodType<T>, source?: ValidationSource): (req: Request, _res: Response, next: NextFunction) => void;
|
|
35
34
|
}
|
|
35
|
+
export {};
|
|
@@ -1,48 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file validate.middleware.ts
|
|
4
|
+
* @description Request validation middleware leveraging Zod schema definitions.
|
|
5
|
+
* Supports validating multiple request sources without overwriting previous results.
|
|
6
|
+
*/
|
|
2
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
8
|
exports.Validator = void 0;
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
6
|
-
* ────────────────────────────────────────────────
|
|
7
|
-
* Validates and parses request input against a Zod schema.
|
|
8
|
-
* On success, attaches the parsed (coerced + stripped) data to req.validated.
|
|
9
|
-
* On failure, passes the ZodError to next() → GlobalErrorHandler → 400.
|
|
10
|
-
*
|
|
11
|
-
* Source options:
|
|
12
|
-
* "body" — req.body (POST/PUT/PATCH payloads)
|
|
13
|
-
* "params" — req.params (URL path parameters)
|
|
14
|
-
* "query" — req.query (URL query string)
|
|
15
|
-
* "headers" — req.headers (custom header validation, e.g. API keys)
|
|
16
|
-
*
|
|
17
|
-
* Narrowing req.validated in a controller:
|
|
18
|
-
* req.validated is typed as unknown in express.d.ts — consumers must
|
|
19
|
-
* narrow it to the schema's inferred type at the call site:
|
|
20
|
-
*
|
|
21
|
-
* const body = req.validated as z.infer<typeof CreateOrderSchema>;
|
|
22
|
-
*
|
|
23
|
-
* This is intentional: the middleware is schema-agnostic and cannot
|
|
24
|
-
* carry the generic type through Express's untyped req object.
|
|
25
|
-
*
|
|
26
|
-
* Usage:
|
|
27
|
-
* router.post(
|
|
28
|
-
* "/orders",
|
|
29
|
-
* requireAuth,
|
|
30
|
-
* Validator.validate(CreateOrderSchema, "body"),
|
|
31
|
-
* controller.create,
|
|
32
|
-
* );
|
|
10
|
+
* Validator utility class containing Express middleware generation methods.
|
|
33
11
|
*/
|
|
34
12
|
class Validator {
|
|
13
|
+
/**
|
|
14
|
+
* Generates middleware to parse and validate request parts.
|
|
15
|
+
*
|
|
16
|
+
* Validated data is stored by source:
|
|
17
|
+
*
|
|
18
|
+
* req.validated = {
|
|
19
|
+
* params: {},
|
|
20
|
+
* query: {},
|
|
21
|
+
* body: {},
|
|
22
|
+
* headers: {}
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* Multiple validators can safely run on the same request.
|
|
26
|
+
*
|
|
27
|
+
* @param {ZodType<T>} schema - Zod validation schema.
|
|
28
|
+
* @param {ValidationSource} source - Request source.
|
|
29
|
+
*/
|
|
35
30
|
static validate(schema, source = "body") {
|
|
36
31
|
return (req, _res, next) => {
|
|
37
32
|
const result = schema.safeParse(req[source]);
|
|
38
33
|
if (!result.success) {
|
|
39
|
-
// Pass the ZodError to GlobalErrorHandler — it flattens and returns 400.
|
|
40
34
|
next(result.error);
|
|
41
35
|
return;
|
|
42
36
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
req.validated = {
|
|
38
|
+
...(req.validated ?? {}),
|
|
39
|
+
[source]: {
|
|
40
|
+
...(req.validated?.[source] ?? {}),
|
|
41
|
+
...result.data,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
46
44
|
next();
|
|
47
45
|
};
|
|
48
46
|
}
|
|
@@ -1,49 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Single source of truth for everything attached to Express's Request object
|
|
5
|
-
* and all internal JWT payload shapes.
|
|
6
|
-
*
|
|
7
|
-
* Import in each service's entrypoint to activate the augmentation:
|
|
8
|
-
* import "@discover-cloud/shared/types/express";
|
|
9
|
-
*
|
|
10
|
-
* ─── Permission flow ────────────────────────────────────────────────────
|
|
11
|
-
* JWT carries: accountId + accountRole (lean payload, no perms)
|
|
12
|
-
* Redis derives: GlobalPermission[] (role map on cache miss)
|
|
13
|
-
* AccessContext has: perms (resolved by auth middleware)
|
|
14
|
-
*
|
|
15
|
-
* perms never touch the JWT — they live in the Redis permission cache and
|
|
16
|
-
* are resolved into req.accessContext by RequireAuthMiddleware per request.
|
|
17
|
-
* ────────────────────────────────────────────────────────────────────────
|
|
2
|
+
* @file express.types.ts
|
|
3
|
+
* @description Express Request type augmentations and authentication context definitions.
|
|
18
4
|
*/
|
|
19
5
|
import { JWTPayload } from "jose";
|
|
20
|
-
|
|
21
|
-
interface BaseInternalJwtPayload extends JWTPayload {
|
|
22
|
-
/**
|
|
23
|
-
* JWT ID — required.
|
|
24
|
-
* Used for the Redis kill-switch (session revocation / account suspension).
|
|
25
|
-
* Overrides JWTPayload's optional jti with a required string.
|
|
26
|
-
*/
|
|
6
|
+
interface BaseTokenPayload extends JWTPayload {
|
|
27
7
|
jti: string;
|
|
28
|
-
/**
|
|
29
|
-
* Token type discriminator — guards against token confusion attacks.
|
|
30
|
-
* InternalJwtVerifier rejects any token where typ !== "internal",
|
|
31
|
-
* even if it passes signature verification.
|
|
32
|
-
*/
|
|
33
|
-
typ: "internal";
|
|
34
|
-
/**
|
|
35
|
-
* The entity that last minted or forwarded this token.
|
|
36
|
-
* "api-gateway" → direct user-initiated request
|
|
37
|
-
* "notification-service" → service-to-service call on behalf of a user
|
|
38
|
-
* Used for audit logging and conditional authorization.
|
|
39
|
-
*/
|
|
40
|
-
caller: string;
|
|
41
|
-
/**
|
|
42
|
-
* Propagated request ID for distributed tracing.
|
|
43
|
-
* Ties together logs across the entire service call chain.
|
|
44
|
-
* Set by the gateway from the incoming x-request-id header.
|
|
45
|
-
*/
|
|
46
|
-
requestId?: string;
|
|
47
8
|
iss: string;
|
|
48
9
|
aud: string | string[];
|
|
49
10
|
iat: number;
|
|
@@ -51,116 +12,116 @@ interface BaseInternalJwtPayload extends JWTPayload {
|
|
|
51
12
|
nbf: number;
|
|
52
13
|
}
|
|
53
14
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
15
|
+
* Short-lived user access token.
|
|
16
|
+
*
|
|
17
|
+
* Contains authentication identity only.
|
|
18
|
+
* Authorization is resolved dynamically using RBAC.
|
|
19
|
+
*/
|
|
20
|
+
export interface AccessTokenPayload extends BaseTokenPayload {
|
|
21
|
+
typ: "access";
|
|
22
|
+
/** Account identifier. */
|
|
23
|
+
accountId: string;
|
|
24
|
+
/** Active session identifier. */
|
|
25
|
+
sessionId: string;
|
|
26
|
+
/** Token/version invalidation marker. */
|
|
27
|
+
ver: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Long-lived refresh token payload.
|
|
31
|
+
*
|
|
32
|
+
* Refresh tokens are only used for rotation.
|
|
58
33
|
*/
|
|
59
|
-
export interface
|
|
60
|
-
|
|
34
|
+
export interface RefreshTokenPayload extends BaseTokenPayload {
|
|
35
|
+
typ: "refresh";
|
|
61
36
|
accountId: string;
|
|
62
|
-
accountRole: AccountRole;
|
|
63
|
-
externalJti: string;
|
|
64
|
-
externalExp: number;
|
|
65
37
|
}
|
|
66
38
|
/**
|
|
67
|
-
*
|
|
68
|
-
* Issued for service-to-service calls with no user context.
|
|
69
|
-
* Intentionally carries no user fields — their absence is enforced by the type.
|
|
39
|
+
* Service-to-service authentication token.
|
|
70
40
|
*/
|
|
71
|
-
export interface
|
|
72
|
-
|
|
41
|
+
export interface MachineTokenPayload extends BaseTokenPayload {
|
|
42
|
+
typ: "machine";
|
|
73
43
|
serviceId: string;
|
|
44
|
+
scopes: string[];
|
|
45
|
+
requestId?: string;
|
|
74
46
|
}
|
|
75
|
-
/**
|
|
76
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Any verified JWT generated by auth-service.
|
|
49
|
+
*/
|
|
50
|
+
export type VerifiedTokenPayload = AccessTokenPayload | RefreshTokenPayload | MachineTokenPayload;
|
|
51
|
+
/**
|
|
52
|
+
* Minimal data needed to revoke a JWT.
|
|
53
|
+
*/
|
|
77
54
|
export interface TokenRevocationContext {
|
|
78
55
|
jti: string;
|
|
79
56
|
exp: number;
|
|
80
57
|
}
|
|
81
|
-
|
|
82
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Authenticated user request context.
|
|
60
|
+
*
|
|
61
|
+
* Created from access tokens only.
|
|
62
|
+
*/
|
|
63
|
+
export interface UserContext {
|
|
64
|
+
type: "user";
|
|
83
65
|
accountId: string;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Resolved from Redis permission cache — NOT from the JWT.
|
|
87
|
-
* Always present by the time req.accessContext is populated.
|
|
88
|
-
* Invalidated on role change, suspension, and account deletion.
|
|
89
|
-
*/
|
|
90
|
-
perms: GlobalPermission[];
|
|
66
|
+
sessionId: string;
|
|
91
67
|
}
|
|
92
|
-
|
|
93
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Authenticated internal service context.
|
|
70
|
+
*/
|
|
71
|
+
export interface MachineContext {
|
|
72
|
+
type: "machine";
|
|
94
73
|
serviceId: string;
|
|
74
|
+
scopes: string[];
|
|
75
|
+
}
|
|
76
|
+
export type AuthContext = UserContext | MachineContext;
|
|
77
|
+
export interface RequestMetadataContext {
|
|
78
|
+
requestId: string;
|
|
79
|
+
ipAddress: string | null;
|
|
80
|
+
userAgent: string | null;
|
|
95
81
|
}
|
|
96
|
-
export type AccessContext = HumanAccessContext | MachineAccessContext;
|
|
97
|
-
export declare function isHumanPayload(payload: InternalJwtPayload): payload is HumanInternalJwtPayload;
|
|
98
|
-
export declare function isMachinePayload(payload: InternalJwtPayload): payload is MachineInternalJwtPayload;
|
|
99
82
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* jose returns a generic JWTPayload because it cannot know
|
|
103
|
-
* our custom claims. After verification + this assertion,
|
|
104
|
-
* TypeScript safely treats it as MachineInternalJwtPayload.
|
|
83
|
+
* Passed into application services for immutable auditing.
|
|
105
84
|
*/
|
|
106
|
-
export
|
|
107
|
-
|
|
108
|
-
export declare function isMachineContext(ctx: AccessContext): ctx is MachineAccessContext;
|
|
109
|
-
export interface VerifiedAccessPayload {
|
|
110
|
-
accountId: string;
|
|
111
|
-
accountRole: AccountRole;
|
|
112
|
-
typ: "access";
|
|
113
|
-
iss: string;
|
|
114
|
-
aud: string | string[];
|
|
115
|
-
iat: number;
|
|
116
|
-
exp: number;
|
|
117
|
-
nbf: number;
|
|
118
|
-
jti: string;
|
|
85
|
+
export interface AuditContext extends Partial<RequestMetadataContext> {
|
|
86
|
+
actorId: string | null;
|
|
119
87
|
}
|
|
120
|
-
export
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
88
|
+
export declare function isAccessToken(payload: VerifiedTokenPayload): payload is AccessTokenPayload;
|
|
89
|
+
export declare function isRefreshToken(payload: VerifiedTokenPayload): payload is RefreshTokenPayload;
|
|
90
|
+
export declare function isMachineToken(payload: VerifiedTokenPayload): payload is MachineTokenPayload;
|
|
91
|
+
export declare function isUserContext(ctx: AuthContext): ctx is UserContext;
|
|
92
|
+
export declare function isMachineContext(ctx: AuthContext): ctx is MachineContext;
|
|
93
|
+
/**
|
|
94
|
+
* Runtime assertion for service tokens.
|
|
95
|
+
*/
|
|
96
|
+
export declare function assertMachineToken(payload: JWTPayload): asserts payload is MachineTokenPayload;
|
|
97
|
+
export interface ValidatedRequestData {
|
|
98
|
+
params?: Record<string, unknown>;
|
|
99
|
+
query?: Record<string, unknown>;
|
|
100
|
+
body?: Record<string, unknown>;
|
|
101
|
+
headers?: Record<string, unknown>;
|
|
129
102
|
}
|
|
130
103
|
declare module "express-serve-static-core" {
|
|
131
104
|
interface Request {
|
|
132
105
|
/**
|
|
133
|
-
*
|
|
134
|
-
* Respects upstream x-request-id forwarded by the gateway / load balancer.
|
|
135
|
-
* Always present after requestId middleware runs.
|
|
106
|
+
* Request correlation ID.
|
|
136
107
|
*/
|
|
137
108
|
id: string;
|
|
138
109
|
/**
|
|
139
|
-
*
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
*
|
|
144
|
-
* Never cast to any.
|
|
110
|
+
* Request metadata.
|
|
111
|
+
*/
|
|
112
|
+
requestContext: RequestMetadataContext;
|
|
113
|
+
/**
|
|
114
|
+
* Validated request data.
|
|
145
115
|
*/
|
|
146
|
-
validated?:
|
|
116
|
+
validated?: ValidatedRequestData;
|
|
147
117
|
/**
|
|
148
|
-
* Raw verified
|
|
149
|
-
* Set by RequireAuthMiddleware after InternalJwtVerifier.verifyInternal().
|
|
150
|
-
*
|
|
151
|
-
* Prefer req.accessContext in route handlers and controllers.
|
|
152
|
-
* Access internalAuth only when you need raw JWT claims (e.g. jti).
|
|
118
|
+
* Raw verified JWT payload.
|
|
153
119
|
*/
|
|
154
|
-
|
|
120
|
+
authPayload?: VerifiedTokenPayload;
|
|
155
121
|
/**
|
|
156
|
-
*
|
|
157
|
-
* This is what route handlers, controllers, and domain services consume.
|
|
158
|
-
*
|
|
159
|
-
* if (isHumanContext(req.accessContext)) {
|
|
160
|
-
* const { accountId, accountRole, perms } = req.accessContext;
|
|
161
|
-
* }
|
|
122
|
+
* Runtime authenticated identity.
|
|
162
123
|
*/
|
|
163
|
-
|
|
124
|
+
authContext?: AuthContext;
|
|
164
125
|
}
|
|
165
126
|
}
|
|
166
127
|
export {};
|
|
@@ -1,60 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Single source of truth for everything attached to Express's Request object
|
|
6
|
-
* and all internal JWT payload shapes.
|
|
7
|
-
*
|
|
8
|
-
* Import in each service's entrypoint to activate the augmentation:
|
|
9
|
-
* import "@discover-cloud/shared/types/express";
|
|
10
|
-
*
|
|
11
|
-
* ─── Permission flow ────────────────────────────────────────────────────
|
|
12
|
-
* JWT carries: accountId + accountRole (lean payload, no perms)
|
|
13
|
-
* Redis derives: GlobalPermission[] (role map on cache miss)
|
|
14
|
-
* AccessContext has: perms (resolved by auth middleware)
|
|
15
|
-
*
|
|
16
|
-
* perms never touch the JWT — they live in the Redis permission cache and
|
|
17
|
-
* are resolved into req.accessContext by RequireAuthMiddleware per request.
|
|
18
|
-
* ────────────────────────────────────────────────────────────────────────
|
|
3
|
+
* @file express.types.ts
|
|
4
|
+
* @description Express Request type augmentations and authentication context definitions.
|
|
19
5
|
*/
|
|
20
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
7
|
+
exports.isAccessToken = isAccessToken;
|
|
8
|
+
exports.isRefreshToken = isRefreshToken;
|
|
9
|
+
exports.isMachineToken = isMachineToken;
|
|
10
|
+
exports.isUserContext = isUserContext;
|
|
25
11
|
exports.isMachineContext = isMachineContext;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
==================================================================== */
|
|
33
|
-
function isHumanPayload(payload) {
|
|
34
|
-
return payload.isMachine === false;
|
|
12
|
+
exports.assertMachineToken = assertMachineToken;
|
|
13
|
+
/* ---------------------------------------------------------------------------
|
|
14
|
+
Type Guards
|
|
15
|
+
--------------------------------------------------------------------------- */
|
|
16
|
+
function isAccessToken(payload) {
|
|
17
|
+
return payload.typ === "access";
|
|
35
18
|
}
|
|
36
|
-
function
|
|
37
|
-
return payload.
|
|
19
|
+
function isRefreshToken(payload) {
|
|
20
|
+
return payload.typ === "refresh";
|
|
21
|
+
}
|
|
22
|
+
function isMachineToken(payload) {
|
|
23
|
+
return payload.typ === "machine";
|
|
24
|
+
}
|
|
25
|
+
function isUserContext(ctx) {
|
|
26
|
+
return ctx.type === "user";
|
|
27
|
+
}
|
|
28
|
+
function isMachineContext(ctx) {
|
|
29
|
+
return ctx.type === "machine";
|
|
38
30
|
}
|
|
39
31
|
/**
|
|
40
|
-
* Runtime assertion for
|
|
41
|
-
*
|
|
42
|
-
* jose returns a generic JWTPayload because it cannot know
|
|
43
|
-
* our custom claims. After verification + this assertion,
|
|
44
|
-
* TypeScript safely treats it as MachineInternalJwtPayload.
|
|
32
|
+
* Runtime assertion for service tokens.
|
|
45
33
|
*/
|
|
46
|
-
function
|
|
47
|
-
if (payload["typ"] !== "
|
|
48
|
-
payload["isMachine"] !== true ||
|
|
34
|
+
function assertMachineToken(payload) {
|
|
35
|
+
if (payload["typ"] !== "machine" ||
|
|
49
36
|
typeof payload["serviceId"] !== "string" ||
|
|
50
37
|
typeof payload["jti"] !== "string" ||
|
|
51
|
-
|
|
52
|
-
throw new Error("Invalid machine
|
|
38
|
+
!Array.isArray(payload["scopes"])) {
|
|
39
|
+
throw new Error("Invalid machine token payload");
|
|
53
40
|
}
|
|
54
41
|
}
|
|
55
|
-
function isHumanContext(ctx) {
|
|
56
|
-
return ctx.kind === "human";
|
|
57
|
-
}
|
|
58
|
-
function isMachineContext(ctx) {
|
|
59
|
-
return ctx.kind === "machine";
|
|
60
|
-
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file date.util.ts
|
|
4
|
+
* @description Date formatting and serialization helper utilities.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.toIsoOrNull = exports.toIso = void 0;
|
|
8
|
+
const toIso = (date) => date.toISOString();
|
|
9
|
+
exports.toIso = toIso;
|
|
10
|
+
const toIsoOrNull = (date) => date ? date.toISOString() : null;
|
|
11
|
+
exports.toIsoOrNull = toIsoOrNull;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file env.util.ts
|
|
4
|
+
* @description Helper functions to retrieve and validate environment variable settings safely.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getEnv = getEnv;
|
|
8
|
+
exports.getEnvOptional = getEnvOptional;
|
|
9
|
+
function getEnv(name) {
|
|
10
|
+
const value = process.env[name];
|
|
11
|
+
if (!value) {
|
|
12
|
+
throw new Error(`Missing required environment variable: ${name}. Ensure it is set in your .env file.`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function getEnvOptional(name) {
|
|
17
|
+
const value = process.env[name];
|
|
18
|
+
return value === "" ? undefined : value;
|
|
19
|
+
}
|