Skip to content

[1.x] Add first-party Audit extension#4704

Merged
imorland merged 9 commits into
1.xfrom
im/audit-extension
Jun 11, 2026
Merged

[1.x] Add first-party Audit extension#4704
imorland merged 9 commits into
1.xfrom
im/audit-extension

Conversation

@imorland

Copy link
Copy Markdown
Member

Adds flarum/audit, a first-party audit log extension, to the monorepo. It records moderation and administration actions and exposes a log browser in both the admin panel and the forum for users with the relevant permissions.

What it logs

Actions are stored in a flarum_audit_log table with the actor, client type, IP address, action string and a JSON payload. Core coverage includes user lifecycle (created, deleted, activated, renamed, email/password/group changes, logins), discussions, posts, settings, permissions, extension enable/disable/uninstall, and cache clears. First-party integrations add coverage for approval, flags, lock, sticky, suspend, nicknames and tags.

Extender API

A public Flarum\Audit\Extend\Audit extender lets any extension declare its own audit integrations — registering action strings, listening to events, and returning a payload to store. Core and first-party integrations use this extender internally. Third-party integrations (FoF, ClarkWinkelmann) are gated behind Extend\Conditional and kept in extend.thirdparty.php, which is excluded from PHPStan since it references packages outside the monorepo.

Search

The browser supports gambits for action, actor, client, discussion, ip and user. Registered gambits and the known action vocabulary are exposed to the frontend (permission-aware) to drive clickable filter chips, a syntax help panel, and autocomplete for action: and client: values.

GeoIP

IP-to-country display is optional and provided through fof/geoip via core's IPAddress component. The extension bundles no geo database and adds no extra runtime dependency for it.

Password reset logging

Password reset requests are logged at the HTTP layer (/api/forgot), capturing the request IP and recording attempts even when the email matches no account. The queued worker continues to log the issued-token event separately.

Upgrade path

The table-creation migration renames an existing kilowhat_audit_log table in place when upgrading from the kilowhat audit extension, preserving existing data and the primary key sequence. Composer replace removes the superseded kilowhat/flarum-ext-audit, -pro and -free packages.

Notes

  • Targets the 1.x branch.
  • js/dist and js/dist-typings are intentionally not committed; they are generated at merge time.
  • Includes unit and integration test suites and a per-extension backend CI workflow.

@imorland

Copy link
Copy Markdown
Member Author

Documentation: flarum/docs#535

imorland and others added 7 commits June 11, 2026 17:29
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.
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.
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.
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.
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.
@imorland imorland force-pushed the im/audit-extension branch from 4e5affe to 2310e85 Compare June 11, 2026 18:00
@imorland imorland marked this pull request as ready for review June 11, 2026 19:29
@imorland imorland requested a review from a team as a code owner June 11, 2026 19:29
@imorland imorland merged commit b5ac64b into 1.x Jun 11, 2026
440 checks passed
@imorland imorland deleted the im/audit-extension branch June 11, 2026 19:45
imorland added a commit that referenced this pull request Jun 12, 2026
… coverage (#4711)

* [1.x] Add first-party Audit extension (#4704)

* [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>

* [2.x] Migrate audit extension to 2.x; move first-party integrations out; 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

* Apply fixes from StyleCI

* Fix approval AuditTest seed: first_post_id must reference an existing 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.

* chore: add composer link to docs

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
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