Module: StandardAudit

Defined in:
lib/standard_audit.rb,
lib/standard_audit/engine.rb,
lib/standard_audit/version.rb,
lib/standard_audit/auditable.rb,
lib/standard_audit/subscriber.rb,
lib/standard_audit/audit_scope.rb,
lib/standard_audit/configuration.rb,
app/jobs/standard_audit/cleanup_job.rb,
app/models/standard_audit/audit_log.rb,
lib/standard_audit/event_subscriber.rb,
lib/standard_audit/presets/standard_id.rb,
app/jobs/standard_audit/create_audit_log_job.rb,
app/models/standard_audit/application_record.rb,
lib/generators/standard_audit/install/install_generator.rb,
lib/generators/standard_audit/add_checksums/add_checksums_generator.rb

Defined Under Namespace

Modules: AuditScope, Auditable, Generators, Presets Classes: ApplicationRecord, AuditLog, CleanupJob, Configuration, CreateAuditLogJob, Engine, EventSubscriber, Subscriber

Constant Summary collapse

RESERVED_METADATA_KEYS =

Metadata keys owned internally by StandardAudit. Never filtered by ‘sensitive_keys` even if a user adds them there.

%w[_tags _source].freeze
VERSION =
"0.4.0"

Class Method Summary collapse

Class Method Details

.batchObject

Buffers record calls and flushes them via insert_all! on block exit. If the block raises, buffered records are dropped — only successful batches are persisted. Nested batches flush independently. Block-form record calls (with AS::Notifications) bypass the buffer and are processed normally since they don’t persist records directly. Note: uses Thread.current for storage, which is not fiber-safe. Apps using async adapters (Falcon) should avoid concurrent batches.



84
85
86
87
88
89
90
91
92
93
# File 'lib/standard_audit.rb', line 84

def batch
  previous = Thread.current[:standard_audit_batch]
  buffer = Thread.current[:standard_audit_batch] = []

  yield

  flush_batch(buffer) if buffer.any?
ensure
  Thread.current[:standard_audit_batch] = previous
end

.configObject



19
20
21
# File 'lib/standard_audit.rb', line 19

def config
  @configuration ||= Configuration.new
end

.configure {|config| ... } ⇒ Object

Yields:



15
16
17
# File 'lib/standard_audit.rb', line 15

def configure
  yield(config) if block_given?
end

.event_subscriberObject



99
100
101
# File 'lib/standard_audit.rb', line 99

def event_subscriber
  @event_subscriber ||= EventSubscriber.new
end

.record(event_type, actor: nil, target: nil, scope: nil, metadata: {}, **options) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/standard_audit.rb', line 23

def record(event_type, actor: nil, target: nil, scope: nil, metadata: {}, **options)
  return unless config.enabled

  actor ||= config.current_actor_resolver.call

  # Filter sensitive keys. `_tags` and `_source` are reserved internal
  # metadata keys owned by EventSubscriber and are never stripped.
  sensitive = config.sensitive_keys.map(&:to_s) - RESERVED_METADATA_KEYS
   = .reject { |k, _| sensitive.include?(k.to_s) }

  attrs = {
    event_type: event_type,
    occurred_at: Time.current,
    request_id: options[:request_id] || config.current_request_id_resolver.call,
    ip_address: options[:ip_address] || config.current_ip_address_resolver.call,
    user_agent: options[:user_agent] || config.current_user_agent_resolver.call,
    session_id: options[:session_id] || config.current_session_id_resolver.call,
    metadata: 
  }

  if block_given?
    # Block form: instrument via ActiveSupport::Notifications
    ActiveSupport::Notifications.instrument(event_type, .merge(
      actor: actor, target: target, scope: scope
    )) do
      yield
    end
    return
  end

  gid_attrs = {
    actor_gid: actor&.to_global_id&.to_s,
    actor_type: actor&.class&.name,
    target_gid: target&.to_global_id&.to_s,
    target_type: target&.class&.name,
    scope_gid: scope&.to_global_id&.to_s,
    scope_type: scope&.class&.name
  }

  if batching?
    Thread.current[:standard_audit_batch] << attrs.merge(gid_attrs)
    nil
  elsif config.async
    StandardAudit::CreateAuditLogJob.perform_later(attrs.merge(gid_attrs).stringify_keys)
  else
    log = StandardAudit::AuditLog.new(attrs)
    log.actor = actor
    log.target = target
    log.scope = scope
    log.save!
    log
  end
end

.reset_configuration!Object



103
104
105
# File 'lib/standard_audit.rb', line 103

def reset_configuration!
  @configuration = nil
end

.subscriberObject



95
96
97
# File 'lib/standard_audit.rb', line 95

def subscriber
  @subscriber ||= Subscriber.new
end