forked from Yara724/api
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
import { Types } from "mongoose";
|
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
|
import { iranMobileLookupVariants } from "./iran-mobile";
|
|
|
|
/** ObjectId + string variants for one or more user ids (legacy duplicate accounts). */
|
|
export function collectUserIdVariants(
|
|
...ids: (string | Types.ObjectId | undefined | null)[]
|
|
): unknown[] {
|
|
const seen = new Set<string>();
|
|
const out: unknown[] = [];
|
|
for (const id of ids) {
|
|
if (id == null || id === "") continue;
|
|
const s = String(id);
|
|
if (seen.has(s)) continue;
|
|
seen.add(s);
|
|
out.push(s);
|
|
if (Types.ObjectId.isValid(s)) {
|
|
out.push(new Types.ObjectId(s));
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
export function buildUserIdFieldOrFilter(
|
|
fieldPath: string,
|
|
idVariants: unknown[],
|
|
): Record<string, unknown> | null {
|
|
if (!idVariants.length) return null;
|
|
if (idVariants.length === 1) {
|
|
return { [fieldPath]: idVariants[0] };
|
|
}
|
|
return { $or: idVariants.map((id) => ({ [fieldPath]: id })) };
|
|
}
|
|
|
|
/**
|
|
* Mongo filter matching a user id field stored as ObjectId or legacy string.
|
|
*/
|
|
export function mongoUserIdEqualityFilter(
|
|
fieldPath: string,
|
|
userSub: string,
|
|
): Record<string, unknown> | null {
|
|
return buildUserIdFieldOrFilter(fieldPath, collectUserIdVariants(userSub));
|
|
}
|
|
|
|
/** `$or` branches: user is a party on a blame case (by userId and/or phone). */
|
|
export function buildBlamePartyAccessOrConditions(
|
|
user: {
|
|
sub?: string;
|
|
username?: string;
|
|
},
|
|
linkedUserIdStrings: string[] = [],
|
|
): Record<string, unknown>[] {
|
|
const or: Record<string, unknown>[] = [];
|
|
const idVariants = collectUserIdVariants(user.sub, ...linkedUserIdStrings);
|
|
const userIdCond = buildUserIdFieldOrFilter(
|
|
"parties.person.userId",
|
|
idVariants,
|
|
);
|
|
if (userIdCond) or.push(userIdCond);
|
|
|
|
const phoneVariants = user?.username
|
|
? iranMobileLookupVariants(user.username)
|
|
: [];
|
|
if (phoneVariants.length > 0) {
|
|
or.push({ "parties.person.phoneNumber": { $in: phoneVariants } });
|
|
}
|
|
return or;
|
|
}
|
|
|
|
/** `$or` branches: user owns a claim or is tied via blame party membership. */
|
|
export function buildClaimOwnerAccessOrConditions(
|
|
userSub: string,
|
|
username?: string,
|
|
): Record<string, unknown>[] {
|
|
const or: Record<string, unknown>[] = [];
|
|
const ownerIdCond = mongoUserIdEqualityFilter("owner.userId", userSub);
|
|
if (ownerIdCond) or.push(ownerIdCond);
|
|
return or;
|
|
}
|
|
|
|
export function buildFieldExpertInitiatedBlameFilter(
|
|
expertSub: string,
|
|
): Record<string, unknown> {
|
|
const expertOid = Types.ObjectId.isValid(expertSub)
|
|
? new Types.ObjectId(expertSub)
|
|
: expertSub;
|
|
return {
|
|
expertInitiated: true,
|
|
initiatedByFieldExpertId: expertOid,
|
|
};
|
|
}
|
|
|
|
export function buildFieldExpertClaimAccessOrConditions(
|
|
expertSub: string,
|
|
expertBlameIds: unknown[],
|
|
): Record<string, unknown>[] {
|
|
const expertOid = Types.ObjectId.isValid(expertSub)
|
|
? new Types.ObjectId(expertSub)
|
|
: expertSub;
|
|
const or: Record<string, unknown>[] = [
|
|
{ initiatedByFieldExpertId: expertOid },
|
|
];
|
|
if (expertBlameIds.length > 0) {
|
|
or.push({ blameRequestId: { $in: expertBlameIds } });
|
|
}
|
|
return or;
|
|
}
|
|
|
|
/** True when actor is the field expert who initiated this blame (any JWT shape). */
|
|
export function actorIsFieldExpertInitiator(
|
|
actor: { sub?: string; role?: string } | null | undefined,
|
|
): boolean {
|
|
return actor?.role === RoleEnum.FIELD_EXPERT && !!actor?.sub;
|
|
}
|