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/checks/retention.rb,
lib/standard_audit/event_subscriber.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, Checks, Generators 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.6.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.



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

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



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

def config
  @configuration ||= Configuration.new
end

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

Yields:



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

def configure
  yield(config) if block_given?
end

.event_subscriberObject



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

def event_subscriber
  @event_subscriber ||= EventSubscriber.new
end

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



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
76
# File 'lib/standard_audit.rb', line 24

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



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

def reset_configuration!
  @configuration = nil
end

.subscriberObject



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

def subscriber
  @subscriber ||= Subscriber.new
end