Commit Graph
17 Commits
Author SHA1 Message Date
kolaenteandTink 1c0cca9b3f feat(projects): audit and announce permanent project deletion
Maintainer decision: ProjectPurgedEvent (project.purged) per purged project, like TaskPurgedEvent
2026-11-04 23:14:28 +01:00
kolaenteandTink 150e17d826 feat(projects): trash and restore project trees 2026-11-04 23:14:28 +01:00
kolaenteandTink 375738ff7e feat(tasks): audit and announce task trash events
Address pr-swarm finding: reloadTaskInEvent dropped task.deleted on ErrTaskIsInTrash
2026-11-04 23:14:28 +01:00
kolaenteandTink 94f501c61d feat(tasks): add task trash lifecycle and permissions 2026-11-04 23:14:28 +01:00
kolaente 51f0cf0c69 fix(audit): never overwrite an existing rotated log file
Rotated files were named by millisecond timestamp only, so two rotations
within the same millisecond renamed onto the same path and replaced the
earlier file. Append a counter until the name is free.
2026-11-03 18:49:00 +01:00
kolaente f5c68ac049 fix(bots): 404 unowned bots, require status on PUT, audit owner status changes
- Owners can manage bots in any status (disabled or locked); unowned,
  missing and non-bot ids all return 404 so existence never leaks.
- PUT /api/v2/user/bots/{bot} requires status, so a partial body can no
  longer re-enable a disabled bot. A bot_status validator tag limits it
  to active/disabled.
- BotUser.Status is a pointer: the v1 update route keeps the current
  status when the body omits it instead of unlocking the bot.
- Owner status changes dispatch bot.status.changed for the audit log.
- Regenerate the frontend client and adapt the bot update callers.

Refs #3999
2026-10-01 15:27:34 +02:00
kolaente 27ccdba7a0 feat(invites): add admin-managed user invite links 2026-09-12 18:21:35 +02:00
kolaente c549e7ff51 feat(audit): audit full personal data export requests
Around 38 events are registered for audit logging, including every admin
action, but a full personal data export left no trace at all. It is
dispatched from both v1 and v2, so one registration covers both.
2026-07-28 18:05:34 +02:00
kolaenteandkolaente 3a666da860 feat: audit admin reads and denied admin access
GET /admin/users returns every user's email address; compliance
regimes commonly require admin PII access to be logged, so the list
read now emits admin.users.listed. The admin gate additionally emits
admin.access.denied (outcome=failure, with method and path) whenever
an authenticated user without the instance-admin flag probes an
/admin/* route — dispatched directly with the request context since
there is no transaction.

Both entries have no single affected resource, so the entry target is
now omitted when empty instead of serialising a zero value.
2026-07-03 16:56:03 +00:00
kolaenteandkolaente 6f21c57c78 feat: audit admin user and project mutations
Every mutating admin action now dispatches a typed event on commit
(admin.user.created, admin.user.admin.granted/revoked,
admin.user.status.changed, admin.user.password.set,
admin.user.password_reset.sent, admin.user.deleted,
admin.project.owner.changed) which the audit catalog maps onto an
entry with the acting admin as actor and the affected user or project
as target. The model actions grow a doer parameter since the acting
admin was not threaded into the models layer before.

Admin creation deliberately emits both user.created (actor = the new
user, unchanged semantics) and admin.user.created (actor = the admin).
The events are instance-level, not project-scoped, so they are not
exposed as webhook events. No password or reset-token material is ever
part of a payload.
2026-07-03 16:56:03 +00:00
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 2e0e8e9582 refactor(audit): move package docs into entry.go 2026-06-12 08:56:08 +00:00
kolaenteandkolaente 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
kolaenteandkolaente fc831719cd docs(audit): add package documentation 2026-06-12 08:56:08 +00:00
kolaenteandkolaente dbdf4a04cb test(audit): cover listener pipeline, license gating and rotation 2026-06-12 08:56:08 +00:00
kolaenteandkolaente 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