@discover-cloud/shared 1.2.9 → 1.4.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 +2 -2
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/permission-cache.service.d.ts +104 -16
- package/dist/authorization/permission-cache.service.js +177 -148
- package/dist/authorization/permission-checker.d.ts +30 -0
- package/dist/authorization/permission-checker.js +40 -0
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +1 -1
- package/dist/context/request-context.storage.d.ts +21 -0
- package/dist/context/request-context.storage.js +37 -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/contracts/index.d.ts +1 -0
- package/dist/{security → contracts}/index.js +1 -1
- package/dist/contracts/visibility.d.ts +63 -0
- package/dist/contracts/visibility.js +10 -0
- package/dist/dtos/audit-service.types.d.ts +42 -0
- package/dist/dtos/audit-service.types.js +31 -0
- package/dist/dtos/auth-service.dto.d.ts +87 -26
- package/dist/dtos/auth-service.dto.js +4 -0
- package/dist/dtos/cloud-service.dto.d.ts +57 -27
- package/dist/dtos/cloud-service.dto.js +5 -0
- package/dist/dtos/index.d.ts +6 -5
- package/dist/dtos/index.js +1 -0
- package/dist/dtos/insights-service.dto.d.ts +184 -32
- package/dist/dtos/insights-service.dto.js +8 -0
- package/dist/dtos/response.dto.d.ts +12 -83
- package/dist/dtos/response.dto.js +2 -21
- package/dist/dtos/user-service.dto.d.ts +163 -13
- package/dist/enums/domain.enums.d.ts +272 -75
- package/dist/enums/domain.enums.js +329 -102
- package/dist/enums/index.d.ts +2 -2
- 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 +15 -21
- package/dist/errors/http-errors.d.ts +106 -18
- package/dist/errors/http-errors.js +136 -51
- package/dist/errors/index.d.ts +2 -2
- package/dist/http/index.d.ts +2 -1
- 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 +93 -40
- package/dist/http/service-client.js +104 -55
- package/dist/index.d.ts +11 -9
- package/dist/index.js +2 -0
- package/dist/jwt/index.d.ts +3 -2
- package/dist/jwt/index.js +2 -1
- package/dist/jwt/machine-jwt-verifier.d.ts +21 -0
- package/dist/jwt/machine-jwt-verifier.js +83 -0
- package/dist/jwt/machine-token-client.d.ts +36 -5
- package/dist/jwt/machine-token-client.js +61 -27
- package/dist/jwt/user-jwt-verifier.d.ts +31 -0
- package/dist/jwt/user-jwt-verifier.js +102 -0
- package/dist/messaging/audit.publisher.d.ts +14 -0
- package/dist/messaging/audit.publisher.js +49 -0
- package/dist/messaging/index.d.ts +2 -0
- package/dist/{dto → messaging}/index.js +2 -3
- package/dist/messaging/rabbitmq.client.d.ts +15 -0
- package/dist/messaging/rabbitmq.client.js +133 -0
- package/dist/middleware/error-handler.middleware.d.ts +8 -28
- package/dist/middleware/error-handler.middleware.js +63 -54
- package/dist/middleware/index.d.ts +10 -8
- package/dist/middleware/index.js +7 -5
- package/dist/middleware/request-context.middleware.d.ts +6 -0
- package/dist/middleware/request-context.middleware.js +48 -0
- package/dist/middleware/require-auth.middleware.d.ts +7 -76
- package/dist/middleware/require-auth.middleware.js +29 -117
- package/dist/middleware/require-machine.middleware.d.ts +13 -0
- package/dist/middleware/require-machine.middleware.js +128 -0
- package/dist/middleware/require-org-permission-from-body.middleware.d.ts +10 -0
- package/dist/middleware/require-org-permission-from-body.middleware.js +23 -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 +31 -31
- package/dist/middleware/validate.middleware.js +31 -33
- package/dist/types/audit.types.d.ts +39 -0
- package/dist/types/audit.types.js +30 -0
- package/dist/types/express.types.d.ts +92 -124
- package/dist/types/express.types.js +30 -49
- package/dist/types/index.d.ts +1 -1
- 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 +31 -0
- package/dist/utils/logger.util.js +91 -0
- package/dist/utils/pagination.util.d.ts +6 -0
- package/dist/utils/pagination.util.js +20 -0
- package/dist/utils/request-context.als.d.ts +10 -0
- package/dist/utils/request-context.als.js +5 -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 +75 -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/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/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
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.requireHuman = void 0;
|
|
4
|
-
const utils_1 = require("../utils");
|
|
5
|
-
const types_1 = require("../types");
|
|
6
|
-
const requireHuman = (req, res, next) => {
|
|
7
|
-
const ctx = req.accessContext;
|
|
8
|
-
if (!ctx) {
|
|
9
|
-
(0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
if (!(0, types_1.isHumanContext)(ctx)) {
|
|
13
|
-
(0, utils_1.failure)(res, req, "Human token required", "FORBIDDEN", 403);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
next();
|
|
17
|
-
};
|
|
18
|
-
exports.requireHuman = requireHuman;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { ILogger } from "../utils/logger.utils";
|
|
3
|
-
export interface RequireInternalOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Gateway JWKS URI.
|
|
6
|
-
* Defaults to GATEWAY_JWKS_URI env var, then the docker-compose default.
|
|
7
|
-
* Must point to the gateway, not any individual service.
|
|
8
|
-
*/
|
|
9
|
-
gatewayJwksUri?: string;
|
|
10
|
-
/**
|
|
11
|
-
* Permitted serviceIds. Reject any machine token whose serviceId is
|
|
12
|
-
* not in this list, even if the signature is valid.
|
|
13
|
-
* Omit to accept any valid machine token (not recommended in production).
|
|
14
|
-
*/
|
|
15
|
-
allowedServices?: string[];
|
|
16
|
-
logger?: ILogger;
|
|
17
|
-
}
|
|
18
|
-
export declare function requireInternal(options?: RequireInternalOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -1,183 +0,0 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.requireInternal = requireInternal;
|
|
37
|
-
const jose = __importStar(require("jose"));
|
|
38
|
-
const types_1 = require("../types");
|
|
39
|
-
const logger_utils_1 = require("../utils/logger.utils");
|
|
40
|
-
/**
|
|
41
|
-
* REQUIRE INTERNAL MIDDLEWARE (@discover-cloud/shared)
|
|
42
|
-
* ─────────────────────────────────────────────────────────────────────────
|
|
43
|
-
* Protects /internal/* routes by verifying a gateway-signed machine JWT.
|
|
44
|
-
* Replaces X-Internal-Secret on all service-to-service endpoints.
|
|
45
|
-
*
|
|
46
|
-
* Trust model:
|
|
47
|
-
* All machine tokens are signed by the gateway's private key and verified
|
|
48
|
-
* against the gateway's JWKS endpoint — the same trust root used for
|
|
49
|
-
* human tokens. There is exactly one signing authority in the system.
|
|
50
|
-
*
|
|
51
|
-
* Verification:
|
|
52
|
-
* 1. Extract Bearer token from Authorization header
|
|
53
|
-
* 2. Verify RS256 signature against gateway JWKS
|
|
54
|
-
* 3. Assert typ === "internal" (token confusion defence)
|
|
55
|
-
* 4. Assert isMachine === true (rejects human tokens on internal routes)
|
|
56
|
-
* 5. Assert serviceId is in the allowlist (optional but recommended)
|
|
57
|
-
* 6. Attach MachineAccessContext to req.accessContext
|
|
58
|
-
*
|
|
59
|
-
* JWKS caching:
|
|
60
|
-
* One RemoteJWKSet instance is created per middleware instance (i.e. per
|
|
61
|
-
* service process), cached internally by jose for JWKS_CACHE_MAX_AGE_MS.
|
|
62
|
-
* No per-request network call under normal operation.
|
|
63
|
-
*
|
|
64
|
-
* Allowlist:
|
|
65
|
-
* Pass allowedServices to restrict which services can call this endpoint.
|
|
66
|
-
* Example: requireInternal({ allowedServices: ["cloud-service"] })
|
|
67
|
-
* Defaults to accepting any valid machine token if omitted.
|
|
68
|
-
* Always set this in production — it limits blast radius if a service
|
|
69
|
-
* is compromised.
|
|
70
|
-
*
|
|
71
|
-
* Usage:
|
|
72
|
-
* // insights-service internal routes
|
|
73
|
-
* router.use("/internal", requireInternal({
|
|
74
|
-
* gatewayJwksUri: process.env.GATEWAY_JWKS_URI,
|
|
75
|
-
* allowedServices: ["cloud-service"],
|
|
76
|
-
* logger,
|
|
77
|
-
* }));
|
|
78
|
-
*/
|
|
79
|
-
const CLOCK_TOLERANCE_S = 30;
|
|
80
|
-
const JWKS_CACHE_MAX_AGE_MS = 10 * 60 * 1000;
|
|
81
|
-
const JWKS_FETCH_TIMEOUT_MS = 5000;
|
|
82
|
-
function requireInternal(options = {}) {
|
|
83
|
-
const logger = options.logger ?? logger_utils_1.noopLogger;
|
|
84
|
-
const jwksUri = options.gatewayJwksUri ??
|
|
85
|
-
process.env["GATEWAY_JWKS_URI"] ??
|
|
86
|
-
"http://api-gateway:3000/.well-known/jwks.json";
|
|
87
|
-
const issuer = process.env["INTERNAL_JWT_ISSUER"] ?? "discover-cloud:api-gateway";
|
|
88
|
-
const audience = process.env["INTERNAL_JWT_AUDIENCE"] ?? "discover-cloud:internal";
|
|
89
|
-
// One JWKS instance per middleware — jose caches the key set internally
|
|
90
|
-
const jwks = jose.createRemoteJWKSet(new URL(jwksUri), {
|
|
91
|
-
cacheMaxAge: JWKS_CACHE_MAX_AGE_MS,
|
|
92
|
-
timeoutDuration: JWKS_FETCH_TIMEOUT_MS,
|
|
93
|
-
});
|
|
94
|
-
const { allowedServices } = options;
|
|
95
|
-
return async (req, res, next) => {
|
|
96
|
-
const header = req.headers.authorization;
|
|
97
|
-
if (!header?.startsWith("Bearer ")) {
|
|
98
|
-
res.status(401).json({
|
|
99
|
-
success: false,
|
|
100
|
-
error: { code: "UNAUTHORIZED", message: "Missing Authorization header" },
|
|
101
|
-
});
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const token = header.slice(7);
|
|
105
|
-
try {
|
|
106
|
-
const { payload } = await jose.jwtVerify(token, jwks, {
|
|
107
|
-
issuer,
|
|
108
|
-
audience,
|
|
109
|
-
algorithms: ["RS256"],
|
|
110
|
-
clockTolerance: CLOCK_TOLERANCE_S,
|
|
111
|
-
});
|
|
112
|
-
// Token confusion defence — reject human tokens that somehow
|
|
113
|
-
// reach an internal route (wrong header forwarded by caller)
|
|
114
|
-
if (payload["typ"] !== "internal") {
|
|
115
|
-
res.status(401).json({
|
|
116
|
-
success: false,
|
|
117
|
-
error: { code: "INVALID_TOKEN", message: "Token type must be 'internal'" },
|
|
118
|
-
});
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
// Machine guard — reject human internal tokens on machine-only routes
|
|
122
|
-
if (payload["isMachine"] !== true) {
|
|
123
|
-
res.status(403).json({
|
|
124
|
-
success: false,
|
|
125
|
-
error: { code: "FORBIDDEN", message: "Machine token required for internal routes" },
|
|
126
|
-
});
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
try {
|
|
130
|
-
(0, types_1.assertMachinePayload)(payload);
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
res.status(401).json({
|
|
134
|
-
success: false,
|
|
135
|
-
error: {
|
|
136
|
-
code: "INVALID_TOKEN",
|
|
137
|
-
message: "Invalid machine token payload",
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const serviceId = payload.serviceId;
|
|
143
|
-
// Allowlist — tightest possible scope per endpoint
|
|
144
|
-
if (allowedServices && !allowedServices.includes(serviceId)) {
|
|
145
|
-
logger.warn({ requestId: req.id, serviceId }, "[requireInternal] serviceId not in allowlist");
|
|
146
|
-
res.status(403).json({
|
|
147
|
-
success: false,
|
|
148
|
-
error: { code: "FORBIDDEN", message: "Service not permitted to call this endpoint" },
|
|
149
|
-
});
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const context = { kind: "machine", serviceId };
|
|
153
|
-
req.internalAuth = payload;
|
|
154
|
-
req.accessContext = context;
|
|
155
|
-
logger.debug({ requestId: req.id, serviceId, jti: payload.jti }, "[requireInternal] Machine token verified");
|
|
156
|
-
next();
|
|
157
|
-
}
|
|
158
|
-
catch (err) {
|
|
159
|
-
if (err instanceof jose.errors.JWTExpired) {
|
|
160
|
-
res.status(401).json({
|
|
161
|
-
success: false,
|
|
162
|
-
error: { code: "TOKEN_EXPIRED", message: "Machine token has expired" },
|
|
163
|
-
});
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (err instanceof jose.errors.JWSSignatureVerificationFailed ||
|
|
167
|
-
err instanceof jose.errors.JWTClaimValidationFailed ||
|
|
168
|
-
err instanceof jose.errors.JWSInvalid ||
|
|
169
|
-
err instanceof jose.errors.JWTInvalid) {
|
|
170
|
-
res.status(401).json({
|
|
171
|
-
success: false,
|
|
172
|
-
error: { code: "INVALID_TOKEN", message: "Machine token is invalid" },
|
|
173
|
-
});
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
logger.error({ err, requestId: req.id }, "[requireInternal] Unexpected error verifying machine token");
|
|
177
|
-
res.status(503).json({
|
|
178
|
-
success: false,
|
|
179
|
-
error: { code: "SERVICE_UNAVAILABLE", message: "Authentication temporarily unavailable" },
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Validator = void 0;
|
|
4
|
-
class Validator {
|
|
5
|
-
static validate(schema, source = "body") {
|
|
6
|
-
return (req, _res, next) => {
|
|
7
|
-
const result = schema.safeParse(req[source]);
|
|
8
|
-
if (!result.success) {
|
|
9
|
-
// Pass to GlobalErrorHandler
|
|
10
|
-
return next(result.error);
|
|
11
|
-
}
|
|
12
|
-
// Populate req.validated (defined in your shared .d.ts)
|
|
13
|
-
req.validated = result.data;
|
|
14
|
-
next();
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
exports.Validator = Validator;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
/**
|
|
3
|
-
* VALIDATED MERGE HELPERS
|
|
4
|
-
* ─────────────────────────
|
|
5
|
-
* Used in routes that run two Validator.validate calls (e.g. params + body,
|
|
6
|
-
* or params + query). Each call overwrites req.validated, so values from
|
|
7
|
-
* the first validator must be captured before the second runs.
|
|
8
|
-
*
|
|
9
|
-
* Pattern:
|
|
10
|
-
* Validator.validate(paramsSchema, "params"), // req.validated = { id }
|
|
11
|
-
* captureValidated("id", "_capturedId"), // stashes id
|
|
12
|
-
* Validator.validate(bodySchema, "body"), // req.validated = { role }
|
|
13
|
-
* mergeValidated("id", "_capturedId"), // req.validated = { id, role }
|
|
14
|
-
*
|
|
15
|
-
* The cast through unknown is required — TypeScript won't allow a direct
|
|
16
|
-
* cast from Request to Record<string, unknown> because they don't overlap
|
|
17
|
-
* enough. Casting to unknown first tells TypeScript we know what we're doing.
|
|
18
|
-
*/
|
|
19
|
-
export declare const captureValidated: (key: string, tempKey: string) => (req: Request, _res: Response, next: NextFunction) => void;
|
|
20
|
-
export declare const mergeValidated: (key: string, tempKey: string) => (req: Request, _res: Response, next: NextFunction) => void;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mergeValidated = exports.captureValidated = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* VALIDATED MERGE HELPERS
|
|
6
|
-
* ─────────────────────────
|
|
7
|
-
* Used in routes that run two Validator.validate calls (e.g. params + body,
|
|
8
|
-
* or params + query). Each call overwrites req.validated, so values from
|
|
9
|
-
* the first validator must be captured before the second runs.
|
|
10
|
-
*
|
|
11
|
-
* Pattern:
|
|
12
|
-
* Validator.validate(paramsSchema, "params"), // req.validated = { id }
|
|
13
|
-
* captureValidated("id", "_capturedId"), // stashes id
|
|
14
|
-
* Validator.validate(bodySchema, "body"), // req.validated = { role }
|
|
15
|
-
* mergeValidated("id", "_capturedId"), // req.validated = { id, role }
|
|
16
|
-
*
|
|
17
|
-
* The cast through unknown is required — TypeScript won't allow a direct
|
|
18
|
-
* cast from Request to Record<string, unknown> because they don't overlap
|
|
19
|
-
* enough. Casting to unknown first tells TypeScript we know what we're doing.
|
|
20
|
-
*/
|
|
21
|
-
const captureValidated = (key, tempKey) => (req, _res, next) => {
|
|
22
|
-
req[tempKey] = req.validated[key];
|
|
23
|
-
next();
|
|
24
|
-
};
|
|
25
|
-
exports.captureValidated = captureValidated;
|
|
26
|
-
const mergeValidated = (key, tempKey) => (req, _res, next) => {
|
|
27
|
-
req.validated = {
|
|
28
|
-
...req.validated,
|
|
29
|
-
[key]: req[tempKey],
|
|
30
|
-
};
|
|
31
|
-
next();
|
|
32
|
-
};
|
|
33
|
-
exports.mergeValidated = mergeValidated;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { InternalJwtService } from "../internal";
|
|
3
|
-
export declare class VerifyInternalJwtMiddleware {
|
|
4
|
-
private readonly internalJwt;
|
|
5
|
-
constructor(internalJwt: InternalJwtService);
|
|
6
|
-
handle: (req: Request, _res: Response, next: NextFunction) => Promise<void>;
|
|
7
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VerifyInternalJwtMiddleware = void 0;
|
|
4
|
-
const errors_1 = require("../errors"); // Shared errors
|
|
5
|
-
class VerifyInternalJwtMiddleware {
|
|
6
|
-
constructor(internalJwt) {
|
|
7
|
-
this.internalJwt = internalJwt;
|
|
8
|
-
this.handle = async (req, _res, next) => {
|
|
9
|
-
const raw = req.headers["x-internal-token"]; // Standardize header name
|
|
10
|
-
if (!raw || typeof raw !== "string") {
|
|
11
|
-
return next(new errors_1.UnauthorizedError("Missing internal token"));
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
const payload = await this.internalJwt.verify(raw);
|
|
15
|
-
// Populate standardized internalAuth (from your shared .d.ts)
|
|
16
|
-
req.internalAuth = payload;
|
|
17
|
-
next();
|
|
18
|
-
}
|
|
19
|
-
catch (err) {
|
|
20
|
-
next(new errors_1.UnauthorizedError("Invalid internal token"));
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
exports.VerifyInternalJwtMiddleware = VerifyInternalJwtMiddleware;
|
package/dist/security/guard.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AccountRole, GlobalPermission, OrganizationRole, OrgPermission } from "../enums";
|
|
2
|
-
import { AccessContext } from "../types";
|
|
3
|
-
export declare const globalRolePermissions: Record<AccountRole, readonly GlobalPermission[]>;
|
|
4
|
-
export declare const orgRolePermissions: Record<OrganizationRole, readonly OrgPermission[]>;
|
|
5
|
-
/**
|
|
6
|
-
* Updated isAllowed
|
|
7
|
-
* Since memberships are no longer in the JWT, this function
|
|
8
|
-
* primarily validates Global permissions.
|
|
9
|
-
*/
|
|
10
|
-
export declare const isAllowed: (ctx: AccessContext, permission: GlobalPermission | OrgPermission) => boolean;
|
package/dist/security/guard.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAllowed = exports.orgRolePermissions = exports.globalRolePermissions = void 0;
|
|
4
|
-
const enums_1 = require("../enums");
|
|
5
|
-
exports.globalRolePermissions = {
|
|
6
|
-
[enums_1.AccountRole.SUPERADMIN]: Object.values(enums_1.GlobalPermission), // Superadmin gets everything
|
|
7
|
-
[enums_1.AccountRole.ADMIN]: [
|
|
8
|
-
enums_1.GlobalPermission.MANAGE_USERS,
|
|
9
|
-
enums_1.GlobalPermission.SUPPORT_ACTIONS,
|
|
10
|
-
enums_1.GlobalPermission.VIEW_SYSTEM_LOGS
|
|
11
|
-
],
|
|
12
|
-
[enums_1.AccountRole.SUPPORT]: [enums_1.GlobalPermission.SUPPORT_ACTIONS],
|
|
13
|
-
[enums_1.AccountRole.MODERATOR]: [enums_1.GlobalPermission.MODERATE_CONTENT],
|
|
14
|
-
[enums_1.AccountRole.USER]: []
|
|
15
|
-
};
|
|
16
|
-
// We keep this here as a reference for local service lookups
|
|
17
|
-
exports.orgRolePermissions = {
|
|
18
|
-
[enums_1.OrganizationRole.OWNER]: [enums_1.OrgPermission.MANAGE_ORG, enums_1.OrgPermission.MANAGE_MEMBERS],
|
|
19
|
-
[enums_1.OrganizationRole.ADMIN]: [enums_1.OrgPermission.MANAGE_MEMBERS],
|
|
20
|
-
[enums_1.OrganizationRole.EDITOR]: [],
|
|
21
|
-
[enums_1.OrganizationRole.VIEWER]: [],
|
|
22
|
-
};
|
|
23
|
-
const canPerformGlobalPermission = (role, permission) => {
|
|
24
|
-
return exports.globalRolePermissions[role]?.includes(permission) ?? false;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Updated isAllowed
|
|
28
|
-
* Since memberships are no longer in the JWT, this function
|
|
29
|
-
* primarily validates Global permissions.
|
|
30
|
-
*/
|
|
31
|
-
const isAllowed = (ctx, permission) => {
|
|
32
|
-
// 1. Check if it's a Global Permission (Handled via JWT role)
|
|
33
|
-
if (Object.values(enums_1.GlobalPermission).includes(permission)) {
|
|
34
|
-
return canPerformGlobalPermission(ctx.accountRole, permission);
|
|
35
|
-
}
|
|
36
|
-
// 2. Org Permissions are now handled by specific "Tenant Middleware"
|
|
37
|
-
// within the microservices because they require a DB check.
|
|
38
|
-
return false;
|
|
39
|
-
};
|
|
40
|
-
exports.isAllowed = isAllowed;
|
package/dist/security/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./guard";
|
package/dist/types/express.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import "express-serve-static-core";
|
|
2
|
-
import { JWTPayload } from "jose";
|
|
3
|
-
import { AccountRole } from "../enums";
|
|
4
|
-
export interface AccessContext {
|
|
5
|
-
accountId: string;
|
|
6
|
-
accountRole: AccountRole;
|
|
7
|
-
}
|
|
8
|
-
export interface InternalJwtPayload extends JWTPayload {
|
|
9
|
-
jti: string;
|
|
10
|
-
accountId?: string;
|
|
11
|
-
accountRole?: string;
|
|
12
|
-
isMachine?: boolean;
|
|
13
|
-
}
|
|
14
|
-
declare module "express-serve-static-core" {
|
|
15
|
-
interface Request {
|
|
16
|
-
id: string;
|
|
17
|
-
validated?: any;
|
|
18
|
-
accessContext?: AccessContext;
|
|
19
|
-
internalAuth?: InternalJwtPayload;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export {};
|
package/dist/types/express.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DATE SERIALIZATION UTILS (@discover-cloud/shared)
|
|
3
|
-
* ─────────────────────────────────────────────────────
|
|
4
|
-
* Converts Date objects to ISO 8601 strings for HTTP responses.
|
|
5
|
-
* Use these in toDto() mappers — never call .toISOString() inline.
|
|
6
|
-
*
|
|
7
|
-
* Why centralise this?
|
|
8
|
-
* DTOs cross HTTP boundaries where JSON has no Date type. Domain models
|
|
9
|
-
* keep Date objects internally; these helpers handle the conversion at
|
|
10
|
-
* the boundary in a single consistent place. If the serialisation format
|
|
11
|
-
* ever needs to change (e.g. add milliseconds truncation, force UTC
|
|
12
|
-
* suffix), there is one place to update.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* toIso
|
|
16
|
-
* Converts a Date to an ISO 8601 string.
|
|
17
|
-
* Use for non-nullable timestamp fields in DTOs.
|
|
18
|
-
*/
|
|
19
|
-
export declare const toIso: (date: Date) => string;
|
|
20
|
-
/**
|
|
21
|
-
* toIsoOrNull
|
|
22
|
-
* Converts a Date to an ISO 8601 string, or returns null if the value
|
|
23
|
-
* is absent. Use for nullable timestamp fields (e.g. revokedAt, deletedAt).
|
|
24
|
-
*/
|
|
25
|
-
export declare const toIsoOrNull: (date: Date | null | undefined) => string | null;
|
package/dist/utils/date.utils.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* DATE SERIALIZATION UTILS (@discover-cloud/shared)
|
|
4
|
-
* ─────────────────────────────────────────────────────
|
|
5
|
-
* Converts Date objects to ISO 8601 strings for HTTP responses.
|
|
6
|
-
* Use these in toDto() mappers — never call .toISOString() inline.
|
|
7
|
-
*
|
|
8
|
-
* Why centralise this?
|
|
9
|
-
* DTOs cross HTTP boundaries where JSON has no Date type. Domain models
|
|
10
|
-
* keep Date objects internally; these helpers handle the conversion at
|
|
11
|
-
* the boundary in a single consistent place. If the serialisation format
|
|
12
|
-
* ever needs to change (e.g. add milliseconds truncation, force UTC
|
|
13
|
-
* suffix), there is one place to update.
|
|
14
|
-
*/
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.toIsoOrNull = exports.toIso = void 0;
|
|
17
|
-
/**
|
|
18
|
-
* toIso
|
|
19
|
-
* Converts a Date to an ISO 8601 string.
|
|
20
|
-
* Use for non-nullable timestamp fields in DTOs.
|
|
21
|
-
*/
|
|
22
|
-
const toIso = (date) => date.toISOString();
|
|
23
|
-
exports.toIso = toIso;
|
|
24
|
-
/**
|
|
25
|
-
* toIsoOrNull
|
|
26
|
-
* Converts a Date to an ISO 8601 string, or returns null if the value
|
|
27
|
-
* is absent. Use for nullable timestamp fields (e.g. revokedAt, deletedAt).
|
|
28
|
-
*/
|
|
29
|
-
const toIsoOrNull = (date) => date ? date.toISOString() : null;
|
|
30
|
-
exports.toIsoOrNull = toIsoOrNull;
|
package/dist/utils/env.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ENVIRONMENT HELPERS (@discover-cloud/shared)
|
|
3
|
-
* ───────────────────────────────────────────────
|
|
4
|
-
* Typed accessors for process.env values.
|
|
5
|
-
*
|
|
6
|
-
* Why not read process.env directly?
|
|
7
|
-
* - process.env values are always string | undefined. Reading them inline
|
|
8
|
-
* forces every callsite to handle undefined or cast — this pushes that
|
|
9
|
-
* contract to one place.
|
|
10
|
-
* - getEnv() fails fast at startup (before serving any traffic) if a
|
|
11
|
-
* required variable is absent, surfacing misconfiguration immediately
|
|
12
|
-
* rather than at runtime inside a request handler.
|
|
13
|
-
* - Centralised access makes it straightforward to add validation, type
|
|
14
|
-
* coercion, or secret-redaction logic later without touching callsites.
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* // Required — throws at startup if missing
|
|
18
|
-
* const dbUrl = getEnv("DATABASE_URL");
|
|
19
|
-
* const jwtSecret = getEnv("JWT_SECRET");
|
|
20
|
-
*
|
|
21
|
-
* // Optional — returns undefined (or a typed default) when absent
|
|
22
|
-
* const logLevel = getEnvOptional("LOG_LEVEL") ?? "info";
|
|
23
|
-
* const port = Number(getEnvOptional("PORT") ?? "3000");
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* getEnv
|
|
27
|
-
* Returns the value of a required environment variable.
|
|
28
|
-
* Throws at call time (typically during service startup) if the variable
|
|
29
|
-
* is absent or empty — this is intentional: missing required config should
|
|
30
|
-
* crash the process before it begins serving traffic.
|
|
31
|
-
*
|
|
32
|
-
* Empty string ("") is treated as missing because it is almost always an
|
|
33
|
-
* accidental misconfiguration (e.g. `SECRET=` with no value in a .env file).
|
|
34
|
-
*/
|
|
35
|
-
export declare function getEnv(name: string): string;
|
|
36
|
-
/**
|
|
37
|
-
* getEnvOptional
|
|
38
|
-
* Returns the value of an optional environment variable, or undefined
|
|
39
|
-
* if it is absent or empty. Use with a nullish coalescing default:
|
|
40
|
-
*
|
|
41
|
-
* const logLevel = getEnvOptional("LOG_LEVEL") ?? "info";
|
|
42
|
-
*
|
|
43
|
-
* Returns undefined (not empty string) so callers can safely use `??`
|
|
44
|
-
* and `||` without needing to guard against empty strings separately.
|
|
45
|
-
*/
|
|
46
|
-
export declare function getEnvOptional(name: string): string | undefined;
|
package/dist/utils/env.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* ENVIRONMENT HELPERS (@discover-cloud/shared)
|
|
4
|
-
* ───────────────────────────────────────────────
|
|
5
|
-
* Typed accessors for process.env values.
|
|
6
|
-
*
|
|
7
|
-
* Why not read process.env directly?
|
|
8
|
-
* - process.env values are always string | undefined. Reading them inline
|
|
9
|
-
* forces every callsite to handle undefined or cast — this pushes that
|
|
10
|
-
* contract to one place.
|
|
11
|
-
* - getEnv() fails fast at startup (before serving any traffic) if a
|
|
12
|
-
* required variable is absent, surfacing misconfiguration immediately
|
|
13
|
-
* rather than at runtime inside a request handler.
|
|
14
|
-
* - Centralised access makes it straightforward to add validation, type
|
|
15
|
-
* coercion, or secret-redaction logic later without touching callsites.
|
|
16
|
-
*
|
|
17
|
-
* Usage:
|
|
18
|
-
* // Required — throws at startup if missing
|
|
19
|
-
* const dbUrl = getEnv("DATABASE_URL");
|
|
20
|
-
* const jwtSecret = getEnv("JWT_SECRET");
|
|
21
|
-
*
|
|
22
|
-
* // Optional — returns undefined (or a typed default) when absent
|
|
23
|
-
* const logLevel = getEnvOptional("LOG_LEVEL") ?? "info";
|
|
24
|
-
* const port = Number(getEnvOptional("PORT") ?? "3000");
|
|
25
|
-
*/
|
|
26
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
-
exports.getEnv = getEnv;
|
|
28
|
-
exports.getEnvOptional = getEnvOptional;
|
|
29
|
-
/**
|
|
30
|
-
* getEnv
|
|
31
|
-
* Returns the value of a required environment variable.
|
|
32
|
-
* Throws at call time (typically during service startup) if the variable
|
|
33
|
-
* is absent or empty — this is intentional: missing required config should
|
|
34
|
-
* crash the process before it begins serving traffic.
|
|
35
|
-
*
|
|
36
|
-
* Empty string ("") is treated as missing because it is almost always an
|
|
37
|
-
* accidental misconfiguration (e.g. `SECRET=` with no value in a .env file).
|
|
38
|
-
*/
|
|
39
|
-
function getEnv(name) {
|
|
40
|
-
const value = process.env[name];
|
|
41
|
-
if (!value) {
|
|
42
|
-
throw new Error(`Missing required environment variable: ${name}. ` +
|
|
43
|
-
`Ensure it is set in your .env file or deployment environment.`);
|
|
44
|
-
}
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* getEnvOptional
|
|
49
|
-
* Returns the value of an optional environment variable, or undefined
|
|
50
|
-
* if it is absent or empty. Use with a nullish coalescing default:
|
|
51
|
-
*
|
|
52
|
-
* const logLevel = getEnvOptional("LOG_LEVEL") ?? "info";
|
|
53
|
-
*
|
|
54
|
-
* Returns undefined (not empty string) so callers can safely use `??`
|
|
55
|
-
* and `||` without needing to guard against empty strings separately.
|
|
56
|
-
*/
|
|
57
|
-
function getEnvOptional(name) {
|
|
58
|
-
const value = process.env[name];
|
|
59
|
-
// Normalise empty string to undefined — same convention as getEnv.
|
|
60
|
-
return value === "" ? undefined : value;
|
|
61
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ENVIRONMENT HELPERS (@discover-cloud/shared)
|
|
3
|
-
* ───────────────────────────────────────────────
|
|
4
|
-
* Typed accessors for process.env values.
|
|
5
|
-
*
|
|
6
|
-
* Why not read process.env directly?
|
|
7
|
-
* - process.env values are always string | undefined. Reading them inline
|
|
8
|
-
* forces every callsite to handle undefined or cast — this pushes that
|
|
9
|
-
* contract to one place.
|
|
10
|
-
* - getEnv() fails fast at startup (before serving any traffic) if a
|
|
11
|
-
* required variable is absent, surfacing misconfiguration immediately
|
|
12
|
-
* rather than at runtime inside a request handler.
|
|
13
|
-
* - Centralised access makes it straightforward to add validation, type
|
|
14
|
-
* coercion, or secret-redaction logic later without touching callsites.
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* // Required — throws at startup if missing
|
|
18
|
-
* const dbUrl = getEnv("DATABASE_URL");
|
|
19
|
-
* const jwtSecret = getEnv("JWT_SECRET");
|
|
20
|
-
*
|
|
21
|
-
* // Optional — returns undefined (or a typed default) when absent
|
|
22
|
-
* const logLevel = getEnvOptional("LOG_LEVEL") ?? "info";
|
|
23
|
-
* const port = Number(getEnvOptional("PORT") ?? "3000");
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* getEnv
|
|
27
|
-
* Returns the value of a required environment variable.
|
|
28
|
-
* Throws at call time (typically during service startup) if the variable
|
|
29
|
-
* is absent or empty — this is intentional: missing required config should
|
|
30
|
-
* crash the process before it begins serving traffic.
|
|
31
|
-
*
|
|
32
|
-
* Empty string ("") is treated as missing because it is almost always an
|
|
33
|
-
* accidental misconfiguration (e.g. `SECRET=` with no value in a .env file).
|
|
34
|
-
*/
|
|
35
|
-
export declare function getEnv(name: string): string;
|
|
36
|
-
/**
|
|
37
|
-
* getEnvOptional
|
|
38
|
-
* Returns the value of an optional environment variable, or undefined
|
|
39
|
-
* if it is absent or empty. Use with a nullish coalescing default:
|
|
40
|
-
*
|
|
41
|
-
* const logLevel = getEnvOptional("LOG_LEVEL") ?? "info";
|
|
42
|
-
*
|
|
43
|
-
* Returns undefined (not empty string) so callers can safely use `??`
|
|
44
|
-
* and `||` without needing to guard against empty strings separately.
|
|
45
|
-
*/
|
|
46
|
-
export declare function getEnvOptional(name: string): string | undefined;
|