@discover-cloud/shared 1.3.0 → 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/permission-cache.service.js +27 -11
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +17 -0
- 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 +1 -1
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +17 -0
- 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 +16 -1
- package/dist/dtos/cloud-service.dto.d.ts +9 -3
- package/dist/dtos/index.d.ts +6 -5
- package/dist/dtos/index.js +1 -0
- package/dist/dtos/insights-service.dto.d.ts +174 -13
- package/dist/dtos/response.dto.d.ts +6 -0
- package/dist/dtos/user-service.dto.d.ts +52 -7
- package/dist/enums/domain.enums.d.ts +52 -26
- package/dist/enums/domain.enums.js +64 -40
- package/dist/enums/index.d.ts +2 -2
- package/dist/errors/app-error.js +1 -1
- package/dist/errors/http-errors.d.ts +1 -1
- package/dist/errors/http-errors.js +26 -26
- package/dist/errors/index.d.ts +2 -2
- package/dist/http/index.d.ts +2 -2
- package/dist/http/request-context.d.ts +2 -2
- package/dist/http/request-context.js +5 -5
- package/dist/http/service-client.d.ts +2 -2
- package/dist/http/service-client.js +2 -2
- package/dist/index.d.ts +11 -10
- package/dist/index.js +1 -0
- package/dist/jwt/index.d.ts +3 -3
- package/dist/jwt/machine-jwt-verifier.d.ts +1 -1
- package/dist/jwt/machine-jwt-verifier.js +13 -14
- package/dist/jwt/machine-token-client.d.ts +1 -1
- package/dist/jwt/machine-token-client.js +8 -8
- package/dist/jwt/user-jwt-verifier.d.ts +1 -1
- package/dist/jwt/user-jwt-verifier.js +17 -16
- package/dist/messaging/audit.publisher.d.ts +14 -0
- package/dist/messaging/audit.publisher.js +49 -0
- package/dist/messaging/index.d.ts +1 -0
- package/dist/messaging/index.js +1 -0
- package/dist/messaging/rabbitmq.client.d.ts +5 -3
- package/dist/messaging/rabbitmq.client.js +17 -11
- package/dist/middleware/error-handler.middleware.d.ts +2 -2
- package/dist/middleware/error-handler.middleware.js +10 -10
- package/dist/middleware/index.d.ts +10 -10
- package/dist/middleware/request-context.middleware.d.ts +1 -12
- package/dist/middleware/request-context.middleware.js +17 -42
- package/dist/middleware/require-auth.middleware.d.ts +3 -19
- package/dist/middleware/require-auth.middleware.js +17 -26
- package/dist/middleware/require-machine.middleware.d.ts +2 -27
- package/dist/middleware/require-machine.middleware.js +30 -25
- package/dist/middleware/require-org-permission-from-body.middleware.js +1 -2
- package/dist/middleware/require-org-permission.middleware.d.ts +3 -3
- package/dist/middleware/require-org-permission.middleware.js +28 -28
- package/dist/middleware/require-platform-permission.middleware.d.ts +3 -3
- package/dist/middleware/require-platform-permission.middleware.js +10 -10
- package/dist/middleware/require-user.middleware.d.ts +1 -1
- package/dist/middleware/require-user.middleware.js +2 -2
- package/dist/middleware/require-workspace-permission.middleware.d.ts +3 -3
- package/dist/middleware/require-workspace-permission.middleware.js +25 -25
- package/dist/middleware/validate.middleware.d.ts +3 -3
- package/dist/middleware/validate.middleware.js +1 -1
- package/dist/types/audit.types.d.ts +39 -0
- package/dist/types/audit.types.js +30 -0
- package/dist/types/express.types.d.ts +14 -7
- package/dist/types/express.types.js +10 -10
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/env.util.js +1 -1
- package/dist/utils/index.d.ts +5 -5
- package/dist/utils/logger.util.d.ts +5 -0
- package/dist/utils/logger.util.js +46 -8
- 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 +1 -1
- package/dist/utils/url-safety.util.js +4 -1
- package/package.json +1 -1
|
@@ -35,12 +35,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.RabbitMQClient = void 0;
|
|
37
37
|
const amqplib = __importStar(require("amqplib"));
|
|
38
|
+
const logger_util_1 = require("../utils/logger.util");
|
|
38
39
|
class RabbitMQClient {
|
|
39
|
-
constructor(url) {
|
|
40
|
+
constructor(url, logger = logger_util_1.consoleLogger) {
|
|
40
41
|
this.connection = null;
|
|
41
42
|
this.channel = null;
|
|
42
43
|
this.exchangeName = 'discovercloud.topic';
|
|
43
44
|
this.url = url;
|
|
45
|
+
this.logger = logger;
|
|
44
46
|
}
|
|
45
47
|
async connect() {
|
|
46
48
|
if (this.connection && this.channel) {
|
|
@@ -49,21 +51,23 @@ class RabbitMQClient {
|
|
|
49
51
|
try {
|
|
50
52
|
this.connection = await amqplib.connect(this.url);
|
|
51
53
|
this.connection.on('error', (err) => {
|
|
52
|
-
|
|
54
|
+
this.logger.error(err, 'RabbitMQ Connection Error');
|
|
53
55
|
});
|
|
54
56
|
this.connection.on('close', () => {
|
|
55
|
-
|
|
57
|
+
this.logger.warn('RabbitMQ Connection Closed. Reconnecting...');
|
|
56
58
|
this.connection = null;
|
|
57
59
|
this.channel = null;
|
|
58
60
|
setTimeout(() => this.connect(), 5000);
|
|
59
61
|
});
|
|
60
62
|
this.channel = await this.connection.createChannel();
|
|
61
63
|
// Ensure the main topic exchange exists
|
|
62
|
-
await this.channel.assertExchange(this.exchangeName, 'topic', {
|
|
63
|
-
|
|
64
|
+
await this.channel.assertExchange(this.exchangeName, 'topic', {
|
|
65
|
+
durable: true,
|
|
66
|
+
});
|
|
67
|
+
this.logger.info('✅ Connected to RabbitMQ');
|
|
64
68
|
}
|
|
65
69
|
catch (error) {
|
|
66
|
-
|
|
70
|
+
this.logger.error(error, '❌ Failed to connect to RabbitMQ');
|
|
67
71
|
setTimeout(() => this.connect(), 5000);
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -104,15 +108,17 @@ class RabbitMQClient {
|
|
|
104
108
|
try {
|
|
105
109
|
const payload = JSON.parse(msg.content.toString());
|
|
106
110
|
await handler(payload);
|
|
107
|
-
|
|
111
|
+
// Fix: Use optional chaining instead of non-null assertion (!)
|
|
112
|
+
this.channel?.ack(msg);
|
|
108
113
|
}
|
|
109
114
|
catch (error) {
|
|
110
|
-
|
|
115
|
+
this.logger.error(error, `Error processing message from ${queueName}`);
|
|
116
|
+
// Fix: Use optional chaining instead of non-null assertion (!)
|
|
111
117
|
// Nack the message and don't requeue, so it goes to the DLX
|
|
112
|
-
this.channel
|
|
118
|
+
this.channel?.nack(msg, false, false);
|
|
113
119
|
}
|
|
114
120
|
});
|
|
115
|
-
|
|
121
|
+
this.logger.info(`📡 Subscribed to queue [${queueName}] with routing key [${routingKey}]`);
|
|
116
122
|
}
|
|
117
123
|
async close() {
|
|
118
124
|
if (this.channel) {
|
|
@@ -121,7 +127,7 @@ class RabbitMQClient {
|
|
|
121
127
|
if (this.connection) {
|
|
122
128
|
await this.connection.close();
|
|
123
129
|
}
|
|
124
|
-
|
|
130
|
+
this.logger.info('RabbitMQ connection closed.');
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
exports.RabbitMQClient = RabbitMQClient;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @file error-handler.middleware.ts
|
|
3
3
|
* @description Central Express error handler. Register as the final middleware.
|
|
4
4
|
*/
|
|
5
|
-
import { ErrorRequestHandler } from
|
|
6
|
-
import { ILogger } from
|
|
5
|
+
import { ErrorRequestHandler } from 'express';
|
|
6
|
+
import { ILogger } from '../utils';
|
|
7
7
|
/**
|
|
8
8
|
* Creates global Express error middleware.
|
|
9
9
|
*/
|
|
@@ -13,9 +13,9 @@ const utils_1 = require("../utils");
|
|
|
13
13
|
*/
|
|
14
14
|
function isMalformedJsonError(err) {
|
|
15
15
|
return (err instanceof SyntaxError &&
|
|
16
|
-
|
|
17
|
-
typeof err.status ===
|
|
18
|
-
|
|
16
|
+
'status' in err &&
|
|
17
|
+
typeof err.status === 'number' &&
|
|
18
|
+
'body' in err);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Converts any thrown value into Error.
|
|
@@ -24,7 +24,7 @@ function normalizeError(err) {
|
|
|
24
24
|
if (err instanceof Error) {
|
|
25
25
|
return err;
|
|
26
26
|
}
|
|
27
|
-
if (typeof err ===
|
|
27
|
+
if (typeof err === 'string') {
|
|
28
28
|
return new Error(err);
|
|
29
29
|
}
|
|
30
30
|
return new Error(JSON.stringify(err));
|
|
@@ -40,16 +40,16 @@ function createGlobalErrorHandler(logger = utils_1.noopLogger) {
|
|
|
40
40
|
* Malformed JSON
|
|
41
41
|
*/
|
|
42
42
|
if (isMalformedJsonError(err) && err.status === 400) {
|
|
43
|
-
logger.warn({ requestId, err: error },
|
|
44
|
-
(0, utils_1.failure)(res, req,
|
|
43
|
+
logger.warn({ requestId, err: error }, 'Malformed JSON payload');
|
|
44
|
+
(0, utils_1.failure)(res, req, 'Invalid JSON payload', 'BAD_REQUEST', 400);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* Validation failures
|
|
49
49
|
*/
|
|
50
50
|
if (err instanceof zod_1.ZodError) {
|
|
51
|
-
logger.warn({ requestId, err: error },
|
|
52
|
-
(0, utils_1.failure)(res, req,
|
|
51
|
+
logger.warn({ requestId, err: error }, 'Request validation failed');
|
|
52
|
+
(0, utils_1.failure)(res, req, 'Validation failed', 'VALIDATION_ERROR', 400, err.flatten());
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
@@ -73,7 +73,7 @@ function createGlobalErrorHandler(logger = utils_1.noopLogger) {
|
|
|
73
73
|
/**
|
|
74
74
|
* Unknown crashes
|
|
75
75
|
*/
|
|
76
|
-
logger.error({ requestId, err: error },
|
|
77
|
-
(0, utils_1.failure)(res, req,
|
|
76
|
+
logger.error({ requestId, err: error }, 'Unhandled exception');
|
|
77
|
+
(0, utils_1.failure)(res, req, 'Internal Server Error', 'INTERNAL_SERVER_ERROR', 500);
|
|
78
78
|
};
|
|
79
79
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
1
|
+
export * from './error-handler.middleware';
|
|
2
|
+
export * from './validate.middleware';
|
|
3
|
+
export * from './request-context.middleware';
|
|
4
|
+
export * from './require-machine.middleware';
|
|
5
|
+
export * from './require-auth.middleware';
|
|
6
|
+
export * from './require-user.middleware';
|
|
7
|
+
export * from './require-org-permission.middleware';
|
|
8
|
+
export * from './require-workspace-permission.middleware';
|
|
9
|
+
export * from './require-platform-permission.middleware';
|
|
10
|
+
export * from './require-org-permission-from-body.middleware';
|
|
@@ -2,16 +2,5 @@
|
|
|
2
2
|
* @file request-context.middleware.ts
|
|
3
3
|
* @description Express middleware to assign request tracing and audit metadata context.
|
|
4
4
|
*/
|
|
5
|
-
import { Request, Response, NextFunction } from
|
|
6
|
-
/**
|
|
7
|
-
* Attaches request metadata used for:
|
|
8
|
-
* - distributed tracing
|
|
9
|
-
* - audit logs
|
|
10
|
-
* - debugging correlation
|
|
11
|
-
*
|
|
12
|
-
* @param {Request} req - Express request object.
|
|
13
|
-
* @param {Response} res - Express response object.
|
|
14
|
-
* @param {NextFunction} next - Express next middleware function.
|
|
15
|
-
* @returns {void}
|
|
16
|
-
*/
|
|
5
|
+
import { Request, Response, NextFunction } from 'express';
|
|
17
6
|
export declare const requestContext: (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -6,68 +6,43 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.requestContext = void 0;
|
|
8
8
|
const crypto_1 = require("crypto");
|
|
9
|
-
|
|
10
|
-
* Normalizes IPv4-mapped IPv6 addresses.
|
|
11
|
-
*
|
|
12
|
-
* @param {string | null} ip - Raw IP.
|
|
13
|
-
* @returns {string | null} Normalized IP.
|
|
14
|
-
*/
|
|
9
|
+
const request_context_storage_1 = require("../context/request-context.storage");
|
|
15
10
|
function normalizeIp(ip) {
|
|
16
|
-
if (!ip)
|
|
11
|
+
if (!ip)
|
|
17
12
|
return null;
|
|
13
|
+
if (ip === '::1') {
|
|
14
|
+
return '127.0.0.1';
|
|
18
15
|
}
|
|
19
|
-
if (ip
|
|
20
|
-
return "127.0.0.1";
|
|
21
|
-
}
|
|
22
|
-
if (ip.startsWith("::ffff:")) {
|
|
16
|
+
if (ip.startsWith('::ffff:')) {
|
|
23
17
|
return ip.substring(7);
|
|
24
18
|
}
|
|
25
19
|
return ip;
|
|
26
20
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Resolves original client IP forwarded by the API Gateway.
|
|
29
|
-
*
|
|
30
|
-
* Priority:
|
|
31
|
-
* 1. x-forwarded-for
|
|
32
|
-
* 2. x-real-ip
|
|
33
|
-
* 3. socket fallback (local development only)
|
|
34
|
-
*
|
|
35
|
-
* @param {Request} req - Express request object.
|
|
36
|
-
* @returns {string | null} Client IP address.
|
|
37
|
-
*/
|
|
38
21
|
function resolveClientIp(req) {
|
|
39
|
-
const forwarded = req.headers[
|
|
22
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
40
23
|
if (forwarded) {
|
|
41
24
|
const value = Array.isArray(forwarded) ? forwarded[0] : forwarded;
|
|
42
|
-
return normalizeIp(value.split(
|
|
25
|
+
return normalizeIp(value.split(',')[0].trim());
|
|
43
26
|
}
|
|
44
|
-
const realIp = req.headers[
|
|
27
|
+
const realIp = req.headers['x-real-ip'];
|
|
45
28
|
if (realIp) {
|
|
46
29
|
return normalizeIp(Array.isArray(realIp) ? realIp[0] : realIp);
|
|
47
30
|
}
|
|
48
31
|
return normalizeIp(req.socket.remoteAddress ?? null);
|
|
49
32
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Attaches request metadata used for:
|
|
52
|
-
* - distributed tracing
|
|
53
|
-
* - audit logs
|
|
54
|
-
* - debugging correlation
|
|
55
|
-
*
|
|
56
|
-
* @param {Request} req - Express request object.
|
|
57
|
-
* @param {Response} res - Express response object.
|
|
58
|
-
* @param {NextFunction} next - Express next middleware function.
|
|
59
|
-
* @returns {void}
|
|
60
|
-
*/
|
|
61
33
|
const requestContext = (req, res, next) => {
|
|
62
|
-
const upstream = req.headers[
|
|
34
|
+
const upstream = req.headers['x-request-id'];
|
|
63
35
|
const requestId = Array.isArray(upstream) ? upstream[0] : (upstream ?? (0, crypto_1.randomUUID)());
|
|
64
|
-
|
|
65
|
-
|
|
36
|
+
// Explicitly type this as AuditRequestContext so TS knows it's mutable
|
|
37
|
+
// and allows the `.actor` property to be assigned by the auth middlewares later.
|
|
38
|
+
const context = {
|
|
66
39
|
requestId,
|
|
67
40
|
ipAddress: resolveClientIp(req),
|
|
68
|
-
userAgent: req.headers[
|
|
41
|
+
userAgent: req.headers['user-agent'] ?? null,
|
|
69
42
|
};
|
|
70
|
-
|
|
71
|
-
|
|
43
|
+
req.id = requestId;
|
|
44
|
+
req.requestContext = context;
|
|
45
|
+
res.setHeader('x-request-id', requestId);
|
|
46
|
+
request_context_storage_1.requestContextStorage.run(context, () => next());
|
|
72
47
|
};
|
|
73
48
|
exports.requestContext = requestContext;
|
|
@@ -3,29 +3,13 @@
|
|
|
3
3
|
* @description Authentication boundary middleware. Verifies the internal gateway JWT,
|
|
4
4
|
* builds the auth context, and gates endpoints.
|
|
5
5
|
*/
|
|
6
|
-
import { Request, Response, NextFunction } from
|
|
7
|
-
import { ILogger } from
|
|
8
|
-
import { UserJwtVerifier } from
|
|
9
|
-
/**
|
|
10
|
-
* Middleware handling authentication validation.
|
|
11
|
-
* Extracts the JWT, verifies it via UserJwtVerifier, and attaches the AuthContext.
|
|
12
|
-
*/
|
|
6
|
+
import { Request, Response, NextFunction } from 'express';
|
|
7
|
+
import { ILogger } from '../utils';
|
|
8
|
+
import { UserJwtVerifier } from '../jwt/user-jwt-verifier';
|
|
13
9
|
export declare class RequireAuthMiddleware {
|
|
14
10
|
private readonly verifier;
|
|
15
11
|
private readonly logger;
|
|
16
|
-
/**
|
|
17
|
-
* @param {UserJwtVerifier} verifier - The token verification engine.
|
|
18
|
-
* @param {ILogger} [logger] - Optional logger instance.
|
|
19
|
-
*/
|
|
20
12
|
constructor(verifier: UserJwtVerifier, logger?: ILogger);
|
|
21
|
-
/**
|
|
22
|
-
* Core Express middleware handler for authenticating requests.
|
|
23
|
-
*
|
|
24
|
-
* @param {Request} req - Express request object.
|
|
25
|
-
* @param {Response} res - Express response object.
|
|
26
|
-
* @param {NextFunction} next - Express next function.
|
|
27
|
-
* @returns {Promise<void>}
|
|
28
|
-
*/
|
|
29
13
|
handle: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
30
14
|
private extractBearer;
|
|
31
15
|
private handleJwtError;
|
|
@@ -8,41 +8,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
exports.RequireAuthMiddleware = void 0;
|
|
9
9
|
const jose_1 = require("jose");
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
|
-
|
|
12
|
-
* Middleware handling authentication validation.
|
|
13
|
-
* Extracts the JWT, verifies it via UserJwtVerifier, and attaches the AuthContext.
|
|
14
|
-
*/
|
|
11
|
+
const request_context_storage_1 = require("../context/request-context.storage");
|
|
15
12
|
class RequireAuthMiddleware {
|
|
16
|
-
/**
|
|
17
|
-
* @param {UserJwtVerifier} verifier - The token verification engine.
|
|
18
|
-
* @param {ILogger} [logger] - Optional logger instance.
|
|
19
|
-
*/
|
|
20
13
|
constructor(verifier, logger = utils_1.noopLogger) {
|
|
21
14
|
this.verifier = verifier;
|
|
22
15
|
this.logger = logger;
|
|
23
|
-
/**
|
|
24
|
-
* Core Express middleware handler for authenticating requests.
|
|
25
|
-
*
|
|
26
|
-
* @param {Request} req - Express request object.
|
|
27
|
-
* @param {Response} res - Express response object.
|
|
28
|
-
* @param {NextFunction} next - Express next function.
|
|
29
|
-
* @returns {Promise<void>}
|
|
30
|
-
*/
|
|
31
16
|
this.handle = async (req, res, next) => {
|
|
32
17
|
const token = this.extractBearer(req);
|
|
33
18
|
if (!token) {
|
|
34
|
-
(0, utils_1.failure)(res, req,
|
|
19
|
+
(0, utils_1.failure)(res, req, 'Missing Authorization header', 'UNAUTHORIZED', 401);
|
|
35
20
|
return;
|
|
36
21
|
}
|
|
37
22
|
try {
|
|
38
23
|
const { payload, context } = await this.verifier.buildAccessContext(token);
|
|
39
24
|
req.authPayload = payload;
|
|
40
25
|
req.authContext = context;
|
|
26
|
+
// Attach audit context. Will throw if requestContextStorage is not initialized.
|
|
27
|
+
request_context_storage_1.RequestContextProvider.setActor(context);
|
|
41
28
|
this.logger.debug({
|
|
42
29
|
requestId: req.id,
|
|
43
30
|
identity: context.accountId,
|
|
44
31
|
type: context.type,
|
|
45
|
-
},
|
|
32
|
+
}, 'Request authenticated');
|
|
46
33
|
next();
|
|
47
34
|
}
|
|
48
35
|
catch (err) {
|
|
@@ -55,29 +42,33 @@ class RequireAuthMiddleware {
|
|
|
55
42
|
--------------------------------------------------------------------------- */
|
|
56
43
|
extractBearer(req) {
|
|
57
44
|
const header = req.headers.authorization;
|
|
58
|
-
if (!header)
|
|
45
|
+
if (!header)
|
|
59
46
|
return null;
|
|
60
|
-
}
|
|
61
47
|
const [scheme, token] = header.trim().split(/\s+/);
|
|
62
|
-
if (scheme !==
|
|
48
|
+
if (scheme !== 'Bearer' || !token)
|
|
63
49
|
return null;
|
|
64
|
-
}
|
|
65
50
|
return token;
|
|
66
51
|
}
|
|
67
52
|
handleJwtError(err, req, res) {
|
|
68
53
|
if (err instanceof jose_1.errors.JWTExpired) {
|
|
69
|
-
(0, utils_1.failure)(res, req,
|
|
54
|
+
(0, utils_1.failure)(res, req, 'Your session has expired. Please log in again.', 'TOKEN_EXPIRED', 401);
|
|
70
55
|
return;
|
|
71
56
|
}
|
|
72
57
|
if (err instanceof jose_1.errors.JWTClaimValidationFailed ||
|
|
73
58
|
err instanceof jose_1.errors.JWSSignatureVerificationFailed ||
|
|
74
59
|
err instanceof jose_1.errors.JWSInvalid ||
|
|
75
60
|
err instanceof jose_1.errors.JWTInvalid) {
|
|
76
|
-
(0, utils_1.failure)(res, req,
|
|
61
|
+
(0, utils_1.failure)(res, req, 'Token is invalid or has been tampered with.', 'INVALID_TOKEN', 401);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Catch the specific Developer Error from RequestContextProvider
|
|
65
|
+
if (err instanceof Error && err.message.includes('AuditRequestContext')) {
|
|
66
|
+
this.logger.error({ err, requestId: req.id }, 'CRITICAL: RequireAuthMiddleware failed because requestContextStorage is not initialized. Check your middleware order.');
|
|
67
|
+
(0, utils_1.failure)(res, req, 'Internal server configuration error.', 'INTERNAL_SERVER_ERROR', 500);
|
|
77
68
|
return;
|
|
78
69
|
}
|
|
79
|
-
this.logger.error({ err, requestId: req.id },
|
|
80
|
-
(0, utils_1.failure)(res, req,
|
|
70
|
+
this.logger.error({ err, requestId: req.id }, 'RequireAuthMiddleware: unexpected error during JWT verification');
|
|
71
|
+
(0, utils_1.failure)(res, req, 'Authentication service is temporarily unavailable.', 'SERVICE_UNAVAILABLE', 503);
|
|
81
72
|
}
|
|
82
73
|
}
|
|
83
74
|
exports.RequireAuthMiddleware = RequireAuthMiddleware;
|
|
@@ -3,36 +3,11 @@
|
|
|
3
3
|
* @description Express middleware enforcing auth-service issued machine JWT verification
|
|
4
4
|
* for service-to-service communication endpoints.
|
|
5
5
|
*/
|
|
6
|
-
import { RequestHandler } from
|
|
7
|
-
import { ILogger } from
|
|
8
|
-
/**
|
|
9
|
-
* Configuration options for machine authentication middleware.
|
|
10
|
-
*/
|
|
6
|
+
import { RequestHandler } from 'express';
|
|
7
|
+
import { ILogger } from '../utils/logger.util';
|
|
11
8
|
export interface RequireMachineOptions {
|
|
12
|
-
/**
|
|
13
|
-
* Auth-service JWKS endpoint used to verify machine JWT signatures.
|
|
14
|
-
*/
|
|
15
9
|
authServiceJwksUri?: string;
|
|
16
|
-
/**
|
|
17
|
-
* Service identifiers allowed to access this endpoint.
|
|
18
|
-
*/
|
|
19
10
|
allowedServices?: string[];
|
|
20
|
-
/**
|
|
21
|
-
* Optional structured logger.
|
|
22
|
-
*/
|
|
23
11
|
logger?: ILogger;
|
|
24
12
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Creates middleware requiring a valid machine JWT.
|
|
27
|
-
*
|
|
28
|
-
* Flow:
|
|
29
|
-
* - Extract Bearer token
|
|
30
|
-
* - Verify RS256 signature using auth-service JWKS
|
|
31
|
-
* - Validate machine token claims
|
|
32
|
-
* - Apply service allowlist
|
|
33
|
-
* - Attach MachineContext
|
|
34
|
-
*
|
|
35
|
-
* @param {RequireMachineOptions} [options] - Middleware configuration options.
|
|
36
|
-
* @returns {RequestHandler} Express middleware handler.
|
|
37
|
-
*/
|
|
38
13
|
export declare function requireMachine(options?: RequireMachineOptions): RequestHandler;
|
|
@@ -42,29 +42,20 @@ exports.requireMachine = requireMachine;
|
|
|
42
42
|
const jose = __importStar(require("jose"));
|
|
43
43
|
const logger_util_1 = require("../utils/logger.util");
|
|
44
44
|
const machine_jwt_verifier_1 = require("../jwt/machine-jwt-verifier");
|
|
45
|
-
|
|
46
|
-
* Creates middleware requiring a valid machine JWT.
|
|
47
|
-
*
|
|
48
|
-
* Flow:
|
|
49
|
-
* - Extract Bearer token
|
|
50
|
-
* - Verify RS256 signature using auth-service JWKS
|
|
51
|
-
* - Validate machine token claims
|
|
52
|
-
* - Apply service allowlist
|
|
53
|
-
* - Attach MachineContext
|
|
54
|
-
*
|
|
55
|
-
* @param {RequireMachineOptions} [options] - Middleware configuration options.
|
|
56
|
-
* @returns {RequestHandler} Express middleware handler.
|
|
57
|
-
*/
|
|
45
|
+
const request_context_storage_1 = require("../context/request-context.storage");
|
|
58
46
|
function requireMachine(options = {}) {
|
|
59
47
|
const logger = options.logger ?? logger_util_1.noopLogger;
|
|
60
48
|
const verifier = new machine_jwt_verifier_1.MachineJwtVerifier(options.authServiceJwksUri);
|
|
61
49
|
const { allowedServices } = options;
|
|
62
50
|
return async (req, res, next) => {
|
|
63
51
|
const header = req.headers.authorization;
|
|
64
|
-
if (!header?.startsWith(
|
|
52
|
+
if (!header?.startsWith('Bearer ')) {
|
|
65
53
|
res.status(401).json({
|
|
66
54
|
success: false,
|
|
67
|
-
error: {
|
|
55
|
+
error: {
|
|
56
|
+
code: 'UNAUTHORIZED',
|
|
57
|
+
message: 'Missing Authorization header',
|
|
58
|
+
},
|
|
68
59
|
});
|
|
69
60
|
return;
|
|
70
61
|
}
|
|
@@ -72,31 +63,33 @@ function requireMachine(options = {}) {
|
|
|
72
63
|
try {
|
|
73
64
|
const payload = await verifier.verify(token);
|
|
74
65
|
if (allowedServices && !allowedServices.includes(payload.serviceId)) {
|
|
75
|
-
logger.warn({ requestId: req.id, serviceId: payload.serviceId },
|
|
66
|
+
logger.warn({ requestId: req.id, serviceId: payload.serviceId }, '[requireMachine] Service rejected');
|
|
76
67
|
res.status(403).json({
|
|
77
68
|
success: false,
|
|
78
69
|
error: {
|
|
79
|
-
code:
|
|
80
|
-
message:
|
|
70
|
+
code: 'FORBIDDEN',
|
|
71
|
+
message: 'Service is not permitted to access this endpoint',
|
|
81
72
|
},
|
|
82
73
|
});
|
|
83
74
|
return;
|
|
84
75
|
}
|
|
85
76
|
const context = {
|
|
86
|
-
type:
|
|
77
|
+
type: 'machine',
|
|
87
78
|
serviceId: payload.serviceId,
|
|
88
79
|
scopes: payload.scopes,
|
|
89
80
|
};
|
|
90
81
|
req.authPayload = payload;
|
|
91
82
|
req.authContext = context;
|
|
92
|
-
|
|
83
|
+
// Attach audit context. Will throw if requestContextStorage is not initialized.
|
|
84
|
+
request_context_storage_1.RequestContextProvider.setActor(context);
|
|
85
|
+
logger.debug({ requestId: req.id, serviceId: payload.serviceId, jti: payload.jti }, '[requireMachine] Machine authenticated');
|
|
93
86
|
next();
|
|
94
87
|
}
|
|
95
88
|
catch (err) {
|
|
96
89
|
if (err instanceof jose.errors.JWTExpired) {
|
|
97
90
|
res.status(401).json({
|
|
98
91
|
success: false,
|
|
99
|
-
error: { code:
|
|
92
|
+
error: { code: 'TOKEN_EXPIRED', message: 'Machine token expired' },
|
|
100
93
|
});
|
|
101
94
|
return;
|
|
102
95
|
}
|
|
@@ -106,16 +99,28 @@ function requireMachine(options = {}) {
|
|
|
106
99
|
err instanceof jose.errors.JWTInvalid) {
|
|
107
100
|
res.status(401).json({
|
|
108
101
|
success: false,
|
|
109
|
-
error: { code:
|
|
102
|
+
error: { code: 'INVALID_TOKEN', message: 'Invalid machine token' },
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Catch the specific Developer Error from RequestContextProvider
|
|
107
|
+
if (err instanceof Error && err.message.includes('AuditRequestContext')) {
|
|
108
|
+
logger.error({ err, requestId: req.id }, 'CRITICAL: [requireMachine] failed because requestContextStorage is not initialized. Check middleware order.');
|
|
109
|
+
res.status(500).json({
|
|
110
|
+
success: false,
|
|
111
|
+
error: {
|
|
112
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
113
|
+
message: 'Internal server configuration error',
|
|
114
|
+
},
|
|
110
115
|
});
|
|
111
116
|
return;
|
|
112
117
|
}
|
|
113
|
-
logger.error({ err, requestId: req.id },
|
|
118
|
+
logger.error({ err, requestId: req.id }, '[requireMachine] Machine authentication failure');
|
|
114
119
|
res.status(503).json({
|
|
115
120
|
success: false,
|
|
116
121
|
error: {
|
|
117
|
-
code:
|
|
118
|
-
message:
|
|
122
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
123
|
+
message: 'Machine authentication temporarily unavailable',
|
|
119
124
|
},
|
|
120
125
|
});
|
|
121
126
|
}
|
|
@@ -9,8 +9,7 @@ exports.createRequireOrgPermissionFromBody = void 0;
|
|
|
9
9
|
const createRequireOrgPermissionFromBody = (requireOrgPermission) => {
|
|
10
10
|
const requireOrgPermissionFromBody = (permission) => {
|
|
11
11
|
return async (req, res, next) => {
|
|
12
|
-
const organizationId = req.body?.organizationId ??
|
|
13
|
-
req.validated?.body?.organizationId;
|
|
12
|
+
const organizationId = req.body?.organizationId ?? req.validated?.body?.organizationId;
|
|
14
13
|
if (!organizationId) {
|
|
15
14
|
return next();
|
|
16
15
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* @file require-org-permission.middleware.ts
|
|
3
3
|
* @description Express middleware generator to enforce organization-scoped permission checks.
|
|
4
4
|
*/
|
|
5
|
-
import { RequestHandler } from
|
|
6
|
-
import { Permission } from
|
|
7
|
-
import { PermissionCacheService } from
|
|
5
|
+
import { RequestHandler } from 'express';
|
|
6
|
+
import { Permission } from '../enums';
|
|
7
|
+
import { PermissionCacheService } from '../authorization/permission-cache.service';
|
|
8
8
|
/**
|
|
9
9
|
* Creates requireOrgPermission middleware factory.
|
|
10
10
|
*
|