Skip to main content

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}