ecrs-auth-core 1.0.110 → 1.0.113
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-customer.controller.js +5 -2
- package/dist/auth-customer.service.d.ts +2 -1
- package/dist/auth-customer.service.js +13 -3
- package/dist/auth.service.js +6 -5
- package/dist/jwt/jwt-customer.strategy.d.ts +3 -1
- package/dist/jwt/jwt-customer.strategy.js +44 -2
- package/package.json +1 -1
- package/dist/entities/user-customer-module-access.entity copy.d.ts +0 -13
- package/dist/entities/user-customer-module-access.entity copy.js +0 -76
|
@@ -28,8 +28,11 @@ let AuthCustomerController = class AuthCustomerController {
|
|
|
28
28
|
const clientIp = this.getClientIp(request);
|
|
29
29
|
const userAgent = request.get("user-agent") || "Unknown";
|
|
30
30
|
const additionalData = this.extractClientData(request, userAgent);
|
|
31
|
-
|
|
32
|
-
const
|
|
31
|
+
const host = request.get("host") || "";
|
|
32
|
+
const hostParts = host.split(".");
|
|
33
|
+
const subdomain = hostParts.length > 2 ? hostParts[0] : null;
|
|
34
|
+
console.log(`📍 Customer Emplyee login attempt from IP: ${clientIp}, User-Agent: ${userAgent}, subdomain: ${subdomain ?? "none"}`);
|
|
35
|
+
const user = await this.authCustomerService.validateUser(body.email, body.password, clientIp, subdomain ?? undefined);
|
|
33
36
|
if (!user) {
|
|
34
37
|
await this.authCustomerService
|
|
35
38
|
.saveLastLogin({ email: body.email }, clientIp, "failed", "Invalid credentials or IP not allowed", additionalData)
|
|
@@ -12,7 +12,7 @@ export declare class AuthCustomerService {
|
|
|
12
12
|
private readonly employeeWorkProfileRepo;
|
|
13
13
|
private uploadPhotoDir;
|
|
14
14
|
constructor(jwtService: JwtService, options: AuthCustomerCoreOptions);
|
|
15
|
-
validateUser(email: string, password: string, clientIp?: string): Promise<UserCustomer>;
|
|
15
|
+
validateUser(email: string, password: string, clientIp?: string, subdomain?: string): Promise<UserCustomer>;
|
|
16
16
|
hasModuleAccess(userId: number, moduleId: number): Promise<boolean>;
|
|
17
17
|
saveLastLogin(user: UserCustomer, clientIp: string, loginStatus?: "success" | "failed" | "blocked", failureReason?: string, additionalData?: {
|
|
18
18
|
browser?: string;
|
|
@@ -63,5 +63,6 @@ export declare class AuthCustomerService {
|
|
|
63
63
|
access_token: string;
|
|
64
64
|
}>;
|
|
65
65
|
findUserById(id: number): Promise<UserCustomer | null>;
|
|
66
|
+
getCustomerIdBySubdomain(subdomain: string): Promise<number | null>;
|
|
66
67
|
findEcrsEmployeeInCustomer(ecrsEmployeeId: number): Promise<UserCustomer | null>;
|
|
67
68
|
}
|
|
@@ -64,7 +64,7 @@ let AuthCustomerService = class AuthCustomerService {
|
|
|
64
64
|
this.loginDetailsRepo = repositories.loginDetailsRepo || null;
|
|
65
65
|
this.employeeWorkProfileRepo = repositories.employeeWorkProfileRepo || null;
|
|
66
66
|
}
|
|
67
|
-
async validateUser(email, password, clientIp) {
|
|
67
|
+
async validateUser(email, password, clientIp, subdomain) {
|
|
68
68
|
if (!email || !password || password.length > 72 || email.length > 254) {
|
|
69
69
|
throw new common_1.UnauthorizedException("Invalid credentials");
|
|
70
70
|
}
|
|
@@ -82,8 +82,14 @@ let AuthCustomerService = class AuthCustomerService {
|
|
|
82
82
|
if (!user || !isValid) {
|
|
83
83
|
throw new common_1.UnauthorizedException("Invalid credentials");
|
|
84
84
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
85
|
+
if (subdomain) {
|
|
86
|
+
const rows = await this.userRepo.query(`SELECT customer_id FROM public.tbl_super_customer_ets_setup_details WHERE subdomain = $1 LIMIT 1`, [subdomain]);
|
|
87
|
+
const customerId = rows[0]?.customer_id;
|
|
88
|
+
if (!customerId || customerId !== user.parentId) {
|
|
89
|
+
console.log(`❌ Domain not match: subdomain="${subdomain}" → customer_id=${customerId ?? "not found"}, user.parentId=${user.parentId}`);
|
|
90
|
+
throw new common_1.UnauthorizedException("Invalid domain");
|
|
91
|
+
}
|
|
92
|
+
console.log(`✅ Domain match: subdomain="${subdomain}" → customer_id=${customerId} matches user.parentId=${user.parentId}`);
|
|
87
93
|
}
|
|
88
94
|
return user;
|
|
89
95
|
}
|
|
@@ -318,6 +324,10 @@ let AuthCustomerService = class AuthCustomerService {
|
|
|
318
324
|
async findUserById(id) {
|
|
319
325
|
return this.userRepo.findOne({ where: { id } });
|
|
320
326
|
}
|
|
327
|
+
async getCustomerIdBySubdomain(subdomain) {
|
|
328
|
+
const rows = await this.userRepo.query(`SELECT customer_id FROM public.tbl_super_customer_ets_setup_details WHERE subdomain = $1 LIMIT 1`, [subdomain]);
|
|
329
|
+
return rows[0]?.customer_id ?? null;
|
|
330
|
+
}
|
|
321
331
|
// In auth-customer.service.ts — add this new method
|
|
322
332
|
async findEcrsEmployeeInCustomer(ecrsEmployeeId) {
|
|
323
333
|
return this.userRepo.findOne({
|
package/dist/auth.service.js
CHANGED
|
@@ -82,8 +82,8 @@ let AuthService = class AuthService {
|
|
|
82
82
|
const normalizedEmail = email.trim().toLowerCase();
|
|
83
83
|
const whereClause = {
|
|
84
84
|
email: (0, typeorm_1.ILike)(normalizedEmail),
|
|
85
|
-
deletedBy:
|
|
86
|
-
deletedAt:
|
|
85
|
+
deletedBy: (0, typeorm_1.IsNull)(),
|
|
86
|
+
deletedAt: (0, typeorm_1.IsNull)(),
|
|
87
87
|
status: 1,
|
|
88
88
|
};
|
|
89
89
|
//role-id
|
|
@@ -100,7 +100,6 @@ let AuthService = class AuthService {
|
|
|
100
100
|
else if (moduleId !== undefined && modulearray.includes(moduleId)) {
|
|
101
101
|
whereClause.roleId = (0, typeorm_1.In)([1, 2, 3]);
|
|
102
102
|
}
|
|
103
|
-
console.log(`🔍 Validating user with email: ${normalizedEmail}, moduleId: ${moduleId}, whereClause:`, whereClause);
|
|
104
103
|
const user = await this.userRepo.findOne({ where: whereClause });
|
|
105
104
|
// Always run bcrypt.compare regardless of whether the user was found.
|
|
106
105
|
// This prevents timing-based user enumeration: both paths take the same time.
|
|
@@ -108,13 +107,15 @@ let AuthService = class AuthService {
|
|
|
108
107
|
// const DUMMY_HASH = '$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ012345';
|
|
109
108
|
// const hashToCompare = user?.password ?? DUMMY_HASH;
|
|
110
109
|
// const isValid = await bcrypt.compare(password, hashToCompare);
|
|
111
|
-
console.log(`🔐 Comparing password for user ${user?.id}`);
|
|
112
|
-
console.log(`🔐 Comparing password for user ${user?.password}`);
|
|
110
|
+
// console.log(`🔐 Comparing password for user ${user?.id}`);
|
|
111
|
+
// console.log(`🔐 Comparing password for user ${user?.password}`);
|
|
113
112
|
const isValid = user
|
|
114
113
|
? await bcrypt.compare(password, user.password)
|
|
115
114
|
: false;
|
|
116
115
|
// Single generic message — never reveal whether the email exists
|
|
117
116
|
if (!user || !isValid) {
|
|
117
|
+
console.log(`🔍 Validating user with email: ${normalizedEmail}, moduleId: ${moduleId}, whereClause:`, whereClause);
|
|
118
|
+
console.log(`🔐 Comparing password for user ${user?.id}`);
|
|
118
119
|
throw new common_1.UnauthorizedException("Invalid credentials");
|
|
119
120
|
}
|
|
120
121
|
if (clientIp && this.ipRestrictionsRepo) {
|
|
@@ -4,13 +4,15 @@ declare const JwtCustomerStrategy_base: new (...args: any[]) => Strategy;
|
|
|
4
4
|
export declare class JwtCustomerStrategy extends JwtCustomerStrategy_base {
|
|
5
5
|
private readonly authCustomerService;
|
|
6
6
|
constructor(authCustomerService: AuthCustomerService);
|
|
7
|
-
validate(payload: any): Promise<{
|
|
7
|
+
validate(req: any, payload: any): Promise<{
|
|
8
8
|
id: number;
|
|
9
9
|
email: string;
|
|
10
10
|
roleId: number;
|
|
11
11
|
moduleId: any;
|
|
12
12
|
is_ecrs_employee: boolean;
|
|
13
13
|
referenceId: number;
|
|
14
|
+
customerIds: number[];
|
|
15
|
+
parentId: any;
|
|
14
16
|
}>;
|
|
15
17
|
}
|
|
16
18
|
export {};
|
|
@@ -20,15 +20,55 @@ let JwtCustomerStrategy = class JwtCustomerStrategy extends (0, passport_1.Passp
|
|
|
20
20
|
jwtFromRequest: passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
21
21
|
ignoreExpiration: false,
|
|
22
22
|
secretOrKey: process.env.JWT_SECRET || "your-secret-key",
|
|
23
|
+
passReqToCallback: true,
|
|
23
24
|
});
|
|
24
25
|
this.authCustomerService = authCustomerService;
|
|
25
26
|
}
|
|
26
|
-
async validate(payload) {
|
|
27
|
-
// 1.
|
|
27
|
+
async validate(req, payload) {
|
|
28
|
+
// 1. Subdomain check against tbl_super_customer_ets_setup_details
|
|
29
|
+
const host = req.get("host") || "";
|
|
30
|
+
const hostParts = host.split(".");
|
|
31
|
+
const subdomain = hostParts.length > 2 ? hostParts[0] : null;
|
|
32
|
+
if (subdomain) {
|
|
33
|
+
const customerId = await this.authCustomerService.getCustomerIdBySubdomain(subdomain);
|
|
34
|
+
if (!customerId) {
|
|
35
|
+
console.log(`❌ Domain not match: subdomain="${subdomain}" not found in setup details`);
|
|
36
|
+
throw new common_1.UnauthorizedException("INVALID_DOMAIN");
|
|
37
|
+
}
|
|
38
|
+
// Will be compared with user.parentId after user is loaded
|
|
39
|
+
payload._resolvedCustomerId = customerId;
|
|
40
|
+
console.log(`🔍 Subdomain "${subdomain}" → customer_id=${customerId}`);
|
|
41
|
+
}
|
|
42
|
+
// 2. Find user in tbl_users_customer
|
|
28
43
|
const user = await this.authCustomerService.findUserById(payload.id);
|
|
29
44
|
if (!user) {
|
|
30
45
|
throw new common_1.UnauthorizedException("INVALID_USER");
|
|
31
46
|
}
|
|
47
|
+
// 3a. Verify resolved customer_id matches user.parentId
|
|
48
|
+
if (payload._resolvedCustomerId !== undefined) {
|
|
49
|
+
if (user.roleId === 1) {
|
|
50
|
+
// Superadmin: skip domain restriction — _resolvedCustomerId is returned as parentId
|
|
51
|
+
console.log(`✅ Superadmin domain bypass: customer_id=${payload._resolvedCustomerId} accepted for superadmin user=${user.id}`);
|
|
52
|
+
}
|
|
53
|
+
else if (payload._resolvedCustomerId !== user.parentId &&
|
|
54
|
+
user.is_ecrs_employee !== true) {
|
|
55
|
+
console.log(`❌ Domain not match: customer_id=${payload._resolvedCustomerId} does not match user.parentId=${user.parentId}`);
|
|
56
|
+
throw new common_1.UnauthorizedException("INVALID_DOMAIN");
|
|
57
|
+
}
|
|
58
|
+
else if (user.is_ecrs_employee === true) {
|
|
59
|
+
const allowedIds = Array.isArray(user.customerIds)
|
|
60
|
+
? user.customerIds
|
|
61
|
+
: [];
|
|
62
|
+
if (!allowedIds.includes(payload._resolvedCustomerId)) {
|
|
63
|
+
console.log(`❌ Domain not match for ECRS employee: customer_id=${payload._resolvedCustomerId} not found in user.customerIds=${JSON.stringify(allowedIds)}`);
|
|
64
|
+
throw new common_1.UnauthorizedException("INVALID_DOMAIN");
|
|
65
|
+
}
|
|
66
|
+
console.log(`✅ Domain match for ECRS employee: customer_id=${payload._resolvedCustomerId} found in user.customerIds=${JSON.stringify(allowedIds)}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(`✅ Domain match: customer_id=${payload._resolvedCustomerId} matches user.parentId=${user.parentId}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
32
72
|
// 2. Token version check — catches password change / logout-all
|
|
33
73
|
if (user.token_version !== payload.tokenVersion) {
|
|
34
74
|
console.warn(`⚠️ Token version mismatch for customer ${user.id}. ` +
|
|
@@ -68,6 +108,8 @@ let JwtCustomerStrategy = class JwtCustomerStrategy extends (0, passport_1.Passp
|
|
|
68
108
|
moduleId: payload.moduleId ?? user.moduleId,
|
|
69
109
|
is_ecrs_employee: user.is_ecrs_employee ?? false,
|
|
70
110
|
referenceId: user.referenceId ?? null,
|
|
111
|
+
customerIds: user.customerIds ?? [],
|
|
112
|
+
parentId: payload._resolvedCustomerId ?? user.parentId,
|
|
71
113
|
};
|
|
72
114
|
}
|
|
73
115
|
};
|
package/package.json
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare class UserCustomerModuleAccess {
|
|
2
|
-
id: number;
|
|
3
|
-
customer_user_id: number;
|
|
4
|
-
moduleId: number;
|
|
5
|
-
accessLevel: string;
|
|
6
|
-
status: number;
|
|
7
|
-
permissions: string[];
|
|
8
|
-
createdAt: Date;
|
|
9
|
-
updatedAt: Date;
|
|
10
|
-
createdBy: number;
|
|
11
|
-
updatedBy?: number;
|
|
12
|
-
isDeleted: number;
|
|
13
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
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;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.UserCustomerModuleAccess = void 0;
|
|
13
|
-
// src/entities/user-module-access.entity.ts
|
|
14
|
-
const typeorm_1 = require("typeorm");
|
|
15
|
-
let UserCustomerModuleAccess = class UserCustomerModuleAccess {
|
|
16
|
-
};
|
|
17
|
-
exports.UserCustomerModuleAccess = UserCustomerModuleAccess;
|
|
18
|
-
__decorate([
|
|
19
|
-
(0, typeorm_1.PrimaryGeneratedColumn)(),
|
|
20
|
-
__metadata("design:type", Number)
|
|
21
|
-
], UserCustomerModuleAccess.prototype, "id", void 0);
|
|
22
|
-
__decorate([
|
|
23
|
-
(0, typeorm_1.Column)({ name: "customer_user_id" }),
|
|
24
|
-
__metadata("design:type", Number)
|
|
25
|
-
], UserCustomerModuleAccess.prototype, "customer_user_id", void 0);
|
|
26
|
-
__decorate([
|
|
27
|
-
(0, typeorm_1.Column)({ name: "module_id" }),
|
|
28
|
-
__metadata("design:type", Number)
|
|
29
|
-
], UserCustomerModuleAccess.prototype, "moduleId", void 0);
|
|
30
|
-
__decorate([
|
|
31
|
-
(0, typeorm_1.Column)({ name: "access_level", default: "view" }),
|
|
32
|
-
__metadata("design:type", String)
|
|
33
|
-
], UserCustomerModuleAccess.prototype, "accessLevel", void 0);
|
|
34
|
-
__decorate([
|
|
35
|
-
(0, typeorm_1.Column)({ type: "smallint", default: 1 }),
|
|
36
|
-
__metadata("design:type", Number)
|
|
37
|
-
], UserCustomerModuleAccess.prototype, "status", void 0);
|
|
38
|
-
__decorate([
|
|
39
|
-
(0, typeorm_1.Column)({
|
|
40
|
-
name: "permissions",
|
|
41
|
-
type: "json",
|
|
42
|
-
nullable: true,
|
|
43
|
-
}),
|
|
44
|
-
__metadata("design:type", Array)
|
|
45
|
-
], UserCustomerModuleAccess.prototype, "permissions", void 0);
|
|
46
|
-
__decorate([
|
|
47
|
-
(0, typeorm_1.Column)({
|
|
48
|
-
name: "created_at",
|
|
49
|
-
type: "timestamp",
|
|
50
|
-
default: () => "CURRENT_TIMESTAMP",
|
|
51
|
-
}),
|
|
52
|
-
__metadata("design:type", Date)
|
|
53
|
-
], UserCustomerModuleAccess.prototype, "createdAt", void 0);
|
|
54
|
-
__decorate([
|
|
55
|
-
(0, typeorm_1.Column)({
|
|
56
|
-
name: "updated_at",
|
|
57
|
-
type: "timestamp",
|
|
58
|
-
default: () => "CURRENT_TIMESTAMP",
|
|
59
|
-
}),
|
|
60
|
-
__metadata("design:type", Date)
|
|
61
|
-
], UserCustomerModuleAccess.prototype, "updatedAt", void 0);
|
|
62
|
-
__decorate([
|
|
63
|
-
(0, typeorm_1.Column)({ name: "created_by" }),
|
|
64
|
-
__metadata("design:type", Number)
|
|
65
|
-
], UserCustomerModuleAccess.prototype, "createdBy", void 0);
|
|
66
|
-
__decorate([
|
|
67
|
-
(0, typeorm_1.Column)({ name: "updated_by", nullable: true }),
|
|
68
|
-
__metadata("design:type", Number)
|
|
69
|
-
], UserCustomerModuleAccess.prototype, "updatedBy", void 0);
|
|
70
|
-
__decorate([
|
|
71
|
-
(0, typeorm_1.Column)({ name: "is_deleted", type: "smallint", default: 0 }),
|
|
72
|
-
__metadata("design:type", Number)
|
|
73
|
-
], UserCustomerModuleAccess.prototype, "isDeleted", void 0);
|
|
74
|
-
exports.UserCustomerModuleAccess = UserCustomerModuleAccess = __decorate([
|
|
75
|
-
(0, typeorm_1.Entity)({ name: "tbl_c_users_customer_module_access_new" })
|
|
76
|
-
], UserCustomerModuleAccess);
|