ecrs-auth-core 1.0.71 → 1.0.72
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/auth.controller.js +207 -207
- package/dist/auth.service.js +361 -361
- package/dist/guards/api-key.guard.js +1 -0
- package/dist/index.d.ts +31 -31
- package/dist/index.js +54 -54
- package/dist/interfaces/auth-core-options.interface.d.ts +38 -38
- package/package.json +1 -1
package/dist/auth.service.js
CHANGED
|
@@ -1,361 +1,361 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
-
};
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
-
};
|
|
44
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
-
};
|
|
47
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.AuthService = void 0;
|
|
49
|
-
const common_1 = require("@nestjs/common");
|
|
50
|
-
const jwt_1 = require("@nestjs/jwt");
|
|
51
|
-
const bcrypt = __importStar(require("bcrypt"));
|
|
52
|
-
const constants_1 = require("./constants/constants");
|
|
53
|
-
let AuthService = class AuthService {
|
|
54
|
-
constructor(jwtService, options) {
|
|
55
|
-
this.jwtService = jwtService;
|
|
56
|
-
this.options = options;
|
|
57
|
-
this.uploadPhotoDir = './uploads/organization/photos';
|
|
58
|
-
const { repositories } = options;
|
|
59
|
-
this.userRepo = repositories.userRepo;
|
|
60
|
-
this.roleRepo = repositories.roleRepo;
|
|
61
|
-
this.moduleRepo = repositories.moduleRepo;
|
|
62
|
-
this.featureRepo = repositories.featureRepo;
|
|
63
|
-
this.routeRepo = repositories.routeRepo;
|
|
64
|
-
this.featureAccessRepo = repositories.featureAccessRepo;
|
|
65
|
-
this.moduleAccessRepo = repositories.moduleAccessRepo;
|
|
66
|
-
this.screenPermissionRepo = repositories.screenPermissionRepo;
|
|
67
|
-
// Optional repositories
|
|
68
|
-
this.ipRestrictionsRepo = repositories.ipRestrictionsRepo || null;
|
|
69
|
-
this.userLastLoginRepo = repositories.userLastLoginRepo || null;
|
|
70
|
-
this.employeeWorkProfileRepo = repositories.employeeWorkProfileRepo || null;
|
|
71
|
-
}
|
|
72
|
-
async validateUser(email, password, clientIp) {
|
|
73
|
-
const user = await this.userRepo.findOne({ where: { email } });
|
|
74
|
-
if (!user)
|
|
75
|
-
return null;
|
|
76
|
-
const isValid = await bcrypt.compare(password, user.password);
|
|
77
|
-
if (!isValid)
|
|
78
|
-
return null;
|
|
79
|
-
// console.log(this.ipRestrictionsRepo);
|
|
80
|
-
// Check IP restrictions if provided and repository is available
|
|
81
|
-
// if (clientIp && this.ipRestrictionsRepo) {
|
|
82
|
-
// const ipAllowed = await this.validateIpRestriction(user.id, clientIp);
|
|
83
|
-
// if (!ipAllowed) {
|
|
84
|
-
// // IP restriction exists but doesn't match - return null to block login
|
|
85
|
-
// return null;
|
|
86
|
-
// }
|
|
87
|
-
// }
|
|
88
|
-
return user;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Validate IP restriction for a user
|
|
92
|
-
*
|
|
93
|
-
* Logic:
|
|
94
|
-
* 1. Check if user has any IP restrictions (is_active = 1)
|
|
95
|
-
* 2. If NO restrictions exist → Allow login (return true)
|
|
96
|
-
* 3. If restrictions exist → Check if requestIp matches any allowed IP
|
|
97
|
-
* 4. If match found → Allow login (return true)
|
|
98
|
-
* 5. If NO match → Deny login (return false)
|
|
99
|
-
*/
|
|
100
|
-
normalizeIp(ip) {
|
|
101
|
-
// Remove descriptive text if present (e.g., "IPv4 Address. . . . . . : 192.167.0.173")
|
|
102
|
-
let cleanIp = ip.trim();
|
|
103
|
-
if (cleanIp.includes(':')) {
|
|
104
|
-
const parts = cleanIp.split(':');
|
|
105
|
-
cleanIp = parts[parts.length - 1].trim();
|
|
106
|
-
}
|
|
107
|
-
// Convert IPv6-mapped IPv4 addresses (::ffff:x.x.x.x) to plain IPv4 (x.x.x.x)
|
|
108
|
-
if (cleanIp.startsWith('::ffff:')) {
|
|
109
|
-
return cleanIp.substring(7);
|
|
110
|
-
}
|
|
111
|
-
// Also handle other IPv6 prefixes
|
|
112
|
-
if (cleanIp.startsWith('::1')) {
|
|
113
|
-
return '127.0.0.1'; // IPv6 loopback to IPv4 loopback
|
|
114
|
-
}
|
|
115
|
-
return cleanIp;
|
|
116
|
-
}
|
|
117
|
-
async validateIpRestriction(userId, requestIp) {
|
|
118
|
-
if (!this.ipRestrictionsRepo) {
|
|
119
|
-
// No IP restrictions repository configured - allow login
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
try {
|
|
123
|
-
// Normalize the incoming IP
|
|
124
|
-
const normalizedRequestIp = this.normalizeIp(requestIp);
|
|
125
|
-
// Get all active IP restrictions for this user
|
|
126
|
-
const restrictions = await this.ipRestrictionsRepo.find({
|
|
127
|
-
where: {
|
|
128
|
-
user_id: userId,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
// If no restrictions exist, allow login
|
|
132
|
-
if (!restrictions || restrictions.length === 0) {
|
|
133
|
-
console.log(`✅ User ${userId}: No IP restrictions configured - Allow login`);
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
// Check if request IP matches any allowed IP
|
|
137
|
-
const ipMatches = restrictions.some((restriction) => {
|
|
138
|
-
// Handle both property name variations
|
|
139
|
-
const allowedIp = restriction.allowed_ip_address || restriction.ip_address;
|
|
140
|
-
const normalizedAllowedIp = this.normalizeIp(allowedIp);
|
|
141
|
-
return normalizedAllowedIp === normalizedRequestIp;
|
|
142
|
-
});
|
|
143
|
-
if (ipMatches) {
|
|
144
|
-
console.log(`✅ User ${userId}: IP ${requestIp} (normalized: ${normalizedRequestIp}) matches allowed IP - Allow login`);
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
// IP doesn't match any allowed IP
|
|
148
|
-
const allowedIps = restrictions.map((r) => {
|
|
149
|
-
const ip = r.allowed_ip_address || r.ip_address;
|
|
150
|
-
return this.normalizeIp(ip);
|
|
151
|
-
}).join(', ');
|
|
152
|
-
console.log(`❌ User ${userId}: IP ${requestIp} (normalized: ${normalizedRequestIp}) does not match allowed IPs - Deny login`);
|
|
153
|
-
console.log(` Allowed IPs: ${allowedIps}`);
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
console.error('Error validating IP restriction:', error);
|
|
158
|
-
// On error, allow login (fail open)
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async hasModuleAccess(userId, moduleId) {
|
|
163
|
-
if (!Number.isFinite(moduleId))
|
|
164
|
-
return false;
|
|
165
|
-
const access = await this.moduleAccessRepo.findOne({
|
|
166
|
-
where: { userId, moduleId, isDeleted: 0, status: 1 },
|
|
167
|
-
});
|
|
168
|
-
return !!access;
|
|
169
|
-
}
|
|
170
|
-
async getPermissions(userId) {
|
|
171
|
-
return this.loadPermissions(userId);
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Save user last login details
|
|
175
|
-
* Updates the tbl_user_last_login table with latest login info
|
|
176
|
-
*/
|
|
177
|
-
async saveLastLogin(user, clientIp, loginStatus = 'success', failureReason, additionalData) {
|
|
178
|
-
if (!this.userLastLoginRepo) {
|
|
179
|
-
// Last login tracking not configured
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
const lastLoginData = {
|
|
184
|
-
user_id: user.id,
|
|
185
|
-
email: user.email,
|
|
186
|
-
first_name: user.firstName,
|
|
187
|
-
last_name: user.lastName,
|
|
188
|
-
ip_address: clientIp,
|
|
189
|
-
login_status: loginStatus,
|
|
190
|
-
failure_reason: failureReason,
|
|
191
|
-
login_time: new Date(),
|
|
192
|
-
...additionalData,
|
|
193
|
-
};
|
|
194
|
-
// Upsert: Update if exists, insert if not
|
|
195
|
-
await this.userLastLoginRepo.upsert(lastLoginData, {
|
|
196
|
-
conflictPaths: ['user_id'],
|
|
197
|
-
skipUpdateIfNoValuesChanged: true,
|
|
198
|
-
});
|
|
199
|
-
console.log(`📝 Last login details saved for user ${user.id} (${loginStatus})`);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
console.error('Error saving last login details:', error);
|
|
203
|
-
// Don't throw error - this shouldn't block login
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
async login(user, selectedModuleId) {
|
|
207
|
-
const permissionTree = await this.loadPermissions(user.id);
|
|
208
|
-
const role = await this.roleRepo.findOne({ where: { id: user.roleId } });
|
|
209
|
-
const roleName = role?.roleName || null;
|
|
210
|
-
const effectiveModuleId = Number.isFinite(selectedModuleId) ? selectedModuleId : user.moduleId ?? null;
|
|
211
|
-
// Fetch workprofile/employee details from EmployeeWorkProfileEntity
|
|
212
|
-
let branchId = null;
|
|
213
|
-
let dispatchCenterId = null;
|
|
214
|
-
let departmentId = null;
|
|
215
|
-
let designationId = null;
|
|
216
|
-
if (this.employeeWorkProfileRepo) {
|
|
217
|
-
try {
|
|
218
|
-
const workProfile = await this.employeeWorkProfileRepo.findOne({
|
|
219
|
-
where: { employee_id: user.referenceId },
|
|
220
|
-
});
|
|
221
|
-
if (workProfile) {
|
|
222
|
-
branchId = workProfile?.branch_id || null;
|
|
223
|
-
dispatchCenterId = workProfile?.
|
|
224
|
-
departmentId = workProfile?.department_id || null;
|
|
225
|
-
designationId = workProfile?.designation_id || null;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
console.error('Error fetching work profile:', error);
|
|
230
|
-
// Continue with null values if fetch fails
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const payload = {
|
|
234
|
-
id: user.id,
|
|
235
|
-
email: user.email,
|
|
236
|
-
roleId: user.roleId,
|
|
237
|
-
roleName: roleName,
|
|
238
|
-
moduleId: effectiveModuleId,
|
|
239
|
-
name: `${user.firstName} ${user.lastName}`,
|
|
240
|
-
firstName: user.firstName,
|
|
241
|
-
lastName: user.lastName,
|
|
242
|
-
mobileNo: user.mobileNo,
|
|
243
|
-
userImage: user.userImage,
|
|
244
|
-
employeeId: user.referenceId,
|
|
245
|
-
permissions: permissionTree,
|
|
246
|
-
parentId: user.parentId,
|
|
247
|
-
referenceId: user.referenceId,
|
|
248
|
-
branchId,
|
|
249
|
-
dispatchCenterId,
|
|
250
|
-
departmentId,
|
|
251
|
-
designationId,
|
|
252
|
-
};
|
|
253
|
-
return {
|
|
254
|
-
status: true,
|
|
255
|
-
message: 'Login successful',
|
|
256
|
-
data: {
|
|
257
|
-
user: {
|
|
258
|
-
id: user.id,
|
|
259
|
-
email: user.email,
|
|
260
|
-
roleId: user.roleId,
|
|
261
|
-
roleName: roleName,
|
|
262
|
-
moduleId: effectiveModuleId,
|
|
263
|
-
name: `${user.firstName} ${user.lastName}`,
|
|
264
|
-
firstName: user.firstName,
|
|
265
|
-
lastName: user.lastName,
|
|
266
|
-
mobileNo: user.mobileNo,
|
|
267
|
-
userImage: user.userImage,
|
|
268
|
-
employeeId: user.referenceId,
|
|
269
|
-
parentId: user.parentId,
|
|
270
|
-
referenceId: user.referenceId,
|
|
271
|
-
branchId,
|
|
272
|
-
dispatchCenterId,
|
|
273
|
-
departmentId,
|
|
274
|
-
designationId,
|
|
275
|
-
profile_photo_url: `${this.uploadPhotoDir}`,
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
access_token: this.jwtService.sign(payload),
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Extract clean IPv4 address from request
|
|
283
|
-
* Handles various formats: IPv6-mapped, plain IPv4, descriptive text
|
|
284
|
-
*/
|
|
285
|
-
extractUserIpv4(clientIp) {
|
|
286
|
-
if (!clientIp)
|
|
287
|
-
return '';
|
|
288
|
-
return this.normalizeIp(clientIp);
|
|
289
|
-
}
|
|
290
|
-
async findUserById(id) {
|
|
291
|
-
return this.userRepo.findOne({ where: { id } });
|
|
292
|
-
}
|
|
293
|
-
async loadPermissions(userId) {
|
|
294
|
-
// Feature Permissions
|
|
295
|
-
const featureAccessList = await this.featureAccessRepo.find({
|
|
296
|
-
where: { userId, isDeleted: 0 },
|
|
297
|
-
});
|
|
298
|
-
const featurePermissions = {};
|
|
299
|
-
const allFeatures = await this.featureRepo.find();
|
|
300
|
-
const featureMap = new Map(allFeatures.map((f) => [f.id, f]));
|
|
301
|
-
for (const access of featureAccessList) {
|
|
302
|
-
const feature = featureMap.get(access.featureId);
|
|
303
|
-
if (!feature)
|
|
304
|
-
continue;
|
|
305
|
-
const perms = [];
|
|
306
|
-
if (access.canView)
|
|
307
|
-
perms.push('view');
|
|
308
|
-
if (access.canCreate)
|
|
309
|
-
perms.push('create');
|
|
310
|
-
if (access.canModify)
|
|
311
|
-
perms.push('update');
|
|
312
|
-
if (access.canDelete)
|
|
313
|
-
perms.push('delete');
|
|
314
|
-
if (access.canImport)
|
|
315
|
-
perms.push('import');
|
|
316
|
-
if (access.canExport)
|
|
317
|
-
perms.push('export');
|
|
318
|
-
if (perms.length) {
|
|
319
|
-
featurePermissions[feature.featureName] = perms;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
// Module Access - only count active (non-deleted, status=1) assignments
|
|
323
|
-
const moduleAccess = await this.moduleAccessRepo.find({
|
|
324
|
-
where: { userId, isDeleted: 0, status: 1 },
|
|
325
|
-
});
|
|
326
|
-
const moduleIds = moduleAccess.map((m) => m.moduleId);
|
|
327
|
-
// Route Permissions
|
|
328
|
-
const screenPermissionsList = await this.screenPermissionRepo.find({
|
|
329
|
-
where: { userId, isActive: true },
|
|
330
|
-
});
|
|
331
|
-
const routePermissions = {};
|
|
332
|
-
const allRoutes = await this.routeRepo.find();
|
|
333
|
-
const routeMap = new Map(allRoutes.map((r) => [r.id, r]));
|
|
334
|
-
for (const screen of screenPermissionsList) {
|
|
335
|
-
const route = routeMap.get(screen.moduleRouteId);
|
|
336
|
-
if (!route)
|
|
337
|
-
continue;
|
|
338
|
-
const perms = [];
|
|
339
|
-
const keys = ['view', 'create', 'update', 'delete', 'import', 'export'];
|
|
340
|
-
for (const key of keys) {
|
|
341
|
-
if (screen.permissions?.[key]) {
|
|
342
|
-
perms.push(key);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (perms.length) {
|
|
346
|
-
routePermissions[route.routeName] = perms;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return {
|
|
350
|
-
features: featurePermissions,
|
|
351
|
-
modules: moduleIds,
|
|
352
|
-
routes: routePermissions,
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
exports.AuthService = AuthService;
|
|
357
|
-
exports.AuthService = AuthService = __decorate([
|
|
358
|
-
(0, common_1.Injectable)(),
|
|
359
|
-
__param(1, (0, common_1.Inject)(constants_1.AUTH_CORE_OPTIONS)),
|
|
360
|
-
__metadata("design:paramtypes", [jwt_1.JwtService, Object])
|
|
361
|
-
], AuthService);
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.AuthService = void 0;
|
|
49
|
+
const common_1 = require("@nestjs/common");
|
|
50
|
+
const jwt_1 = require("@nestjs/jwt");
|
|
51
|
+
const bcrypt = __importStar(require("bcrypt"));
|
|
52
|
+
const constants_1 = require("./constants/constants");
|
|
53
|
+
let AuthService = class AuthService {
|
|
54
|
+
constructor(jwtService, options) {
|
|
55
|
+
this.jwtService = jwtService;
|
|
56
|
+
this.options = options;
|
|
57
|
+
this.uploadPhotoDir = './uploads/organization/photos';
|
|
58
|
+
const { repositories } = options;
|
|
59
|
+
this.userRepo = repositories.userRepo;
|
|
60
|
+
this.roleRepo = repositories.roleRepo;
|
|
61
|
+
this.moduleRepo = repositories.moduleRepo;
|
|
62
|
+
this.featureRepo = repositories.featureRepo;
|
|
63
|
+
this.routeRepo = repositories.routeRepo;
|
|
64
|
+
this.featureAccessRepo = repositories.featureAccessRepo;
|
|
65
|
+
this.moduleAccessRepo = repositories.moduleAccessRepo;
|
|
66
|
+
this.screenPermissionRepo = repositories.screenPermissionRepo;
|
|
67
|
+
// Optional repositories
|
|
68
|
+
this.ipRestrictionsRepo = repositories.ipRestrictionsRepo || null;
|
|
69
|
+
this.userLastLoginRepo = repositories.userLastLoginRepo || null;
|
|
70
|
+
this.employeeWorkProfileRepo = repositories.employeeWorkProfileRepo || null;
|
|
71
|
+
}
|
|
72
|
+
async validateUser(email, password, clientIp) {
|
|
73
|
+
const user = await this.userRepo.findOne({ where: { email } });
|
|
74
|
+
if (!user)
|
|
75
|
+
return null;
|
|
76
|
+
const isValid = await bcrypt.compare(password, user.password);
|
|
77
|
+
if (!isValid)
|
|
78
|
+
return null;
|
|
79
|
+
// console.log(this.ipRestrictionsRepo);
|
|
80
|
+
// Check IP restrictions if provided and repository is available
|
|
81
|
+
// if (clientIp && this.ipRestrictionsRepo) {
|
|
82
|
+
// const ipAllowed = await this.validateIpRestriction(user.id, clientIp);
|
|
83
|
+
// if (!ipAllowed) {
|
|
84
|
+
// // IP restriction exists but doesn't match - return null to block login
|
|
85
|
+
// return null;
|
|
86
|
+
// }
|
|
87
|
+
// }
|
|
88
|
+
return user;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Validate IP restriction for a user
|
|
92
|
+
*
|
|
93
|
+
* Logic:
|
|
94
|
+
* 1. Check if user has any IP restrictions (is_active = 1)
|
|
95
|
+
* 2. If NO restrictions exist → Allow login (return true)
|
|
96
|
+
* 3. If restrictions exist → Check if requestIp matches any allowed IP
|
|
97
|
+
* 4. If match found → Allow login (return true)
|
|
98
|
+
* 5. If NO match → Deny login (return false)
|
|
99
|
+
*/
|
|
100
|
+
normalizeIp(ip) {
|
|
101
|
+
// Remove descriptive text if present (e.g., "IPv4 Address. . . . . . : 192.167.0.173")
|
|
102
|
+
let cleanIp = ip.trim();
|
|
103
|
+
if (cleanIp.includes(':')) {
|
|
104
|
+
const parts = cleanIp.split(':');
|
|
105
|
+
cleanIp = parts[parts.length - 1].trim();
|
|
106
|
+
}
|
|
107
|
+
// Convert IPv6-mapped IPv4 addresses (::ffff:x.x.x.x) to plain IPv4 (x.x.x.x)
|
|
108
|
+
if (cleanIp.startsWith('::ffff:')) {
|
|
109
|
+
return cleanIp.substring(7);
|
|
110
|
+
}
|
|
111
|
+
// Also handle other IPv6 prefixes
|
|
112
|
+
if (cleanIp.startsWith('::1')) {
|
|
113
|
+
return '127.0.0.1'; // IPv6 loopback to IPv4 loopback
|
|
114
|
+
}
|
|
115
|
+
return cleanIp;
|
|
116
|
+
}
|
|
117
|
+
async validateIpRestriction(userId, requestIp) {
|
|
118
|
+
if (!this.ipRestrictionsRepo) {
|
|
119
|
+
// No IP restrictions repository configured - allow login
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
// Normalize the incoming IP
|
|
124
|
+
const normalizedRequestIp = this.normalizeIp(requestIp);
|
|
125
|
+
// Get all active IP restrictions for this user
|
|
126
|
+
const restrictions = await this.ipRestrictionsRepo.find({
|
|
127
|
+
where: {
|
|
128
|
+
user_id: userId,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
// If no restrictions exist, allow login
|
|
132
|
+
if (!restrictions || restrictions.length === 0) {
|
|
133
|
+
console.log(`✅ User ${userId}: No IP restrictions configured - Allow login`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// Check if request IP matches any allowed IP
|
|
137
|
+
const ipMatches = restrictions.some((restriction) => {
|
|
138
|
+
// Handle both property name variations
|
|
139
|
+
const allowedIp = restriction.allowed_ip_address || restriction.ip_address;
|
|
140
|
+
const normalizedAllowedIp = this.normalizeIp(allowedIp);
|
|
141
|
+
return normalizedAllowedIp === normalizedRequestIp;
|
|
142
|
+
});
|
|
143
|
+
if (ipMatches) {
|
|
144
|
+
console.log(`✅ User ${userId}: IP ${requestIp} (normalized: ${normalizedRequestIp}) matches allowed IP - Allow login`);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
// IP doesn't match any allowed IP
|
|
148
|
+
const allowedIps = restrictions.map((r) => {
|
|
149
|
+
const ip = r.allowed_ip_address || r.ip_address;
|
|
150
|
+
return this.normalizeIp(ip);
|
|
151
|
+
}).join(', ');
|
|
152
|
+
console.log(`❌ User ${userId}: IP ${requestIp} (normalized: ${normalizedRequestIp}) does not match allowed IPs - Deny login`);
|
|
153
|
+
console.log(` Allowed IPs: ${allowedIps}`);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error('Error validating IP restriction:', error);
|
|
158
|
+
// On error, allow login (fail open)
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async hasModuleAccess(userId, moduleId) {
|
|
163
|
+
if (!Number.isFinite(moduleId))
|
|
164
|
+
return false;
|
|
165
|
+
const access = await this.moduleAccessRepo.findOne({
|
|
166
|
+
where: { userId, moduleId, isDeleted: 0, status: 1 },
|
|
167
|
+
});
|
|
168
|
+
return !!access;
|
|
169
|
+
}
|
|
170
|
+
async getPermissions(userId) {
|
|
171
|
+
return this.loadPermissions(userId);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Save user last login details
|
|
175
|
+
* Updates the tbl_user_last_login table with latest login info
|
|
176
|
+
*/
|
|
177
|
+
async saveLastLogin(user, clientIp, loginStatus = 'success', failureReason, additionalData) {
|
|
178
|
+
if (!this.userLastLoginRepo) {
|
|
179
|
+
// Last login tracking not configured
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const lastLoginData = {
|
|
184
|
+
user_id: user.id,
|
|
185
|
+
email: user.email,
|
|
186
|
+
first_name: user.firstName,
|
|
187
|
+
last_name: user.lastName,
|
|
188
|
+
ip_address: clientIp,
|
|
189
|
+
login_status: loginStatus,
|
|
190
|
+
failure_reason: failureReason,
|
|
191
|
+
login_time: new Date(),
|
|
192
|
+
...additionalData,
|
|
193
|
+
};
|
|
194
|
+
// Upsert: Update if exists, insert if not
|
|
195
|
+
await this.userLastLoginRepo.upsert(lastLoginData, {
|
|
196
|
+
conflictPaths: ['user_id'],
|
|
197
|
+
skipUpdateIfNoValuesChanged: true,
|
|
198
|
+
});
|
|
199
|
+
console.log(`📝 Last login details saved for user ${user.id} (${loginStatus})`);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
console.error('Error saving last login details:', error);
|
|
203
|
+
// Don't throw error - this shouldn't block login
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async login(user, selectedModuleId) {
|
|
207
|
+
const permissionTree = await this.loadPermissions(user.id);
|
|
208
|
+
const role = await this.roleRepo.findOne({ where: { id: user.roleId } });
|
|
209
|
+
const roleName = role?.roleName || null;
|
|
210
|
+
const effectiveModuleId = Number.isFinite(selectedModuleId) ? selectedModuleId : user.moduleId ?? null;
|
|
211
|
+
// Fetch workprofile/employee details from EmployeeWorkProfileEntity
|
|
212
|
+
let branchId = null;
|
|
213
|
+
let dispatchCenterId = null;
|
|
214
|
+
let departmentId = null;
|
|
215
|
+
let designationId = null;
|
|
216
|
+
if (this.employeeWorkProfileRepo) {
|
|
217
|
+
try {
|
|
218
|
+
const workProfile = await this.employeeWorkProfileRepo.findOne({
|
|
219
|
+
where: { employee_id: user.referenceId },
|
|
220
|
+
});
|
|
221
|
+
if (workProfile) {
|
|
222
|
+
branchId = workProfile?.branch_id || null;
|
|
223
|
+
dispatchCenterId = workProfile?.dispatch_center_id || null;
|
|
224
|
+
departmentId = workProfile?.department_id || null;
|
|
225
|
+
designationId = workProfile?.designation_id || null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
console.error('Error fetching work profile:', error);
|
|
230
|
+
// Continue with null values if fetch fails
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const payload = {
|
|
234
|
+
id: user.id,
|
|
235
|
+
email: user.email,
|
|
236
|
+
roleId: user.roleId,
|
|
237
|
+
roleName: roleName,
|
|
238
|
+
moduleId: effectiveModuleId,
|
|
239
|
+
name: `${user.firstName} ${user.lastName}`,
|
|
240
|
+
firstName: user.firstName,
|
|
241
|
+
lastName: user.lastName,
|
|
242
|
+
mobileNo: user.mobileNo,
|
|
243
|
+
userImage: user.userImage,
|
|
244
|
+
employeeId: user.referenceId,
|
|
245
|
+
permissions: permissionTree,
|
|
246
|
+
parentId: user.parentId,
|
|
247
|
+
referenceId: user.referenceId,
|
|
248
|
+
branchId,
|
|
249
|
+
dispatchCenterId,
|
|
250
|
+
departmentId,
|
|
251
|
+
designationId,
|
|
252
|
+
};
|
|
253
|
+
return {
|
|
254
|
+
status: true,
|
|
255
|
+
message: 'Login successful',
|
|
256
|
+
data: {
|
|
257
|
+
user: {
|
|
258
|
+
id: user.id,
|
|
259
|
+
email: user.email,
|
|
260
|
+
roleId: user.roleId,
|
|
261
|
+
roleName: roleName,
|
|
262
|
+
moduleId: effectiveModuleId,
|
|
263
|
+
name: `${user.firstName} ${user.lastName}`,
|
|
264
|
+
firstName: user.firstName,
|
|
265
|
+
lastName: user.lastName,
|
|
266
|
+
mobileNo: user.mobileNo,
|
|
267
|
+
userImage: user.userImage,
|
|
268
|
+
employeeId: user.referenceId,
|
|
269
|
+
parentId: user.parentId,
|
|
270
|
+
referenceId: user.referenceId,
|
|
271
|
+
branchId,
|
|
272
|
+
dispatchCenterId,
|
|
273
|
+
departmentId,
|
|
274
|
+
designationId,
|
|
275
|
+
profile_photo_url: `${this.uploadPhotoDir}`,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
access_token: this.jwtService.sign(payload),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Extract clean IPv4 address from request
|
|
283
|
+
* Handles various formats: IPv6-mapped, plain IPv4, descriptive text
|
|
284
|
+
*/
|
|
285
|
+
extractUserIpv4(clientIp) {
|
|
286
|
+
if (!clientIp)
|
|
287
|
+
return '';
|
|
288
|
+
return this.normalizeIp(clientIp);
|
|
289
|
+
}
|
|
290
|
+
async findUserById(id) {
|
|
291
|
+
return this.userRepo.findOne({ where: { id } });
|
|
292
|
+
}
|
|
293
|
+
async loadPermissions(userId) {
|
|
294
|
+
// Feature Permissions
|
|
295
|
+
const featureAccessList = await this.featureAccessRepo.find({
|
|
296
|
+
where: { userId, isDeleted: 0 },
|
|
297
|
+
});
|
|
298
|
+
const featurePermissions = {};
|
|
299
|
+
const allFeatures = await this.featureRepo.find();
|
|
300
|
+
const featureMap = new Map(allFeatures.map((f) => [f.id, f]));
|
|
301
|
+
for (const access of featureAccessList) {
|
|
302
|
+
const feature = featureMap.get(access.featureId);
|
|
303
|
+
if (!feature)
|
|
304
|
+
continue;
|
|
305
|
+
const perms = [];
|
|
306
|
+
if (access.canView)
|
|
307
|
+
perms.push('view');
|
|
308
|
+
if (access.canCreate)
|
|
309
|
+
perms.push('create');
|
|
310
|
+
if (access.canModify)
|
|
311
|
+
perms.push('update');
|
|
312
|
+
if (access.canDelete)
|
|
313
|
+
perms.push('delete');
|
|
314
|
+
if (access.canImport)
|
|
315
|
+
perms.push('import');
|
|
316
|
+
if (access.canExport)
|
|
317
|
+
perms.push('export');
|
|
318
|
+
if (perms.length) {
|
|
319
|
+
featurePermissions[feature.featureName] = perms;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Module Access - only count active (non-deleted, status=1) assignments
|
|
323
|
+
const moduleAccess = await this.moduleAccessRepo.find({
|
|
324
|
+
where: { userId, isDeleted: 0, status: 1 },
|
|
325
|
+
});
|
|
326
|
+
const moduleIds = moduleAccess.map((m) => m.moduleId);
|
|
327
|
+
// Route Permissions
|
|
328
|
+
const screenPermissionsList = await this.screenPermissionRepo.find({
|
|
329
|
+
where: { userId, isActive: true },
|
|
330
|
+
});
|
|
331
|
+
const routePermissions = {};
|
|
332
|
+
const allRoutes = await this.routeRepo.find();
|
|
333
|
+
const routeMap = new Map(allRoutes.map((r) => [r.id, r]));
|
|
334
|
+
for (const screen of screenPermissionsList) {
|
|
335
|
+
const route = routeMap.get(screen.moduleRouteId);
|
|
336
|
+
if (!route)
|
|
337
|
+
continue;
|
|
338
|
+
const perms = [];
|
|
339
|
+
const keys = ['view', 'create', 'update', 'delete', 'import', 'export'];
|
|
340
|
+
for (const key of keys) {
|
|
341
|
+
if (screen.permissions?.[key]) {
|
|
342
|
+
perms.push(key);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (perms.length) {
|
|
346
|
+
routePermissions[route.routeName] = perms;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
features: featurePermissions,
|
|
351
|
+
modules: moduleIds,
|
|
352
|
+
routes: routePermissions,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
exports.AuthService = AuthService;
|
|
357
|
+
exports.AuthService = AuthService = __decorate([
|
|
358
|
+
(0, common_1.Injectable)(),
|
|
359
|
+
__param(1, (0, common_1.Inject)(constants_1.AUTH_CORE_OPTIONS)),
|
|
360
|
+
__metadata("design:paramtypes", [jwt_1.JwtService, Object])
|
|
361
|
+
], AuthService);
|