audited 5.0.1 → 5.1.0
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 +128 -0
- data/Appraisals +11 -1
- data/CHANGELOG.md +42 -0
- data/README.md +39 -14
- data/gemfiles/rails50.gemfile +1 -0
- data/gemfiles/rails51.gemfile +1 -0
- data/gemfiles/rails52.gemfile +2 -1
- data/gemfiles/rails70.gemfile +10 -0
- data/lib/audited/audit.rb +4 -4
- data/lib/audited/auditor.rb +58 -30
- data/lib/audited/rspec_matchers.rb +1 -1
- data/lib/audited/version.rb +1 -1
- data/lib/audited.rb +6 -2
- data/lib/generators/audited/migration.rb +10 -2
- data/spec/audited/audit_spec.rb +2 -2
- data/spec/audited/auditor_spec.rb +31 -5
- data/spec/rails_app/config/application.rb +6 -0
- data/spec/rails_app/config/database.yml +3 -2
- data/spec/rails_app/config/environments/test.rb +5 -0
- data/spec/support/active_record/models.rb +8 -0
- metadata +13 -12
- data/.travis.yml +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e18ef251e91f3a66070afa58f5d9e5f12ac81777ca5389b3939a9fd349602e92
|
4
|
+
data.tar.gz: c0fbd5a136b6e43ae6e39229de6690811d24adf4f1f2f555a49752cac3bd230b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bbb38bc63b2cbafd43b7090ab307acde19e9ca21d22710ddc7b42a5a10fc08f0151c1b43b557a1978699005ac66ce7932eec7f8d5f3de6b3c14d7a4d77c73af
|
7
|
+
data.tar.gz: 707e5ddda189015b0475e663d7fb4faa3576a1a373dbe3f78436fce96e97cb7ed0a852fc328cec177716f95e035a45dd291f70888ba783572e4b854a84721a45
|
@@ -0,0 +1,128 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- master
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
fail-fast: false
|
14
|
+
matrix:
|
15
|
+
ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1]
|
16
|
+
appraisal:
|
17
|
+
- rails50
|
18
|
+
- rails51
|
19
|
+
- rails52
|
20
|
+
- rails60
|
21
|
+
- rails61
|
22
|
+
- rails70
|
23
|
+
db: [POSTGRES, MYSQL, SQLITE]
|
24
|
+
exclude:
|
25
|
+
# MySQL has issues on Ruby 2.3
|
26
|
+
# https://github.com/ruby/setup-ruby/issues/150
|
27
|
+
- ruby: 2.3
|
28
|
+
db: MYSQL
|
29
|
+
|
30
|
+
# PostgresSQL is segfaulting on 2.3
|
31
|
+
# Doesn't seem worth solving.
|
32
|
+
- ruby: 2.3
|
33
|
+
db: POSTGRES
|
34
|
+
|
35
|
+
# Rails 5.0 supports Ruby 2.2-2.4
|
36
|
+
- appraisal: rails50
|
37
|
+
ruby: 2.5
|
38
|
+
- appraisal: rails50
|
39
|
+
ruby: 2.6
|
40
|
+
- appraisal: rails50
|
41
|
+
ruby: 2.7
|
42
|
+
- appraisal: rails50
|
43
|
+
ruby: 3.0
|
44
|
+
- appraisal: rails50
|
45
|
+
ruby: 3.1
|
46
|
+
|
47
|
+
# Rails 5.1 supports Ruby 2.2-2.5
|
48
|
+
- appraisal: rails51
|
49
|
+
ruby: 2.6
|
50
|
+
- appraisal: rails51
|
51
|
+
ruby: 2.7
|
52
|
+
- appraisal: rails51
|
53
|
+
ruby: 3.0
|
54
|
+
- appraisal: rails51
|
55
|
+
ruby: 3.1
|
56
|
+
|
57
|
+
# Rails 5.2 supports Ruby 2.2-2.5
|
58
|
+
- appraisal: rails52
|
59
|
+
ruby: 2.6
|
60
|
+
- appraisal: rails52
|
61
|
+
ruby: 2.7
|
62
|
+
- appraisal: rails52
|
63
|
+
ruby: 3.0
|
64
|
+
- appraisal: rails52
|
65
|
+
ruby: 3.1
|
66
|
+
|
67
|
+
# Rails 6.0 supports Ruby 2.5-2.7
|
68
|
+
- appraisal: rails60
|
69
|
+
ruby: 2.3
|
70
|
+
- appraisal: rails60
|
71
|
+
ruby: 2.4
|
72
|
+
- appraisal: rails60
|
73
|
+
ruby: 3.0
|
74
|
+
- appraisal: rails60
|
75
|
+
ruby: 3.1
|
76
|
+
|
77
|
+
# Rails 6.1 supports Ruby 2.5+
|
78
|
+
- appraisal: rails61
|
79
|
+
ruby: 2.3
|
80
|
+
- appraisal: rails61
|
81
|
+
ruby: 2.4
|
82
|
+
|
83
|
+
# Rails 7 supports Ruby 2.7+
|
84
|
+
- appraisal: rails70
|
85
|
+
ruby: 2.3
|
86
|
+
- appraisal: rails70
|
87
|
+
ruby: 2.4
|
88
|
+
- appraisal: rails70
|
89
|
+
ruby: 2.5
|
90
|
+
- appraisal: rails70
|
91
|
+
ruby: 2.6
|
92
|
+
|
93
|
+
services:
|
94
|
+
postgres:
|
95
|
+
image: postgres
|
96
|
+
env:
|
97
|
+
POSTGRES_USER: postgres
|
98
|
+
POSTGRES_PASSWORD: postgres
|
99
|
+
POSTGRES_DB: audited_test
|
100
|
+
ports:
|
101
|
+
- 5432:5432
|
102
|
+
# needed because the postgres container does not provide a healthcheck
|
103
|
+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
104
|
+
|
105
|
+
env:
|
106
|
+
DB_DATABASE: audited_test
|
107
|
+
DB_USER: root
|
108
|
+
DB_PASSWORD: 'root'
|
109
|
+
DB_HOST: localhost
|
110
|
+
|
111
|
+
steps:
|
112
|
+
- name: Setup MySQL
|
113
|
+
run: |
|
114
|
+
sudo /etc/init.d/mysql start
|
115
|
+
mysql -e 'CREATE DATABASE audited_test;' -uroot -proot
|
116
|
+
mysql -e 'SHOW DATABASES;' -uroot -proot
|
117
|
+
- uses: actions/checkout@v3
|
118
|
+
- name: Copy Gemfile
|
119
|
+
run: sed 's/\.\././' gemfiles/${{ matrix.appraisal }}.gemfile > Gemfile
|
120
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
121
|
+
uses: ruby/setup-ruby@v1
|
122
|
+
with:
|
123
|
+
ruby-version: ${{ matrix.ruby }}
|
124
|
+
bundler-cache: true
|
125
|
+
- name: Run tests
|
126
|
+
env:
|
127
|
+
DB: ${{ matrix.db }}
|
128
|
+
run: bundle exec rake
|
data/Appraisals
CHANGED
@@ -6,6 +6,7 @@ appraise "rails50" do
|
|
6
6
|
gem "mysql2", ">= 0.3.18", "< 0.6.0"
|
7
7
|
gem "pg", ">= 0.18", "< 2.0"
|
8
8
|
gem "sqlite3", "~> 1.3.6"
|
9
|
+
gem "psych", "~> 3.1"
|
9
10
|
end
|
10
11
|
|
11
12
|
appraise "rails51" do
|
@@ -13,13 +14,15 @@ appraise "rails51" do
|
|
13
14
|
gem "mysql2", ">= 0.3.18", "< 0.6.0"
|
14
15
|
gem "pg", ">= 0.18", "< 2.0"
|
15
16
|
gem "sqlite3", "~> 1.3.6"
|
17
|
+
gem "psych", "~> 3.1"
|
16
18
|
end
|
17
19
|
|
18
20
|
appraise "rails52" do
|
19
|
-
gem "rails", ">= 5.2.
|
21
|
+
gem "rails", ">= 5.2.8.1", "< 5.3"
|
20
22
|
gem "mysql2", ">= 0.4.4", "< 0.6.0"
|
21
23
|
gem "pg", ">= 0.18", "< 2.0"
|
22
24
|
gem "sqlite3", "~> 1.3.6"
|
25
|
+
gem "psych", "~> 3.1"
|
23
26
|
end
|
24
27
|
|
25
28
|
appraise "rails60" do
|
@@ -35,3 +38,10 @@ appraise "rails61" do
|
|
35
38
|
gem "pg", ">= 1.1", "< 2.0"
|
36
39
|
gem "sqlite3", "~> 1.4"
|
37
40
|
end
|
41
|
+
|
42
|
+
appraise "rails70" do
|
43
|
+
gem "rails", ">= 7.0.0", "< 7.1"
|
44
|
+
gem "mysql2", ">= 0.4.4"
|
45
|
+
gem "pg", ">= 1.1"
|
46
|
+
gem "sqlite3", ">= 1.4"
|
47
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,47 @@
|
|
1
1
|
# Audited ChangeLog
|
2
2
|
|
3
|
+
## 5.1.0 (2022-12-23)
|
4
|
+
|
5
|
+
Changed
|
6
|
+
|
7
|
+
- config.audit_class takes a string - @simmerz
|
8
|
+
[#609](https://github.com/collectiveidea/audited/pull/609)
|
9
|
+
- Filter encrypted attributes automatically - @vlad-psh
|
10
|
+
[#630](https://github.com/collectiveidea/audited/pull/630)
|
11
|
+
|
12
|
+
Improved
|
13
|
+
|
14
|
+
- README improvements - @jess, @mstroming
|
15
|
+
[#605](https://github.com/collectiveidea/audited/pull/605)
|
16
|
+
[#640](https://github.com/collectiveidea/audited/issues/640)
|
17
|
+
- Ignore deadlocks in concurrent audit combinations - @Crammaman
|
18
|
+
[#621](https://github.com/collectiveidea/audited/pull/621)
|
19
|
+
- Fix timestamped_migrations deprecation warning - @shouichi
|
20
|
+
[#624](https://github.com/collectiveidea/audited/pull/624)
|
21
|
+
- Ensure audits are re-enabled after blocks - @dcorlett
|
22
|
+
[#632](https://github.com/collectiveidea/audited/pull/632)
|
23
|
+
- Replace raw string where clause with query methods - @macowie
|
24
|
+
[#642](https://github.com/collectiveidea/audited/pull/642)
|
25
|
+
- Test against more Ruby/Rails Versions - @enomotodev, @danielmorrison
|
26
|
+
[#610](https://github.com/collectiveidea/audited/pull/610)
|
27
|
+
[#643](https://github.com/collectiveidea/audited/pull/643)
|
28
|
+
|
29
|
+
## 5.0.2 (2021-09-16)
|
30
|
+
|
31
|
+
Added
|
32
|
+
|
33
|
+
- Relax ActiveRecord version constraint to support Rails 7
|
34
|
+
[#597](https://github.com/collectiveidea/audited/pull/597)
|
35
|
+
|
36
|
+
Improved
|
37
|
+
|
38
|
+
- Improve loading - @mvastola
|
39
|
+
[#592](https://github.com/collectiveidea/audited/pull/592)
|
40
|
+
- Update README - @danirod, @clement1234
|
41
|
+
[#596](https://github.com/collectiveidea/audited/pull/596)
|
42
|
+
[#594](https://github.com/collectiveidea/audited/pull/594)
|
43
|
+
|
44
|
+
|
3
45
|
## 5.0.1 (2021-06-11)
|
4
46
|
|
5
47
|
Improved
|
data/README.md
CHANGED
@@ -1,23 +1,29 @@
|
|
1
|
-
Audited
|
1
|
+
Audited
|
2
|
+
[![Gem Version](https://img.shields.io/gem/v/audited.svg)](http://rubygems.org/gems/audited)
|
3
|
+
![Build Status](https://github.com/collectiveidea/audited/actions/workflows/ci.yml/badge.svg)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/collectiveidea/audited.svg)](https://codeclimate.com/github/collectiveidea/audited)
|
2
5
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
3
6
|
=======
|
4
7
|
|
5
8
|
**Audited** (previously acts_as_audited) is an ORM extension that logs all changes to your models. Audited can also record who made those changes, save comments and associate models related to the changes.
|
6
9
|
|
7
10
|
|
8
|
-
Audited currently (5.x) works with Rails 6.1, 6.0, 5.2, 5.1, and 5.0.
|
11
|
+
Audited currently (5.x) works with Rails 7.0, 6.1, 6.0, 5.2, 5.1, and 5.0.
|
9
12
|
|
10
13
|
For Rails 4, use gem version 4.x
|
11
14
|
For Rails 3, use gem version 3.0 or see the [3.0-stable branch](https://github.com/collectiveidea/audited/tree/3.0-stable).
|
12
15
|
|
13
16
|
## Supported Rubies
|
14
17
|
|
15
|
-
Audited supports and is [tested against](
|
18
|
+
Audited supports and is [tested against](https://github.com/collectiveidea/audited/actions/workflows/ci.yml) the following Ruby versions:
|
16
19
|
|
17
|
-
* 2.3
|
18
|
-
* 2.4
|
19
|
-
* 2.5
|
20
|
-
* 2.6
|
20
|
+
* 2.3 (only tested on Sqlite due to testing issues with other DBs)
|
21
|
+
* 2.4
|
22
|
+
* 2.5
|
23
|
+
* 2.6
|
24
|
+
* 2.7
|
25
|
+
* 3.0
|
26
|
+
* 3.1
|
21
27
|
|
22
28
|
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).
|
23
29
|
|
@@ -30,7 +36,16 @@ Audited is currently ActiveRecord-only. In a previous life, Audited worked with
|
|
30
36
|
Add the gem to your Gemfile:
|
31
37
|
|
32
38
|
```ruby
|
33
|
-
gem "audited", "~>
|
39
|
+
gem "audited", "~> 5.0"
|
40
|
+
```
|
41
|
+
|
42
|
+
And if you're using ```require: false``` you must add initializers like this:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
#./config/initializers/audited.rb
|
46
|
+
require "audited"
|
47
|
+
|
48
|
+
Audited::Railtie.initializers.each(&:run)
|
34
49
|
```
|
35
50
|
|
36
51
|
Then, from your Rails app directory, create the `audits` table:
|
@@ -204,7 +219,7 @@ Audited.current_user_method = :authenticated_user
|
|
204
219
|
Outside of a request, Audited can still record the user with the `as_user` method:
|
205
220
|
|
206
221
|
```ruby
|
207
|
-
Audited.
|
222
|
+
Audited.audit_model.as_user(User.find(1)) do
|
208
223
|
post.update!(title: "Hello, world!")
|
209
224
|
end
|
210
225
|
post.audits.last.user # => #<User id: 1>
|
@@ -231,7 +246,7 @@ end
|
|
231
246
|
`as_user` also accepts a string, which can be useful for auditing updates made in a CLI environment:
|
232
247
|
|
233
248
|
```rb
|
234
|
-
Audited.
|
249
|
+
Audited.audit_model.as_user("console-user-#{ENV['SSH_USER']}") do
|
235
250
|
post.update_attributes!(title: "Hello, world!")
|
236
251
|
end
|
237
252
|
post.audits.last.user # => 'console-user-username'
|
@@ -271,6 +286,7 @@ class User < ActiveRecord::Base
|
|
271
286
|
end
|
272
287
|
|
273
288
|
class Company < ActiveRecord::Base
|
289
|
+
audited
|
274
290
|
has_many :users
|
275
291
|
has_associated_audits
|
276
292
|
end
|
@@ -298,9 +314,7 @@ If you want to audit only under specific conditions, you can provide conditional
|
|
298
314
|
```ruby
|
299
315
|
class User < ActiveRecord::Base
|
300
316
|
audited if: :active?
|
301
|
-
|
302
|
-
private
|
303
|
-
|
317
|
+
|
304
318
|
def active?
|
305
319
|
last_login > 6.months.ago
|
306
320
|
end
|
@@ -371,6 +385,17 @@ User.auditing_enabled = false
|
|
371
385
|
end
|
372
386
|
```
|
373
387
|
|
388
|
+
### Encrypted attributes
|
389
|
+
|
390
|
+
If you're using ActiveRecord's encryption (available from Rails 7) to encrypt some attributes, Audited will automatically filter values of these attributes. No additional configuration is required. Changes to encrypted attributes will be logged as `[FILTERED]`.
|
391
|
+
|
392
|
+
```ruby
|
393
|
+
class User < ActiveRecord::Base
|
394
|
+
audited
|
395
|
+
encrypts :password
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
374
399
|
### Custom `Audit` model
|
375
400
|
|
376
401
|
If you want to extend or modify the audit model, create a new class that
|
@@ -387,7 +412,7 @@ Then set it in an initializer:
|
|
387
412
|
# config/initializers/audited.rb
|
388
413
|
|
389
414
|
Audited.config do |config|
|
390
|
-
config.audit_class = CustomAudit
|
415
|
+
config.audit_class = "CustomAudit"
|
391
416
|
end
|
392
417
|
```
|
393
418
|
|
data/gemfiles/rails50.gemfile
CHANGED
data/gemfiles/rails51.gemfile
CHANGED
data/gemfiles/rails52.gemfile
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", ">= 5.2.
|
5
|
+
gem "rails", ">= 5.2.8.1", "< 5.3"
|
6
6
|
gem "mysql2", ">= 0.4.4", "< 0.6.0"
|
7
7
|
gem "pg", ">= 0.18", "< 2.0"
|
8
8
|
gem "sqlite3", "~> 1.3.6"
|
9
|
+
gem "psych", "~> 3.1"
|
9
10
|
|
10
11
|
gemspec name: "audited", path: "../"
|
data/lib/audited/audit.rb
CHANGED
@@ -34,7 +34,7 @@ module Audited
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def text_column?
|
37
|
-
Audited.
|
37
|
+
Audited.audit_model.columns_hash["audited_changes"].type.to_s == "text"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -78,14 +78,14 @@ module Audited
|
|
78
78
|
# Returns a hash of the changed attributes with the new values
|
79
79
|
def new_attributes
|
80
80
|
(audited_changes || {}).each_with_object({}.with_indifferent_access) do |(attr, values), attrs|
|
81
|
-
attrs[attr] = (action == "update" ? values.last : values
|
81
|
+
attrs[attr] = (action == "update") ? values.last : values
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
85
|
# Returns a hash of the changed attributes with the old values
|
86
86
|
def old_attributes
|
87
87
|
(audited_changes || {}).each_with_object({}.with_indifferent_access) do |(attr, values), attrs|
|
88
|
-
attrs[attr] = (action == "update" ? values.first : values
|
88
|
+
attrs[attr] = (action == "update") ? values.first : values
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
@@ -174,7 +174,7 @@ module Audited
|
|
174
174
|
if action == "create"
|
175
175
|
self.version = 1
|
176
176
|
else
|
177
|
-
collection = Rails::VERSION::MAJOR >= 6 ? self.class.unscoped : self.class
|
177
|
+
collection = (Rails::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
@@ -13,7 +13,7 @@ module Audited
|
|
13
13
|
#
|
14
14
|
# See <tt>Audited::Auditor::ClassMethods#audited</tt>
|
15
15
|
# for configuration options
|
16
|
-
module Auditor
|
16
|
+
module Auditor # :nodoc:
|
17
17
|
extend ActiveSupport::Concern
|
18
18
|
|
19
19
|
CALLBACKS = [:audit_create, :audit_update, :audit_destroy]
|
@@ -79,8 +79,8 @@ module Audited
|
|
79
79
|
before_destroy :require_comment if audited_options[:on].include?(:destroy)
|
80
80
|
end
|
81
81
|
|
82
|
-
has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audited.audit_class
|
83
|
-
Audited.
|
82
|
+
has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audited.audit_class, inverse_of: :auditable
|
83
|
+
Audited.audit_model.audited_class_names << to_s
|
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)
|
@@ -97,7 +97,7 @@ module Audited
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def has_associated_audits
|
100
|
-
has_many :associated_audits, as: :associated, class_name: Audited.
|
100
|
+
has_many :associated_audits, as: :associated, class_name: Audited.audit_model.name
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
@@ -159,27 +159,28 @@ module Audited
|
|
159
159
|
# Returns nil for versions greater than revisions count
|
160
160
|
def revision(version)
|
161
161
|
if version == :previous || audits.last.version >= version
|
162
|
-
revision_with Audited.
|
162
|
+
revision_with Audited.audit_model.reconstruct_attributes(audits_to(version))
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
166
166
|
# Find the oldest revision recorded prior to the date/time provided.
|
167
167
|
def revision_at(date_or_time)
|
168
168
|
audits = self.audits.up_until(date_or_time)
|
169
|
-
revision_with Audited.
|
169
|
+
revision_with Audited.audit_model.reconstruct_attributes(audits) unless audits.empty?
|
170
170
|
end
|
171
171
|
|
172
172
|
# List of attributes that are audited.
|
173
173
|
def audited_attributes
|
174
174
|
audited_attributes = attributes.except(*self.class.non_audited_columns)
|
175
|
+
audited_attributes = redact_values(audited_attributes)
|
176
|
+
audited_attributes = filter_encrypted_attrs(audited_attributes)
|
175
177
|
normalize_enum_changes(audited_attributes)
|
176
178
|
end
|
177
179
|
|
178
180
|
# Returns a list combined of record audits and associated audits.
|
179
181
|
def own_and_associated_audits
|
180
|
-
Audited.
|
181
|
-
.where(
|
182
|
-
type: self.class.base_class.name, id: id)
|
182
|
+
Audited.audit_model.unscoped.where(auditable: self)
|
183
|
+
.or(Audited.audit_model.unscoped.where(associated: self))
|
183
184
|
.order(created_at: :desc)
|
184
185
|
end
|
185
186
|
|
@@ -190,8 +191,13 @@ module Audited
|
|
190
191
|
combine_target.comment = "#{combine_target.comment}\nThis audit is the result of multiple audits being combined."
|
191
192
|
|
192
193
|
transaction do
|
193
|
-
|
194
|
-
|
194
|
+
begin
|
195
|
+
combine_target.save!
|
196
|
+
audits_to_combine.unscope(:limit).where("version < ?", combine_target.version).delete_all
|
197
|
+
rescue ActiveRecord::Deadlocked
|
198
|
+
# Ignore Deadlocks, if the same record is getting its old audits combined more than once at the same time then
|
199
|
+
# both combining operations will be the same. Ignoring this error allows one of the combines to go through successfully.
|
200
|
+
end
|
195
201
|
end
|
196
202
|
end
|
197
203
|
|
@@ -206,7 +212,7 @@ module Audited
|
|
206
212
|
revision.send :instance_variable_set, "@destroyed", false
|
207
213
|
revision.send :instance_variable_set, "@_destroyed", false
|
208
214
|
revision.send :instance_variable_set, "@marked_for_destruction", false
|
209
|
-
Audited.
|
215
|
+
Audited.audit_model.assign_revision_attributes(revision, attributes)
|
210
216
|
|
211
217
|
# Remove any association proxies so that they will be recreated
|
212
218
|
# and reference the correct object for this revision. The only way
|
@@ -234,6 +240,7 @@ module Audited
|
|
234
240
|
end
|
235
241
|
|
236
242
|
filtered_changes = redact_values(filtered_changes)
|
243
|
+
filtered_changes = filter_encrypted_attrs(filtered_changes)
|
237
244
|
filtered_changes = normalize_enum_changes(filtered_changes)
|
238
245
|
filtered_changes.to_hash
|
239
246
|
end
|
@@ -257,19 +264,36 @@ module Audited
|
|
257
264
|
end
|
258
265
|
|
259
266
|
def redact_values(filtered_changes)
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
filtered_changes
|
267
|
+
filter_attr_values(
|
268
|
+
audited_changes: filtered_changes,
|
269
|
+
attrs: Array(audited_options[:redacted]).map(&:to_s),
|
270
|
+
placeholder: audited_options[:redaction_value] || REDACTED
|
271
|
+
)
|
272
|
+
end
|
273
|
+
|
274
|
+
def filter_encrypted_attrs(filtered_changes)
|
275
|
+
filter_attr_values(
|
276
|
+
audited_changes: filtered_changes,
|
277
|
+
attrs: respond_to?(:encrypted_attributes) ? Array(encrypted_attributes).map(&:to_s) : []
|
278
|
+
)
|
279
|
+
end
|
280
|
+
|
281
|
+
# Replace values for given attrs to a placeholder and return modified hash
|
282
|
+
#
|
283
|
+
# @param audited_changes [Hash] Hash of changes to be saved to audited version record
|
284
|
+
# @param attrs [Array<String>] Array of attrs, values of which will be replaced to placeholder value
|
285
|
+
# @param placeholder [String] Placeholder to replace original attr values
|
286
|
+
def filter_attr_values(audited_changes: {}, attrs: [], placeholder: "[FILTERED]")
|
287
|
+
attrs.each do |attr|
|
288
|
+
next unless audited_changes.key?(attr)
|
289
|
+
|
290
|
+
changes = audited_changes[attr]
|
291
|
+
values = changes.is_a?(Array) ? changes.map { placeholder } : placeholder
|
292
|
+
|
293
|
+
audited_changes[attr] = values
|
270
294
|
end
|
271
295
|
|
272
|
-
|
296
|
+
audited_changes
|
273
297
|
end
|
274
298
|
|
275
299
|
def rails_below?(rails_version)
|
@@ -290,20 +314,20 @@ module Audited
|
|
290
314
|
|
291
315
|
def audit_create
|
292
316
|
write_audit(action: "create", audited_changes: audited_attributes,
|
293
|
-
|
317
|
+
comment: audit_comment)
|
294
318
|
end
|
295
319
|
|
296
320
|
def audit_update
|
297
321
|
unless (changes = audited_changes).empty? && (audit_comment.blank? || audited_options[:update_with_comment_only] == false)
|
298
322
|
write_audit(action: "update", audited_changes: changes,
|
299
|
-
|
323
|
+
comment: audit_comment)
|
300
324
|
end
|
301
325
|
end
|
302
326
|
|
303
327
|
def audit_destroy
|
304
328
|
unless new_record?
|
305
329
|
write_audit(action: "destroy", audited_changes: audited_attributes,
|
306
|
-
|
330
|
+
comment: audit_comment)
|
307
331
|
end
|
308
332
|
end
|
309
333
|
|
@@ -397,7 +421,7 @@ module Audited
|
|
397
421
|
# end
|
398
422
|
#
|
399
423
|
def without_auditing
|
400
|
-
auditing_was_enabled =
|
424
|
+
auditing_was_enabled = class_auditing_enabled
|
401
425
|
disable_auditing
|
402
426
|
yield
|
403
427
|
ensure
|
@@ -411,7 +435,7 @@ module Audited
|
|
411
435
|
# end
|
412
436
|
#
|
413
437
|
def with_auditing
|
414
|
-
auditing_was_enabled =
|
438
|
+
auditing_was_enabled = class_auditing_enabled
|
415
439
|
enable_auditing
|
416
440
|
yield
|
417
441
|
ensure
|
@@ -431,11 +455,11 @@ module Audited
|
|
431
455
|
# convenience wrapper around
|
432
456
|
# @see Audit#as_user.
|
433
457
|
def audit_as(user, &block)
|
434
|
-
Audited.
|
458
|
+
Audited.audit_model.as_user(user, &block)
|
435
459
|
end
|
436
460
|
|
437
461
|
def auditing_enabled
|
438
|
-
|
462
|
+
class_auditing_enabled && Audited.auditing_enabled
|
439
463
|
end
|
440
464
|
|
441
465
|
def auditing_enabled=(val)
|
@@ -466,6 +490,10 @@ module Audited
|
|
466
490
|
default_ignored_attributes
|
467
491
|
end
|
468
492
|
end
|
493
|
+
|
494
|
+
def class_auditing_enabled
|
495
|
+
Audited.store.fetch("#{table_name}_auditing_enabled", true)
|
496
|
+
end
|
469
497
|
end
|
470
498
|
end
|
471
499
|
end
|
@@ -221,7 +221,7 @@ module Audited
|
|
221
221
|
def association_exists?
|
222
222
|
!reflection.nil? &&
|
223
223
|
reflection.macro == :has_many &&
|
224
|
-
reflection.options[:class_name] == Audited.
|
224
|
+
reflection.options[:class_name] == Audited.audit_model.name
|
225
225
|
end
|
226
226
|
end
|
227
227
|
end
|
data/lib/audited/version.rb
CHANGED
data/lib/audited.rb
CHANGED
@@ -13,7 +13,11 @@ module Audited
|
|
13
13
|
attr_writer :audit_class
|
14
14
|
|
15
15
|
def audit_class
|
16
|
-
@audit_class ||= Audit
|
16
|
+
@audit_class ||= "Audited::Audit"
|
17
|
+
end
|
18
|
+
|
19
|
+
def audit_model
|
20
|
+
audit_class.constantize
|
17
21
|
end
|
18
22
|
|
19
23
|
def store
|
@@ -39,9 +43,9 @@ module Audited
|
|
39
43
|
end
|
40
44
|
|
41
45
|
require "audited/auditor"
|
42
|
-
require "audited/audit"
|
43
46
|
|
44
47
|
ActiveSupport.on_load :active_record do
|
48
|
+
require "audited/audit"
|
45
49
|
include Audited::Auditor
|
46
50
|
end
|
47
51
|
|
@@ -4,14 +4,22 @@ module Audited
|
|
4
4
|
module Generators
|
5
5
|
module Migration
|
6
6
|
# Implement the required interface for Rails::Generators::Migration.
|
7
|
-
def next_migration_number(dirname)
|
7
|
+
def next_migration_number(dirname) # :nodoc:
|
8
8
|
next_migration_number = current_migration_number(dirname) + 1
|
9
|
-
if
|
9
|
+
if timestamped_migrations?
|
10
10
|
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
11
11
|
else
|
12
12
|
"%.3d" % next_migration_number
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def timestamped_migrations?
|
19
|
+
(Rails.version >= "7.0") ?
|
20
|
+
::ActiveRecord.timestamped_migrations :
|
21
|
+
::ActiveRecord::Base.timestamped_migrations
|
22
|
+
end
|
15
23
|
end
|
16
24
|
end
|
17
25
|
end
|
data/spec/audited/audit_spec.rb
CHANGED
@@ -37,7 +37,7 @@ describe Audited::Audit do
|
|
37
37
|
|
38
38
|
context "when a custom audit class is configured" do
|
39
39
|
it "should be used in place of #{described_class}" do
|
40
|
-
Audited.config { |config| config.audit_class = CustomAudit }
|
40
|
+
Audited.config { |config| config.audit_class = "CustomAudit" }
|
41
41
|
TempModel1.audited
|
42
42
|
|
43
43
|
record = TempModel1.create
|
@@ -62,7 +62,7 @@ describe Audited::Audit do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
describe "#audited_changes" do
|
65
|
-
let(:audit) { Audited.
|
65
|
+
let(:audit) { Audited.audit_model.new }
|
66
66
|
|
67
67
|
it "can unserialize yaml from text columns" do
|
68
68
|
audit.audited_changes = {foo: "bar"}
|
@@ -234,6 +234,14 @@ describe Audited::Auditor do
|
|
234
234
|
expect(user.audits.last.audited_changes["password"]).to eq(["My", "Custom", "Value", 7])
|
235
235
|
end
|
236
236
|
|
237
|
+
if ::ActiveRecord::VERSION::MAJOR >= 7
|
238
|
+
it "should filter encrypted attributes" do
|
239
|
+
user = Models::ActiveRecord::UserWithEncryptedPassword.create(password: "password")
|
240
|
+
user.save
|
241
|
+
expect(user.audits.last.audited_changes["password"]).to eq("[FILTERED]")
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
237
245
|
if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
238
246
|
describe "'json' and 'jsonb' audited_changes column type" do
|
239
247
|
let(:migrations_path) { SPEC_ROOT.join("support/active_record/postgres") }
|
@@ -272,11 +280,11 @@ describe Audited::Auditor do
|
|
272
280
|
yesterday = 1.day.ago
|
273
281
|
|
274
282
|
u = Models::ActiveRecord::NoAttributeProtectionUser.new(name: "name",
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
283
|
+
username: "username",
|
284
|
+
password: "password",
|
285
|
+
activated: true,
|
286
|
+
suspended_at: yesterday,
|
287
|
+
logins: 2)
|
280
288
|
|
281
289
|
expect(u.name).to eq("name")
|
282
290
|
expect(u.username).to eq("username")
|
@@ -814,6 +822,15 @@ describe Audited::Auditor do
|
|
814
822
|
}.to_not change(Audited::Audit, :count)
|
815
823
|
end
|
816
824
|
|
825
|
+
context "when global audits are disabled" do
|
826
|
+
it "should re-enable class audits after #without_auditing block" do
|
827
|
+
Audited.auditing_enabled = false
|
828
|
+
Models::ActiveRecord::User.without_auditing {}
|
829
|
+
Audited.auditing_enabled = true
|
830
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eql(true)
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
817
834
|
it "should reset auditing status even it raises an exception" do
|
818
835
|
begin
|
819
836
|
Models::ActiveRecord::User.without_auditing { raise }
|
@@ -884,6 +901,15 @@ describe Audited::Auditor do
|
|
884
901
|
}.to change(Audited::Audit, :count).by(1)
|
885
902
|
end
|
886
903
|
|
904
|
+
context "when global audits are disabled" do
|
905
|
+
it "should re-enable class audits after #with_auditing block" do
|
906
|
+
Audited.auditing_enabled = false
|
907
|
+
Models::ActiveRecord::User.with_auditing {}
|
908
|
+
Audited.auditing_enabled = true
|
909
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eql(true)
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
887
913
|
it "should reset auditing status even it raises an exception" do
|
888
914
|
Models::ActiveRecord::User.disable_auditing
|
889
915
|
begin
|
@@ -4,6 +4,12 @@ module RailsApp
|
|
4
4
|
class Application < Rails::Application
|
5
5
|
config.root = File.expand_path("../../", __FILE__)
|
6
6
|
config.i18n.enforce_available_locales = true
|
7
|
+
|
8
|
+
if !Rails.version.start_with?("5.0") && !Rails.version.start_with?("5.1") && config.active_record.respond_to?(:yaml_column_permitted_classes=)
|
9
|
+
config.active_record.yaml_column_permitted_classes =
|
10
|
+
%w[String Symbol Integer NilClass Float Time Date FalseClass Hash Array DateTime TrueClass BigDecimal
|
11
|
+
ActiveSupport::TimeWithZone ActiveSupport::TimeZone ActiveSupport::HashWithIndifferentAccess]
|
12
|
+
end
|
7
13
|
end
|
8
14
|
end
|
9
15
|
|
@@ -9,7 +9,8 @@ sqlite3: &SQLITE
|
|
9
9
|
postgresql: &POSTGRES
|
10
10
|
adapter: postgresql
|
11
11
|
username: postgres
|
12
|
-
password:
|
12
|
+
password: postgres
|
13
|
+
host: localhost
|
13
14
|
database: audited_test
|
14
15
|
min_messages: ERROR
|
15
16
|
|
@@ -17,7 +18,7 @@ mysql: &MYSQL
|
|
17
18
|
adapter: mysql2
|
18
19
|
host: localhost
|
19
20
|
username: root
|
20
|
-
password:
|
21
|
+
password: root
|
21
22
|
database: audited_test
|
22
23
|
charset: utf8
|
23
24
|
|
@@ -44,4 +44,9 @@ RailsApp::Application.configure do
|
|
44
44
|
|
45
45
|
# Raises error for missing translations
|
46
46
|
# config.action_view.raise_on_missing_translations = true
|
47
|
+
|
48
|
+
if ::ActiveRecord::VERSION::MAJOR >= 7
|
49
|
+
config.active_record.encryption.key_derivation_salt = SecureRandom.hex
|
50
|
+
config.active_record.encryption.primary_key = SecureRandom.hex
|
51
|
+
end
|
47
52
|
end
|
@@ -41,6 +41,14 @@ module Models
|
|
41
41
|
audited redacted: :password, redaction_value: ["My", "Custom", "Value", 7]
|
42
42
|
end
|
43
43
|
|
44
|
+
if ::ActiveRecord::VERSION::MAJOR >= 7
|
45
|
+
class UserWithEncryptedPassword < ::ActiveRecord::Base
|
46
|
+
self.table_name = :users
|
47
|
+
audited
|
48
|
+
encrypts :password
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
44
52
|
class CommentRequiredUser < ::ActiveRecord::Base
|
45
53
|
self.table_name = :users
|
46
54
|
audited except: :password, comment_required: true
|
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.0
|
4
|
+
version: 5.1.0
|
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:
|
16
|
+
date: 2022-12-23 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: activerecord
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
version: '5.0'
|
25
25
|
- - "<"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '7.1'
|
28
28
|
type: :runtime
|
29
29
|
prerelease: false
|
30
30
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -34,7 +34,7 @@ dependencies:
|
|
34
34
|
version: '5.0'
|
35
35
|
- - "<"
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
37
|
+
version: '7.1'
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: appraisal
|
40
40
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,7 +58,7 @@ dependencies:
|
|
58
58
|
version: '5.0'
|
59
59
|
- - "<"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '7.1'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
version: '5.0'
|
69
69
|
- - "<"
|
70
70
|
- !ruby/object:Gem::Version
|
71
|
-
version: '
|
71
|
+
version: '7.1'
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
73
|
name: rspec-rails
|
74
74
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,16 +115,16 @@ dependencies:
|
|
115
115
|
name: sqlite3
|
116
116
|
requirement: !ruby/object:Gem::Requirement
|
117
117
|
requirements:
|
118
|
-
- - "
|
118
|
+
- - ">="
|
119
119
|
- !ruby/object:Gem::Version
|
120
|
-
version:
|
120
|
+
version: 1.3.6
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
123
|
version_requirements: !ruby/object:Gem::Requirement
|
124
124
|
requirements:
|
125
|
-
- - "
|
125
|
+
- - ">="
|
126
126
|
- !ruby/object:Gem::Version
|
127
|
-
version:
|
127
|
+
version: 1.3.6
|
128
128
|
- !ruby/object:Gem::Dependency
|
129
129
|
name: mysql2
|
130
130
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,9 +165,9 @@ executables: []
|
|
165
165
|
extensions: []
|
166
166
|
extra_rdoc_files: []
|
167
167
|
files:
|
168
|
+
- ".github/workflows/ci.yml"
|
168
169
|
- ".gitignore"
|
169
170
|
- ".standard.yml"
|
170
|
-
- ".travis.yml"
|
171
171
|
- ".yardopts"
|
172
172
|
- Appraisals
|
173
173
|
- CHANGELOG.md
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- gemfiles/rails52.gemfile
|
181
181
|
- gemfiles/rails60.gemfile
|
182
182
|
- gemfiles/rails61.gemfile
|
183
|
+
- gemfiles/rails70.gemfile
|
183
184
|
- lib/audited-rspec.rb
|
184
185
|
- lib/audited.rb
|
185
186
|
- lib/audited/audit.rb
|
@@ -250,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
251
|
- !ruby/object:Gem::Version
|
251
252
|
version: '0'
|
252
253
|
requirements: []
|
253
|
-
rubygems_version: 3.
|
254
|
+
rubygems_version: 3.3.7
|
254
255
|
signing_key:
|
255
256
|
specification_version: 4
|
256
257
|
summary: Log all changes to your models
|
data/.travis.yml
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
cache: bundler
|
3
|
-
rvm:
|
4
|
-
- 2.3.8
|
5
|
-
- 2.4.10
|
6
|
-
- 2.5.9
|
7
|
-
- 2.6.7
|
8
|
-
- 2.7.3
|
9
|
-
- 3.0.1
|
10
|
-
- ruby-head
|
11
|
-
env:
|
12
|
-
- DB=SQLITE
|
13
|
-
- DB=POSTGRES
|
14
|
-
- DB=MYSQL
|
15
|
-
addons:
|
16
|
-
postgresql: "9.4"
|
17
|
-
services:
|
18
|
-
- mysql
|
19
|
-
before_install:
|
20
|
-
# https://github.com/travis-ci/travis-ci/issues/8978
|
21
|
-
- "travis_retry gem update --system"
|
22
|
-
gemfile:
|
23
|
-
- gemfiles/rails50.gemfile
|
24
|
-
- gemfiles/rails51.gemfile
|
25
|
-
- gemfiles/rails52.gemfile
|
26
|
-
- gemfiles/rails60.gemfile
|
27
|
-
- gemfiles/rails61.gemfile
|
28
|
-
matrix:
|
29
|
-
include:
|
30
|
-
- rvm: 2.6.7
|
31
|
-
script: bundle exec standardrb
|
32
|
-
env: DB=standard # make travis build display nicer
|
33
|
-
exclude:
|
34
|
-
- rvm: 2.3.8
|
35
|
-
gemfile: gemfiles/rails61.gemfile
|
36
|
-
- rvm: 2.4.10
|
37
|
-
gemfile: gemfiles/rails61.gemfile
|
38
|
-
- rvm: 2.3.8
|
39
|
-
gemfile: gemfiles/rails60.gemfile
|
40
|
-
- rvm: 2.4.10
|
41
|
-
gemfile: gemfiles/rails60.gemfile
|
42
|
-
- rvm: 2.6.7
|
43
|
-
gemfile: gemfiles/rails42.gemfile
|
44
|
-
- rvm: 2.7.3
|
45
|
-
gemfile: gemfiles/rails42.gemfile
|
46
|
-
- rvm: 3.0.1
|
47
|
-
gemfile: gemfiles/rails42.gemfile
|
48
|
-
- rvm: 3.0.1
|
49
|
-
gemfile: gemfiles/rails50.gemfile
|
50
|
-
- rvm: 3.0.1
|
51
|
-
gemfile: gemfiles/rails51.gemfile
|
52
|
-
- rvm: 3.0.1
|
53
|
-
gemfile: gemfiles/rails52.gemfile
|
54
|
-
- rvm: ruby-head
|
55
|
-
gemfile: gemfiles/rails42.gemfile
|
56
|
-
allow_failures:
|
57
|
-
- rvm: ruby-head
|
58
|
-
fast_finish: true
|
59
|
-
branches:
|
60
|
-
only:
|
61
|
-
- master
|
62
|
-
- /.*-stable$/
|
63
|
-
notifications:
|
64
|
-
webhooks:
|
65
|
-
urls:
|
66
|
-
- https://buildlight.collectiveidea.com/
|
67
|
-
on_start: always
|