1
0
forked from Yara724/api
Files
yara724-api/src/helpers/party-access-queries.ts

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;
}