Skip to content

Conversation

@coratgerl
Copy link
Contributor

@coratgerl coratgerl commented Nov 6, 2025

Issue

Fixes: #9510

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)

Summary by CodeRabbit

  • New Features

    • New beforePasswordResetRequest Cloud Code hook to customize password reset requests with access to user and request metadata.
    • Optional rate-limiting for the password-reset endpoint and ability to block resets via validation logic.
  • Bug Fixes / Improvements

    • Improved validation and clearer error messages around allowed password-reset triggers.
  • Tests

    • Expanded test coverage for beforePasswordResetRequest scenarios and error handling.

@parse-github-assistant
Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title feat: add beforePasswordResetRequest hook feat: Add beforePasswordResetRequest hook Nov 6, 2025
@parse-github-assistant
Copy link

parse-github-assistant bot commented Nov 6, 2025

🚀 Thanks for opening this pull request!

@coratgerl coratgerl changed the title feat: Add beforePasswordResetRequest hook feat: add beforePasswordResetRequest hook Nov 6, 2025
@parseplatformorg
Copy link
Contributor

parseplatformorg commented Nov 6, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@parse-github-assistant
Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title feat: add beforePasswordResetRequest hook feat: Add beforePasswordResetRequest hook Nov 6, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

📝 Walkthrough

Walkthrough

Adds a new Cloud Code trigger beforePasswordResetRequest, registers it in trigger types, exposes Parse.Cloud.beforePasswordResetRequest(...), invokes the hook during password-reset requests in the Users router, and adds extensive tests validating behavior and request context.

Changes

Cohort / File(s) Change Summary
Tests
spec/CloudCode.spec.js
Adds extensive tests for beforePasswordResetRequest: valid user flows, blocking behavior (including with attached files), non-existent email handling, request object properties (object, headers, ip, installationId, context, config), class restriction validation, and integrates new trigger into existing trigger-validation tests.
Router / Password Reset Flow
src/Routers/UsersRouter.js
Updates handleResetRequest to lookup user by token or by email/username, sanitize auth data, expand attached user files, inflate the user object, and execute the beforePasswordResetRequest trigger before sending reset email. Imports inflate from ../triggers.
Cloud Code API
src/cloud-code/Parse.Cloud.js
Adds Parse.Cloud.beforePasswordResetRequest(handler, validationHandler) supporting optional class/constructor arg and optional validationHandler.rateLimit to configure rate limiting for /requestPasswordReset POST. Registers trigger via triggers.addTrigger.
Trigger Types & Request Handling
src/triggers.js
Adds new trigger type beforePasswordResetRequest to exported Types, extends trigger validation to include this type for _User, and updates getRequestObject to copy context for this trigger where applicable.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant UsersRouter as UsersRouter\n(handleResetRequest)
    participant Triggers as Trigger Registry
    participant Hook as beforePasswordResetRequest\nHandler
    participant Email as Email Adapter

    Client->>UsersRouter: POST /requestPasswordReset
    UsersRouter->>UsersRouter: Find user by token or email/username
    UsersRouter->>UsersRouter: Sanitize auth data & expand files
    UsersRouter->>Triggers: Invoke beforePasswordResetRequest with inflated user
    Triggers->>Hook: Call handler(request)
    alt Handler allows
        Hook-->>Triggers: Return success
        Triggers-->>UsersRouter: Continue
        UsersRouter->>Email: sendPasswordResetEmail
        Email-->>Client: 200 OK
    else Handler throws
        Hook-->>Triggers: Throw error
        Triggers-->>UsersRouter: Propagate error
        UsersRouter-->>Client: Error response
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Focus review on: spec/CloudCode.spec.js (many new scenarios), src/Routers/UsersRouter.js (user lookup, file inflation, trigger invocation), src/cloud-code/Parse.Cloud.js (API signature and rate-limit wiring), and src/triggers.js (Types and request-object logic).

Possibly related PRs

Suggested reviewers

  • Moumouls

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a new beforePasswordResetRequest hook.
Description check ✅ Passed The description references the linked issue #9510, indicates tests and documentation have been added, but lacks detailed explanation of the approach.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #9510 requirements: adds beforePasswordResetRequest trigger, enables email validation before password reset processing, and includes comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the beforePasswordResetRequest hook across tests, triggers, routers, and Cloud Code APIs as specified in issue #9510.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c354da3 and e30572d.

📒 Files selected for processing (1)
  • src/Routers/UsersRouter.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Routers/UsersRouter.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redis Cache
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: Node 20
  • GitHub Check: Node 22
  • GitHub Check: Node 18
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: Docker Build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov
Copy link

codecov bot commented Nov 6, 2025

Codecov Report

❌ Patch coverage is 96.29630% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.06%. Comparing base (9776386) to head (e30572d).
⚠️ Report is 4 commits behind head on alpha.

Files with missing lines Patch % Lines
src/cloud-code/Parse.Cloud.js 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #9906      +/-   ##
==========================================
+ Coverage   93.04%   93.06%   +0.01%     
==========================================
  Files         187      187              
  Lines       15160    15182      +22     
  Branches      177      177              
==========================================
+ Hits        14106    14129      +23     
+ Misses       1042     1041       -1     
  Partials       12       12              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/triggers.js (1)

62-66: Validation updated to restrict to _User — OK; consider throwing Error objects.

Logic is correct; throwing strings matches existing pattern but is brittle. Consider new Parse.Error for future cleanup.

src/Routers/UsersRouter.js (1)

449-474: Minor: reduce variables and narrow scope.

userResults is only used to grab the first element. Simplify to a single const userRecord to reduce cognitive load.

src/cloud-code/Parse.Cloud.js (1)

352-394: Add validator wiring and validate input (parity + future-proofing).

Current implementation mirrors beforeLogin (rateLimit only). If you want validator features (skipWithMasterKey, requireMaster, etc.) or just to validate rateLimit shape, call validateValidator and pass validationHandler to addTrigger.

 ParseCloud.beforePasswordResetRequest = function (handler, validationHandler) {
   let className = '_User';
   if (typeof handler === 'string' || isParseObjectConstructor(handler)) {
     // validation will occur downstream, this is to maintain internal
     // code consistency with the other hook types.
     className = triggers.getClassName(handler);
     handler = arguments[1];
     validationHandler = arguments.length >= 2 ? arguments[2] : null;
   }
-  triggers.addTrigger(triggers.Types.beforePasswordResetRequest, className, handler, Parse.applicationId);
+  validateValidator(validationHandler);
+  triggers.addTrigger(
+    triggers.Types.beforePasswordResetRequest,
+    className,
+    handler,
+    Parse.applicationId,
+    validationHandler
+  );
   if (validationHandler && validationHandler.rateLimit) {
     addRateLimit(
       { requestPath: `/requestPasswordReset`, requestMethods: 'POST', ...validationHandler.rateLimit },
       Parse.applicationId,
       true
     );
   }
 };

Also consider documenting the optional validationHandler param in the JSDoc.

spec/CloudCode.spec.js (2)

3782-3978: Prefer async/await without done in new tests to reduce flakiness.

The new tests use async done => { ...; done(); }. Drop done and rely on async function completion/throws instead. This is the repository’s preferred pattern. Based on learnings.


3800-3816: Add an assertion that password/authData are not exposed to Cloud Code.

After the UsersRouter sanitization fix, add a quick check:

 Parse.Cloud.beforePasswordResetRequest(req => {
   // existing expectations...
+  expect(req.object.get('password')).toBeUndefined();
+  expect(req.object.get('authData')).toBeUndefined();
 });

Helps prevent regressions.

Also applies to: 3836-3859, 3878-3904, 3932-3962

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f27b050 and c354da3.

📒 Files selected for processing (4)
  • spec/CloudCode.spec.js (2 hunks)
  • src/Routers/UsersRouter.js (2 hunks)
  • src/cloud-code/Parse.Cloud.js (1 hunks)
  • src/triggers.js (3 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-10-16T19:27:05.311Z
Learnt from: Moumouls
Repo: parse-community/parse-server PR: 9883
File: spec/CloudCodeLogger.spec.js:410-412
Timestamp: 2025-10-16T19:27:05.311Z
Learning: In spec/CloudCodeLogger.spec.js, the test "should log cloud function triggers using the silent log level" (around lines 383-420) is known to be flaky and requires the extra `await new Promise(resolve => setTimeout(resolve, 100))` timeout after awaiting `afterSavePromise` for reliability, even though it may appear redundant.

Applied to files:

  • spec/CloudCode.spec.js
📚 Learning: 2025-08-27T12:33:06.237Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:467-477
Timestamp: 2025-08-27T12:33:06.237Z
Learning: In the Parse Server codebase, maybeRunAfterFindTrigger is called in production with Parse.Query objects constructed via withJSON(), so the plain object query handling bug only affects tests, not production code paths.

Applied to files:

  • spec/CloudCode.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.

Applied to files:

  • spec/CloudCode.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.

Applied to files:

  • spec/CloudCode.spec.js
📚 Learning: 2025-05-04T20:41:05.147Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.

Applied to files:

  • spec/CloudCode.spec.js
🧬 Code graph analysis (3)
src/Routers/UsersRouter.js (1)
src/triggers.js (2)
  • inflate (1002-1008)
  • maybeRunTrigger (895-998)
src/cloud-code/Parse.Cloud.js (2)
src/Routers/CloudCodeRouter.js (1)
  • triggers (4-4)
src/middlewares.js (2)
  • addRateLimit (520-623)
  • addRateLimit (520-623)
spec/CloudCode.spec.js (2)
spec/helper.js (2)
  • Parse (4-4)
  • reconfigureServer (180-214)
src/cloud-code/Parse.Cloud.js (1)
  • emailAdapter (604-604)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Docker Build
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: Node 22
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: Node 18
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: Node 20
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: Redis Cache
🔇 Additional comments (2)
src/triggers.js (2)

9-9: New trigger type added correctly.

Entry in Types for beforePasswordResetRequest looks consistent and non-breaking.


291-296: Context exposure includes beforePasswordResetRequest — LGTM.

Request.context now set for this trigger type; aligns with other auth-related hooks.

Comment on lines +454 to +471
userResults = await req.config.database.find('_User', {
_perishable_token: token,
_perishable_token_expires_at: { $lt: Parse._encode(new Date()) },
});
if (results && results[0] && results[0].email) {
email = results[0].email;
if (userResults && userResults.length > 0) {
userData = userResults[0];
if (userData.email) {
email = userData.email;
}
}
// Or using email if no token provided
} else if (typeof email === 'string') {
userResults = await req.config.database.find(
'_User',
{ $or: [{ email }, { username: email, email: { $exists: false } }] },
{ limit: 1 },
Auth.maintenance(req.config)
);
Copy link

@coderabbitai coderabbitai bot Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Token lookup uses inverted expiry comparator and lacks Auth.maintenance/limit.

  • _perishable_token_expires_at should be greater than “now” for a valid token.
  • Use Auth.maintenance like the email path for consistency.
  • Limit results to 1.

Apply:

-      userResults = await req.config.database.find('_User', {
-        _perishable_token: token,
-        _perishable_token_expires_at: { $lt: Parse._encode(new Date()) },
-      });
+      userResults = await req.config.database.find(
+        '_User',
+        {
+          _perishable_token: token,
+          _perishable_token_expires_at: { $gt: Parse._encode(new Date()) },
+        },
+        { limit: 1 },
+        Auth.maintenance(req.config)
+      );
🤖 Prompt for AI Agents
In src/Routers/UsersRouter.js around lines 454 to 471, the token lookup query
uses the wrong expiry comparator and is missing the same options as the email
path; change the _perishable_token_expires_at check to $gt Parse._encode(new
Date()) so only unexpired tokens match, and call req.config.database.find with
the same options as the email branch: pass { limit: 1 } and
Auth.maintenance(req.config) as the query options so the lookup is limited to
one result and runs under maintenance context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtrezza an opininion since this code already exists before my modifcations ?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add beforePasswordResetRequest trigger

2 participants