Commit Graph

410 Commits

Author SHA1 Message Date
kolaente 119d7df796 fix: use assert.Empty instead of assert.Equal for empty string check 2026-04-08 09:49:14 +00:00
kolaente a5fb01cc3d fix: reset SSO avatar provider to default when picture claim is removed 2026-04-08 09:49:14 +00:00
kolaente 1065bdd84c test: add tests for SSO avatar provider reset on empty picture URL 2026-04-08 09:49:14 +00:00
kolaente d5051c97e4 fix(background): use targeted column update when removing background
Fixes #2552. RemoveProjectBackground was passing a minimal Project struct
(only ID set) through UpdateProject, which always includes 'title' in its
Cols() list. This caused XORM to write the zero-value empty title to the
DB, wiping the real project title. Now uses ClearProjectBackground which
only updates background_file_id and background_blur_hash.
2026-04-08 09:07:15 +00:00
kolaente c166eff95f test: remove obsolete invalid-cache-type test for avatar upload
RememberValue[T] always gob-decodes to the correct type, so the
corrupted-cache recovery path no longer exists.
2026-04-08 08:56:22 +00:00
kolaente 59b047f76a fix: register gob types and use RememberValue for avatar and unsplash cache
Register CachedAvatar and Photo with encoding/gob so Redis can properly
deserialize them. Migrate both to use RememberValue[T] which calls
GetWithValue() internally, fixing the broken type assertion when Redis
is the keyvalue backend.

Also removes the recursion-depth fallback in upload.go since
RememberValue eliminates the type mismatch failure mode entirely.
2026-04-08 08:56:22 +00:00
kolaente e2de681b71 feat: add generic RememberValue[T] for type-safe keyvalue caching
RememberValue uses GetWithValue() internally for proper gob-decoding,
which is required when Redis is used as the keyvalue backend.
2026-04-08 08:56:22 +00:00
kolaente bc0bb556ad feat(migration): flatten project hierarchy for single-project imports 2026-04-07 15:20:06 +00:00
kolaente 3437f98dc3 feat(migration): add skip rows option to CSV import
Allow users to skip the first N data rows when importing CSV files.
This is useful when the CSV contains metadata rows before the actual
task data begins. Adds skip_rows to ImportConfig (backend) and a
number input in the parsing options UI (frontend).
2026-04-07 15:20:06 +00:00
Claude f555762def feat(migration): add generic CSV import with column mapping
Add a new CSV migration module that allows users to import tasks from
any CSV file with custom column mapping and parsing options.

Backend changes:
- New CSV migrator module with detection, preview, and import endpoints
- Auto-detection of delimiter, quote character, and date format
- Suggested column mappings based on column name patterns
- Transactional import using InsertFromStructure

Frontend changes:
- New CSV migration UI with two-step flow (upload -> mapping -> import)
- Column mapping selectors for all task attributes
- Live preview showing first 5 tasks with current mapping
- Parsing option controls for delimiter and date format

The CSV migrator creates a parent "Imported from CSV" project with
child projects based on the project column if provided, or a default
"Tasks" project for tasks without a specified project.
2026-04-07 15:20:06 +00:00
kolaente e40877cca1 fix(migration): delete all default buckets when migration provides its own
Previously only the "To-Do" default bucket was deleted, leaving "Doing"
and "Done" as duplicates alongside migration-provided buckets. Now all
default-created buckets are removed when migration data already provides
bucket assignments for all tasks.
2026-04-07 12:05:47 +00:00
kolaente ccf1468884 fix(migration): correct TickTick swagger annotation to PUT 2026-04-07 12:05:47 +00:00
kolaente 56ce73738d test(migration): add WeKan migration tests and fixture
Add comprehensive tests for the WeKan conversion function including
edge cases (empty board, orphan cards, color mapping, multiple
checklists, unsupported fields) and a realistic JSON fixture file.
2026-04-07 12:05:47 +00:00
kolaente 4cc7715951 feat(migration): add WeKan board JSON import
Add a file-based migration importer that reads WeKan board JSON exports
and creates Vikunja projects with kanban buckets, tasks, labels,
checklists, and comments.

WeKan lists become kanban buckets. Checklists are converted to HTML
task lists in the description. Card descriptions and comments are
converted from markdown to HTML using goldmark. Label colors are
mapped from WeKan's CSS color names to their actual hex values.
2026-04-07 12:05:47 +00:00
kolaente 55ea5bd966 refactor(auth): extract shared token validation into auth package
Move JWT parsing (GetUserIDFromToken) and API token validation
(ValidateAPITokenString) into pkg/modules/auth so both HTTP middleware
and WebSocket auth use the same logic. This ensures consistent token
validity checks including expiry and user status (disabled/locked).

The HTTP API token middleware now delegates to the shared function,
removing duplicated lookup/expiry logic.
2026-04-02 16:30:23 +00:00
Claude fc9c21915d feat(auth): enforce OpenID Connect issuer uniqueness across providers
Detect when two configured OIDC providers resolve to the same issuer URL
at startup and halt with a fatal error, preventing team sync data
corruption caused by ambiguous (external_id, issuer) matching.

Also adds duplicate issuer detection to the doctor service diagnostics
and comprehensive tests with mock OIDC discovery servers.
2026-03-30 22:41:50 +00:00
kolaente 83bac15841
feat: rename ServiceJWTSecret to ServiceSecret with deprecation (#2502) 2026-03-30 12:07:01 +02:00
kolaente 649043aceb test: add tests for OAuth 2.0 authorization flow
Add web tests covering the authorize endpoint, token exchange, PKCE
verification, single-use codes, and refresh token rotation. Add unit
tests for redirect URI validation and PKCE. Add E2E test for the full
browser-based authorization code flow with login redirect.

Extract setupApiUrl helper for E2E tests to avoid duplication.
2026-03-27 23:05:04 +00:00
kolaente 7827ff64b9 feat: add OAuth 2.0 token endpoint
Add POST /api/v1/oauth/token supporting authorization_code and
refresh_token grant types. Validates PKCE, exchanges codes for
JWT access tokens with refresh token rotation. Uses the shared
RefreshSession helper for the refresh grant.
2026-03-27 23:05:04 +00:00
kolaente 8b379b7466 feat: add OAuth 2.0 authorize endpoint
Add POST /api/v1/oauth/authorize behind auth middleware. Validates
OAuth parameters (response_type, redirect_uri, PKCE), fetches the
authenticated user, creates an authorization code, and returns it
as JSON for the frontend to handle the redirect.
2026-03-27 23:05:04 +00:00
kolaente a6e7475153 feat: add OAuth client validation and PKCE verification
Add redirect URI validation that allowlists vikunja-* custom protocol
schemes, rejecting http/https and dangerous schemes like javascript:.
Add PKCE S256 verification following RFC 7636.
2026-03-27 23:05:04 +00:00
kolaente 7a258f67c7 refactor: extract shared RefreshSession helper
The cookie-based /user/token/refresh handler had session refresh logic
(lookup, expiry check, token rotation, user fetch, JWT generation)
that will be reused by the OAuth token endpoint. Extract it into
auth.RefreshSession() and rewrite RefreshToken to use it.
2026-03-27 23:05:04 +00:00
kolaente 112e486314 test: add test for deeply nested TickTick task ordering 2026-03-26 15:08:12 +00:00
kolaente 9b1c52e9e3 fix: sort TickTick tasks so parents come before children
TickTick CSV exports don't guarantee parent tasks appear before their
subtasks. When a child row came first, the shared migration pipeline
tried to create a title-less placeholder for the missing parent, which
failed with 'Task title cannot be empty'.

Resolves go-vikunja/vikunja#2487
2026-03-26 15:08:12 +00:00
kolaente c49636430f test: add failing test for TickTick child-before-parent CSV order 2026-03-26 15:08:12 +00:00
Claude 121fd3c9f1 feat: use openid provider name instead of generic "OIDC" in synced team names
Teams synced from OpenID Connect providers were always named with "(OIDC)"
suffix (e.g., "DevTeam (OIDC)"). This changes it to use the configured
provider name instead (e.g., "DevTeam (Keycloak)"), making it easier to
identify which provider a team came from when multiple OIDC providers are
configured. Existing team names will be updated automatically on next user
login.

https://claude.ai/code/session_012LXXPvYe6i27WTcha1PL7A
2026-03-24 12:30:06 +00:00
kolaente fd452b9cb6 fix(auth): skip profile updates for disabled LDAP users
When a disabled/locked LDAP user authenticates, return early from
getOrCreateLdapUser without updating their profile info or syncing
avatar. The login handler already rejects them, but this avoids
unnecessary database writes.

Ref: GHSA-94xm-jj8x-3cr4
2026-03-23 16:37:26 +00:00
kolaente cc22acdf3e chore(lint): suppress gosec false positives on SSRF-safe HTTP client calls 2026-03-23 16:34:22 +00:00
kolaente a94109e1be fix: prevent SSRF via Unsplash background image download 2026-03-23 16:34:22 +00:00
kolaente 73edbb6d46 fix: prevent SSRF via Microsoft Todo migration pagination links 2026-03-23 16:34:22 +00:00
kolaente 9329774223 fix: prevent SSRF via migration file attachment URLs (GHSA-g66v-54v9-52pr) 2026-03-23 16:34:22 +00:00
MidoriKurage 68a74416a4 fix(openid): Merge VikunjaGroups and ExtraSettingsLinks from userinfo
When `forceuserinfo: true`, `mergeClaims` discards `vikunja_groups`
and `extra_settings_links` claims fetched from the userinfo endpoint,
failing team sync for opaque tokens.

Fixes team sync for OIDC providers using opaque tokens.
2026-03-23 16:11:17 +00:00
kolaente 212968cec4
chore(lint): suppress additional gosec false positives
Add #nosec comments for G703/G704 findings in db, doctor, webhooks,
gravatar, unsplash, and migration helper code.
2026-03-23 16:40:07 +01:00
kolaente 2053426062
chore(lint): suppress known gosec false positives
Add config-level exclusions for G117 (secret-named struct fields),
G101 in test files, G702/G704 in magefile, and goheader in plugins.
Add inline #nosec comments for specific G703/G704 false positives
in export, dump/restore, migration, and avatar code.
2026-03-23 16:23:15 +01:00
kolaente a7a8ae072a fix(auth): return correct error type for locked users in OIDC callback
Return ErrAccountLocked for locked users instead of ErrAccountDisabled.
Also skip profile updates and avatar sync for disabled/locked users
found during OIDC login — HandleCallback rejects the auth anyway.
2026-03-23 12:06:16 +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 22a4b6fbb8 fix(auth): reject disabled/locked users in OIDC callback 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 4b91e5efa1 refactor: rename checkProjectBackgroundWriteRights to checkProjectBackgroundWritePermissions 2026-03-20 11:41:28 +00:00
kolaente f066eb3ea4 fix: require CanUpdate for project background deletion
RemoveProjectBackground previously used checkProjectBackgroundRights
which only checks CanRead, allowing read-only users to delete project
backgrounds. Added checkProjectBackgroundWriteRights that checks
CanUpdate and use it in RemoveProjectBackground.

Ref: GHSA-564f-wx8x-878h
2026-03-20 11:41:28 +00: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
Weijie Zhao 54d977532e
fix: allow browser caching for file downloads (#2349) 2026-03-04 17:43:03 +01:00
kolaente 3dd2ba4aa4 feat: register Vikunja tables with db package at init 2026-03-04 15:37:54 +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 530973c475 fix(auth): make SameSite=None conditional on HTTPS for refresh cookie
SameSite=None requires Secure=true per browser spec. When running over
plain HTTP (local dev, e2e tests), browsers reject or downgrade the
cookie, breaking session refresh. Fall back to SameSite=Lax for HTTP
while keeping SameSite=None for HTTPS (needed for the Electron desktop
app cross-origin scenario).
2026-03-03 10:41:19 +01:00
kolaente 28f98a7a96
fix(auth): use SameSite=None for refresh token cookie to fix desktop app
SameSite=Strict prevents the browser from sending the HttpOnly refresh
token cookie in cross-origin contexts like the Electron desktop app,
where the page runs on localhost but the API is on a remote host. This
caused sessions to expire quickly because refresh requests never
included the cookie.

SameSite=None allows cross-origin sending while HttpOnly still prevents
JavaScript from reading the cookie value (XSS protection).

Resolves #2309
2026-03-02 13:54:10 +01:00
kolaente a7e4a4f4af
fix(migration): support space-separated date format in TickTick importer
Fixes https://github.com/go-vikunja/vikunja/issues/2324
2026-03-02 08:35:35 +01:00
kolaente 39da47e435 fix: detect and fail on oversized zip entries instead of silent truncation
Replace io.LimitReader with a new readZipEntry helper that reads one extra
byte to detect when content exceeds maxZipEntrySize (500MB). This prevents
silent data corruption where partial file bytes would be stored as if the
upload succeeded.

The import now fails with ErrFileTooLarge instead of accepting truncated
content for attachments and background blobs.
2026-02-25 13:01:00 +01:00
kolaente db4fa9a4b6 fix(restore): extract preValidateTableData to reduce cyclomatic complexity 2026-02-25 13:01:00 +01:00
kolaente f3ac0574c0 fix(auth): use checked type assertions for all JWT claims 2026-02-25 13:01:00 +01:00