aip-master-node-sumit 1.0.3 → 1.0.7
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 +104 -18
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const getSafeMasterUrl = (url) => {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
const AIP_MASTER_API = getSafeMasterUrl(process.env.AIP_MASTER_URL);
|
|
14
|
-
const API_KEY = process.env.AIP_MASTER_API_KEY;
|
|
14
|
+
const API_KEY = process.env.AIP_MASTER_API_KEY;
|
|
15
15
|
|
|
16
16
|
if (API_KEY) {
|
|
17
17
|
const sendHeartbeat = async () => {
|
|
@@ -24,6 +24,7 @@ if (API_KEY) {
|
|
|
24
24
|
// Silently fail so the client's app doesn't crash if Master API is updating
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
+
|
|
27
28
|
sendHeartbeat();
|
|
28
29
|
setInterval(sendHeartbeat, 5 * 60 * 1000);
|
|
29
30
|
}
|
|
@@ -31,40 +32,66 @@ if (API_KEY) {
|
|
|
31
32
|
const aipGuard = (options = { requireLogin: true }) => {
|
|
32
33
|
return async (req, res, next) => {
|
|
33
34
|
|
|
34
|
-
//
|
|
35
|
+
// ==========================================
|
|
36
|
+
// 🛡️ LAYER 0: DYNAMIC CORS & SECURITY HEADERS
|
|
37
|
+
// ==========================================
|
|
38
|
+
const origin = req.headers.origin || req.headers.Origin;
|
|
39
|
+
|
|
40
|
+
if (origin) {
|
|
41
|
+
// If origin is present, dynamically echo it to satisfy strict browser credential policies
|
|
42
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
43
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
44
|
+
} else {
|
|
45
|
+
// Fallback for non-browser clients (like Postman or cURL)
|
|
46
|
+
res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
50
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
51
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
52
|
+
|
|
53
|
+
// Handle preflight requests gracefully
|
|
54
|
+
if (req.method === 'OPTIONS') {
|
|
55
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
|
|
56
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-AIP-Token, X-Requested-With, Accept');
|
|
57
|
+
return res.status(200).end();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ==========================================
|
|
61
|
+
// 🛡️ LAYER 1: INFRASTRUCTURE SHIELD
|
|
62
|
+
// ==========================================
|
|
35
63
|
const MAX_PAYLOAD_BYTES = 2 * 1024 * 1024;
|
|
36
64
|
|
|
37
65
|
if (req.headers['transfer-encoding'] && req.headers['transfer-encoding'].includes('chunked')) {
|
|
38
|
-
return res.status(411).
|
|
66
|
+
return res.status(411).json({ error: "AIP Infrastructure Shield: Chunked encoding rejected." });
|
|
39
67
|
}
|
|
40
68
|
|
|
41
69
|
if (req.headers['content-length'] && parseInt(req.headers['content-length']) > MAX_PAYLOAD_BYTES) {
|
|
42
|
-
return res.status(413).
|
|
70
|
+
return res.status(413).json({ error: "AIP Infrastructure Shield: Payload Too Large." });
|
|
43
71
|
}
|
|
44
|
-
|
|
45
|
-
res.setHeader('X-Frame-Options', 'DENY');
|
|
46
|
-
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
47
|
-
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
48
|
-
res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
|
|
49
72
|
|
|
50
73
|
let token = req.query.token || (req.cookies && req.cookies.aip_session);
|
|
74
|
+
|
|
51
75
|
if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
|
|
52
76
|
token = req.headers.authorization.split(' ')[1];
|
|
53
77
|
} else if (!token && req.headers['x-aip-token']) {
|
|
54
78
|
token = req.headers['x-aip-token'];
|
|
55
79
|
}
|
|
56
80
|
|
|
57
|
-
//
|
|
81
|
+
// ==========================================
|
|
82
|
+
// 🛡️ LAYER 2: IDENTITY CHECK & RBAC
|
|
83
|
+
// ==========================================
|
|
58
84
|
if (options.requireLogin) {
|
|
59
|
-
if (!token) return res.status(401).
|
|
85
|
+
if (!token) return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
|
|
60
86
|
|
|
61
87
|
try {
|
|
62
88
|
const currentAction = req.method + " " + (req.baseUrl + req.path);
|
|
89
|
+
|
|
63
90
|
const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
|
|
64
91
|
{ session_token: token, action: currentAction },
|
|
65
92
|
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
|
|
66
93
|
);
|
|
67
|
-
|
|
94
|
+
|
|
68
95
|
req.user = authRes.data.user;
|
|
69
96
|
|
|
70
97
|
const isProd = process.env.NODE_ENV === 'production';
|
|
@@ -73,24 +100,27 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
73
100
|
} catch (error) {
|
|
74
101
|
const status = error.response?.status || 500;
|
|
75
102
|
const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
|
|
76
|
-
|
|
103
|
+
|
|
77
104
|
if (status === 401 && res.clearCookie) {
|
|
78
105
|
res.clearCookie('aip_session');
|
|
79
106
|
}
|
|
80
107
|
|
|
81
|
-
return res.status(status === 401 ? 401 : 403).
|
|
108
|
+
return res.status(status === 401 ? 401 : 403).json({ error: errMsg });
|
|
82
109
|
}
|
|
83
110
|
}
|
|
84
111
|
|
|
85
|
-
//
|
|
112
|
+
// ==========================================
|
|
113
|
+
// 🛡️ LAYER 3: WAF THREAT SCAN
|
|
114
|
+
// ==========================================
|
|
86
115
|
try {
|
|
87
116
|
const payloadData = JSON.stringify({
|
|
88
117
|
body: req.body || {},
|
|
89
118
|
query: req.query || {}
|
|
90
119
|
});
|
|
120
|
+
|
|
91
121
|
const forwardedFor = req.headers['x-forwarded-for'];
|
|
92
122
|
let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
|
|
93
|
-
|
|
123
|
+
|
|
94
124
|
if (forwardedFor) {
|
|
95
125
|
clientIp = Array.isArray(forwardedFor)
|
|
96
126
|
? forwardedFor[0].trim()
|
|
@@ -109,16 +139,72 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
109
139
|
});
|
|
110
140
|
|
|
111
141
|
if (wafRes.data.action === "block") {
|
|
112
|
-
return res.status(403).
|
|
142
|
+
return res.status(403).json({ error: `AIP Firewall Blocked Request: ${wafRes.data.reason}` });
|
|
113
143
|
}
|
|
114
144
|
|
|
115
145
|
} catch (error) {
|
|
116
146
|
console.error("WAF Engine Unreachable");
|
|
117
|
-
return res.status(500).
|
|
147
|
+
return res.status(500).json({ error: "Security verification failed." });
|
|
118
148
|
}
|
|
119
149
|
|
|
120
150
|
next();
|
|
121
151
|
};
|
|
122
152
|
};
|
|
123
153
|
|
|
154
|
+
// ==========================================
|
|
155
|
+
// 🛡️ NEW: IaaS Admin Client SDK
|
|
156
|
+
// ==========================================
|
|
157
|
+
class AipAdminClient {
|
|
158
|
+
constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
|
|
159
|
+
if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
|
|
160
|
+
this.apiKey = apiKey;
|
|
161
|
+
this.baseUrl = getSafeMasterUrl(masterUrl);
|
|
162
|
+
|
|
163
|
+
this.client = axios.create({
|
|
164
|
+
baseURL: this.baseUrl,
|
|
165
|
+
headers: { 'Authorization': `Bearer ${this.apiKey}` }
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// --- User Management ---
|
|
170
|
+
async getUsers() {
|
|
171
|
+
const res = await this.client.get('/sdk/admin/users');
|
|
172
|
+
return res.data;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async createUser({ name, email, password, role_id, send_email = false }) {
|
|
176
|
+
const res = await this.client.post('/sdk/admin/users', { name, email, password, role_id, send_email });
|
|
177
|
+
return res.data;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async updateUser(userId, { name, role_id }) {
|
|
181
|
+
const res = await this.client.put(`/sdk/admin/users/${userId}`, { name, role_id });
|
|
182
|
+
return res.data;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async updateRBAC(userId, permissions) {
|
|
186
|
+
const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
|
|
187
|
+
return res.data;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async deleteUser(userId) {
|
|
191
|
+
const res = await this.client.delete(`/sdk/admin/users/${userId}`);
|
|
192
|
+
return res.data;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// --- Role Management ---
|
|
196
|
+
async getRoles() {
|
|
197
|
+
const res = await this.client.get('/sdk/admin/roles');
|
|
198
|
+
return res.data;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async createRole({ name, slug, permissions }) {
|
|
202
|
+
const res = await this.client.post('/sdk/admin/roles', { name, slug, permissions });
|
|
203
|
+
return res.data;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 🛡️ Attach the Admin Client directly to the Guard export for backward compatibility
|
|
208
|
+
aipGuard.AipAdminClient = AipAdminClient;
|
|
209
|
+
|
|
124
210
|
module.exports = aipGuard;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aip-master-node-sumit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Enterprise-grade WAF and IAM security middleware for Node.js.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,4 +17,4 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"axios": "^1.13.6"
|
|
19
19
|
}
|
|
20
|
-
}
|
|
20
|
+
}
|