651 Commits
Author SHA1 Message Date
kolaente 8f9b9aba68 feat(migration): index the columns that reference stored files
Adds indexes on task_attachments.file_id, users.avatar_file_id,
users.export_file_id, projects.background_file_id and
migration_status.upload_file_id.
2026-11-05 15:42:45 +01:00
kolaente 8ee689add4 fix(backgrounds): clear backgrounds that point at files used elsewhere
A migration clears the background of projects whose background file is
also a task attachment, an avatar, a data export, a pending import upload
or another project's background. Of projects sharing a file, the oldest
keeps it.

The Unsplash and upload providers now remove the old background through
DeleteBackgroundFileIfExists.
2026-11-05 15:42:45 +01:00
kolaente 27ec08bb03 fix(images): limit the side length of decoded images
Images longer than MaxSide (65535) pixels on either side are now rejected
before decoding, like images over the pixel limit.

Attachments with such images are still stored; only their preview is not
generated. Project backgrounds and avatars reject them with the existing
image too large error.
2026-11-05 15:42:45 +01:00
kolaente e3acf407fb fix(export): export related tasks as references
The user data export embedded every related task in full, repeating its
description once per relation. Related tasks are now exported as id and
project id only, since every related task is part of the export anyway.

The importer resolves such references across all imported projects and
creates relations once every project exists, so relations between
projects survive the round trip instead of duplicating the related task.
Exports with the full related task shape import as before.
2026-11-05 15:42:45 +01:00
kolaente 1f79a8e01a fix(migration): reject repeated csv column mappings, cap labels per row
- Reject a config that maps a column more than once, already when the
  import is started or previewed.
- Import repeated labels of a row once and reject rows with more than
  MaxLabelsPerTask (100) labels.
- Count items while converting rows so a file over the import item limit
  fails before the whole structure is built.
- Walk mappings sorted by column so a row only visits the cells it has.
2026-11-05 15:42:45 +01:00
kolaente 8725e89725 refactor(utils): share rune-safe string truncation 2026-11-05 15:42:45 +01:00
kolaente 462a76a466 fix(migration): cap planka names copied into every task and comment
Custom field, group and user names are repeated per value or comment, so one long name multiplied the description size.
2026-11-05 15:42:45 +01:00
kolaente deeee79834 fix(migration): import repeated planka lists and cards only once
Lists, cards, checklists and attachments are looked up by id while
converting a board, so a repeated id imported the whole group once per
copy. Keep the first entry of each id and stop the conversion as soon as
the import item limit is exceeded instead of after building everything.
2026-11-05 15:42:45 +01:00
kolaente d73b5e4963 fix(migration): mask credentials in urls of failed migration errors
Request urls are masked before a failed migration is logged or reported.
2026-11-05 15:42:45 +01:00
kolaente 886ce004c2 feat(migration): add a helper to mask credentials in error urls 2026-11-05 15:42:45 +01:00
kolaente a8501c28e5 fix(mcp): hint at listing all actions when find_action matches nothing
An empty find_action result looked identical to "not authorized", so
agents guessing a wrong resource prefix (task_relations instead of
tasks_relations) concluded their token lacked permission.

Refs #4342
2026-11-05 13:34:47 +01:00
kolaenteandTink b2c1686d11 feat(auth): preview verified OIDC accounts and reject unavailable identities 2026-11-05 12:44:50 +01:00
kolaenteandTink 859b5c5255 feat(user): notify users when an OIDC identity is linked 2026-11-05 12:44:50 +01:00
kolaenteandTink f0625e6d1c feat(api): add reauthenticated OIDC identity management endpoints 2026-11-05 12:44:50 +01:00
kolaenteandTink 68feffefc3 test(openid): count code exchanges in the mock IdP 2026-11-05 12:44:50 +01:00
kolaenteandTink 934ced938e feat(auth): persist linked OIDC identities and authenticate local accounts 2026-11-05 12:44:49 +01:00
kolaenteandTink 5a3ef8e565 refactor(openid): extract issuer to provider lookup from the admin user view
FindProviderByIssuer is the lookup linked identities will use to resolve
their provider; the admin user view switches to it.
2026-11-05 12:44:49 +01:00
kolaenteandTink 833feb90ed test(openid): pin callback login behaviour with a signing mock IdP
Adds a mock OpenID provider issuing signed ID tokens so AuthenticateCallback
can be tested end to end, and pins the current behaviour for OIDC-created
users (email/name/avatar/team sync, disabled user refused). The proxy test
reuses the same mock instead of its inline copy.
2026-11-05 12:44:49 +01:00
kolaenteandTink 61f8af603f refactor(user): share password and TOTP reauthentication 2026-11-05 12:44:49 +01:00
kolaenteandTink 96150b84f8 fix(projects): share background removal across API versions
Address pr-swarm finding: removal passed a stub project, file never deleted
2026-11-04 23:14:28 +01:00
kolaenteandTink 87b85a8ae5 fix(projects): defer background deletion until purge commits
Address pr-swarm finding: blobs deleted before commit
2026-11-04 23:14:28 +01:00
kolaente f44e605075 feat(auth): warn once per provider about enabled fallback options 2026-11-04 16:02:57 +01:00
kolaente 45f06adc27 feat(auth): log a deprecation warning on openid fallback matches
Every login matched through emailfallback or usernamefallback now warns that both options will be removed and the user has to link the provider from their account settings.
2026-11-04 16:02:57 +01:00
kolaente 3830d2277c fix(migration): cap views per project on import 2026-11-03 23:41:40 +01:00
kolaente 1dca7b52e8 fix(migration): count views and per-view task positions in import item limit 2026-11-03 23:41:40 +01:00
kolaente 02ed6b293f fix(tasks): only create import relations between tasks of the imported batch
Review finding: CreateTaskRelationsForImport relied on an unenforced
invariant that every related task came from the same import. It now takes
the imported batch, builds the relations itself and rejects related tasks
outside the batch.
2026-11-03 21:04:05 +01:00
kolaente 64f542c1a1 perf(migration): check the imported task hierarchy for cycles once instead of per relation 2026-11-03 21:04:05 +01:00
kolaente 852e6d6d40 fix(migration): clean up stored import blobs when the import panics 2026-11-03 19:33:30 +01:00
kolaente ae77326e17 fix(migration): skip null positions and task buckets on import 2026-11-03 19:33:30 +01:00
kolaente d4e98a3b5b fix(images): stop accepting TIFF uploads 2026-11-03 18:46:00 +01:00
kolaente 404c5a35a3 fix(backgrounds): cap the page number for the featured unsplash collection 2026-11-03 18:46:00 +01:00
kolaente 43da8bc1c6 fix(migration): check filters.json item limit before decoding saved filters 2026-11-03 18:28:14 +01:00
s-seidelandGitHub ded000f11c feat(auth): sync teams from oidc groups claim (#4311) 2026-11-03 17:13:41 +00:00
kolaente 4fa4e20a7c fix(mcp): allocate field projection map only when a field matches 2026-11-03 09:23:07 +01:00
kolaente d335888c89 fix(mcp): forward fields body argument on tools without projection
The fields projection argument was stripped from every tool's arguments,
including write tools like tasks_bulk_update where fields is part of the
request body, so bulk updates always failed with ErrBulkTaskFieldsRequired.

Fixes #4235
2026-10-23 14:51:17 +02:00
kolaente e6c3b9a943 fix(migration): explain blocked private planka hosts
Planka instances on a private network were rejected by the SSRF guard with
a generic "could not reach" error. Return a dedicated error pointing at
outgoingrequests.allownonroutableips instead.

Refs #4234
2026-10-23 14:50:36 +02:00
kolaente 01c3884bb1 feat(auth): sync provider avatars in the background after login
Address review decision: the post-commit avatar sync still blocked the
LDAP/OIDC login response for the download and up to 30s of decode
queueing. It now runs in a goroutine on a context that outlives the
request, with panics recovered and failures logged.
2026-10-20 23:05:30 +02:00
kolaente 819455ca12 fix(avatar): cancel an avatar decode when the client goes away
Address pr-swarm findings: avatar GETs queued for decode capacity on
context.Background(), so an abandoned request still waited 30s. The
request context now reaches the providers; gravatar's shared fetch keeps
running for other waiters when one disconnects. Busy shedding is logged
as a warning instead of an error.
2026-10-20 23:05:30 +02:00
kolaente f31acee824 fix(avatar): flush avatar caches only after the replacement commits
Address pr-swarm finding: the flush ran before commit while the old blob
stays readable until then, so a queued avatar GET could re-cache the old
image with no expiry. The flush now runs on commit and the cache key
carries the file id.
2026-10-20 23:05:30 +02:00
kolaente 891d11da34 fix(avatar): keep the shared default avatar when replacing it
Address pr-swarm finding: users start on defaultsettings.avatar_file_id,
so the first avatar upload or provider sync deleted that shared file for
everyone. Replacing now skips it, through the same check account
deletion uses.
2026-10-20 23:05:30 +02:00
kolaente d2ec3fefc5 fix(avatar): remove a replaced avatar's blob only after commit
Address review decision: SaveAvatar removed the old file's blob from
storage inside the transaction, so a later rollback left the user on an
avatar whose content was gone. The files row is still deleted in the
session; the blob and its previews go once the session commits.
2026-10-20 23:05:30 +02:00
kolaente 98abca499a fix(auth): sync OIDC and LDAP avatars after the login commits
Address review decision: the provider avatar was decoded inside the login
transaction after the user row was written, holding it while queued for
decode capacity, and a later rollback (TOTP, disabled user) discarded the
work. Logins now commit first; the avatar is decoded without a session
and saved in its own short transaction.
2026-10-20 23:05:30 +02:00
kolaente e7a2daa3ff fix(background): decode backgrounds before opening the write session
Address review decision: a background upload queued for decode capacity
held its transaction for up to 30s. The permission check now runs in a
short read session, the image is decoded and encoded without a session,
and a fresh write session re-checks permission and stores the result.
2026-10-20 23:05:30 +02:00
kolaente c8b816bdfa refactor(imageutils)!: answer every busy decode with code 4037
Address review decision: previews (4037) and other decodes (4040) had two
busy errors for one condition. ErrDecodeBusy now carries 4037 and the
preview-only error is gone.
2026-10-20 23:05:30 +02:00
kolaente 9467ad5787 fix(avatar): decode avatars before opening a database session
Address review decision: a decode queued for capacity held the request's
transaction for up to 30s, so on SQLite a concurrent write made the
upload fail with database is locked. Uploads and avatar reads now decode
and encode first; only the encoded PNG enters the transaction.
2026-10-20 23:05:30 +02:00
kolaente 4f8b06499e feat(avatar)!: store every avatar as a centered square
Address review decision: uploads skipped the square crop synced avatars
got, so non-square API uploads were stored as-is although avatars render
square. Non-square uploads are now center-cropped.
2026-10-20 23:05:30 +02:00
kolaente a1865b0151 docs(api): document decode failures on avatar and background uploads
Address pr-swarm finding: the routes did not mention 400/4032 for
undecodable or oversized images, or 503/4040 when decoding is busy.
2026-10-20 23:05:30 +02:00
kolaente b764cf77eb fix(avatar): sync LDAP and OIDC avatars through one decode
Address pr-swarm finding: both logins copied crop, store and flush, had
diverged, decoded every avatar twice, and imaging.Fill cloned images
thinner than 100 px at full resolution outside the decode budget.
2026-10-20 23:05:30 +02:00
kolaente dc2b7ae37d fix(files): drop the file row when storing its content fails
Address pr-swarm finding: avatar sync on login no longer rolls back on a
store error, so a blobless files row would be committed.
2026-10-20 23:05:30 +02:00
kolaente 36136362a2 fix(background): name the right mime type when rejecting an image
Address pr-swarm finding: SaveBackgroundFile sniffed an already-read
stream, so the 400 reported text/plain.
2026-10-20 23:05:30 +02:00