standard_audit 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a6da3bdedaecde5d44a57afe47372bf419b9290ace08a2312d1c522df5a67561
|
|
4
|
+
data.tar.gz: 283e8c2839ddce46fa2ec6468423c75380256f51f05a0a52844763d5454b7da3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 96cd2995bbaaab665bc485770604f37b0b4188cf052a30639b804882a70cc276fad7e10b7ee21c65fa0294018687e06cea921ae7e387f4666a3f7298d29bbd09
|
|
7
|
+
data.tar.gz: 810d2becd35d3e0a5325786b86415c48318b4c23d7c8df46742916053cff6eaf5f97e3cba1e94b8ab479437751ba94177c28b05bc4c12837208a975698788aa0
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.1] - 2026-09-24
|
|
11
|
+
|
|
12
|
+
### Upgrade steps
|
|
13
|
+
|
|
14
|
+
1. **If you installed the 0.12.0 `add_anonymized_at` migration, check your copy.** The 0.12.0 template put `return if column_exists?(:audit_logs, :anonymized_at)` inside `change`. That early return also runs on rollback, so `db:rollback` deleted the `schema_migrations` row and silently left the column in place. Replace the body with `add_column :audit_logs, :anonymized_at, :datetime, if_not_exists: true` (in `change`), or copy the new up/down template. Nothing to do if your copy already uses `if_not_exists:` (sidekick-web, jumpdrive-web) or if you never ran the generator.
|
|
15
|
+
2. If an error-tracker search or alert matches `before_checksum` hook failures on the `audit_event` context key, see Fixed.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **The `add_anonymized_at` migration is reversible.** The template now uses explicit `up` / `down` with `add_column ..., if_not_exists: true` and `remove_column ..., if_exists: true`. It is still idempotent, and rollback now removes the column. A new generator spec runs the generated migration up, down and up.
|
|
20
|
+
- **`before_checksum` hook failures use `config.audit_error_context_key`.** The hook's `Rails.error.report` hard-coded `audit_event:` as its context key. Every other audit-error report site uses the configured key (default `:audit_action`). Apps that set `audit_error_context_key = :audit_event` see no change. Apps on the default now get `audit_action:` for hook failures too, matching every other audit error.
|
|
21
|
+
|
|
22
|
+
### Documentation
|
|
23
|
+
|
|
24
|
+
- `current_scope_resolver` is for scope derived from `Current`. Scope derived from the row (e.g. the target's organisation) belongs in a `before_checksum` or `before_write` hook. The README no longer describes sidekick-web's target-derived hook as a `Current` back-fill.
|
|
25
|
+
- New note: `before_write` / `before_checksum` run once per row, batched writes included. Memoize per-actor lookups (e.g. in a `CurrentAttributes` cache) to avoid an N+1 on a batch flush.
|
|
26
|
+
- `record(raise: false)` reports through `Rails.error`, so failures reach Sentry only if a `Rails.error` subscriber is registered (`sentry-rails` registers one).
|
|
27
|
+
|
|
10
28
|
## [0.12.0] - 2026-09-24
|
|
11
29
|
|
|
12
30
|
### Upgrade steps
|
data/README.md
CHANGED
|
@@ -116,6 +116,12 @@ StandardAudit.record("auth.token_invalid",
|
|
|
116
116
|
The default is `raise: true` (unchanged). In block form the option only
|
|
117
117
|
governs the audit write; errors from your block always propagate.
|
|
118
118
|
|
|
119
|
+
`raise: false` does not talk to Sentry (or any other tracker) itself. It calls
|
|
120
|
+
`Rails.error.report`, so a swallowed failure reaches your error tracker only if
|
|
121
|
+
something subscribes to `Rails.error`. `sentry-rails` registers that subscriber
|
|
122
|
+
for you. With a hand-rolled Sentry setup, register one
|
|
123
|
+
(`Rails.error.subscribe(...)`), or these failures show up only in the log.
|
|
124
|
+
|
|
119
125
|
**Replace your host code with** `raise: false`. It supersedes the
|
|
120
126
|
`AuditAuthFailure#record_auth_failure` rescue-and-report wrapper
|
|
121
127
|
(sidekick-web, luminality-web, nutripod-web
|
|
@@ -179,6 +185,27 @@ dereferencing and `sensitive_keys` redaction, so anything it injects is still
|
|
|
179
185
|
filtered. Mutate `entry` in place; the return value is ignored. Raising aborts
|
|
180
186
|
the write: direct callers see the error, the subscribers rescue and report it.
|
|
181
187
|
|
|
188
|
+
**Hooks run once per row, batched writes included.** `before_write` and
|
|
189
|
+
`before_checksum` run for every row inside `StandardAudit.batch` too (at flush
|
|
190
|
+
time for `before_checksum`). A hook that looks something up per actor (a role,
|
|
191
|
+
a membership, a tenant) therefore runs one query per row and turns a batch into
|
|
192
|
+
an N+1. Memoize those lookups for the unit of work, for example in a
|
|
193
|
+
`CurrentAttributes` cache that resets with the request or job:
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
class Current < ActiveSupport::CurrentAttributes
|
|
197
|
+
attribute :audit_actor_roles
|
|
198
|
+
|
|
199
|
+
def self.audit_role_for(actor)
|
|
200
|
+
self.audit_actor_roles ||= {}
|
|
201
|
+
audit_actor_roles[actor.to_global_id.to_s] ||= actor.audit_role
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# actor_role: a column your app added to audit_logs
|
|
206
|
+
config.before_checksum { |log| log.actor_role = Current.audit_role_for(log.actor) if log.actor }
|
|
207
|
+
```
|
|
208
|
+
|
|
182
209
|
**Replace your host code with** a `before_write`. It supersedes:
|
|
183
210
|
|
|
184
211
|
- a hand-built job-side wrapper that runs a guard before `StandardAudit.record`
|
|
@@ -641,14 +668,25 @@ It is a *fallback*: an explicit `scope:` and a scope found by `scope_extractor`
|
|
|
641
668
|
always win. It applies on every write path (direct `record`, `audit!`,
|
|
642
669
|
`record_audit`, both subscribers; sync, async and batched). Default `nil`.
|
|
643
670
|
|
|
644
|
-
|
|
671
|
+
`current_scope_resolver` is for scope that comes from **ambient request
|
|
672
|
+
state** (`Current`). It takes no arguments and never sees the row. Scope that
|
|
673
|
+
derives from the **row itself**, such as the organisation that owns the
|
|
674
|
+
target, belongs in a `before_checksum` hook (or `before_write`), which receives
|
|
675
|
+
the record or entry:
|
|
676
|
+
|
|
677
|
+
```ruby
|
|
678
|
+
config.before_checksum do |log|
|
|
679
|
+
log.scope ||= log.target.organisation if log.target.respond_to?(:organisation)
|
|
680
|
+
end
|
|
681
|
+
```
|
|
645
682
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
683
|
+
**Replace your host code with** the one line above when your fallback reads
|
|
684
|
+
`Current`. It supersedes a `scope_extractor` that falls back to `Current`
|
|
685
|
+
(nutripod-web:
|
|
686
|
+
`->(payload) { payload[:scope] || Current.channel || Current.organisation }`),
|
|
687
|
+
which only ever covered the subscriber path. Keep `scope_extractor` for reading
|
|
688
|
+
the payload and move the `Current` fallback here. Target-derived scope hooks
|
|
689
|
+
stay as they are; since 0.12.0 they also run on the batched path.
|
|
652
690
|
|
|
653
691
|
## Async Processing
|
|
654
692
|
|
|
@@ -597,7 +597,13 @@ module StandardAudit
|
|
|
597
597
|
# persist — so the hook would not actually be "skipped".
|
|
598
598
|
restore_attributes_from(snapshot)
|
|
599
599
|
Rails.logger.warn("[StandardAudit] before_checksum hook failed: #{e.class}: #{e.message}")
|
|
600
|
-
Rails.
|
|
600
|
+
if Rails.respond_to?(:error) && Rails.error
|
|
601
|
+
Rails.error.report(
|
|
602
|
+
e,
|
|
603
|
+
handled: true,
|
|
604
|
+
context: { StandardAudit.config.audit_error_context_key => event_type }
|
|
605
|
+
)
|
|
606
|
+
end
|
|
601
607
|
nil
|
|
602
608
|
end
|
|
603
609
|
end
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
class AddAnonymizedAtToAuditLogs < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
# A nullable column with no default: a metadata-only change on PostgreSQL,
|
|
3
|
+
# no table rewrite, nothing for StrongMigrations to object to. Idempotent,
|
|
4
|
+
# so it is safe on a host that already has the column (a 0.12+ install).
|
|
5
|
+
#
|
|
6
|
+
# Rows anonymized BEFORE this migration keep a NULL stamp and still fail
|
|
7
|
+
# `verify_chain` as `digest_mismatch`; there is no way to tell them apart
|
|
8
|
+
# from tampered rows after the fact. If your erasure records identify them,
|
|
9
|
+
# stamp them by hand (`update_columns(anonymized_at: ...)`).
|
|
10
|
+
#
|
|
11
|
+
# Explicit up/down with `if_not_exists:` / `if_exists:` rather than an early
|
|
12
|
+
# `return if column_exists?` inside `change`: that return also runs on
|
|
13
|
+
# rollback, which then deletes the schema_migrations row and silently leaves
|
|
14
|
+
# the column behind.
|
|
15
|
+
#
|
|
16
|
+
# Rolling back drops the column even if it came from your create_audit_logs
|
|
17
|
+
# migration (0.12+ installs). Those installs do not need this migration.
|
|
18
|
+
def up
|
|
19
|
+
add_column :audit_logs, :anonymized_at, :datetime, if_not_exists: true
|
|
20
|
+
end
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
def down
|
|
23
|
+
remove_column :audit_logs, :anonymized_at, if_exists: true
|
|
14
24
|
end
|
|
15
25
|
end
|