ecrs-auth-core 1.0.112 → 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/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({
|
|
@@ -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);
|