Skip to content

feat: add expiring secure send links#104

Merged
DenizAltunkapan merged 1 commit into
Vault-Web:mainfrom
arnabnandy7:security/expiring-external-shares
Jul 19, 2026
Merged

feat: add expiring secure send links#104
DenizAltunkapan merged 1 commit into
Vault-Web:mainfrom
arnabnandy7:security/expiring-external-shares

Conversation

@arnabnandy7

@arnabnandy7 arnabnandy7 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add Secure Send links for sharing a single file with external recipients.
  • Support configurable expiration and optional bcrypt-hashed password protection.
  • Add authenticated endpoints to create, list, and revoke links.
  • Add a public token-based download endpoint that exposes only the selected file.
  • Store only SHA-256 token hashes; raw tokens are returned once in the generated URL.
  • Enforce existing user-root path validation and pin resolved file paths before download.
  • Return clean errors for expired, revoked, missing, or password-protected links.
  • Add separate rate limits for link creation and public access.
  • Add scheduled cleanup of expired link records.
  • Update README and project structure documentation.
  • Add service, controller, path-boundary, password, expiry, revocation, and rate-limit tests.

Linked issue

Part of #100

How to test

cd backend
./mvnw test

Expected result:

Tests run: 188
Failures: 0
Errors: 0
Skipped: 1

Manual flow:

  1. Authenticate with a valid Vault Web JWT.
  2. Create a link:
POST /api/secure-sends
Content-Type: application/json
Authorization: Bearer <token>

{
  "filePath": "documents/report.pdf",
  "expiresAt": "2026-07-19T10:00:00Z",
  "password": "optional password"
}
  1. Open the returned url without authentication.
  2. For protected links, include:
X-Secure-Send-Password: optional password
  1. Verify links can be listed with GET /api/secure-sends.
  2. Revoke one with DELETE /api/secure-sends/{id} and verify its public URL returns 404.
  3. Verify expired links return 404 and incorrect passwords return 401.

Notes / Risk

  • Hibernate creates the new secure_sends table through the existing spring.jpa.hibernate.ddl-auto=update configuration. No explicit migration is included.
  • The public endpoint is intentionally unauthenticated; access is controlled by a random 256-bit token, optional password, expiry, revocation, and IP-based rate limiting.
  • Raw tokens and plaintext passwords are never persisted. The generated URL is returned only at creation; listing links does not reproduce it.
  • Expired records remain for the configured retention period before scheduled cleanup removes them.
  • Moving, renaming, or deleting the source file invalidates the link.
  • Default maximum expiry and retention are 30 days and are configurable through cloudpage.secure-send.*.
  • File-viewer and chat UI integration must be completed in their respective frontend repositories using the generated URL.

@DenizAltunkapan DenizAltunkapan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The backend implementation is genuinely strong: hashed 256-bit tokens, bcrypt password protection, the password sent via header rather than the URL, user-root validation repeated at download time, uniform unavailable-link responses, and separate create/access rate limits. Two blockers before it can go in, plus a couple of small non-blocking notes.

Blocker 1: the PR is not mergeable (GitHub reports CONFLICTING). PR #103 (scan rate limit) merged on 2026-07-18 and touches the same files: RateLimitCategory, RateLimitProperties, RateLimitFilter, application.properties, and the rate-limit tests. Please rebase onto main and make sure SCAN, SECURE_SEND_CREATE, and SECURE_SEND_ACCESS all survive the merge.

Blocker 2: Closes #100 is not accurate. #100 requires creating a Secure Send from the file viewer and sharing it into a chat, and the PR itself says both frontend integrations are still outstanding. Please change it to Part of #100 (or Backend support for #100), or split the remaining frontend and chat work into their own tracked issues, so #100 is not auto-closed with its main criteria unmet.

The non-blocking notes are inline.

Comment thread backend/src/main/java/cloudpage/ratelimit/RateLimitCategory.java
Comment thread backend/src/main/java/cloudpage/service/SecureSendService.java Outdated
Comment thread backend/src/main/java/cloudpage/service/SecureSendService.java
@arnabnandy7
arnabnandy7 force-pushed the security/expiring-external-shares branch from 9ed43e6 to 7fc2124 Compare July 18, 2026 10:04

@DenizAltunkapan DenizAltunkapan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@arnabnandy7 thanks again, looks good.

Signed-off-by: Arnab Nandy <arnab_nandy7@yahoo.com>
@arnabnandy7
arnabnandy7 force-pushed the security/expiring-external-shares branch from 7fc2124 to 7fc9915 Compare July 19, 2026 12:00
@DenizAltunkapan
DenizAltunkapan merged commit 1cc23ca into Vault-Web:main Jul 19, 2026
1 check passed
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.

2 participants