第32章 Audit 审计日志
从 KILOWHAT Audit 扩展升级
本扩展是 kilowhat/flarum-ext-audit、kilowhat/flarum-ext-audit-pro 和 kilowhat/flarum-ext-audit-free 这几个扩展的官方继任者。
安装 flarum/audit 时,Composer 的 replace 规则会自动移除上述旧包。首次执行 migrate 时,如果已存在 kilowhat_audit_log 表,会被原地重命名为 audit_log,所有已有条目及其 ID 序列都会完整保留——不会丢失任何数据。
付费版与免费版的区别已不复存在:这里描述的所有功能全部内置。
权限
扩展会在后台管理的权限页面新增两个权限,均归属于「管理」分类下。
之所以划分两个级别,是为了让你可以在不向更大范围的管理团队暴露 IP 地址、账号修改记录等个人身份信息(PII)的前提下,让他们了解管理操作。建议将完整的「查看审计日志」权限只授予少数可信人员,其余团队成员则通过「查看受限审计日志」获得所需的监督能力,同时尽量减少 PII 暴露——这有助于你履行 GDPR 等数据保护义务。
查看审计日志(View audit log)
flarum-audit.view —— 授予日志的完整访问权:所有操作、IP 地址以及操作者。拥有此权限的用户可以从会话下拉菜单(后台和论坛)以及用户和话题的操作菜单中打开日志。
查看受限审计日志(View limited audit log)
flarum-audit.viewLimited —— 使用同一个日志浏览器,但只能查看可配置的日志子集。点击权限旁边的「Configure」即可打开受限访问设置:
- 查看 IP 地址 —— 受限用户是否可以查看并按 IP 地址搜索。国旗标识(如有)不受此开关影响。
- 操作类型 —— 选择受限用户可以看到哪些操作。如果所有
user.*操作都被隐藏,用户菜单中的「审计日志(账号修改)」按钮将被移除;如果所有discussion.*和post.*操作都被隐藏,话题的审计按钮将被移除。
受限访问列表留空时,受限用户可以看到所有内容(IP 地址除外,它由自己的开关控制)。
浏览日志
可从以下入口打开日志:
- 后台管理面板中扩展自己的页面。
- 会话下拉菜单(论坛顶部菜单),适用于任何拥有查看权限的用户。
- 某个用户的操作菜单——「审计日志(账号修改)」显示影响该用户的操作,「审计日志(该用户的操作)」显示该用户执行的操作。
- 某个话题的操作菜单——「审计日志」显示影响该话题的操作。
每条记录都会显示操作者、操作类型、受影响的资源、客户端类型、IP 地址和时间。点击「Refresh」可在不刷新页面的情况下拉取最新条目;通过每条记录的菜单,可以按操作者、IP、客户端、操作类型、用户或话题进行筛选,也可以查看原始存储的 payload。
搜索
筛选框使用一组固定的搜索 gambit。日志浏览器会显示可点击的筛选标签,并提供一个「Search help」面板说明每个 gambit 的含义;action: 和 client: 筛选器在输入时会自动补全可用值。
| Gambit | 说明 |
|---|---|
| action:<name> | 匹配操作名称。 |
| actor:<username> | 匹配执行操作的操作者。对于没有认证操作者的操作(如 CLI 或系统活动),使用 actor:guest。 |
| user:<username> | 匹配影响某个用户的操作。 |
| client:<client> | 匹配客户端类型。 |
| discussion:<id> | 匹配某个话题 ID 的 discussion 和 post 事件。 |
| ip:<ip> | 匹配 IP 地址(仅对有权限查看 IP 的用户可用)。 |
Gambit 支持逗号分隔的多个值来匹配其中任意一个(例如 action:post.created,post.deleted),前缀 - 表示排除匹配(例如 -client:cli)。
info
筛选框只认这些 gambit。输入的不符合 gambit 格式的自由文本不会产生任何效果。
IP 所属国家标识
Audit 本身不负责将 IP 地址解析为所在国家。不过,如果安装并启用了 FriendsOfFlarum GeoIP 扩展(fof/geoip),日志中显示的 IP 地址会通过 Flarum 的 IP 地址组件渲染,而 GeoIP 会为该组件附加国旗和查询详情。Audit 不需要单独提供任何地理数据库,也不会为此引入额外依赖。
记录的结构
每条日志记录包含:
- 操作名称(完整列表见下文)。
- 日期和时间。
- 受影响资源的 ID(如话题、帖子、用户、标签等),如有,存储在 payload 中。
- 客户端类型,取值之一:
session—— Cookie 会话,通常是 Flarum 网页界面。access_token—— 使用用户 access token 发起的直接 API 请求。api_key—— 使用主 API key 发起的直接 API 请求。cli—— 通过控制台运行的命令,或队列任务。unknown—— 无法确定以上任何一种。
- IP 地址,Web 请求时可用。
- 操作者,Web 请求时可用。
- 下文各操作说明中列出的额外 payload。
info
当某个操作由队列任务而非原始 Web 请求处理时(例如密码重置,或 GDPR 的数据擦除与导出),该任务无法获取原始请求的 IP 地址和操作者。此类记录会以cli客户端保存,且没有 IP。如果责任用户仍然重要——例如执行 GDPR 擦除的管理员——集成会在 payload 中显式记录(见下文 GDPR 部分);而系统驱动的操作(如定时擦除)则不记录操作者。
日志浏览器会在其记录的操作者名下显示
cli条目,并在时间旁标注「Command line」。只有没有操作者的条目才会以「Command line」代替操作者显示。在 Flarum 2.0.1 之前,所有cli条目都会这样显示,即使它记录了操作者。
删除数据
出于安全考虑,禁用扩展、点击「Purge」按钮或运行 php flarum migrate:reset 都不会删除日志数据。
要在启用扩展的状态下永久销毁数据,请使用以下控制台命令:
php flarum audit:clear --reset
此操作将删除 audit_log 表,清除其迁移记录,并禁用该扩展。如果该包是单独安装的,随后可以使用 composer remove flarum/audit 将其移除。 若希望仅删除较早的日志记录而保留近期数据,可使用 --before 选项,该选项接受任何 Carbon 可解析的日期或相对时间表达式: php flarum audit:clear --before="-1 year"
使用 `--before` 参数删除审计日志时,该操作本身也会记录为 `audit_log_cleared`。若需跳过确认提示,请添加 `--force` 参数。运行 `php flarum audit:clear --help` 可查看所有可用选项。 记录的操作 以下是内置记录的完整操作列表。核心操作始终会被记录。由其他扩展贡献的操作仅在该扩展启用时生效。 在 Flarum 2.x 中,其他扩展的审计覆盖功能需在其自身内部实现,通过公开的 `Flarum\Audit\Extend\Audit` 扩展器进行声明(参见“与 Audit 集成”)。Audit 组件本身仅记录核心操作;下方文档中列出的各扩展操作随相应的内置扩展一同分发。无论声明位置如何,操作名称和负载内容均保持一致。 为避免重复记录,部分操作被刻意排除在日志之外: - `user.activated` 不会在账户创建时通过 `isEmailConfirmed` 属性确认邮箱时记录。 - `user.avatar_changed` 不会在通过社交登录自动设置头像时记录。 - 由于讨论的创建已被记录,`post.created` 不会记录讨论的首帖。 内部操作 - `audit_log_cleared`:通过 `audit:clear --before` 删除了条目(负载:查询日期、删除的条目数)。若无条目匹配,则不记录。 - `extension.enabled`:Audit 扩展本身被启用。记录此操作是为了保留日志在审计功能失效期间的痕迹。由于此时请求上下文尚未可用,该操作不归属于任何具体执行者。 Flarum 核心 - `cache_cleared`:缓存已清除。 - `discussion.created`:发起了新讨论。 - `discussion.deleted`:讨论已被永久删除。 - `discussion.hidden`:讨论已被软删除。 - `discussion.renamed`:讨论标题已变更(负载:旧标题、新标题)。 - `discussion.restored`:软删除的讨论已恢复。 - `developer_token_created`:创建了开发者(长期有效)API 访问令牌(负载:拥有者用户 ID、标题)。令牌值本身永远不会被记录。 - `extension.disabled`:扩展通过后台面板被禁用(负载:软件包名称)。 - `extension.enabled`:扩展通过后台面板被启用(负载:软件包名称)。 - `extension.uninstalled`:扩展通过后台面板被回滚(负载:软件包名称)。 - `group.created`:用户组已创建(负载:组 ID、名称)。 - `group.renamed`:用户组已重命名(负载:组 ID、旧名称、新名称)。 - `group.deleted`:用户组已删除(负载:组 ID、名称)。 - `permission_changed`:权限已编辑(负载:旧群组、新群组)。 - `post.created`:创建了回复(讨论的首帖除外)。 - `post.deleted`:帖子已被永久删除。 - `post.hidden`:帖子已被软删除。 - `post.restored`:软删除的帖子已恢复。 - `post.revised`:帖子内容已编辑。 - `setting_changed`:设置已编辑(负载:键;仅对已知非敏感设置列表记录旧值和新值)。 - `settings_reset`:一项或多项设置已重置为默认值(负载:扩展 ID、重置的键)。 - `user.activated`:账户由管理员或扩展手动激活。 - `user.activated_with_email`:账户通过确认邮件激活。 - `user.avatar_changed`:用户头像已被替换。 - `user.avatar_removed`:用户头像已被移除。 - `user.created`:账户已创建。 - `user.deleted`:账户已被永久删除。 - `user.email_changed`:用户邮箱已变更(负载:旧邮箱、新邮箱)。 - `user.email_change_requested`:已请求更改邮箱(负载:新邮箱)。 - `user.groups_changed`:用户所属群组已变更(负载:旧群组 ID、新群组 ID)。 - `user.logged_in`:用户通过登录表单登录。 - `user.logged_in_with_provider`:用户通过社交登录登录(负载:提供商、标识符)。 - `user.logged_out`:用户已登出。 - `user.password_changed`:用户密码已更改。 - `user.password_change_requested`:已为用户颁发密码重置令牌。 - `user.password_reset_attempted`:通过 `/forgot` 请求重置密码(负载:是否匹配到账户;仅在未匹配到账户时存储邮箱,否则记录匹配到的用户)。此操作在 HTTP 层捕获,因此保留了请求者的 IP 并记录针对未知邮箱的尝试。 - `user.provider_connected`:社交登录已关联至账户(负载:提供商、标识符)。 - `user.username_changed`:用户名已更改(负载:旧用户名、新用户名)。 Flarum Approval - `post.approved`:帖子已获批准(负载:讨论 ID、帖子 ID)。 - `discussion.approved`:讨论已获批准(在其首帖获批准时记录;负载:讨论 ID)。 Flarum Flags - `post.flagged`:用户标记了帖子(负载:原因)。Approval/Akismet 的标记不予记录。 - `post.dismissed_flags`:帖子的标记已被忽略。 Flarum GDPR 这些操作涵盖 `flarum/gdpr` 中的隐私操作。由于擦除和导出通过队列任务运行,负责用户是显式记录的,而非源自请求上下文。 - `user.gdpr_erasure_requested`:用户请求擦除其数据(负载:用户 ID)。 - `user.gdpr_erasure_confirmed`:用户通过邮件链接确认了擦除请求(负载:用户 ID)。即使链接是在未登录状态下打开的,该操作也归属于该用户。 - `user.gdpr_erasure_cancelled`:擦除请求已被取消,由用户或版主执行(负载:用户 ID)。归属于执行取消操作的人。 - `user.gdpr_deleted`:用户数据通过删除方式擦除(负载:用户 ID、处理器)。条目归属于处理该请求的管理员;计划性的(自动)擦除没有执行者。 - `user.gdpr_anonymized`:用户数据通过匿名化方式擦除(负载:用户 ID、处理器),归属同上。 - `user.gdpr_exported`:请求了用户数据导出(负载:用户 ID),归属于请求的执行者。 Flarum Lock - `discussion.locked`:讨论已锁定。 - `discussion.unlocked`:讨论已解锁。 Flarum Nicknames - `user.nickname_changed`:昵称已更改(负载:旧昵称、新昵称)。 Flarum Sticky - `discussion.stickied`:讨论已置顶。 - `discussion.unstickied`:讨论已取消置顶。 Flarum Suspend 封禁自然过期时不会生成条目。 - `user.suspended`:用户已被封禁(负载:若设置,则包含封禁截止日期)。 - `user.unsuspended`:用户已被手动解封。 Flarum Tags - `discussion.tagged`:讨论的标签已修改(负载:旧标签、新标签)。 - `tag.created`:标签已创建。 - `tag.updated`:标签通过后台面板已修改。 - `tag.deleted`:标签已被永久删除。 第三方扩展集成 Audit 还捆绑了 Flarum monorepo 之外若干扩展的集成。这些覆盖范围源自最初的 KILOWHAT 扩展;每个集成仅在相应扩展启用时激活。 这些集成旨在逐步从 Audit 移至相应扩展中,使用方式与上述第一方扩展相同,均通过公开的 `Flarum\Audit\Extend\Audit` 扩展器声明。当扩展采用此 API 后,此处对应的代码块将被移除,以便扩展拥有自己的审计集成——记录的操作和负载保持不变。如果您维护这些扩展之一,鼓励您自行添加集成——参见“与 Audit 集成”——这将使您可以从 Audit 中移除捆绑的副本。 FriendsOfFlarum Ban IPs - `fof_ban_ips.banned`:IP 已被封禁(负载:IP、原因,以及在适用时的用户)。 - `fof_ban_ips.unbanned`:IP 已被解禁(负载:IP,以及在适用时的用户)。 FriendsOfFlarum Impersonate - `user.impersonated`:用户已被模拟登录(负载:原因,若提供)。 FriendsOfFlarum Merge Discussions 两个操作同时记录,以便合并出现在两个讨论的历史中。 - `discussion.merged_away`:讨论已合并至另一讨论(负载:新讨论 ID)。 - `discussion.merged_into`:讨论已合并至此讨论(负载:原讨论 ID、帖子数)。 FriendsOfFlarum Split 两个操作同时记录,以便拆分出现在两个讨论的历史中。 - `discussion.split_away`:帖子已从讨论中拆出(负载:新讨论 ID、帖子数)。 - `discussion.split_into`:帖子已拆分至新讨论(负载:原讨论 ID、帖子数)。 FriendsOfFlarum User Bio - `user.bio_changed`:用户简介已更改。 FriendsOfFlarum Username Request - `user.username_requested`:已请求更改用户名(负载:新用户名)。 - `user.username_request_approved`:用户名更改请求已批准(负载:旧用户名、新用户名)。 - `user.username_request_rejected`:用户名更改请求已拒绝(负载:新用户名、原因)。 - `user.nickname_requested`:已请求更改昵称(负载:新昵称)。 - `user.nickname_request_approved`:昵称更改请求已批准(负载:旧昵称、新昵称)。 - `user.nickname_request_rejected`:昵称更改请求已拒绝(负载:新昵称、原因)。 ClarkWinkelmann Author Change - `discussion.create_date_changed`:讨论的创建日期已更改(负载:旧日期、新日期)。 - `discussion.user_changed`:讨论的作者已更改(负载:旧用户 ID 和新用户 ID)。 - `post.create_date_changed`:帖子的创建日期已更改(负载:旧日期、新日期)。 - `post.edit_date_changed`:帖子的编辑日期已更改(负载:旧日期、新日期)。 - `post.user_changed`:帖子的作者已更改(负载:旧用户 ID 和新用户 ID)。 已知局限 - 当操作由队列任务处理时,原始请求的 IP 和执行者对该任务不可用(参见“条目结构”下的说明)。 - 对于 FriendsOfFlarum Ban IPs,解封单个 IP 可能因 FriendsOfFlarum/ban-ips#4 而未被记录。 - 对于 Merge Discussions 和 Split,早于合并或拆分操作的条目仍附属于原讨论,不会出现在结果讨论的审计弹窗中。