aip-master-node-sumit 1.0.2 → 1.0.3
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/index.js +22 -13
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
const axios = require('axios');
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// 🛡️ THE FIX: Smart URL Cleaner ensures the base API URL is exactly right
|
|
4
|
+
const getSafeMasterUrl = (url) => {
|
|
5
|
+
if (!url) return 'http://localhost:8080/api/v1';
|
|
6
|
+
let clean = url.replace(/\/+$/, '');
|
|
7
|
+
if (clean.endsWith('/iam')) {
|
|
8
|
+
clean = clean.substring(0, clean.length - 4);
|
|
9
|
+
}
|
|
10
|
+
return clean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const AIP_MASTER_API = getSafeMasterUrl(process.env.AIP_MASTER_URL);
|
|
3
14
|
const API_KEY = process.env.AIP_MASTER_API_KEY;
|
|
4
15
|
|
|
5
|
-
// 🛡️ The Heartbeat System.
|
|
6
|
-
// This continuously pings the Go Server every 5 minutes
|
|
7
|
-
// to keep the dashboard "Live" without needing to restart the Express server!
|
|
8
16
|
if (API_KEY) {
|
|
9
17
|
const sendHeartbeat = async () => {
|
|
10
18
|
try {
|
|
11
19
|
await axios.post(`${AIP_MASTER_API}/iam/ping`, { module: 'middleware' }, {
|
|
12
20
|
headers: { 'Authorization': `Bearer ${API_KEY}` },
|
|
13
|
-
timeout: 5000
|
|
21
|
+
timeout: 5000
|
|
14
22
|
});
|
|
15
23
|
} catch (err) {
|
|
16
24
|
// Silently fail so the client's app doesn't crash if Master API is updating
|
|
17
25
|
}
|
|
18
26
|
};
|
|
19
|
-
|
|
20
|
-
sendHeartbeat
|
|
21
|
-
setInterval(sendHeartbeat, 5 * 60 * 1000); // Fire every 5 minutes forever
|
|
27
|
+
sendHeartbeat();
|
|
28
|
+
setInterval(sendHeartbeat, 5 * 60 * 1000);
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
const aipGuard = (options = { requireLogin: true }) => {
|
|
@@ -26,6 +33,7 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
26
33
|
|
|
27
34
|
// LAYER 1: INFRASTRUCTURE SHIELD
|
|
28
35
|
const MAX_PAYLOAD_BYTES = 2 * 1024 * 1024;
|
|
36
|
+
|
|
29
37
|
if (req.headers['transfer-encoding'] && req.headers['transfer-encoding'].includes('chunked')) {
|
|
30
38
|
return res.status(411).send("AIP Infrastructure Shield: Chunked encoding rejected.");
|
|
31
39
|
}
|
|
@@ -39,7 +47,6 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
39
47
|
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
40
48
|
res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
|
|
41
49
|
|
|
42
|
-
// Robust Token Extraction (Checks query, cookies, and Authorization headers)
|
|
43
50
|
let token = req.query.token || (req.cookies && req.cookies.aip_session);
|
|
44
51
|
if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
|
|
45
52
|
token = req.headers.authorization.split(' ')[1];
|
|
@@ -50,22 +57,23 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
50
57
|
// LAYER 2: IDENTITY CHECK & RBAC
|
|
51
58
|
if (options.requireLogin) {
|
|
52
59
|
if (!token) return res.status(401).send("Unauthorized: Missing AIP Session Token");
|
|
60
|
+
|
|
53
61
|
try {
|
|
54
62
|
const currentAction = req.method + " " + (req.baseUrl + req.path);
|
|
55
63
|
const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
|
|
56
64
|
{ session_token: token, action: currentAction },
|
|
57
65
|
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
|
|
58
66
|
);
|
|
67
|
+
|
|
59
68
|
req.user = authRes.data.user;
|
|
60
69
|
|
|
61
70
|
const isProd = process.env.NODE_ENV === 'production';
|
|
62
71
|
res.cookie('aip_session', token, { httpOnly: true, secure: isProd });
|
|
72
|
+
|
|
63
73
|
} catch (error) {
|
|
64
74
|
const status = error.response?.status || 500;
|
|
65
75
|
const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
|
|
66
76
|
|
|
67
|
-
// 🛡️ CRITICAL FIX: Only clear the session cookie if the token is actually dead (401).
|
|
68
|
-
// If it is an RBAC block (403), we just deny the action but keep them logged in!
|
|
69
77
|
if (status === 401 && res.clearCookie) {
|
|
70
78
|
res.clearCookie('aip_session');
|
|
71
79
|
}
|
|
@@ -82,6 +90,7 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
82
90
|
});
|
|
83
91
|
const forwardedFor = req.headers['x-forwarded-for'];
|
|
84
92
|
let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
|
|
93
|
+
|
|
85
94
|
if (forwardedFor) {
|
|
86
95
|
clientIp = Array.isArray(forwardedFor)
|
|
87
96
|
? forwardedFor[0].trim()
|
|
@@ -96,9 +105,9 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
96
105
|
payload: payloadData,
|
|
97
106
|
origin: req.headers.origin || "",
|
|
98
107
|
user_agent: req.headers['user-agent'] || "",
|
|
99
|
-
session_token: token || ""
|
|
108
|
+
session_token: token || ""
|
|
100
109
|
});
|
|
101
|
-
|
|
110
|
+
|
|
102
111
|
if (wafRes.data.action === "block") {
|
|
103
112
|
return res.status(403).send(`AIP Firewall Blocked Request: ${wafRes.data.reason}`);
|
|
104
113
|
}
|