首页 > 解决方案 > Rails 事务启动钩子

问题描述

我想将我公司的数据库从应用程序级审计转移到数据库级审计。我将使用此处详述的 91audit 触发器:https ://github.com/2ndQuadrant/audit-trigger/blob/master/audit.sql

数据库级别审计的问题在于它实际上并没有记录当前用户,它记录的是 postgresql 用户。

应用程序级审计的缺点是我必须通过网络应用程序运行每个事务才能对更改进行审计(如果我在同一个数据库上有多个应用程序,则效果不佳)

我在这里找到了一个非常有趣的解决方案:http: //schinckel.net/2014/06/01/in-database-audit-trail/

该解决方案依赖于在每个事务开始时在临时表上更新/插入一行。

现在

为了让这对我有用,我想在 rails 添加一个钩子,以便在任何保存、更新、销毁或删除调用时更新这个临时表。

我的问题是:

transaction-startRails 5.2+是否存在顶级钩子?最好是我可以在内部和初始化程序中设置的东西,或者ApplicationRecord

我的第一个想法是添加一个初始化程序,但我不知道我会加入什么

我的备用计划是覆盖ApplicationRecord.transaction

class ApplicationRecord < ActiveRecord::Base
  def self.transaction(*args)
    super(*args) do
      if Current.user
        ActiveRecord::Base.connection.execute(%Q(
          CREATE TEMP TABLE IF NOT EXISTS
          "_app_user" (user_id integer, ip_address inet);

          UPDATE
            _app_user
          SET
            user_id=#{Current.user.id},
            ip_address='#{Current.ip_address}';

          INSERT INTO
            _app_user (user_id, ip_address)
          SELECT
            #{Current.user.id}, '#{Current.ip_address}'
          WHERE NOT EXISTS (SELECT * FROM _app_user);
        ))
      end

      yield
    end
  end
end

标签: ruby-on-railspostgresqlauditruby-on-rails-5.2

解决方案


覆盖 ActiveRecord.transaction 似乎工作得很好

class ApplicationRecord < ActiveRecord::Base
  def self.transaction(*args)
    super(*args) do
      if Current.user
        ip = Current.ip_address ? "'#{Current.ip_address}'" : 'NULL'
        ActiveRecord::Base.connection.execute <<-SQL
          CREATE TEMP TABLE IF NOT EXISTS
            "_app_user" (user_id integer, user_type text, ip_address inet);

          UPDATE
            _app_user
          SET
            user_id=#{Current.user.id},
            user_type='#{Current.user.class.to_s}',
            ip_address=#{ip};

          INSERT INTO
            _app_user (user_id, user_type, ip_address)
          SELECT
            #{Current.user.id},
            '#{Current.user.class.to_s}',
            #{ip}
          WHERE NOT EXISTS (SELECT * FROM _app_user);
        SQL
      end

      yield
    end
  end
end

推荐阅读