Commit Graph

402 Commits

Author SHA1 Message Date
kolaente 6aef5aff62 fix: strip BasicAuth credentials from user webhook API responses 2026-03-23 16:35:47 +00:00
kolaente 8409bdb120 refactor(user): export IsErrUserStatusError for use across packages
Make isErrUserStatusError public and replace all verbose
!IsErrAccountDisabled(err) && !IsErrAccountLocked(err) checks
with the shorter IsErrUserStatusError(err) call.
2026-03-23 12:06:16 +00:00
kolaente cd6148511a fix(auth): reject disabled/locked users in API token middleware
checkAPITokenAndPutItInContext now returns 401 Unauthorized when the
token owner's account is disabled or locked, instead of a 500 error.
Also fixes the API token test to match the actual middleware behavior.
2026-03-23 12:06:16 +00:00
kolaente 8b614a4cb3 test: verify disabled user is rejected via CalDAV auth
Also fix BasicAuth to check for status errors from checkUserCaldavTokens
before falling through to password-based auth.
2026-03-23 12:06:16 +00:00
kolaente ea4ba18def fix(user): handle status errors across the codebase, remove redundant checks 2026-03-23 12:06:16 +00:00
kolaente cdf5d30a42 fix: reject CalDAV basic auth when TOTP is enabled 2026-03-20 12:22:27 +00:00
kolaente 4c80932b64 fix: block login for StatusAccountLocked users 2026-03-20 11:23:21 +00:00
kolaente a498dd6991 fix: configure Echo IPExtractor to prevent rate limit bypass via spoofed headers 2026-03-20 11:08:00 +00:00
kolaente 4cd63f93a4 fix: use file mime type instead of hardcoded application/zip in S3 export 2026-03-20 10:59:44 +01:00
kolaente 0e1f44e57e refactor: replace afero with FileStorage interface
Replace the github.com/spf13/afero dependency with a purpose-built
FileStorage interface (Open, Write, Stat, Remove, MkdirAll) with three
implementations: localStorage (with basePath), s3Storage (with key
prefix), and memStorage (for tests).

Each implementation owns its base path — callers pass only file IDs.
Delete s3fs.go, change File.File from afero.File to io.ReadCloser,
and fix duplication flows to buffer content for seeking.
2026-03-20 10:59:44 +01:00
maggch b0ede53c05 fix: handle S3 backend in user export download
http.ServeContent requires io.ReadSeeker which S3 files don't support.
Add S3 branch using io.Copy with explicit headers, matching the pattern
already used in task attachment downloads.

Refs: #2347

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-20 10:59:44 +01:00
Henry Cole e7f1e99878
fix(caldav): use /dav/projects/ as home to make iOS/MacOS reminders work (#2417)
Resolves issue #475 by modifying CalDAV discovery so Apple Reminders can
use /dav/projects/ as the home set without exposing that synthetic path
as a real task list, preserving the existing principal-based flow. This
is because Apple Reminders defaults back to the /dav/projects/ URL,
rather than accepting the /dav/principals/username/ URL specified in
Vikunja.

Resolves #475
2026-03-20 09:33:56 +00:00
kolaente 3bc0093686 fix: invalidate all sessions when enabling TOTP
When a user enables two factor authentication, all existing sessions are
now invalidated, requiring re-authentication. This prevents pre-existing
sessions from bypassing 2FA. The frontend now shows a notice explaining
the logout before the user confirms, and properly logs out after enabling.

Ref: GHSA-pgc7-cmvg-mvp4
2026-03-19 12:27:44 +01:00
Tink ada2ebab9e
fix: preserve CalDAV inverse relations when parent has no RELATED-TO (#2389)
- Fixes `removeStaleRelations` in CalDAV storage provider to only remove
relations of kinds explicitly declared in the incoming VTODO's
`RELATED-TO` properties
- When a VTODO has no `RELATED-TO` at all (e.g., a parent task from
Tasks.org), no relations are removed — they were auto-created as
inverses by child tasks
- When a VTODO declares specific relation kinds (e.g.,
`RELATED-TO;RELTYPE=PARENT`), only relations of that kind are checked
for staleness; other kinds (like auto-created `subtask` inverses) are
preserved

Fixes #2383

---------

Co-authored-by: kolaente <k@knt.li>
2026-03-11 09:40:09 +01:00
kolaente 47a0775c73 feat: add API routes for user-level webhooks
Add CRUD endpoints for user-level webhooks under /user/settings/webhooks.
Users can create webhooks that fire on user-directed events (task.reminder.fired,
task.overdue, tasks.overdue). Includes proper permission checks via
canDoWebhook which loads webhook ownership from DB.
2026-03-08 19:45:53 +01:00
kolaente f9cb0a2de1 fix: include remote IP address in HTTP request logs 2026-03-04 23:47:40 +01:00
kolaente b5086febc7 docs: update user search endpoint description for external team bypass 2026-03-04 20:32:11 +01:00
Weijie Zhao 54d977532e
fix: allow browser caching for file downloads (#2349) 2026-03-04 17:43:03 +01:00
kolaente 77fdf1b84b feat: register task duplicate API route 2026-03-04 17:20:26 +01:00
kolaente 6ed684d708 fix(events): dispatch pending events in migration and export handlers
Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 924eef58d1 fix(events): dispatch pending events in CalDAV handlers after commit
CalDAV handlers manage their own database sessions. Now that model
methods use DispatchOnCommit, the CalDAV handlers must call
DispatchPending after commit and CleanupPending on rollback.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 51f789bf5c fix(e2e): drain event handlers and stop browser between tests
Async event handlers (via Watermill) from the previous test can hold
SQLite connections, starving the next test's fixture setup PATCH request.

Three changes fix this:

1. Track in-flight event handler goroutines with a WaitGroup.
2. Call WaitForPendingHandlers() in the test endpoint before
   truncating/inserting data.
3. Navigate the browser to about:blank in fixture teardown to stop
   notification polling and other frontend requests between tests.
2026-03-03 10:41:19 +01:00
kolaente 39acdac531 fix(caldav): eliminate nested db session in CalDAV auth
checkUserCaldavTokens called user.GetCaldavTokens which creates its own
db.NewSession(), while the caller (BasicAuth) already holds an open
session. With SQLite this caused a deadlock because the second session
blocks on the write lock held by the first session in the same goroutine.

Add GetCaldavTokensWithSession that accepts an existing session and use
it from checkUserCaldavTokens.
2026-03-03 10:41:19 +01:00
kolaente 89c17d3b23 feat(api): enforce password validation on reset and update flows
Add bcrypt_password validation to password reset and update endpoints:
- Add validation tag to PasswordReset.NewPassword struct field
- Add validation tag to UserPassword.NewPassword struct field
- Add c.Validate() calls in both handlers
- Fix off-by-one error in bcrypt_password validator (use <= 72 not < 72)

Password requirements: min 8 chars, max 72 bytes (bcrypt limit)
2026-02-25 13:44:56 +01:00
kolaente f3ac0574c0 fix(auth): use checked type assertions for all JWT claims 2026-02-25 13:01:00 +01:00
kolaente 329c07f24b fix(attachments): use mime.FormatMediaType for Content-Disposition header 2026-02-25 13:01:00 +01:00
kolaente 2f680d041c fix: address review comments on session lifecycle
- user_export.go: Remove defer s.Close() from checkExportRequest since
  it returns the session to callers. Callers now own the session
  lifecycle with their own defer s.Close(). Close session on all error
  paths within checkExportRequest.

- user_delete.go: Close the read session immediately after Find() before
  the per-user deletion loop, avoiding a long-lived transaction holding
  locks unnecessarily.

- user/delete.go: Remove double s.Close() in notifyUsersScheduledForDeletion
  by closing immediately after Find() instead of using both defer and
  explicit close.

- caldav_token.go: Return nil token on Commit() error to prevent callers
  from using an unpersisted token.
2026-02-25 11:03:02 +01:00
kolaente 2f718206f9 fix: add TestMain to caldav tests and fix session conflicts
Add a proper main_test.go for the caldav test package that initializes
the logger, config, test database, and event system. Previously, these
were initialized inline in TestSubTask_Create and TestSubTask_Update
relied on running after it (fragile test ordering).

Fix session handling in TestSubTask_Update: close the read session
before calling UpdateResource (which creates its own internal session)
to avoid SQLite lock conflicts from concurrent transactions.
2026-02-25 11:03:02 +01:00
kolaente a6e6f252db refactor: remove redundant Begin() calls after NewSession auto-begins
Since NewSession() now auto-begins a transaction, explicit Begin()
calls are redundant (xorm's Begin() is a no-op when already in a
transaction). Removing them reduces confusion.

Special case: user_delete.go's loop previously called Begin/Commit
per user on a shared session. Restructured to create a new session
per user deletion so each gets its own transaction.
2026-02-25 11:03:02 +01:00
kolaente c9c250fb1c fix: add missing Commit() to write callers
After NewSession() auto-begins a transaction, callers that perform
writes must explicitly call Commit() for changes to persist. Without
this, writes are silently rolled back when Close() is called.

Affected callers:
- user deletion notification cron
- caldav token generation/deletion
- token cleanup cron
- mark-all-notifications-read endpoint
- saved filter view cron
- project background delete
- typesense reindex
- export cleanup cron
- task last-updated listener
- saved filter view listener
- SSO team cleanup cron
- migration status start/finish
- background set/remove handlers
- orphaned task position cleanup
- file creation
2026-02-25 11:03:02 +01:00
kolaente 8ee069a2a3 feat: add session-based auth with refresh token rotation
- Login creates a server-side session and sets an HttpOnly refresh
  token cookie alongside the short-lived JWT
- POST /user/token/refresh exchanges the cookie for a new JWT and
  rotates the refresh token atomically
- POST /user/logout destroys the session and clears the cookie
- POST /user/token restricted to link share tokens only
- Session list (GET) and delete (DELETE) routes for /user/sessions
- All user sessions invalidated on password change and reset
- CORS configured to allow credentials for cross-origin cookies
- JWT 401 responses use structured error code 11 for client detection
- Refresh token cookie name constants annotated for gosec G101
2026-02-25 10:30:25 +01:00
kolaente a13ecbd3cc
fix: prevent browser from caching API responses
Without explicit Cache-Control headers, browsers may heuristically cache
API JSON responses. This causes stale data to be served on normal page
refresh (F5) — for example, projects newly shared with a team not
appearing until the user performs a hard refresh (Ctrl+Shift+R).

Add Cache-Control: no-store to all API responses via middleware and
configure the service worker's NetworkOnly strategy to explicitly bypass
the browser HTTP cache for API requests.

Ref: https://community.vikunja.io/t/team-members-cannot-see-project/1876
2026-02-24 10:37:49 +01:00
kolaente 1ccc8dce3a
fix: load file content before generating attachment preview
LoadFileByID() was called after the preview branch, so GetPreview()
received a nil io.Reader causing a panic in image.Decode.
2026-02-22 09:28:25 +01:00
kolaente d222d4502a fix: escape attachment download filename 2026-02-22 00:00:11 +01:00
kolaente c6370bb739 fix: fall back to application/octet-stream when the file has no mime type stored 2026-02-22 00:00:11 +01:00
kolaente 4915f535d0 fix: add Content-Disposition attachment header to task attachment downloads
Set Content-Disposition to "attachment" instead of "inline" so browsers
trigger a file download with the correct filename. Also move shared
response headers (Content-Type, Content-Length, Last-Modified) out of
the S3-specific branch so they apply to both storage backends.
2026-02-22 00:00:11 +01:00
kolaente b21c9acb0d fix(routes): restore SPA routing after Echo v5 upgrade
In Echo v5, the 404 error for unmatched routes implements the
HTTPStatusCoder interface but is not a *HTTPError. This caused
the static middleware to fail to catch 404s and serve index.html
for SPA routes, leading to reloading SPA routes returning 404.

Caused by regression introduced in 9a61453e8.

Fixes #2149
Fixes #2152
2026-01-25 11:07:48 +01:00
renovate[bot] 9a61453e86
fix(deps): update module github.com/labstack/echo/v4 to v5 (#2131)
Closes https://github.com/go-vikunja/vikunja/pull/2133

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2026-01-24 20:38:32 +01:00
kolaente 39b4568bc5
refactor: centralize HTTP error handling (#2062)
This changes the error handling to a centralized HTTP error handler in `pkg/routes/error_handler.go` that converts all error types to proper HTTP responses. This simplifies the overall error handling because http handler now only need to return the error instead of calling HandleHTTPError as previously.
It also removes the duplication between handling errors with and without Sentry.

🐰 Hop along, dear errors, no more wrapping today!
We've centralized handlers in a shiny new way,
From scattered to unified, the code flows so clean,
ValidationHTTPError marshals JSON supreme!
Direct propagation hops forward with glee,
A refactor so grand—what a sight to see! 🎉
2026-01-08 10:02:59 +00:00
kolaente 0b3decd869
fix: ensure API consistency for /tasks and empty array responses (#1988)
- Renames the `/tasks/all` endpoint to `/tasks` for consistency with
other collection endpoints like `/projects` and `/labels`
- Returns `[]` instead of `null` for empty pagination results across all
list endpoints
- Updates the frontend service to use the new endpoint path
- Updates API token tests to use the new endpoint path

Fixes #1984
2025-12-15 15:34:13 +00:00
kolaente fb7764d9f1
feat: format user mentions with display names in email notifications (#1930)
Email notifications now display user mentions with inline avatar images for improved visual recognition and easier identification. Mentions gracefully fall back to display names if avatars are unavailable.
2025-12-10 12:39:05 +01:00
kolaente da0822c3f4 feat(caldav): add more error logging 2025-12-04 10:54:31 +01:00
kolaente 51512c1cb4
feat: migrate cypress e2e tests to playwright (#1739) 2025-11-27 16:34:48 +01:00
Mithilesh Gupta 7dddc5dfa2
feat: task unread tracking (#1857)
---------

Co-authored-by: Mithilesh Gupta <guptamithilesh@protonmail.com>
Co-authored-by: kolaente <k@knt.li>
2025-11-27 15:14:42 +01:00
kolaente dcfd096588
feat: allow setting dark custom logo
Resolves https://github.com/go-vikunja/vikunja/issues/1799
2025-11-12 21:07:01 +01:00
Weijie Zhao bc1368abcc
feat: add S3 file storage support (#1688) 2025-11-06 08:37:04 +01:00
kolaente ec89b08fd5
fix(attachments): extend upload file size to form data (#1577)
Resolves https://github.com/go-vikunja/vikunja/issues/1494
2025-09-30 22:23:07 +00:00
kolaente 31c1f98270
fix(caldav): remove METHOD:PUBLISH from caldav exports (#1576) 2025-09-30 18:16:07 +00:00
kolaente 1b5a9dbdea refactor: use helper function to check user local 2025-09-04 18:09:21 +02:00
kolaente b8afdcf62d fix(user): do not reject 2fa for local users
https://github.com/go-vikunja/vikunja/issues/1402
2025-09-04 18:09:21 +02:00