Commit Graph

14441 Commits

Author SHA1 Message Date
renovate[bot] ffcf92936a chore(deps): update dev-dependencies 2026-06-17 12:02:41 +00:00
kolaente c5d615843d test(api/v2): cover background download and unsplash proxy routes
- Download: upload-then-download (real bytes), content-type, If-Modified-Since
  304, read-only access allowed, no-access 403, unauthenticated 401, no
  background 404, and the config-disabled route being absent.
- Unsplash proxies: routes absent when the provider is disabled, and 401 when
  unauthenticated. The live Unsplash fetch is not exercised, matching v1.
2026-06-17 11:31:50 +00:00
kolaente 5ccbd0d74e feat(api/v2): add project background download and unsplash proxies
Port the remaining read-only background blob endpoints to /api/v2:

- GET /projects/{project}/background streams the stored background (project
  CanRead, in-handler), modeled as an image/jpeg binary response. Honors
  If-Modified-Since (304) and serves through the shared WriteProjectBackground.
- GET /backgrounds/unsplash/images/{image} and .../thumb proxy the upstream
  Unsplash image through the SSRF-safe client, gated on the unsplash provider
  like the sibling unsplash routes, modeled as image/jpeg binary responses.

All three reuse the v1 business logic extracted in the previous commit.
2026-06-17 11:31:50 +00:00
kolaente 8bec654595 refactor(background): extract download + unsplash-proxy logic for reuse
Split the HTTP plumbing from the business logic in the v1 project-background
download and Unsplash image proxy handlers so /api/v2 can reuse it without
duplicating it:

- LoadProjectBackgroundForDownload (background/handler) loads the bg file +
  modtime and fires the Unsplash pingback; GetProjectBackground now calls it.
- WriteProjectBackground (web/files) writes v1's exact background wire shape
  (image/jpg, no-cache, stat-modtime Last-Modified, If-Modified-Since 304).
- FetchUnsplashImageByID / FetchUnsplashThumbByID (background/unsplash) return
  the open upstream body for the caller to stream; the v1 proxy handlers now
  call them. A typed ErrUnsplashImageDoesNotExist maps to 404 on both APIs.
- ErrProjectHasNoBackground (models) gives the no-background case a domain
  error; v1 keeps its verbatim 404 message.

v1 responses are unchanged on the wire.
2026-06-17 11:31:50 +00:00
renovate[bot] ea4bb09679 chore(deps): update dev-dependencies 2026-06-17 11:22:03 +00:00
Frederick [Bot] a8bce2ef0b chore(i18n): update translations via Crowdin 2026-06-17 00:35:30 +00:00
renovate[bot] f851e6f959 chore(deps): update dev-dependencies 2026-06-16 11:46:40 +00:00
kolaente e13d3f537c
fix(deps): bump js-yaml to >=4.2.0 where possible
Desktop only has the v4 copy, so a plain override pins it to >=4.2.0
(resolves alert #245). The frontend also pulls js-yaml v3 via
gray-matter (histoire story tooling), which has no v4-compatible
release, so a scoped 'js-yaml@4' override bumps only the v4 copies
(eslint/cosmiconfig) and leaves gray-matter on 3.14.2. Alert #256
stays open for that dev-only, trusted-input path.
2026-06-16 08:33:16 +02:00
kolaente 9cc47a3da4
fix(deps): force @babel/core >=7.29.6
Resolves the @babel/core <=7.29.0 advisory. Transitive; pinned via
pnpm override. Dependabot alert #255 (frontend).
2026-06-16 08:32:36 +02:00
kolaente d054fb7a5b
fix(deps): force launch-editor >=2.14.1
Resolves the launch-editor <=2.14.0 advisory. Transitive (via
vite-plugin-vue-devtools); pinned via pnpm override. Dependabot
alert #257 (frontend).
2026-06-16 08:32:20 +02:00
kolaente be5858aafe
fix(deps): force markdown-it >=14.2.0 to fix ReDoS advisory
Resolves the markdown-it <=14.1.1 advisory. Transitive; pinned via
pnpm override. Dependabot alert #266 (frontend).
2026-06-16 08:31:46 +02:00
kolaente 340be305f8
fix(deps): tighten tar override to >=7.5.16
The ^7.5.11 override resolved to the vulnerable 7.5.15. Pin to
>=7.5.16. Resolves Dependabot alert #246 (desktop).
2026-06-16 08:31:02 +02:00
kolaente 460e8f3ab1
fix(deps): force form-data >=4.0.6 to fix unsafe boundary advisory
Resolves the form-data <4.0.6 advisory (predictable multipart
boundary). Transitive in both workspaces; pinned via pnpm overrides.
Dependabot alerts #247 (desktop) and #258 (frontend).
2026-06-16 08:30:33 +02:00
kolaente 652f61da50
fix(deps): bump dompurify to 3.4.9 to fix XSS advisories
dompurify 3.4.0 was affected by several stacked advisories (mXSS /
sanitizer bypasses). 3.4.9 is past all vulnerable ranges. Resolves
Dependabot alerts #248-#254 (package.json) and #259-#265 (lockfile).
2026-06-16 08:30:00 +02:00
kolaente b42a7fdcc4
fix(deps): force esbuild >=0.28.1 to fix transitive advisories
The frontend pins esbuild 0.28.1 directly, but vite/histoire and
@intlify/bundle-utils pulled in transitive copies (0.27.7 and 0.25.12)
still affected by GHSA-gv7w-rqvm-qjhr (RCE via missing binary integrity
verification) and GHSA-g7r4-m6w7-qqqr (dev-server file read on Windows).
A pnpm override forces all copies to the patched 0.28.1. Dependabot
alerts #239 and #241.
2026-06-16 08:18:18 +02:00
kolaente 1d6d332c18
fix(deps): bump tmp to >=0.2.7 to fix path traversal advisory
Resolves GHSA-7c78-jf6q-g5cm (type-confusion bypass of _assertPath
allowing path traversal). tmp was pinned to >=0.2.6 via pnpm overrides
in both the frontend and desktop workspaces, which resolved to the
vulnerable 0.2.6. Dependabot alerts #243 (desktop) and #244 (frontend).
2026-06-16 08:17:51 +02:00
Frederick [Bot] 85b820fa7c chore(i18n): update translations via Crowdin 2026-06-16 00:40:29 +00:00
dependabot[bot] 35bcb7ed26 chore(deps-dev): bump esbuild from 0.28.0 to 0.28.1 in /frontend
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.28.0 to 0.28.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.0...v0.28.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-15 17:34:06 +02:00
kolaente a8a53c9581 test(api/v2): cover the v2 file and CSV migrator endpoints
Webtests for the file migrators (status, migrate, auth, missing-file) and the
CSV importer (status, detect, preview, migrate happy path, missing/malformed
config, empty file, auth). Each rejected upload is asserted to map to a 4xx
domain error rather than a 500.
2026-06-12 08:51:19 +00:00
kolaente 77416d32e4 feat(api/v2): add the generic CSV importer on /api/v2
Port the CSV importer's status/detect/preview/migrate endpoints to the Huma
API. detect/preview/migrate take a multipart upload; preview and migrate also
carry the import config as a JSON form value (modeled as a typed multipart
form field), unmarshaled in one shared place and reused via csv.RunMigration.
2026-06-12 08:51:19 +00:00
kolaente a21822fcec feat(api/v2): add file migrators (vikunja-file, ticktick, wekan) on /api/v2
Port the file-based migrators' status + migrate endpoints to the Huma API.
A single registerFileMigrator helper wires all three (mirroring the OAuth
migrator registrar); the migrate endpoint takes a multipart upload under the
"import" field and reuses handler.RunFileMigration. POST migrate returns 200
since it runs an import rather than creating a REST resource.
2026-06-12 08:51:19 +00:00
kolaente a881246e80 refactor(migration): extract file/CSV migrate orchestration into shared funcs
Pull the StartMigration -> Migrate -> FinishMigration orchestration out of
the v1 echo handlers into handler.RunFileMigration and csv.RunMigration so
the v2 API can reuse the exact same business logic. v1 is refactored onto
them and stays byte-identical on the wire.

Also tag the CSV detect/preview/config DTOs with doc:/enum: so they carry
descriptions in the v2 OpenAPI schema (ignored by v1 swaggo/xorm).
2026-06-12 08:51:19 +00:00
kolaente 3af5eb8208 feat(api/v2): add project background upload on /api/v2
Port PUT /projects/{project}/backgrounds/upload to the Huma-backed v2 API. The
multipart handler reuses handler.ValidateAndSaveBackgroundUpload (shared with
v1), checks project write access explicitly, and is gated on the upload provider
config flag. Adds webtests covering the happy path, auth/permission failures,
non-image rejection, the disabled-provider case and the multipart spec shape.
2026-06-12 08:47:08 +00:00
kolaente 8381f7543f refactor(background): share upload validation between v1 and v2 handlers
Extract the MIME validation, file storage and project reload from the v1
UploadBackground handler into ValidateAndSaveBackgroundUpload so the upcoming
v2 handler can reuse it instead of duplicating the logic. The v1 handler keeps
its exact wire behaviour; the inline "not an image" check now returns a typed
ErrFileIsNoImage that the handler maps to the same message.
2026-06-12 08:47:08 +00:00
kolaente 5e00fcbbb8 chore(lint): suppress contextcheck on OIDC provider init call sites
Adding a context parameter to the shared package put its call chains in
contextcheck's scope; the flagged background context in the provider
setup is deliberate since provider lifetime exceeds any request.
2026-06-12 08:56:08 +00:00
kolaente acdc2a07f2 feat(audit): emit the login event for the OAuth code exchange
The new v2 OAuth token endpoint mints a fresh session without going
through NewUserAuthTokenResponse, so those logins were missing from the
audit trail. The refresh grant stays unaudited like the v1 refresh.
2026-06-12 08:56:08 +00:00
kolaente 0eb39fae9a fix(events): handle nil auth when building event doers
ProjectUser.Create and friends are called with a nil auth in tests;
the old interface-typed Doer just serialized as null, so a nil doer
keeps that behavior (and maps to the system actor in the audit entry).
2026-06-12 08:56:08 +00:00
kolaente f0eff52949 fix(events): build event doers without re-fetching the user
GetUserOrLinkShareUser re-fetches the account and fails its status
check, which broke deleting a disabled user's projects (the deletion
runs with the disabled account as doer). Convert the authenticated
principal directly instead — it also matches what the events serialized
before the doer became concrete, and drops a query per event.
2026-06-12 08:56:08 +00:00
kolaente b3bcab1f72 refactor(events): use a concrete doer on project and team events
ProjectUpdated/Deleted, ProjectSharedWith* and TeamCreated/Deleted
carried an interface-typed Doer that could not be unmarshaled, forcing
the audit registrations to decode anonymous mirror structs. Hydrate the
doer via GetUserOrLinkShareUser at the dispatch sites like the task
events already do, register the events directly and drop the untyped
audit registration path.

Webhook payloads for these events now serialize link share doers as
their pseudo-user (negative id) instead of the raw link share object,
consistent with task events.
2026-06-12 08:56:08 +00:00
kolaente f33cde82e2 feat(audit): attribute failed logins to the originating request
Thread the request context through CheckUserCredentials so the
LoginFailedEvent carries IP, user agent and request id — without it,
failed logins were the one auth event useless for brute-force tracing.
All four callers have the request at hand.
2026-06-12 08:56:08 +00:00
kolaente 3291556821 fix(audit): only attribute the logout event to user tokens
Link share JWTs carry no sid claim so they returned before the event
fired, but the id claim was read without checking the token type. Make
the guard explicit so a link share id can never appear as a user id.
2026-06-12 08:56:08 +00:00
kolaente 5d7812a093 fix(audit): handle reopen failure after a failed rotation
If both the rename and the reopen fail, logFile stayed nil while
initialized was still true, panicking on the next write. Propagate the
reopen error and retry the open on the next write so it self-heals.
2026-06-12 08:56:08 +00:00
kolaente 1071755625 fix(routes): generate request IDs at the start of the middleware chain
Echo's RequestID middleware reuses the X-Request-Id header from a proxy
or generates one, so logging and audit all see the same ID. RequestMeta
previously read the request header before any later middleware could
have set one, leaving the audit request_id mostly empty.
2026-06-12 08:56:08 +00:00
kolaente 2e0e8e9582 refactor(audit): move package docs into entry.go 2026-06-12 08:56:08 +00:00
kolaente b86710903b fix: dispatch pending events after user creation commits
The register handler, local/LDAP login and the OIDC callback all queue
the user.created event via DispatchOnCommit but never called
DispatchPending, so the event was silently dropped and its queue entry
leaked. Flush after commit and discard on rollback.
2026-06-12 08:56:08 +00:00
kolaente 9da51f5096 refactor(events): pass context to DispatchPending directly
Every DispatchPending caller either has the request context in scope or
is genuinely request-less, so passing it as a parameter replaces the
stored-context mechanism on the pending queue and satisfies
contextcheck. Also fixes lint findings in the audit package.
2026-06-12 08:56:08 +00:00
kolaente fc831719cd docs(audit): add package documentation 2026-06-12 08:56:08 +00:00
kolaente dbdf4a04cb test(audit): cover listener pipeline, license gating and rotation 2026-06-12 08:56:08 +00:00
kolaente 869bec38b5 feat(audit): register the audited event surface
One config-gated block in RegisterListeners maps every opted-in event
to its audit entry. Events with interface-typed doers are decoded via
a small doer ref that distinguishes link shares by their hash field.
2026-06-12 08:56:08 +00:00
kolaente 5f4a21a4c5 feat(events): add auth boundary events
LoginSucceededEvent fires from NewUserAuthTokenResponse (the chokepoint
where local, LDAP and OIDC logins converge), LoginFailedEvent from
handleFailedPassword on every failed password check, LogoutEvent from
the logout handler, and APIToken issued/revoked/used events from the
token model and auth middleware. The token events carry IDs only since
the freshly created token struct holds the raw token string and the
poison queue logs message payloads.

None of these events have a listener yet — the audit registration adds
them. Dispatching to a topic without subscribers is a no-op.
2026-06-12 08:56:08 +00:00
kolaente eea2ecbc72 feat(audit): wire request-meta middleware and writer initialization 2026-06-12 08:56:08 +00:00
kolaente f308fd830a feat(audit): add audit logging package
Entry schema with constructor-enforced actor/target types, a generic
RegisterEventForAudit helper that maps opted-in events to entries on
the existing watermill bus (license-gated per event since licenses are
runtime-mutable), and a JSONL writer with size-based rotation,
age-based cleanup of rotated files and batched fsync.
2026-06-12 08:56:08 +00:00
kolaente 95084087a5 feat(config): add audit logging config keys 2026-06-12 08:56:08 +00:00
kolaente 48f7dafce3 feat(events): carry request metadata onto dispatched event messages
Adds a RequestMeta context bridge so events dispatched during an HTTP
request can be attributed to it: a middleware stashes IP/UA/request-id
on the request context, the generic Do* handlers associate that context
with the transaction key, and DispatchPending/DispatchWithContext copy
the metadata onto the watermill message at publish time. Existing
dispatch call sites are unchanged.
2026-06-12 08:56:08 +00:00
kolaente 2bbe77c141 fix(api/v2): gate /register at registration time, not per request
Per review: when registration is disabled, skip registering the
/register route entirely instead of registering it and returning 404 on
every request. A request to a disabled instance still 404s (unknown
route). ServiceEnableRegistration is static config, so the gate belongs
in the registrar.
2026-06-12 07:58:17 +00:00
kolaente d8ad9d64f5 test(api/v2): cover ported auth/token endpoints
Add webtests mirroring the v1 coverage for the v2 auth surface:
register (incl. registration-disabled 404), password reset request +
reset, email confirm, link-share auth (password matrix), the OAuth token
flow in both JSON and form-urlencoded encodings, oauth/authorize, the
token-test/check endpoints (200, not 418), /routes and link-share token
renewal (incl. user-token rejection).

Also make the link-share auth body optional so a passwordless share
authenticates with no request body, matching v1.
2026-06-12 07:58:17 +00:00
kolaente 56a516045b feat(api/v2): add token-check, token-routes and link-share renew endpoints
Port the token introspection helpers and link-share token renewal to
/api/v2:

- GET/POST /token/test both return a plain 200 "ok"; v1's POST 418
  teapot easter egg becomes an ordinary success.
- GET /routes lists the scoped-token routes for both API versions
  (models.GetAPITokenRoutes already merges v1 + v2).
- POST /user/token renews a link-share JWT; user tokens are rejected
  (they must use the refresh-token flow), mirroring v1.

The renew response inlines the token field rather than returning
auth.Token directly, since Huma names schemas by bare type and a
top-level auth.Token body would collide with user.Token.
2026-06-12 07:58:17 +00:00
kolaente dc4c3a6a17 feat(api/v2): add OAuth 2.0 token and authorize endpoints
Port oauth/token and oauth/authorize to /api/v2, delegating to the
shared oauth2server.ExchangeToken / Authorize cores.

The token endpoint accepts spec-compliant application/x-www-form-urlencoded
bodies (RFC 6749) in addition to JSON; a form-urlencoded format is
registered on the v2 API that binds into the same json-tagged request
struct. The response carries Cache-Control: no-store. The token endpoint
is public; authorize inherits the global JWT auth.
2026-06-12 07:58:17 +00:00
kolaente 37a174b99e feat(api/v2): add public auth routes (register, password, confirm, link-share)
Port the unauthenticated local-account flows and link-share auth to
/api/v2, delegating to the shared business logic:

- POST /register (404 when registration is disabled)
- POST /user/password/token, POST /user/password/reset
- POST /user/confirm
- POST /shares/{share}/auth

Local-account routes register only when local auth is enabled and the
link-share route only when link sharing is enabled, mirroring v1. Each
operation opts out of global auth and its path is added to
unauthenticatedAPIPaths.
2026-06-12 07:58:17 +00:00
kolaente eac1fa2726 refactor(auth): extract shared auth/token business logic for v2 reuse
Pull the HTTP-independent core out of the v1 auth handlers so both
/api/v1 and the upcoming /api/v2 routes share one implementation:

- oauth2server: ExchangeToken and Authorize take plain inputs and return
  typed responses; HandleToken/HandleAuthorize keep binding + headers.
- pkg/routes/api/shared: AuthenticateLinkShare, RegisterUser,
  ResetPassword (+ session clear), RequestPasswordResetToken and
  ConfirmEmail, plus the shared UserRegister and LinkShareToken types.

v1 handlers now delegate to these; their wire output is unchanged.
2026-06-12 07:58:17 +00:00