Skip to content

[2.x] Audit extension 2.x migration, integration split, and GDPR/core coverage#4711

Merged
imorland merged 5 commits into
2.xfrom
im/audit-extension-2x
Jun 12, 2026
Merged

[2.x] Audit extension 2.x migration, integration split, and GDPR/core coverage#4711
imorland merged 5 commits into
2.xfrom
im/audit-extension-2x

Conversation

@imorland

@imorland imorland commented Jun 12, 2026

Copy link
Copy Markdown
Member

This PR brings the first-party flarum/audit extension to Flarum 2.x, then builds on it in three ways: it decouples audit from the other first-party extensions, adds audit logging to GDPR, and adds coverage for several core events.

It targets 2.x and follows on from #4704 (which added the extension on 1.x).

1. flarum/audit 2.x migration

Ports the extension to the 2.x APIs:

  • JSON:API — the Tobscure serializer + list controller are replaced by an AuditLogResource (AbstractDatabaseResource) with an Index endpoint. The list endpoint moves from /api/audit/logs to /api/audit (resource type audit).
  • Search — the gambits are reimplemented as 2.x Filter classes wired through Extend\SearchDriver; the frontend now translates the key:value browser syntax into discrete filter[...] params.
  • Frontendext: imports, method-style model, Avatar/Icon components, the Extend.Admin registry, LESS→CSS variables, and code-splitting (lazy modals).
  • Tests — PHPUnit 12 (#[Test], model-factory seeding, schema), plus added filter coverage.
  • PHP — modernised to the 8.3 baseline (typed properties, promotion, match, etc.).

2. First-party integrations moved into their own extensions

flarum/audit previously hardcoded audit logging for seven extensions. Each now owns its integration, registered via the public Flarum\Audit\Extend\Audit extender behind Extend\Conditional()->whenExtensionEnabled('flarum-audit', …), with an optional flarum/audit dependency — a no-op when audit isn't installed.

Moved: approval, flags, lock, nicknames, sticky, suspend, tags. Action labels travel with each extension (under the flarum-audit.lib.browser.* namespace), and integration tests live in each extension's suite via a shared InteractsWithAuditLog trait. flarum/audit retains only core (non-extension) logging. (lock gained a backend test suite, which it previously lacked.)

3. GDPR audit logging

Adds logging for the two privacy-sensitive GDPR operations:

  • Erasureuser.gdpr_deleted / user.gdpr_anonymized, recording who processed it (the admin, or system for the scheduled task — actor_id null). This required a small additive change to gdpr's Erased event to carry the originating ErasureRequest.
  • Exportuser.gdpr_exported, attributed to the requesting actor.

4. New core event coverage

  • group.created / group.renamed / group.deleted
  • developer_token_created (the raw token value is never logged)
  • settings_reset

Includes one additive core change: Group\Event\Renamed now carries the previous name (mirroring Discussion\Event\Renamed), so the rename log can show old → new.


Documentation: flarum/docs#535

imorland and others added 3 commits June 11, 2026 22:25
* [1.x] Add first-party Audit extension

Adds flarum/audit, a first-party audit log extension, to the monorepo.

It records moderation and administration actions (user, discussion, post,
setting, permission, extension lifecycle and cache events) to a
flarum_audit_log table, and exposes a browser in both the admin panel and
the forum for users with the relevant permissions.

Notable points:

- Public Flarum\Audit\Extend\Audit extender lets any extension declare its
  own audit integrations (register actions, listen to events and produce a
  payload). Core and first-party integrations use this extender internally;
  third-party (FoF, ClarkWinkelmann) integrations are gated behind
  Extend\Conditional and isolated in extend.thirdparty.php.
- Search gambits (action, actor, client, discussion, ip, user) with a
  registry exposed to the frontend for clickable filter chips, a syntax help
  panel, and action/client value autocomplete.
- Optional IP-to-country display via fof/geoip through core's IPAddress
  component; no bundled geo database or extra runtime dependency.
- Password reset requests are logged at the HTTP layer (with the request IP),
  including attempts for emails with no matching account.
- Migration renames an existing kilowhat_audit_log table in place when
  upgrading from the kilowhat audit extension; composer replace removes the
  superseded packages.

* Apply fixes from StyleCI

* chore: change table name

* [1.x] Support PHP 7.3 in Audit extension

Replace arrow functions with closures throughout the extension's extenders
and tests, as arrow functions require PHP 7.4 and the 1.x branch supports
PHP 7.3. Reconcile the audit log table name to audit_log across the console
command and tests to match the migration and model.

* [1.x] Register model lifecycle listeners on the events dispatcher

The integration classes hooked Eloquent model events via the static
Model::event(Closure) API (User::saving, PasswordToken::created,
Tag::created, etc.). That binds closures to the model classes' static
dispatcher, which cannot be serialized under PHPUnit process isolation on
PHP 7.x, causing "Serialization of 'Closure' is not allowed" errors.

Register the same listeners on the events dispatcher instead, using the
"eloquent.<event>: <Model>" keys those static helpers dispatch under, with
named handler methods rather than closures. Behaviour is unchanged.

The flarum/flags HasMany::macro is left as a closure: it relies on $this
being rebound to the HasMany instance and is registered on the relation
class rather than a model instance.

* [1.x] Exempt routes from CSRF in tests instead of the token dance

The integration tests POST to guest routes (register, login, logout,
confirm, reset, forgot, cache clear). The previous GET-token-then-POST
CSRF helper was unreliable in CI. Exempt those routes from CSRF via
Extend\Csrf in the base TestCase, which is the standard approach, and
simplify sendForumCsrfRequest to a plain request.

* [1.x] Test cache logging via the event, not the cache-clear controller

The /api/cache controller also runs assets:publish, which returns a
non-zero exit code (409) in CI where asset publishing isn't available.
Dispatch the ClearingCache event directly and assert the log entry, which
tests the listener without depending on that environment-specific step.

* chore: update extension background color, move away from Clark's brand color

* chore: change color again

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
…ut; add gdpr + core event coverage

- Port flarum/audit to Flarum 2.x (JSON:API resources, search filters, frontend, PHPUnit 12, code-splitting)
- Move first-party audit integrations (approval, flags, lock, nicknames, sticky, suspend, tags) into their own extensions behind Extend\Conditional + optional flarum/audit dependency
- Add audit logging to flarum/gdpr for erasure (recording the processor, or system) and export
- Add core audit actions: group created/renamed/deleted, developer token created, settings reset
- Enrich core Group\Event\Renamed with the previous name
@imorland imorland added this to the 2.0.0-rc.4 milestone Jun 12, 2026
imorland added 2 commits June 12, 2026 01:37
… post (PostgreSQL FK)

The seeded discussion pointed first_post_id at a post id that was never created, which PostgreSQL rejects via discussions_first_post_id_foreign during the approve flow (MySQL/SQLite tolerated it). Point it at the seeded post.
@imorland imorland marked this pull request as ready for review June 12, 2026 01:05
@imorland imorland requested a review from a team as a code owner June 12, 2026 01:05
@imorland imorland merged commit dbe6cba into 2.x Jun 12, 2026
25 checks passed
@imorland imorland deleted the im/audit-extension-2x branch June 12, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants