Coverage for server / utilities / user_id_helper.py: 96%
26 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-10-04 09:33 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-10-04 09:33 +0000
1from bson import ObjectId
4def to_user_id(uuid_str: str):
5 """Convert uuid string to ObjectId if valid, else return as-is.
7 Local JWT users have ObjectId-compatible uuids -> returns ObjectId.
8 Auth0 users have 'auth0|abc123' uuids -> returns string as-is.
9 """
10 if isinstance(uuid_str, ObjectId):
11 return uuid_str
12 if ObjectId.is_valid(str(uuid_str)):
13 return ObjectId(str(uuid_str))
14 return str(uuid_str)
17def enrollment_id_conditions(user_id, email: str | None) -> list:
18 """Build $or conditions matching a class roster entry by _id OR email.
20 Some enrollment records carry a duplicate/stale account _id instead of
21 the live account's (e.g. from a merged/duplicate user document sharing
22 the same email), so relying on _id alone can miss a real enrollment.
23 Every enrollment check against `ClassModel.students` should use this.
24 """
25 conditions = [{"_id": user_id}]
26 if email:
27 conditions.append({"email": email})
28 return conditions
31def resolve_user_query(user_id) -> dict:
32 """Build MongoDB query filter that works for both ObjectId and Auth0 user IDs.
34 ObjectId-compatible strings -> {"_id": ObjectId(uuid_str)}
35 Auth0 sub strings (e.g. "auth0|abc123") -> {"auth0_user_id": uuid_str}
36 """
37 if isinstance(user_id, ObjectId):
38 return {"_id": user_id}
40 user_id_str = str(user_id)
41 if ObjectId.is_valid(user_id_str):
42 return {"_id": ObjectId(user_id_str)}
43 return {"auth0_user_id": user_id_str}
46def caller_owns(request, candidate) -> bool:
47 """True when ``candidate`` identifies the authenticated caller themselves.
49 Accepts either identifier the caller legitimately carries — ``mongodb_id``
50 or ``uuid`` — because which of the two a client echoes back is not
51 something the API has ever specified. Comparing against only the first
52 non-null of the pair, as the teacher branch of user_account_fetch used to,
53 would refuse a caller passing their own id in the other form.
55 Anything not in that set belongs to somebody else, whether it is another
56 user's id, an unknown one, or not an id at all.
57 """
58 user_details = request.state.user_details
59 own = {
60 str(user_details.get("mongodb_id") or ""),
61 str(user_details.get("uuid") or ""),
62 } - {""}
63 return str(candidate) in own
66def user_id_from_request(request, explicit_id: str | None = None) -> str:
67 """Return the authenticated user's MongoDB id string for account lookups."""
68 user_details = request.state.user_details
69 return str(
70 explicit_id or user_details.get("mongodb_id") or user_details.get("uuid") or ""
71 )