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.
Files changed (2) hide show
  1. package/index.js +104 -18
  2. 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
- // LAYER 1: INFRASTRUCTURE SHIELD
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).send("AIP Infrastructure Shield: Chunked encoding rejected.");
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).send("AIP Infrastructure Shield: Payload Too Large.");
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
- // LAYER 2: IDENTITY CHECK & RBAC
81
+ // ==========================================
82
+ // 🛡️ LAYER 2: IDENTITY CHECK & RBAC
83
+ // ==========================================
58
84
  if (options.requireLogin) {
59
- if (!token) return res.status(401).send("Unauthorized: Missing AIP Session Token");
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).send(errMsg);
108
+ return res.status(status === 401 ? 401 : 403).json({ error: errMsg });
82
109
  }
83
110
  }
84
111
 
85
- // LAYER 3: WAF THREAT SCAN
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).send(`AIP Firewall Blocked Request: ${wafRes.data.reason}`);
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).send("Security verification failed.");
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",
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
+ }