audited 5.2.0 → 5.3.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of audited might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +11 -5
- data/CHANGELOG.md +24 -0
- data/README.md +1 -0
- data/lib/audited/audit.rb +1 -1
- data/lib/audited/auditor.rb +27 -3
- data/lib/audited/version.rb +1 -1
- data/spec/audited/auditor_spec.rb +108 -4
- data/spec/support/active_record/models.rb +12 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9441d78ecdc47f146411573dbbba291449a94d062e2e564be52ff58548a2c25
|
4
|
+
data.tar.gz: e2843b1bfe94e4a19aa6666a98c01d23fd165240f96d2c602d5291383b68f4a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbee8a19b3da64248a2d2ea58bdac95f94431b4e57545ceae25897b50470a3adf7bc9b7497da58a7e85e466502d2baa3c0d9e9e96e070b2f48b8a0ebe506cd0d
|
7
|
+
data.tar.gz: e0093f598ceec9caade9d14c9fdba938e8ba92b3355bc164ba9587b1863230681586071ac5864ede9aaf361be58bdad38e08742a9f06e1093e89422c0a04e4ea
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
name: CI
|
2
2
|
|
3
3
|
on:
|
4
|
-
pull_request
|
5
|
-
push
|
6
|
-
branches:
|
7
|
-
- main
|
4
|
+
- pull_request
|
5
|
+
- push
|
8
6
|
|
9
7
|
jobs:
|
10
8
|
build:
|
@@ -12,7 +10,7 @@ jobs:
|
|
12
10
|
strategy:
|
13
11
|
fail-fast: false
|
14
12
|
matrix:
|
15
|
-
ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1]
|
13
|
+
ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2]
|
16
14
|
appraisal:
|
17
15
|
- rails50
|
18
16
|
- rails51
|
@@ -43,6 +41,8 @@ jobs:
|
|
43
41
|
ruby: 3.0
|
44
42
|
- appraisal: rails50
|
45
43
|
ruby: 3.1
|
44
|
+
- appraisal: rails50
|
45
|
+
ruby: 3.2
|
46
46
|
|
47
47
|
# Rails 5.1 supports Ruby 2.2-2.5
|
48
48
|
- appraisal: rails51
|
@@ -53,6 +53,8 @@ jobs:
|
|
53
53
|
ruby: 3.0
|
54
54
|
- appraisal: rails51
|
55
55
|
ruby: 3.1
|
56
|
+
- appraisal: rails51
|
57
|
+
ruby: 3.2
|
56
58
|
|
57
59
|
# Rails 5.2 supports Ruby 2.2-2.5
|
58
60
|
- appraisal: rails52
|
@@ -63,6 +65,8 @@ jobs:
|
|
63
65
|
ruby: 3.0
|
64
66
|
- appraisal: rails52
|
65
67
|
ruby: 3.1
|
68
|
+
- appraisal: rails52
|
69
|
+
ruby: 3.2
|
66
70
|
|
67
71
|
# Rails 6.0 supports Ruby 2.5-2.7
|
68
72
|
- appraisal: rails60
|
@@ -73,6 +77,8 @@ jobs:
|
|
73
77
|
ruby: 3.0
|
74
78
|
- appraisal: rails60
|
75
79
|
ruby: 3.1
|
80
|
+
- appraisal: rails60
|
81
|
+
ruby: 3.2
|
76
82
|
|
77
83
|
# Rails 6.1 supports Ruby 2.5+
|
78
84
|
- appraisal: rails61
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Audited ChangeLog
|
2
2
|
|
3
|
+
## 5.3.2 (2023-02-22)
|
4
|
+
|
5
|
+
- Touch audit bug fixes - @mcyoung
|
6
|
+
[#662](https://github.com/collectiveidea/audited/pull/662)
|
7
|
+
|
8
|
+
## 5.3.1 (2023-02-21)
|
9
|
+
|
10
|
+
- Ensure touch support doesn't cause double audits - @mcyoung
|
11
|
+
[#660](https://github.com/collectiveidea/audited/pull/660)
|
12
|
+
- Testing Improvements - @vlad-psh
|
13
|
+
[#628](https://github.com/collectiveidea/audited/pull/628)
|
14
|
+
- Testing Improvements - @mcyoung
|
15
|
+
[#658](https://github.com/collectiveidea/audited/pull/658)
|
16
|
+
|
17
|
+
## 5.3.0 (2023-02-14)
|
18
|
+
|
19
|
+
- Audit touch calls - @mcyoung
|
20
|
+
[#657](https://github.com/collectiveidea/audited/pull/657)
|
21
|
+
- Allow using with Padrino and other non-Rails projects - @nicduke38degrees
|
22
|
+
[#655](https://github.com/collectiveidea/audited/pull/655)
|
23
|
+
- Testing updates - @jdufresne
|
24
|
+
[#652](https://github.com/collectiveidea/audited/pull/652)
|
25
|
+
[#653](https://github.com/collectiveidea/audited/pull/653)
|
26
|
+
|
3
27
|
## 5.2.0 (2023-01-23)
|
4
28
|
|
5
29
|
Improved
|
data/README.md
CHANGED
@@ -24,6 +24,7 @@ Audited supports and is [tested against](https://github.com/collectiveidea/audit
|
|
24
24
|
* 2.7
|
25
25
|
* 3.0
|
26
26
|
* 3.1
|
27
|
+
* 3.2
|
27
28
|
|
28
29
|
Audited may work just fine with a Ruby version not listed above, but we can't guarantee that it will. If you'd like to maintain a Ruby that isn't listed, please let us know with a [pull request](https://github.com/collectiveidea/audited/pulls).
|
29
30
|
|
data/lib/audited/audit.rb
CHANGED
@@ -174,7 +174,7 @@ module Audited
|
|
174
174
|
if action == "create"
|
175
175
|
self.version = 1
|
176
176
|
else
|
177
|
-
collection = (
|
177
|
+
collection = (ActiveRecord::VERSION::MAJOR >= 6) ? self.class.unscoped : self.class
|
178
178
|
max = collection.auditable_finder(auditable_id, auditable_type).maximum(:version) || 0
|
179
179
|
self.version = max + 1
|
180
180
|
end
|
data/lib/audited/auditor.rb
CHANGED
@@ -84,6 +84,7 @@ module Audited
|
|
84
84
|
|
85
85
|
after_create :audit_create if audited_options[:on].include?(:create)
|
86
86
|
before_update :audit_update if audited_options[:on].include?(:update)
|
87
|
+
after_touch :audit_touch if audited_options[:on].include?(:touch) && ::ActiveRecord::VERSION::MAJOR >= 6
|
87
88
|
before_destroy :audit_destroy if audited_options[:on].include?(:destroy)
|
88
89
|
|
89
90
|
# Define and set after_audit and around_audit callbacks. This might be useful if you want
|
@@ -230,8 +231,15 @@ module Audited
|
|
230
231
|
|
231
232
|
private
|
232
233
|
|
233
|
-
def audited_changes
|
234
|
-
all_changes =
|
234
|
+
def audited_changes(for_touch: false)
|
235
|
+
all_changes = if for_touch
|
236
|
+
previous_changes
|
237
|
+
elsif respond_to?(:changes_to_save)
|
238
|
+
changes_to_save
|
239
|
+
else
|
240
|
+
changes
|
241
|
+
end
|
242
|
+
|
235
243
|
filtered_changes = \
|
236
244
|
if audited_options[:only].present?
|
237
245
|
all_changes.slice(*self.class.audited_columns)
|
@@ -239,6 +247,15 @@ module Audited
|
|
239
247
|
all_changes.except(*self.class.non_audited_columns)
|
240
248
|
end
|
241
249
|
|
250
|
+
if for_touch
|
251
|
+
filtered_changes.reject! do |k, v|
|
252
|
+
next unless audits.present?
|
253
|
+
|
254
|
+
audits.last.audited_changes[k].to_json == v.to_json ||
|
255
|
+
audits.last.audited_changes[k].to_json == v[1].to_json
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
242
259
|
filtered_changes = redact_values(filtered_changes)
|
243
260
|
filtered_changes = filter_encrypted_attrs(filtered_changes)
|
244
261
|
filtered_changes = normalize_enum_changes(filtered_changes)
|
@@ -324,6 +341,13 @@ module Audited
|
|
324
341
|
end
|
325
342
|
end
|
326
343
|
|
344
|
+
def audit_touch
|
345
|
+
unless (changes = audited_changes(for_touch: true)).empty?
|
346
|
+
write_audit(action: "update", audited_changes: changes,
|
347
|
+
comment: audit_comment)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
327
351
|
def audit_destroy
|
328
352
|
unless new_record?
|
329
353
|
write_audit(action: "destroy", audited_changes: audited_attributes,
|
@@ -474,7 +498,7 @@ module Audited
|
|
474
498
|
|
475
499
|
def normalize_audited_options
|
476
500
|
audited_options[:on] = Array.wrap(audited_options[:on])
|
477
|
-
audited_options[:on] = [:create, :update, :destroy] if audited_options[:on].empty?
|
501
|
+
audited_options[:on] = [:create, :update, :touch, :destroy] if audited_options[:on].empty?
|
478
502
|
audited_options[:only] = Array.wrap(audited_options[:only]).map(&:to_s)
|
479
503
|
audited_options[:except] = Array.wrap(audited_options[:except]).map(&:to_s)
|
480
504
|
max_audits = audited_options[:max_audits] || Audited.max_audits
|
data/lib/audited/version.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
|
3
|
+
# not testing proxy_respond_to? hack / 2 methods / deprecation of `version`
|
4
|
+
# also, an additional 6 around `after_touch` for Versions before 6.
|
5
|
+
uncovered = (ActiveRecord::VERSION::MAJOR < 6) ? 15 : 9
|
6
|
+
SingleCov.covered! uncovered: uncovered
|
4
7
|
|
5
8
|
class ConditionalPrivateCompany < ::ActiveRecord::Base
|
6
9
|
self.table_name = "companies"
|
@@ -143,7 +146,7 @@ describe Audited::Auditor do
|
|
143
146
|
end
|
144
147
|
|
145
148
|
it "should be configurable which attributes are not audited via ignored_attributes" do
|
146
|
-
Audited.ignored_attributes = ["delta", "top_secret", "created_at"]
|
149
|
+
Audited.ignored_attributes = ["delta", "top_secret", "created_at", "updated_at"]
|
147
150
|
|
148
151
|
expect(Secret.non_audited_columns).to include("delta", "top_secret", "created_at")
|
149
152
|
end
|
@@ -215,17 +218,25 @@ describe Audited::Auditor do
|
|
215
218
|
redacted = Audited::Auditor::AuditedInstanceMethods::REDACTED
|
216
219
|
user =
|
217
220
|
Models::ActiveRecord::UserMultipleRedactedAttributes.create(
|
218
|
-
password: "password"
|
219
|
-
ssn: 123456789
|
221
|
+
password: "password"
|
220
222
|
)
|
221
223
|
user.save!
|
222
224
|
expect(user.audits.last.audited_changes["password"]).to eq(redacted)
|
225
|
+
# Saving '[REDACTED]' value for 'ssn' even if value wasn't set explicitly when record was created
|
223
226
|
expect(user.audits.last.audited_changes["ssn"]).to eq(redacted)
|
227
|
+
|
224
228
|
user.password = "new_password"
|
225
229
|
user.ssn = 987654321
|
226
230
|
user.save!
|
227
231
|
expect(user.audits.last.audited_changes["password"]).to eq([redacted, redacted])
|
228
232
|
expect(user.audits.last.audited_changes["ssn"]).to eq([redacted, redacted])
|
233
|
+
|
234
|
+
# If we haven't changed any attrs from 'redacted' list, audit should not contain these keys
|
235
|
+
user.name = "new name"
|
236
|
+
user.save!
|
237
|
+
expect(user.audits.last.audited_changes).to have_key("name")
|
238
|
+
expect(user.audits.last.audited_changes).not_to have_key("password")
|
239
|
+
expect(user.audits.last.audited_changes).not_to have_key("ssn")
|
229
240
|
end
|
230
241
|
|
231
242
|
it "should redact columns in 'redacted' column with custom option" do
|
@@ -414,6 +425,99 @@ describe Audited::Auditor do
|
|
414
425
|
end
|
415
426
|
end
|
416
427
|
|
428
|
+
if ::ActiveRecord::VERSION::MAJOR >= 6
|
429
|
+
describe "on touch" do
|
430
|
+
before do
|
431
|
+
@user = create_user(name: "Brandon", status: :active)
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should save an audit" do
|
435
|
+
expect { @user.touch(:suspended_at) }.to change(Audited::Audit, :count).by(1)
|
436
|
+
end
|
437
|
+
|
438
|
+
it "should set the action to 'update'" do
|
439
|
+
@user.touch(:suspended_at)
|
440
|
+
expect(@user.audits.last.action).to eq("update")
|
441
|
+
expect(Audited::Audit.updates.order(:id).last).to eq(@user.audits.last)
|
442
|
+
expect(@user.audits.updates.last).to eq(@user.audits.last)
|
443
|
+
end
|
444
|
+
|
445
|
+
it "should store the changed attributes" do
|
446
|
+
@user.touch(:suspended_at)
|
447
|
+
expect(@user.audits.last.audited_changes["suspended_at"][0]).to be_nil
|
448
|
+
expect(Time.parse(@user.audits.last.audited_changes["suspended_at"][1].to_s)).to be_within(2.seconds).of(Time.current)
|
449
|
+
end
|
450
|
+
|
451
|
+
it "should store audit comment" do
|
452
|
+
@user.audit_comment = "Here exists a touch comment"
|
453
|
+
@user.touch(:suspended_at)
|
454
|
+
expect(@user.audits.last.action).to eq("update")
|
455
|
+
expect(@user.audits.last.comment).to eq("Here exists a touch comment")
|
456
|
+
end
|
457
|
+
|
458
|
+
it "should not save an audit if only specified on create/destroy" do
|
459
|
+
on_create_destroy = Models::ActiveRecord::OnCreateDestroyUser.create(name: "Bart")
|
460
|
+
expect {
|
461
|
+
on_create_destroy.touch(:suspended_at)
|
462
|
+
}.to_not change(Audited::Audit, :count)
|
463
|
+
end
|
464
|
+
|
465
|
+
it "should store an audit if touch is the only audit" do
|
466
|
+
on_touch = Models::ActiveRecord::OnTouchOnly.create(name: "Bart")
|
467
|
+
expect {
|
468
|
+
on_touch.update(name: "NotBart")
|
469
|
+
}.to_not change(Audited::Audit, :count)
|
470
|
+
expect {
|
471
|
+
on_touch.touch(:suspended_at)
|
472
|
+
}.to change(on_touch.audits, :count).from(0).to(1)
|
473
|
+
|
474
|
+
@user.audits.destroy_all
|
475
|
+
expect(@user.audits).to be_empty
|
476
|
+
expect {
|
477
|
+
@user.touch(:suspended_at)
|
478
|
+
}.to change(@user.audits, :count).from(0).to(1)
|
479
|
+
end
|
480
|
+
|
481
|
+
context "don't double audit" do
|
482
|
+
let(:user) { Models::ActiveRecord::Owner.create(name: "OwnerUser", suspended_at: 1.month.ago, companies_attributes: [{name: "OwnedCompany"}]) }
|
483
|
+
let(:company) { user.companies.first }
|
484
|
+
|
485
|
+
it "should only create 1 (create) audit for object" do
|
486
|
+
expect(user.audits.count).to eq(1)
|
487
|
+
expect(user.audits.first.action).to eq("create")
|
488
|
+
end
|
489
|
+
|
490
|
+
it "should only create 1 (create) audit for nested resource" do
|
491
|
+
expect(company.audits.count).to eq(1)
|
492
|
+
expect(company.audits.first.action).to eq("create")
|
493
|
+
end
|
494
|
+
|
495
|
+
context "after creating" do
|
496
|
+
it "updating / touching nested resource shouldn't save touch audit on parent object" do
|
497
|
+
expect { company.touch(:type) }.not_to change(user.audits, :count)
|
498
|
+
expect { company.update(type: "test") }.not_to change(user.audits, :count)
|
499
|
+
end
|
500
|
+
|
501
|
+
it "updating / touching parent object shouldn't save previous data" do
|
502
|
+
expect { user.touch(:suspended_at) }.to change(user.audits, :count).from(1).to(2)
|
503
|
+
expect(user.audits.last.action).to eq("update")
|
504
|
+
expect(user.audits.last.audited_changes.keys).to eq(%w[suspended_at])
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
context "after updating" do
|
509
|
+
it "changing nested resource shouldn't audit owner" do
|
510
|
+
expect { user.update(username: "test") }.to change(user.audits, :count).from(1).to(2)
|
511
|
+
expect { company.update(type: "test") }.not_to change(user.audits, :count)
|
512
|
+
|
513
|
+
expect { user.touch(:suspended_at) }.to change(user.audits, :count).from(2).to(3)
|
514
|
+
expect { company.update(type: "another_test") }.not_to change(user.audits, :count)
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
417
521
|
describe "on destroy" do
|
418
522
|
before do
|
419
523
|
@user = create_user(status: :active)
|
@@ -124,11 +124,12 @@ module Models
|
|
124
124
|
audited
|
125
125
|
has_associated_audits
|
126
126
|
has_many :companies, class_name: "OwnedCompany", dependent: :destroy
|
127
|
+
accepts_nested_attributes_for :companies
|
127
128
|
end
|
128
129
|
|
129
130
|
class OwnedCompany < ::ActiveRecord::Base
|
130
131
|
self.table_name = "companies"
|
131
|
-
belongs_to :owner, class_name: "Owner"
|
132
|
+
belongs_to :owner, class_name: "Owner", touch: true
|
132
133
|
attr_accessible :name, :owner if respond_to?(:attr_accessible) # declare attr_accessible before calling aaa
|
133
134
|
audited associated_with: :owner
|
134
135
|
end
|
@@ -146,6 +147,11 @@ module Models
|
|
146
147
|
audited on: [:create, :destroy]
|
147
148
|
end
|
148
149
|
|
150
|
+
class OnCreateDestroyUser < ::ActiveRecord::Base
|
151
|
+
self.table_name = "users"
|
152
|
+
audited on: [:create, :destroy]
|
153
|
+
end
|
154
|
+
|
149
155
|
class OnCreateDestroyExceptName < ::ActiveRecord::Base
|
150
156
|
self.table_name = "companies"
|
151
157
|
audited except: :name, on: [:create, :destroy]
|
@@ -155,5 +161,10 @@ module Models
|
|
155
161
|
self.table_name = "companies"
|
156
162
|
audited on: [:create, :update]
|
157
163
|
end
|
164
|
+
|
165
|
+
class OnTouchOnly < ::ActiveRecord::Base
|
166
|
+
self.table_name = "users"
|
167
|
+
audited on: [:touch]
|
168
|
+
end
|
158
169
|
end
|
159
170
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: audited
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.2
|
4
|
+
version: 5.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Keepers
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2023-
|
16
|
+
date: 2023-02-22 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: activerecord
|
@@ -252,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
252
252
|
- !ruby/object:Gem::Version
|
253
253
|
version: '0'
|
254
254
|
requirements: []
|
255
|
-
rubygems_version: 3.
|
255
|
+
rubygems_version: 3.4.6
|
256
256
|
signing_key:
|
257
257
|
specification_version: 4
|
258
258
|
summary: Log all changes to your models
|