Skip to content

feat: Add server option fileDownload to restrict file download#10394

Merged
mtrezza merged 3 commits intoparse-community:alphafrom
mtrezza:feat/filedownload-option
Apr 3, 2026
Merged

feat: Add server option fileDownload to restrict file download#10394
mtrezza merged 3 commits intoparse-community:alphafrom
mtrezza:feat/filedownload-option

Conversation

@mtrezza
Copy link
Copy Markdown
Member

@mtrezza mtrezza commented Apr 3, 2026

Pull Request

Issue

File download and metadata endpoints are fully public with no access control. While fileUpload provides granular permission flags, no equivalent exists for downloads. Developers serving files via CDNs have no way to disable direct downloads through Parse Server.

Approach

Adds a new fileDownload option mirroring the fileUpload permission model with three boolean flags: enableForAnonymousUser, enableForAuthenticatedUser, enableForPublic. All default to true to preserve current behavior (non-breaking). Master key always bypasses. Applies to both file download and metadata endpoints.

Tasks

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

Summary by CodeRabbit

  • New Features

    • Added configurable file download access control with three toggles (anonymous, authenticated, public); all default to enabled. Master and maintenance keys bypass restrictions.
  • Tests

    • Added comprehensive tests covering configuration validation, download/metadata authorization across auth contexts, and error cases for disallowed downloads.
  • Documentation

    • Clarified README: file routes are not covered by prior note and download access is controlled separately from upload options.
  • Bug Fixes

    • Enforced download/metadata authorization according to the new settings.

@parse-github-assistant
Copy link
Copy Markdown

parse-github-assistant bot commented Apr 3, 2026

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

Adds a new fileDownload configuration option (three boolean flags) with validation, docs, env mappings, tests, and request-time enforcement in FilesRouter; removes legacy session-token auth resolution for file routes and adjusts middleware order for GET/metadata endpoints.

Changes

Cohort / File(s) Summary
Options & Docs
src/Options/Definitions.js, src/Options/docs.js, src/Options/index.js
Introduce fileDownload option and FileDownloadOptions type with three boolean flags (enableForAnonymousUser, enableForAuthenticatedUser, enableForPublic, defaults true) and add JSDoc/TS annotations.
Config Mapping
resources/buildConfigDefinitions.js
Add FileDownloadOptions to nested option types and map env prefix PARSE_SERVER_FILE_DOWNLOAD_ for config generator.
Configuration Validation
src/Config.js
Add import/validation for fileDownload; new validateFileDownloadOptions enforces object shape, boolean flags, and fills defaults; validateOptions now normalizes fileDownload to {} when omitted.
Files Router & Auth Flow
src/Routers/FilesRouter.js
Remove _resolveAuth and auth import; add initInfo middleware and FilesRouter._validateFileDownload; enforce fileDownload rules in GET/metadata handlers; treat maintenance key as upload bypass.
Tests
spec/FileDownload.spec.js
Add comprehensive tests for config validation, defaulting, allowed/forbidden download flows (public/authenticated/anonymous/master/maintenance) and metadata endpoint behavior.
README
README.md
Clarify routeAllowList note: file upload is governed by fileUpload, while downloads/metadata access are governed by fileDownload.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Client as Client (browser/tool)
participant Router as FilesRouter
participant Session as Middlewares.handleParseSession
participant Config as Config (fileDownload)
participant Storage as FilesAdapter/Storage
participant Auth as AuthContext

Client->>Router: GET /files/:appId/*filepath
Router->>Router: initInfo (populate req.info / req.auth fallback)
Router->>Session: handleParseSession (resolve session -> req.auth)
Session->>Auth: produce auth context (isMaster/isMaintenance/authenticated/anonymous)
Router->>Config: _validateFileDownload(req, config.fileDownload)
alt allowed
    Router->>Storage: fetch file
    Storage-->>Router: file stream / metadata
    Router-->>Client: 200 + file
else forbidden
    Router-->>Client: 403 Parse.Error.OPERATION_FORBIDDEN

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Security Check ⚠️ Warning Session token validation uses falsy check (!sessionToken) instead of null check, allowing empty headers to bypass validation and fall back to public access. Replace !sessionToken with sessionToken == null in initInfo middleware to properly reject empty string tokens instead of treating them as missing.
Engage In Review Feedback ⚠️ Warning Review feedback requested changing line 103 in src/Routers/FilesRouter.js from !sessionToken to sessionToken == null, but the original condition remains unchanged. Change the session token check from !sessionToken to sessionToken == null on line 103 to properly distinguish between absent headers and invalid token values.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: adding a server option to restrict file downloads. It follows the required 'feat:' prefix format.
Description check ✅ Passed The PR description covers Issue, Approach, and completed Tasks sections. All critical sections from the template are present with substantive content.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

❌ Patch coverage is 98.36066% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.52%. Comparing base (19716ad) to head (cf0d6f1).
⚠️ Report is 3 commits behind head on alpha.

Files with missing lines Patch % Lines
src/Config.js 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha   #10394      +/-   ##
==========================================
+ Coverage   92.51%   92.52%   +0.01%     
==========================================
  Files         192      192              
  Lines       16624    16663      +39     
  Branches      229      229              
==========================================
+ Hits        15379    15417      +38     
- Misses       1223     1224       +1     
  Partials       22       22              

☔ 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
Copy Markdown

@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 (2)
src/Config.js (1)

596-622: Optional DRY refactor: unify file access-flag validation.

validateFileDownloadOptions and validateFileUploadOptions share the same boolean-flag normalization pattern. A small shared helper could reduce drift in future updates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Config.js` around lines 596 - 622, Both validateFileDownloadOptions and
validateFileUploadOptions duplicate the same boolean normalization logic;
extract a small helper (e.g., normalizeBooleanFlag(obj, key, defaultValue)) and
use it from both methods. The helper should accept the options object, the
property name (like "enableForAnonymousUser", "enableForPublic",
"enableForAuthenticatedUser"), and the default from
FileDownloadOptions/FileUploadOptions, set obj[key] = default when undefined,
and throw the same type error when the value exists but is not a boolean; keep
the initial object-type check in
validateFileDownloadOptions/validateFileUploadOptions and then call
normalizeBooleanFlag for each of the three flags to preserve identical behavior
and error messages.
spec/FileDownload.spec.js (1)

48-239: Add coverage for the compatibility edge cases this router change touches.

This suite covers the new allow/deny matrix, but it still doesn't pin two easy regressions: a stale/invalid X-Parse-Session-Token should fall back to anonymous/public handling instead of surfacing INVALID_SESSION_TOKEN, and readOnlyMasterKey should bypass read-only download restrictions just like the regular master key. Based on learnings, src/Routers/FilesRouter.js intentionally catches auth.getAuthForSessionToken failures and falls back to anonymous so stale client tokens do not break file downloads.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/FileDownload.spec.js` around lines 48 - 239, Add two tests to
FileDownload.spec.js: one that uploads a file, then attempts to GET it with an
invalid/stale X-Parse-Session-Token and asserts the request falls back to
anonymous/public handling (i.e., does not return
Parse.Error.INVALID_SESSION_TOKEN but obeys fileDownload flags), and another
that configures fileDownload restrictions then performs a GET with the
readOnlyMasterKey header and asserts it is allowed (status 200) just like using
X-Parse-Master-Key. Reference the existing uploadTestFile helper and the request
usage in the suite; these tests should exercise the FilesRouter behavior around
auth.getAuthForSessionToken fallbacks and readOnlyMasterKey bypass logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Routers/FilesRouter.js`:
- Around line 183-205: The _validateFileDownload guard currently only exempts
requests with req.auth.isMaster; include read-only master key requests as well
by checking req.auth.readOnlyMasterKey (or a combined flag like
isMasterOrReadOnly = req.auth?.isMaster || req.auth?.readOnlyMasterKey) and use
that combined flag in each conditional instead of isMaster so
read-only-master-key requests bypass the fileDownload restrictions; update
references in _validateFileDownload and keep all checks against
config.fileDownload.enableForAnonymousUser, enableForAuthenticatedUser, and
enableForPublic unchanged.
- Around line 94-111: The GET file routes currently call initInfo then
Middlewares.handleParseSession which bypasses handleParseHeaders and so never
populates req.config or key-based auth; revert to the original file-download
auth flow by replacing Middlewares.handleParseSession on the routes used for
metadataHandler and getHandler with the file-specific auth middleware (or call
Middlewares.handleParseHeaders before session handling) so req.config and
master/read-only keys are available, and ensure the file-download auth
resolution (the code around auth.getAuthForSessionToken) is preserved to catch
session-token errors and fall back to anonymous/public access instead of
surfacing auth errors for stale tokens.

---

Nitpick comments:
In `@spec/FileDownload.spec.js`:
- Around line 48-239: Add two tests to FileDownload.spec.js: one that uploads a
file, then attempts to GET it with an invalid/stale X-Parse-Session-Token and
asserts the request falls back to anonymous/public handling (i.e., does not
return Parse.Error.INVALID_SESSION_TOKEN but obeys fileDownload flags), and
another that configures fileDownload restrictions then performs a GET with the
readOnlyMasterKey header and asserts it is allowed (status 200) just like using
X-Parse-Master-Key. Reference the existing uploadTestFile helper and the request
usage in the suite; these tests should exercise the FilesRouter behavior around
auth.getAuthForSessionToken fallbacks and readOnlyMasterKey bypass logic.

In `@src/Config.js`:
- Around line 596-622: Both validateFileDownloadOptions and
validateFileUploadOptions duplicate the same boolean normalization logic;
extract a small helper (e.g., normalizeBooleanFlag(obj, key, defaultValue)) and
use it from both methods. The helper should accept the options object, the
property name (like "enableForAnonymousUser", "enableForPublic",
"enableForAuthenticatedUser"), and the default from
FileDownloadOptions/FileUploadOptions, set obj[key] = default when undefined,
and throw the same type error when the value exists but is not a boolean; keep
the initial object-type check in
validateFileDownloadOptions/validateFileUploadOptions and then call
normalizeBooleanFlag for each of the three flags to preserve identical behavior
and error messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 047e67d3-d7d6-430a-9e02-160446340e98

📥 Commits

Reviewing files that changed from the base of the PR and between 0508874 and 0142805.

📒 Files selected for processing (8)
  • README.md
  • resources/buildConfigDefinitions.js
  • spec/FileDownload.spec.js
  • src/Config.js
  • src/Options/Definitions.js
  • src/Options/docs.js
  • src/Options/index.js
  • src/Routers/FilesRouter.js

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (1)
spec/FileDownload.spec.js (1)

48-281: Add a regression for invalid or empty session tokens on GET.

The suite covers public/authenticated/anonymous/master/maintenance, but it does not pin the deliberate change that a bad X-Parse-Session-Token must be rejected instead of silently falling back to public access. One GET case here would catch that behavior, including the empty-header edge path.

Based on learnings, invalid/stale session tokens on file downloads are now intentionally rejected rather than silently falling back to public access.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/FileDownload.spec.js` around lines 48 - 281, Add a regression test in
the permissions suite to ensure an invalid or empty X-Parse-Session-Token does
not fall back to public access: use the existing uploadTestFile() helper to get
file.url, then call the same request(...) GET to file.url but include headers
with 'X-Parse-Session-Token' set to an empty string and another case with an
explicit invalid token; assert the request throws and that e.data.code equals
Parse.Error.OPERATION_FORBIDDEN (similar to other negative tests), placing this
new it(...) alongside the other permission cases so it exercises the same
request handling paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Routers/FilesRouter.js`:
- Around line 96-105: The code treats an empty string X-Parse-Session-Token as
"no token" because it uses a falsy check on sessionToken; update the conditional
so only null/undefined (not empty string) triggers the public path. In
FilesRouter.js, change the check around req.info/sessionToken and req.auth to
explicitly test for undefined/null (e.g., sessionToken === undefined ||
sessionToken === null) so an empty string still counts as a provided token and
will be passed to handleParseSession for validation instead of falling back to
public access.

---

Nitpick comments:
In `@spec/FileDownload.spec.js`:
- Around line 48-281: Add a regression test in the permissions suite to ensure
an invalid or empty X-Parse-Session-Token does not fall back to public access:
use the existing uploadTestFile() helper to get file.url, then call the same
request(...) GET to file.url but include headers with 'X-Parse-Session-Token'
set to an empty string and another case with an explicit invalid token; assert
the request throws and that e.data.code equals Parse.Error.OPERATION_FORBIDDEN
(similar to other negative tests), placing this new it(...) alongside the other
permission cases so it exercises the same request handling paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 838e52fe-c208-4812-af18-3feac3ccb881

📥 Commits

Reviewing files that changed from the base of the PR and between 0142805 and cf0d6f1.

📒 Files selected for processing (2)
  • spec/FileDownload.spec.js
  • src/Routers/FilesRouter.js

@mtrezza mtrezza merged commit fc117ef into parse-community:alpha Apr 3, 2026
23 of 24 checks passed
@mtrezza mtrezza deleted the feat/filedownload-option branch April 3, 2026 18:42
parseplatformorg pushed a commit that referenced this pull request Apr 3, 2026
# [9.8.0-alpha.4](9.8.0-alpha.3...9.8.0-alpha.4) (2026-04-03)

### Features

* Add server option `fileDownload` to restrict file download ([#10394](#10394)) ([fc117ef](fc117ef))
@parseplatformorg
Copy link
Copy Markdown
Contributor

🎉 This change has been released in version 9.8.0-alpha.4

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

Labels

state:released-alpha Released as alpha version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants