forked from Yara724/api
Fixed Lock for Field expert + user view of files
This commit is contained in:
@@ -91,6 +91,12 @@ import {
|
||||
MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS,
|
||||
} from "src/helpers/blame-party-agreement-decision";
|
||||
import { buildFileLink } from "src/helpers/urlCreator";
|
||||
import {
|
||||
buildUserLookupByPhone,
|
||||
iranMobileLookupVariants,
|
||||
normalizeIranMobile,
|
||||
partyPersonMatchesUser,
|
||||
} from "src/helpers/iran-mobile";
|
||||
|
||||
@Injectable()
|
||||
export class RequestManagementService {
|
||||
@@ -209,14 +215,8 @@ export class RequestManagementService {
|
||||
private assertPartyOwner(party: any, user: any, errMsg: string, req?: any) {
|
||||
// The initiating field-expert/registrar fills steps on behalf of parties.
|
||||
if (req && this.isBlameOnBehalfActor(req, user)) return;
|
||||
const partyUserId = party?.person?.userId
|
||||
? String(party.person.userId)
|
||||
: null;
|
||||
const ok =
|
||||
(partyUserId && partyUserId === String(user?.sub)) ||
|
||||
(party?.person?.phoneNumber &&
|
||||
party.person.phoneNumber === user?.username);
|
||||
if (!ok) throw new ForbiddenException(errMsg);
|
||||
if (partyPersonMatchesUser(party?.person, user)) return;
|
||||
throw new ForbiddenException(errMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4434,7 +4434,8 @@ export class RequestManagementService {
|
||||
throw new BadRequestException("First party not found on request");
|
||||
const userId = await this.getOrCreateUserByPhoneNumber(phone);
|
||||
if (!req.parties[firstIdx].person) req.parties[firstIdx].person = {} as any;
|
||||
req.parties[firstIdx].person.phoneNumber = phone;
|
||||
req.parties[firstIdx].person.phoneNumber =
|
||||
normalizeIranMobile(phone) ?? phone;
|
||||
req.parties[firstIdx].person.userId = userId;
|
||||
|
||||
const expertName = `${expert?.lastName || ""}`.trim() || "کارشناس";
|
||||
@@ -4591,7 +4592,9 @@ export class RequestManagementService {
|
||||
throw new BadRequestException("First party not found on request");
|
||||
if (!req.parties[firstIdx].person) req.parties[firstIdx].person = {} as any;
|
||||
req.parties[firstIdx].person.userId = firstUserId;
|
||||
req.parties[firstIdx].person.phoneNumber = dto.firstPartyPhoneNumber;
|
||||
req.parties[firstIdx].person.phoneNumber =
|
||||
normalizeIranMobile(dto.firstPartyPhoneNumber) ??
|
||||
dto.firstPartyPhoneNumber;
|
||||
|
||||
if (
|
||||
req.type === BlameRequestType.THIRD_PARTY &&
|
||||
@@ -4617,7 +4620,9 @@ export class RequestManagementService {
|
||||
if (!req.parties[secondIdx].person)
|
||||
req.parties[secondIdx].person = {} as any;
|
||||
req.parties[secondIdx].person.userId = secondUserId;
|
||||
req.parties[secondIdx].person.phoneNumber = dto.secondPartyPhoneNumber;
|
||||
req.parties[secondIdx].person.phoneNumber =
|
||||
normalizeIranMobile(dto.secondPartyPhoneNumber) ??
|
||||
dto.secondPartyPhoneNumber;
|
||||
}
|
||||
} else if (
|
||||
req.type === BlameRequestType.THIRD_PARTY &&
|
||||
@@ -4794,6 +4799,7 @@ export class RequestManagementService {
|
||||
const valid = await this.hashService.compare(otp, u.otp);
|
||||
if (!valid) throw new BadRequestException(`Invalid OTP for ${phone}`);
|
||||
const userId = u._id as Types.ObjectId;
|
||||
const canonicalPhone = normalizeIranMobile(phone) ?? phone;
|
||||
|
||||
if (role === PartyRole.FIRST) {
|
||||
if (firstIdx === -1)
|
||||
@@ -4801,13 +4807,21 @@ export class RequestManagementService {
|
||||
if (!req.parties[firstIdx].person)
|
||||
req.parties[firstIdx].person = {} as any;
|
||||
req.parties[firstIdx].person.userId = userId;
|
||||
req.parties[firstIdx].person.phoneNumber = phone;
|
||||
req.parties[firstIdx].person.phoneNumber = canonicalPhone;
|
||||
} else {
|
||||
const firstPhone =
|
||||
firstIdx !== -1
|
||||
? (req.parties[firstIdx]?.person as any)?.phoneNumber
|
||||
: undefined;
|
||||
if (firstPhone && firstPhone === phone) {
|
||||
const firstVariants = firstPhone
|
||||
? iranMobileLookupVariants(firstPhone)
|
||||
: [];
|
||||
if (
|
||||
firstPhone &&
|
||||
(firstPhone === phone ||
|
||||
firstVariants.includes(phone) ||
|
||||
firstVariants.includes(canonicalPhone))
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"Second party phone number cannot be the same as first party.",
|
||||
);
|
||||
@@ -4816,13 +4830,13 @@ export class RequestManagementService {
|
||||
if (secondIdx === -1) {
|
||||
req.parties.push({
|
||||
role: PartyRole.SECOND,
|
||||
person: { userId, phoneNumber: phone },
|
||||
person: { userId, phoneNumber: canonicalPhone },
|
||||
} as any);
|
||||
} else {
|
||||
if (!req.parties[secondIdx].person)
|
||||
req.parties[secondIdx].person = {} as any;
|
||||
req.parties[secondIdx].person.userId = userId;
|
||||
req.parties[secondIdx].person.phoneNumber = phone;
|
||||
req.parties[secondIdx].person.phoneNumber = canonicalPhone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5018,9 +5032,16 @@ export class RequestManagementService {
|
||||
user?.sub && Types.ObjectId.isValid(user.sub)
|
||||
? { "parties.person.userId": new Types.ObjectId(user.sub) }
|
||||
: null;
|
||||
const phoneFilter = user?.username
|
||||
? { "parties.person.phoneNumber": user.username }
|
||||
: null;
|
||||
const phoneVariants =
|
||||
user?.role === RoleEnum.USER && user?.username
|
||||
? iranMobileLookupVariants(user.username)
|
||||
: user?.username
|
||||
? [user.username]
|
||||
: [];
|
||||
const phoneFilter =
|
||||
phoneVariants.length > 0
|
||||
? { "parties.person.phoneNumber": { $in: phoneVariants } }
|
||||
: null;
|
||||
|
||||
const orConditions: Record<string, unknown>[] = [];
|
||||
if (userIdFilter) orConditions.push(userIdFilter);
|
||||
@@ -5061,11 +5082,8 @@ export class RequestManagementService {
|
||||
req.registrarInitiated &&
|
||||
String(req.initiatedByRegistrarId) === String(user.sub));
|
||||
|
||||
const party = req.parties?.find(
|
||||
(p: any) =>
|
||||
(p?.person?.userId &&
|
||||
String(p.person.userId) === String(user.sub)) ||
|
||||
(p?.person?.phoneNumber && p.person.phoneNumber === user?.username),
|
||||
const party = req.parties?.find((p: any) =>
|
||||
partyPersonMatchesUser(p?.person, user),
|
||||
);
|
||||
|
||||
const obj = req.toObject();
|
||||
@@ -5340,14 +5358,7 @@ export class RequestManagementService {
|
||||
String(req.initiatedByRegistrarId) === String(user?.sub);
|
||||
const isParty =
|
||||
Array.isArray(req.parties) &&
|
||||
req.parties.some((p: any) => {
|
||||
const pid = p?.person?.userId ? String(p.person.userId) : null;
|
||||
const phone = p?.person?.phoneNumber;
|
||||
return (
|
||||
(pid && pid === String(user?.sub)) ||
|
||||
(phone && phone === user?.username)
|
||||
);
|
||||
});
|
||||
req.parties.some((p: any) => partyPersonMatchesUser(p?.person, user));
|
||||
if (!isFieldExpertOwner && !isRegistrarOwner && !isParty) {
|
||||
throw new ForbiddenException("You do not have access to this request");
|
||||
}
|
||||
@@ -5359,10 +5370,22 @@ export class RequestManagementService {
|
||||
Types.ObjectId.isValid(user.sub)
|
||||
) {
|
||||
let updated = false;
|
||||
const phoneVariants = iranMobileLookupVariants(user?.username);
|
||||
for (const p of req.parties || []) {
|
||||
if (p?.person?.phoneNumber === user?.username && !p.person?.userId) {
|
||||
const person = p?.person;
|
||||
if (!person) continue;
|
||||
const phoneMatch =
|
||||
person.phoneNumber &&
|
||||
phoneVariants.some(
|
||||
(v) => v === person.phoneNumber || v === normalizeIranMobile(person.phoneNumber),
|
||||
);
|
||||
if (phoneMatch && !person?.userId) {
|
||||
p.person = p.person || {};
|
||||
(p.person as any).userId = new Types.ObjectId(user.sub);
|
||||
if (user?.username) {
|
||||
(p.person as any).phoneNumber =
|
||||
normalizeIranMobile(user.username) ?? user.username;
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
@@ -5384,14 +5407,9 @@ export class RequestManagementService {
|
||||
const visibleParties =
|
||||
isFieldExpertOwner || isRegistrarOwner
|
||||
? allParties
|
||||
: allParties.filter((p: any) => {
|
||||
const pid = p?.person?.userId ? String(p.person.userId) : null;
|
||||
const phone = p?.person?.phoneNumber;
|
||||
return (
|
||||
(pid && pid === String(user?.sub)) ||
|
||||
(phone && phone === user?.username)
|
||||
);
|
||||
});
|
||||
: allParties.filter((p: any) =>
|
||||
partyPersonMatchesUser(p?.person, user),
|
||||
);
|
||||
const parties = await Promise.all(
|
||||
visibleParties.map((p: any) => this.sanitizePartyForBlameUserView(p)),
|
||||
);
|
||||
@@ -7288,19 +7306,18 @@ export class RequestManagementService {
|
||||
private async getOrCreateUserByPhoneNumber(
|
||||
phoneNumber: string,
|
||||
): Promise<Types.ObjectId> {
|
||||
// Try to find existing user by phone number (username or mobile)
|
||||
let user = await this.userDbService.findOne({
|
||||
$or: [{ username: phoneNumber }, { mobile: phoneNumber }],
|
||||
});
|
||||
const canonical = normalizeIranMobile(phoneNumber) ?? phoneNumber.trim();
|
||||
let user = await this.userDbService.findOne(
|
||||
buildUserLookupByPhone(canonical),
|
||||
);
|
||||
|
||||
if (user) {
|
||||
return new Types.ObjectId((user as any)._id);
|
||||
}
|
||||
|
||||
// User doesn't exist, create a new one
|
||||
const newUser = await this.userDbService.createUser({
|
||||
mobile: phoneNumber,
|
||||
username: phoneNumber,
|
||||
mobile: canonical,
|
||||
username: canonical,
|
||||
otp: "",
|
||||
tokens: {
|
||||
token: "",
|
||||
|
||||
Reference in New Issue
Block a user