aip-master-node-sumit 1.0.8 → 1.0.9
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 +113 -21
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -36,7 +36,9 @@ if (API_KEY) {
|
|
|
36
36
|
headers: { 'Authorization': `Bearer ${API_KEY}` },
|
|
37
37
|
timeout: 5000
|
|
38
38
|
});
|
|
39
|
-
} catch (err) {
|
|
39
|
+
} catch (err) {
|
|
40
|
+
// Silently fail so the client's app doesn't crash if Master API is updating
|
|
41
|
+
}
|
|
40
42
|
};
|
|
41
43
|
sendHeartbeat();
|
|
42
44
|
setInterval(sendHeartbeat, 5 * 60 * 1000);
|
|
@@ -61,6 +63,7 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
61
63
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
62
64
|
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
63
65
|
|
|
66
|
+
// Handle preflight requests gracefully
|
|
64
67
|
if (req.method === 'OPTIONS') {
|
|
65
68
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
|
|
66
69
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-AIP-Token, X-Requested-With, Accept');
|
|
@@ -93,10 +96,13 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
93
96
|
// 🛡️ LAYER 2: IDENTITY CHECK & RBAC
|
|
94
97
|
// ==========================================
|
|
95
98
|
if (options.requireLogin) {
|
|
96
|
-
if (!token)
|
|
99
|
+
if (!token) {
|
|
100
|
+
return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
|
|
101
|
+
}
|
|
97
102
|
|
|
98
103
|
try {
|
|
99
104
|
const currentAction = req.method + " " + (req.baseUrl + req.path);
|
|
105
|
+
|
|
100
106
|
const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
|
|
101
107
|
{ session_token: token, action: currentAction },
|
|
102
108
|
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
|
|
@@ -106,11 +112,18 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
106
112
|
|
|
107
113
|
// 🛡️ THE FIX: Native Node.js Fallback for Set-Cookie (Framework Agnostic)
|
|
108
114
|
const isProd = process.env.NODE_ENV === 'production';
|
|
115
|
+
|
|
109
116
|
if (typeof res.cookie === 'function') {
|
|
117
|
+
// Express.js environment
|
|
110
118
|
res.cookie('aip_session', token, {
|
|
111
|
-
httpOnly: true,
|
|
119
|
+
httpOnly: true,
|
|
120
|
+
secure: isProd,
|
|
121
|
+
sameSite: isProd ? 'none' : 'lax',
|
|
122
|
+
path: '/',
|
|
123
|
+
maxAge: 12 * 60 * 60 * 1000
|
|
112
124
|
});
|
|
113
125
|
} else {
|
|
126
|
+
// Raw Node.js or alternate framework environment
|
|
114
127
|
const cookieStr = `aip_session=${token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`;
|
|
115
128
|
res.setHeader('Set-Cookie', cookieStr);
|
|
116
129
|
}
|
|
@@ -118,11 +131,15 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
118
131
|
} catch (error) {
|
|
119
132
|
const status = error.response?.status || 500;
|
|
120
133
|
const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
|
|
121
|
-
|
|
122
134
|
const isProd = process.env.NODE_ENV === 'production';
|
|
135
|
+
|
|
123
136
|
if (status === 401) {
|
|
124
137
|
if (typeof res.clearCookie === 'function') {
|
|
125
|
-
res.clearCookie('aip_session', {
|
|
138
|
+
res.clearCookie('aip_session', {
|
|
139
|
+
path: '/',
|
|
140
|
+
sameSite: isProd ? 'none' : 'lax',
|
|
141
|
+
secure: isProd
|
|
142
|
+
});
|
|
126
143
|
} else {
|
|
127
144
|
res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
|
|
128
145
|
}
|
|
@@ -136,16 +153,28 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
136
153
|
// 🛡️ LAYER 3: WAF THREAT SCAN
|
|
137
154
|
// ==========================================
|
|
138
155
|
try {
|
|
139
|
-
const payloadData = JSON.stringify({
|
|
156
|
+
const payloadData = JSON.stringify({
|
|
157
|
+
body: req.body || {},
|
|
158
|
+
query: req.query || {}
|
|
159
|
+
});
|
|
160
|
+
|
|
140
161
|
const forwardedFor = req.headers['x-forwarded-for'];
|
|
141
162
|
let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
|
|
163
|
+
|
|
142
164
|
if (forwardedFor) {
|
|
143
|
-
clientIp = Array.isArray(forwardedFor)
|
|
165
|
+
clientIp = Array.isArray(forwardedFor)
|
|
166
|
+
? forwardedFor[0].trim()
|
|
167
|
+
: forwardedFor.split(',')[0].trim();
|
|
144
168
|
}
|
|
145
169
|
|
|
146
170
|
const wafRes = await axios.post(`${AIP_MASTER_API}/verify`, {
|
|
147
|
-
api_key: API_KEY,
|
|
148
|
-
|
|
171
|
+
api_key: API_KEY,
|
|
172
|
+
ip: clientIp,
|
|
173
|
+
method: req.method,
|
|
174
|
+
url: req.originalUrl,
|
|
175
|
+
payload: payloadData,
|
|
176
|
+
origin: req.headers.origin || "",
|
|
177
|
+
user_agent: req.headers['user-agent'] || "",
|
|
149
178
|
session_token: token || ""
|
|
150
179
|
});
|
|
151
180
|
|
|
@@ -156,26 +185,64 @@ const aipGuard = (options = { requireLogin: true }) => {
|
|
|
156
185
|
} catch (error) {
|
|
157
186
|
return res.status(500).json({ error: "Security verification failed." });
|
|
158
187
|
}
|
|
188
|
+
|
|
159
189
|
next();
|
|
160
190
|
};
|
|
161
191
|
};
|
|
162
192
|
|
|
193
|
+
// ==========================================
|
|
194
|
+
// 🛡️ Admin Client SDK
|
|
195
|
+
// ==========================================
|
|
163
196
|
class AipAdminClient {
|
|
164
197
|
constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
|
|
165
198
|
if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
|
|
166
199
|
this.apiKey = apiKey;
|
|
167
200
|
this.baseUrl = getSafeMasterUrl(masterUrl);
|
|
168
|
-
this.client = axios.create({
|
|
201
|
+
this.client = axios.create({
|
|
202
|
+
baseURL: this.baseUrl,
|
|
203
|
+
headers: { 'Authorization': `Bearer ${this.apiKey}` }
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async getUsers() {
|
|
208
|
+
const res = await this.client.get('/sdk/admin/users');
|
|
209
|
+
return res.data;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async createUser(data) {
|
|
213
|
+
const res = await this.client.post('/sdk/admin/users', data);
|
|
214
|
+
return res.data;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async updateUser(userId, data) {
|
|
218
|
+
const res = await this.client.put(`/sdk/admin/users/${userId}`, data);
|
|
219
|
+
return res.data;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async updateRBAC(userId, permissions) {
|
|
223
|
+
const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
|
|
224
|
+
return res.data;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async deleteUser(userId) {
|
|
228
|
+
const res = await this.client.delete(`/sdk/admin/users/${userId}`);
|
|
229
|
+
return res.data;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async getRoles() {
|
|
233
|
+
const res = await this.client.get('/sdk/admin/roles');
|
|
234
|
+
return res.data;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async createRole(data) {
|
|
238
|
+
const res = await this.client.post('/sdk/admin/roles', data);
|
|
239
|
+
return res.data;
|
|
169
240
|
}
|
|
170
|
-
async getUsers() { const res = await this.client.get('/sdk/admin/users'); return res.data; }
|
|
171
|
-
async createUser(data) { const res = await this.client.post('/sdk/admin/users', data); return res.data; }
|
|
172
|
-
async updateUser(userId, data) { const res = await this.client.put(`/sdk/admin/users/${userId}`, data); return res.data; }
|
|
173
|
-
async updateRBAC(userId, permissions) { const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions }); return res.data; }
|
|
174
|
-
async deleteUser(userId) { const res = await this.client.delete(`/sdk/admin/users/${userId}`); return res.data; }
|
|
175
|
-
async getRoles() { const res = await this.client.get('/sdk/admin/roles'); return res.data; }
|
|
176
|
-
async createRole(data) { const res = await this.client.post('/sdk/admin/roles', data); return res.data; }
|
|
177
241
|
}
|
|
178
242
|
|
|
243
|
+
// ==========================================
|
|
244
|
+
// 🛡️ End-User IAM Client SDK
|
|
245
|
+
// ==========================================
|
|
179
246
|
class AipIAMClient {
|
|
180
247
|
constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
|
|
181
248
|
if (!apiKey) throw new Error("AIP IAM Client requires an API Key");
|
|
@@ -183,33 +250,58 @@ class AipIAMClient {
|
|
|
183
250
|
this.baseUrl = getSafeMasterUrl(masterUrl);
|
|
184
251
|
this.client = axios.create({ baseURL: this.baseUrl });
|
|
185
252
|
}
|
|
253
|
+
|
|
186
254
|
async loginUser(req, res, credentials) {
|
|
187
255
|
let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
|
|
188
256
|
const forwardedFor = req.headers['x-forwarded-for'];
|
|
189
|
-
|
|
257
|
+
|
|
258
|
+
if (forwardedFor) {
|
|
259
|
+
clientIp = Array.isArray(forwardedFor)
|
|
260
|
+
? forwardedFor[0].trim()
|
|
261
|
+
: forwardedFor.split(',')[0].trim();
|
|
262
|
+
}
|
|
190
263
|
|
|
191
|
-
const authRes = await this.client.post('/iam/auth', {
|
|
264
|
+
const authRes = await this.client.post('/iam/auth', {
|
|
265
|
+
api_key: this.apiKey,
|
|
266
|
+
ip: clientIp,
|
|
267
|
+
...credentials
|
|
268
|
+
});
|
|
192
269
|
|
|
193
270
|
if (authRes.data.session_token) {
|
|
194
271
|
const isProd = process.env.NODE_ENV === 'production';
|
|
272
|
+
|
|
195
273
|
if (typeof res.cookie === 'function') {
|
|
196
|
-
res.cookie('aip_session', authRes.data.session_token, {
|
|
274
|
+
res.cookie('aip_session', authRes.data.session_token, {
|
|
275
|
+
httpOnly: true,
|
|
276
|
+
secure: isProd,
|
|
277
|
+
sameSite: isProd ? 'none' : 'lax',
|
|
278
|
+
path: '/',
|
|
279
|
+
maxAge: 12 * 60 * 60 * 1000
|
|
280
|
+
});
|
|
197
281
|
} else {
|
|
198
282
|
res.setHeader('Set-Cookie', `aip_session=${authRes.data.session_token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`);
|
|
199
283
|
}
|
|
200
284
|
}
|
|
201
285
|
return authRes.data;
|
|
202
286
|
}
|
|
287
|
+
|
|
203
288
|
logoutUser(res) {
|
|
204
289
|
const isProd = process.env.NODE_ENV === 'production';
|
|
290
|
+
|
|
205
291
|
if (typeof res.clearCookie === 'function') {
|
|
206
|
-
res.clearCookie('aip_session', {
|
|
292
|
+
res.clearCookie('aip_session', {
|
|
293
|
+
path: '/',
|
|
294
|
+
sameSite: isProd ? 'none' : 'lax',
|
|
295
|
+
secure: isProd
|
|
296
|
+
});
|
|
207
297
|
} else {
|
|
208
298
|
res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
|
|
209
299
|
}
|
|
210
300
|
}
|
|
211
301
|
}
|
|
212
302
|
|
|
303
|
+
// Attach the clients directly to the Guard export for easy importing
|
|
213
304
|
aipGuard.AipAdminClient = AipAdminClient;
|
|
214
305
|
aipGuard.AipIAMClient = AipIAMClient;
|
|
306
|
+
|
|
215
307
|
module.exports = aipGuard;
|