patorashのブログ

方向性はまだない

gem auditedでrails consoleやrake taskの時にデフォルトユーザーを設定する

auditedというgemを使って、監査ログを保存するようにしたいと思い、現在調査中。

github.com

大体はよさそうなので、採用したいと思っているのだけれど、困ったのが、rails consoleとかでデータ変更されたとき。データ変更の履歴は残るものの、誰がやったかがわからない。

流石にそれだと万が一rails console経由で変な処理してしまったら困るので、誰かは分からずともrails console経由であることくらいはわかるようにしたいということで、設定してみました。

rails consoleであることをどうやって検知するか?

これが全然わからなかったのですが、stackoverflowにありました。

stackoverflow.com

これによると、defined?(::Rails::Server)でいけるとあります。

実際にやってみると…。

bin/rails c
Loading development environment (Rails 6.0.3.6)

[1] pry(main)> defined?(::Rails::Server)
=> nil

なるほど、consoleだと::Rails::Serverの定義を読み込まないということですね。

ちなみにRailsの初期化プロセスはRailsガイドにあるので、こちらも読むと参考になります。最初はこれを読んでどこかで処理を差し込むところはないか?を探ってました。

railsguides.jp

auditedのinitializerを定義する

config/initializers/audited.rbを作ります。

unless defined?(::Rails::Server)
  Audited.store[:audited_user] = "console"
end

ということで、これでめでたし、めでたし…とはなりません😢

rake taskのときにも::Rails::Serverは読みこまれないので、rake taskでもusernameが入ってしまいました。

純粋にrails consoleであることを検知するにはどうしたらいいのか…。

rake taskであることを検知する

これもまたstackoverflowでヒットしました。

stackoverflow.com

Rake.application.top_level_tasks.empty?をすることで、現在の処理がrake task経由であることがわかるようです。

ということで、config/initializers/audited.rbを修正。

unless defined?(::Rails::Server)
  if Rake.application.top_level_tasks.empty?
    Audited.store[:audited_user] = "console"
  else
    Audited.store[:audited_user] = "rake-task"
  end
end

これで、今のところ問題なさそう!👍

と思ったけれど、この記事を書いている最中に、rails runnerのことを考慮できてないやんって気付いた…。ダメだ…😵

Railsの設定で可能だった

ここにきて、神速さんから素晴らしい情報をもらった。

これを設定したら簡単、かつ、わかりやすかった。ダメ元でrunner doも付けてみたらrails runnerの時の設定もうまくいった(ぇ。

しかし、rake taskのときの検出方法がRailsの設定ではわからなかったので、さっきのやつを使うことに。

Rails.application.configure do
  console do
    Audited.store[:audited_user] = "console"
  end

  runner do
    Audited.store[:audited_user] = "runner"
  end
end

unless defined?(::Rails::Server)
  if Rake.application.top_level_tasks.present?
    Audited.store[:audited_user] = "rake-task"
  end
end

これでバッチリでした!👍

[3] pry(main)> hoge = Hoge.find(1)
[4] pry(main)> hoge.name
=> "あああ"
[5] pry(main)> hoge.update(name: "アアア")
[6] pry(main)> Audited::Audit.last
  Audited::Audit Load (1.3ms)  SELECT "audits".* FROM "audits" ORDER BY "audits"."id" DESC LIMIT 1
=> #<Audited::Audit:0x000055b996469a88
 id: 380,
 auditable_id: 1,
 auditable_type: "Hoge",
 associated_id: nil,
 associated_type: nil,
 user_id: nil,
 user_type: nil,
 username: "console", # <= ここが重要!!
 action: "update",
 audited_changes: {"name"=>["あああ", "アアア"]},
 version: 1,
 comment: nil,
 remote_address: nil,
 request_uuid: "4f057afe-2edc-4614-88fd-0a17a7b3d93d",
 created_at: Wed, 14 Apr 2021 18:19:19 JST +09:00>