parse_rust_server/routes/sessions.rs
1//! `GET /sessions/me`.
2//!
3//! Upstream: `src/Routers/SessionsRouter.js`. The other three session routes are `ClassesRouter`
4//! handlers with `className()` pinned to `_Session` and live in [`crate::routes::classes`];
5//! `handleMe` is the one that is genuinely its own thing.
6//!
7//! `POST /sessions`, `PUT /sessions/:objectId` and `POST /upgradeToRevocableSession` are out of
8//! scope for 0.2.0 and are **absent** rather than answering 501. They exist to let a client mint
9//! or migrate a session directly, which is a legacy path and a set of `OPERATION_FORBIDDEN`
10//! special cases (`RestWrite.js:1221-1292`) with no bearing on the milestone claim.
11
12use parse_rust_auth::resolve_session;
13use parse_rust_core::{ErrorCode, ParseError, ParseValue};
14use parse_rust_rest::{FindOptions, ParsedClause, ParsedWhere};
15use parse_rust_storage::Constraint;
16use serde_json::Value as Json;
17
18use crate::request::RequestContext;
19use crate::state::AppState;
20
21/// `GET /sessions/me` (`SessionsRouter.js:12-61`).
22///
23/// Three messages, and none of them is `/users/me`'s. A missing token is `Session token
24/// required.`; a token that resolves to nothing at either step is `Session token not found.`
25/// `GET /users/me` answers `Invalid session token` for both, and a client matching on the string
26/// would see the difference.
27///
28/// The two-step shape is upstream's and is not redundant: the row is located with master so the
29/// token can be validated at all, then **re-fetched by objectId with the caller's own auth** so
30/// that protected fields and CLP apply to what comes back.
31pub async fn me_core(state: &AppState, rc: &RequestContext) -> Result<Json, ParseError> {
32 let Some(token) = rc.session_token.as_deref() else {
33 return Err(ParseError::new(
34 ErrorCode::InvalidSessionToken,
35 "Session token required.",
36 ));
37 };
38 let not_found = || ParseError::new(ErrorCode::InvalidSessionToken, "Session token not found.");
39
40 // Step one, under master. `resolve_session` reports its own three failures in upstream's
41 // order; every one of them means the token did not locate a usable session here.
42 let session = resolve_session(state.storage(), token)
43 .await
44 .map_err(|_| not_found())?;
45
46 // Step two, with the caller's own auth. For a master caller that is unrestricted, which is
47 // what upstream's `req.auth.isMaster ? req.auth : ...` produces.
48 //
49 // The `user` half of this is deliberately absent: the read pipeline narrows every non-master
50 // `_Session` read to the caller's own sessions, and adding a second equality on the same field
51 // is `INVALID_QUERY` rather than a redundant conjunct, because the Mongo transform refuses to
52 // let one equality silently overwrite another.
53 let mut where_ = ParsedWhere::default();
54 where_.push(ParsedClause::Field(Constraint::equal(
55 "objectId",
56 ParseValue::String(session.object_id.clone()),
57 )));
58
59 let ctx = rc.ctx(state.storage());
60 let rows = parse_rust_rest::find(
61 &ctx,
62 crate::routes::classes::SESSION_CLASS,
63 where_,
64 FindOptions {
65 limit: Some(1),
66 ..Default::default()
67 },
68 )
69 .await
70 .map_err(|_| not_found())?;
71
72 let Some(row) = rows.into_iter().next() else {
73 return Err(not_found());
74 };
75 Ok(crate::routes::classes::body_of(&row))
76}