Commit Graph
15788 Commits
Author SHA1 Message Date
kolaente aa6bafce28 fixup! feat(config)!: refuse to start with a proxy password option 2026-09-30 16:08:17 +02:00
kolaente 2a7319f103 docs(config): document proxy credentials in the proxy url only 2026-09-30 16:08:17 +02:00
kolaente 9bc9d83436 feat(config)!: refuse to start with a proxy password option
Ignoring the removed keys would leave the proxy rejecting every request
with a 407, so startup fails with a hint to move the credentials into
outgoingrequests.proxyurl.

BREAKING CHANGE: outgoingrequests.proxypassword and
webhooks.proxypassword were removed. Vikunja refuses to start when
either is set.
2026-09-30 16:08:17 +02:00
kolaente 65a8c167b5 feat(utils)!: take proxy credentials only from the proxy url
BREAKING CHANGE: outgoingrequests.proxypassword is no longer used. Put
the credentials into outgoingrequests.proxyurl instead, for example
http://vikunja:password@mole:8080.
2026-09-30 16:08:17 +02:00
kolaente 645bf2d0c8 docs(config): document proxy credentials and OIDC coverage 2026-09-30 16:08:04 +02:00
kolaente 5bae1d342e refactor(utils): rename NewHTTPClient to NewUnguardedHTTPClient
Address review comment: make the missing SSRF guard obvious at the call site
2026-09-30 16:08:04 +02:00
kolaente 99c2d57901 fix(utils): stop exempting direct requests to the proxy address from the SSRF guard
The proxy dial exemption was keyed on addresses learned lazily from earlier
proxied requests, so a direct request (loopback, NO_PROXY or an unproxied
scheme) or a redirect to the proxy's host:port was dialed unguarded. Derive
the exempt addresses once from the configured or environment proxy URLs and
reject direct requests to them with ssrf.ErrProhibitedIP.

Address pr-swarm finding: SSRF guard bypass via the proxy address
2026-09-30 16:08:04 +02:00
kolaente b64aa3b117 fix(doctor): check OIDC discovery through the outgoing proxy 2026-09-30 16:08:03 +02:00
kolaente f32b4206fb fix(migration): route Trello API requests through the outgoing client 2026-09-30 16:08:03 +02:00
kolaente fcb8a2a306 feat(auth): route OIDC requests through the outgoing proxy 2026-09-30 16:08:03 +02:00
kolaente 5035b77780 fix(utils): use the configured proxy without a password
outgoingrequests.proxyurl was silently ignored unless proxypassword was
set too, and the username was hardcoded to vikunja. Credentials in the
proxy url are used now, with proxypassword as the mole-compatible
fallback. Plain http requests through the proxy now authenticate too,
not only CONNECT tunnels. An unparseable proxy url fails requests
instead of connecting directly.
2026-09-30 16:08:03 +02:00
kolaente d6415993ac refactor(utils): split proxy-aware client from the SSRF guard
NewHTTPClient carries the proxy and timeout settings without the SSRF
guard, for admin-configured endpoints like OIDC providers that often
live on a private network. NewSSRFSafeHTTPClient builds on it.
2026-09-24 18:06:17 +02:00
kolaenteandTink 124dcc993a docs(config): document proxy env var fallback for outgoing requests 2026-09-24 18:06:12 +02:00
kolaenteandTink dd52c36dd1 fix(utils): exempt the proxy dial from the SSRF guard
Outgoing proxies usually live on a private network, so the SSRF guard
refused to connect to them unless allownonroutableips was enabled,
which disabled the guard for direct connections too. Only the dial to
the configured proxy skips the guard now; proxied targets are resolved
by the proxy.
2026-09-24 18:06:12 +02:00
kolaenteandTink a741f93f22 fix(utils): honor proxy env vars for outgoing requests
The SSRF-safe client used a bare http.Transport, so HTTP_PROXY,
HTTPS_PROXY and NO_PROXY were ignored. Clone the default transport and
read the proxy from the environment when outgoingrequests.proxyurl is
not set.
2026-09-24 18:06:12 +02:00
06c451da40 fix(auth): link OIDC users by email when username fallback misses (#4012)
Both `usernamefallback` + `emailfallback` on: lookup required username
AND email match, so local user with different username never linked,
duplicate account created. Now verified-email-only lookup always tried
last.

Fixes #2022

## How to verify

1. Create a local user `localuser` with email `me@example.com`.
2. Configure an OIDC provider with `usernamefallback: true` and
`emailfallback: true`. In the IdP, create a user whose email is
`me@example.com` (sent with `email_verified: true`) and whose
username/sub is different from `localuser`.
3. Log in to Vikunja through that OIDC provider.
4. **Expected:** You are logged in as `localuser`, and `vikunja user
list` shows no new user for the provider's issuer.

**Before this PR:** a second user with a random or IdP-provided username
was created.

---------

Co-authored-by: kolaente <k@knt.li>
2026-09-24 06:27:43 +00:00
Frederick [Bot] c65edc42c1 chore(i18n): update translations via Crowdin 2026-09-24 00:16:17 +00:00
kolaente 64815e90f3 test(filters): mock async editor to avoid loading TipTap after teardown
Vue Test Utils still calls an async component's loader when stubbing it, so
TipTap kept importing in the background and could outlive the test
environment, failing CI with EnvironmentTeardownError.
2026-09-24 00:50:20 +02:00
kolaente 0e0a587c2f feat(quick-add): set first due date for repeating tasks without a date
Quick add "call mom every day" created a repeating task with no due date,
so it never showed up in Upcoming, never went overdue and reminders never
fired. Default to today at the user's default due time.
2026-09-24 00:50:20 +02:00
renovate[bot]andkolaente a6b276b61c chore(deps): update postgres:18 docker digest to 86c951e 2026-09-23 21:59:27 +02:00
renovate[bot]andkolaente fdfc50feb9 chore(deps): update dependency brace-expansion@5 to v5.0.12 2026-09-23 21:58:51 +02:00
renovate[bot]andkolaente 3012ea51e2 chore(deps): update node.js to ebfe2f9 2026-09-23 21:58:40 +02:00
renovate[bot]andkolaente 7d49a82bee chore(deps): update dependency ip-address to v10.7.2 2026-09-23 21:58:30 +02:00
renovate[bot]andkolaente 2dc6c17a93 chore(deps): update axllent/mailpit docker tag to v1.31.2 2026-09-23 21:58:19 +02:00
kolaente cf99f21cab test(e2e): open the used-up invite link in its own browser context
The spec cleared the guest's token by hand while that page's boot-time token refresh was still in flight. When the refresh resolved afterwards it persisted a fresh token, so the invite page loaded as the logged-in guest and redirected home instead of showing the invalid-link message.
2026-09-23 21:56:05 +02:00
kolaenteandTink 501861e745 refactor(websocket): connect task and timer cache events 2026-09-22 18:55:09 +02:00
kolaenteandTink 202d6ec99c refactor(websocket): reconcile server events through query mutations 2026-09-22 18:55:09 +02:00
kolaenteandTink 0e5c44e2dd test(websocket): pin cache routing and stale connection rejection 2026-09-22 18:55:09 +02:00
kolaenteandTink 150cccfea7 refactor(time-tracking): delete legacy timer store and service 2026-09-22 18:55:09 +02:00
kolaenteandTink f0d18fc366 refactor(time-tracking): activate query-owned timers and entry lists 2026-09-22 18:55:09 +02:00
kolaenteandTink 61aadbee49 refactor(time-tracking): switch generated fields (mechanical)
Applied using this Python script:

from pathlib import Path
paths=list(Path('frontend/src/components/time-tracking').glob('*.vue'))+[Path('frontend/src/views/time-tracking/TimeTracking.vue'),Path('frontend/src/helpers/time/smartFillStart.ts')]
for p in paths:
 s=p.read_text().replace("import type {ITimeEntry} from '@/modelTypes/ITimeEntry'", "import type {TimeEntry as ITimeEntry} from '@/client/generated'")
 for old,new in [('startTime','start_time'),('endTime','end_time'),('userId','user_id'),('taskId','task_id'),('projectId','project_id')]:
  s=s.replace('.'+old,'.'+new)
 # Component props and unrelated task/project UI contracts retain their names.
 s=s.replace('props.task_id','props.taskId')
 p.write_text(s)
2026-09-22 18:55:09 +02:00
kolaenteandTink 6399a75896 refactor(time-tracking): add entry queries and timer mutations 2026-09-22 18:55:09 +02:00
kolaenteandTink 02821722ac test(time-tracking): pin timer scope and list reconciliation 2026-09-22 18:55:09 +02:00
kolaenteandTink 70e337ca72 refactor(subscriptions): delete legacy subscription model and type 2026-09-22 18:55:09 +02:00
kolaenteandTink d4668f82f3 refactor(subscriptions): remove adapters and wire task mutations 2026-09-22 18:55:09 +02:00
kolaenteandTink 6b237ae9d9 refactor(subscriptions): switch to generated fields (mechanical) 2026-09-22 18:55:09 +02:00
kolaenteandTink 6264575f51 refactor(subscriptions): move task subscriptions into query mutations 2026-09-22 18:55:09 +02:00
kolaenteandTink 14573f6222 test(subscriptions): pin task subscription cache updates 2026-09-22 18:55:09 +02:00
kolaenteandTink ceac2331df refactor(comments): delete legacy comment layers 2026-09-22 18:55:09 +02:00
kolaenteandTink 437c22673c refactor(comments): activate paged queries and local editor drafts 2026-09-22 18:55:09 +02:00
kolaenteandTink 004adb4fe9 refactor(comments): switch consumers to generated types (mechanical) 2026-09-22 18:55:09 +02:00
kolaenteandTink e5f154923b refactor(comments): add paged queries and mutation bookkeeping 2026-09-22 18:55:09 +02:00
kolaenteandTink bccfe54a41 test(comments): pin paging and mutation bookkeeping 2026-09-22 18:55:09 +02:00
kolaenteandTink 4ece3b3af7 refactor(reactions): delete legacy reaction layers 2026-09-22 18:55:09 +02:00
kolaenteandTink 5113e67a23 refactor(reactions): reconcile task reactions without refetching 2026-09-22 18:55:09 +02:00
kolaenteandTink e800ba3e6a refactor(reactions): switch consumers to generated types (mechanical) 2026-09-22 18:55:09 +02:00
kolaenteandTink d62452b8ca refactor(reactions): add generated mutations and immutable updates 2026-09-22 18:55:09 +02:00
kolaenteandTink 7ecfb579ca test(reactions): pin generated requests and task cache updates 2026-09-22 18:55:09 +02:00
kolaenteandTink e07532c604 refactor(attachments): delete legacy attachment and file layers 2026-09-22 18:55:09 +02:00
kolaenteandTink 68ead91a42 refactor(attachments): activate query-owned uploads and previews 2026-09-22 18:55:09 +02:00