active_record_anonymizer 0.1.1 → 0.2.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 +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +3 -3
- data/lib/active_record_anonymizer/attributes_anonymizer.rb +58 -0
- data/lib/active_record_anonymizer/configuration.rb +1 -1
- data/lib/active_record_anonymizer/extensions.rb +9 -43
- data/lib/active_record_anonymizer/{anonymizer.rb → initiator.rb} +2 -2
- data/lib/active_record_anonymizer/version.rb +1 -1
- data/lib/active_record_anonymizer.rb +1 -1
- data/lib/generators/active_record_anonymizer/templates/anonymizer.rb +1 -1
- data/lib/generators/active_record_anonymizer/templates/migration.rb.erb +1 -1
- data/lib/tasks/active_record_anonymizer.rake +8 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a644f35a1b766f9ff29cbc1c8d02f4b8dd521da9af8cb54e3e1ab2056d103af1
|
4
|
+
data.tar.gz: ef0ac08ee8dea107e714ddb22b1c7271f558272421e810556a161d0bb7bb954e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04f40e398b68ba3c74765d550294d6a29f060cc408fec1f7c9e48b22eca5e7fdc3501b7b288f70255ce7b36f1838a368974aa318e1ddd189cac713dc954e9e34
|
7
|
+
data.tar.gz: 3f7430c8b91d50476fd0e52592990814da6827ee13185bb8ce5ee3bf51bd970e96f13f1cae7b98752dde2a92ca899ad7a0b110ccfbb257fe033f1a81fd4d3533
|
data/CHANGELOG.md
CHANGED
@@ -5,3 +5,40 @@
|
|
5
5
|
#### Fixed migration_version not getting generated in Rails v5+
|
6
6
|
|
7
7
|
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/6)
|
8
|
+
|
9
|
+
## Version 0.2.0
|
10
|
+
|
11
|
+
### New Fixes
|
12
|
+
|
13
|
+
#### Removed index from migration
|
14
|
+
|
15
|
+
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/7)
|
16
|
+
|
17
|
+
#### Fixed incorrect generator name in README.md
|
18
|
+
|
19
|
+
## Version 0.2.1
|
20
|
+
|
21
|
+
### New Fixes
|
22
|
+
|
23
|
+
#### Fixed validations breaking the anonymization process
|
24
|
+
|
25
|
+
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/8)
|
26
|
+
|
27
|
+
#### Renamed Anonymizer to Initiater
|
28
|
+
|
29
|
+
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/8)
|
30
|
+
|
31
|
+
#### Fixed populate:all task not loading models
|
32
|
+
|
33
|
+
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/9)
|
34
|
+
|
35
|
+
### Fixed configuration not reloading when changed
|
36
|
+
|
37
|
+
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/10)
|
38
|
+
|
39
|
+
### New Features
|
40
|
+
|
41
|
+
#### Added support for development environment
|
42
|
+
|
43
|
+
* [GitHub PR](https://github.com/keshavbiswa/active_record_anonymizer/pull/11)
|
44
|
+
|
data/README.md
CHANGED
@@ -34,7 +34,7 @@ Install the gem using the following command:
|
|
34
34
|
You must have anonymized columns in your Model to store the anonymized data.
|
35
35
|
You can use the following migration generator to add anonymized columns to your existing table:
|
36
36
|
|
37
|
-
$ bin/rails generate anonymize User first_name last_name
|
37
|
+
$ bin/rails generate active_record_anonymizer:anonymize User first_name last_name
|
38
38
|
This will generate a migration file similar to the following:
|
39
39
|
|
40
40
|
```ruby
|
@@ -82,14 +82,14 @@ end
|
|
82
82
|
|
83
83
|
You can configure the gem using the following options:
|
84
84
|
|
85
|
-
- `:environments` - The environments in which the anonymized data should be used. (Defaults to `[:
|
85
|
+
- `:environments` - The environments in which the anonymized data should be used. (Defaults to `[:development]`)
|
86
86
|
- `:skip_update` - Skip updating the anonymized data when the record is updated. This ensures your anonymized data remains the same even if it's updated. (Defaults to `false`)
|
87
87
|
- `:alias_original_columns` - Alias the original columns to the anonymized columns. You can still access the original value of the attribute using the alias `original_#{attribute_name}`(Defaults to `false`)
|
88
88
|
- `:alias_column_name` - The name of the alias column. (Defaults to `original_#{column_name}`)
|
89
89
|
|
90
90
|
```ruby
|
91
91
|
ActiveRecordAnonymizer.configure do |config|
|
92
|
-
config.environments = [:
|
92
|
+
config.environments = [:development, :staging] # The environments in which the anonymized data should be used
|
93
93
|
config.skip_update = true # Skip updating the anonymized data when the record is updated
|
94
94
|
config.alias_original_columns = true # Alias the original columns to the anonymized columns
|
95
95
|
config.alias_column_name = "original" # The original column will be aliased to "original_#{column_name}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordAnonymizer
|
4
|
+
class AttributesAnonymizer
|
5
|
+
attr_reader :model
|
6
|
+
|
7
|
+
def initialize(model, skip_update: false)
|
8
|
+
@model = model
|
9
|
+
@skip_update = skip_update
|
10
|
+
end
|
11
|
+
|
12
|
+
def anonymize_columns
|
13
|
+
if model.new_record?
|
14
|
+
anonymize_all_attributes
|
15
|
+
else
|
16
|
+
# Skip updating the record if skip_update is true
|
17
|
+
return if @skip_update
|
18
|
+
|
19
|
+
anonymize_changed_columns
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def populate
|
24
|
+
anonymize_all_attributes
|
25
|
+
model.save!
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def anonymize_all_attributes
|
31
|
+
model.class.anonymized_attributes.each_value do |settings|
|
32
|
+
generate_and_write_fake_value(settings[:column], settings[:with])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def anonymize_changed_columns
|
37
|
+
changes = model.changes.keys.map(&:to_sym)
|
38
|
+
changed_columns = changes & model.class.anonymized_attributes.keys
|
39
|
+
|
40
|
+
changed_columns.each do |column|
|
41
|
+
settings = model.class.anonymized_attributes[column]
|
42
|
+
generate_and_write_fake_value(settings[:column], settings[:with])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_and_write_fake_value(anonymized_attr, with_strategy = nil)
|
47
|
+
fake_value = case with_strategy
|
48
|
+
when Proc
|
49
|
+
with_strategy.call(model)
|
50
|
+
when Symbol
|
51
|
+
model.send(with_strategy)
|
52
|
+
else
|
53
|
+
FakeValue.new(anonymized_attr, model.class.columns_hash[anonymized_attr.to_s]).generate_fake_value
|
54
|
+
end
|
55
|
+
model.write_attribute(anonymized_attr, fake_value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -5,7 +5,7 @@ module ActiveRecordAnonymizer
|
|
5
5
|
attr_accessor :environments, :skip_update, :alias_original_columns, :alias_column_name
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
@environments = %i[
|
8
|
+
@environments = %i[development]
|
9
9
|
@skip_update = false
|
10
10
|
@alias_original_columns = false
|
11
11
|
@alias_column_name = "original"
|
@@ -24,63 +24,29 @@ module ActiveRecordAnonymizer
|
|
24
24
|
cattr_accessor :anonymized_attributes, instance_accessor: false unless respond_to?(:anonymized_attributes)
|
25
25
|
self.anonymized_attributes ||= {}
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
initiator = Initiator.new(self, attributes, with: with, column_name: column_name)
|
28
|
+
initiator.validate
|
29
|
+
initiator.configure_anonymization
|
30
30
|
|
31
31
|
# I'm ensuring that the before_save callback is only added once
|
32
32
|
# Models can call anonymize method multiple times per column
|
33
33
|
@setup_mutex.synchronize do
|
34
34
|
unless @anonymizer_setup_done
|
35
|
-
|
35
|
+
before_validation :anonymizer_anonymize_columns, if: :anonymizer_anonymization_enabled?
|
36
36
|
@anonymizer_setup_done = true
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
# Deliberately using anonymizer_ prefix to avoid conflicts
|
43
|
+
def anonymizer_anonymization_enabled?
|
43
44
|
ActiveRecordAnonymizer.anonymization_enabled?
|
44
45
|
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
anonymize_all_attributes
|
50
|
-
else
|
51
|
-
# For existing records, only apply to attributes that have changed
|
52
|
-
return if ActiveRecordAnonymizer.configuration.skip_update
|
53
|
-
|
54
|
-
anonymize_changed_attributes
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def anonymize_all_attributes
|
59
|
-
self.class.anonymized_attributes.each_value do |settings|
|
60
|
-
generate_and_write_fake_value(settings[:column], settings[:with])
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def anonymize_changed_attributes
|
65
|
-
changes = self.changes.keys.map(&:to_sym)
|
66
|
-
changed_attributes = changes & self.class.anonymized_attributes.keys
|
67
|
-
|
68
|
-
changed_attributes.each do |attribute|
|
69
|
-
settings = self.class.anonymized_attributes[attribute]
|
70
|
-
generate_and_write_fake_value(settings[:column], settings[:with])
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def generate_and_write_fake_value(anonymized_attr, with_strategy = nil)
|
75
|
-
fake_value = case with_strategy
|
76
|
-
when Proc
|
77
|
-
with_strategy.call(self)
|
78
|
-
when Symbol
|
79
|
-
send(with_strategy)
|
80
|
-
else
|
81
|
-
FakeValue.new(anonymized_attr, self.class.columns_hash[anonymized_attr.to_s]).generate_fake_value
|
82
|
-
end
|
83
|
-
write_attribute(anonymized_attr, fake_value)
|
47
|
+
# Deliberately using anonymizer_ prefix to avoid conflicts
|
48
|
+
def anonymizer_anonymize_columns
|
49
|
+
AttributesAnonymizer.new(self, skip_update: ActiveRecordAnonymizer.configuration.skip_update).anonymize_columns
|
84
50
|
end
|
85
51
|
end
|
86
52
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecordAnonymizer
|
4
|
-
class
|
4
|
+
class Initiator
|
5
5
|
attr_reader :model, :attributes, :with, :column_name
|
6
6
|
|
7
7
|
def initialize(model, attributes, with: nil, column_name: nil)
|
@@ -17,7 +17,7 @@ module ActiveRecordAnonymizer
|
|
17
17
|
check_for_missing_anonymized_columns(attributes)
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def configure_anonymization
|
21
21
|
attributes.each do |attribute|
|
22
22
|
anonymized_column = anonymized_column_name(attribute)
|
23
23
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
ActiveRecordAnonymizer.configure do |config|
|
4
4
|
# Configure the environments in which anonymization is allowed.
|
5
|
-
config.environments = %i[
|
5
|
+
config.environments = %i[development]
|
6
6
|
|
7
7
|
# Uncomment the following line to skip updating anonymized_columns when updating the original columns.
|
8
8
|
# config.skip_update = true
|
@@ -3,7 +3,7 @@
|
|
3
3
|
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
4
4
|
def self.up
|
5
5
|
<% attributes.each do |column| -%>
|
6
|
-
add_column :<%= table_name %>, :<%= "anonymized_#{column.name}" %>, :<%= column.type
|
6
|
+
add_column :<%= table_name %>, :<%= "anonymized_#{column.name}" %>, :<%= column.type %>
|
7
7
|
<% end -%>
|
8
8
|
end
|
9
9
|
|
@@ -16,16 +16,22 @@ namespace :anonymizer do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
puts "Anonymize columns for #{klass_name}..."
|
19
|
-
model.
|
19
|
+
model.find_each do |record|
|
20
|
+
ActiveRecordAnonymizer::AttributesAnonymizer.new(record).populate
|
21
|
+
end
|
20
22
|
puts "Anonymize columns for #{klass_name} done!"
|
21
23
|
end
|
22
24
|
|
23
25
|
namespace :populate do
|
24
26
|
desc "populate anonymize columns for all models"
|
25
27
|
task all: :environment do
|
28
|
+
Rails.application.eager_load!
|
29
|
+
|
26
30
|
ActiveRecordAnonymizer.models.each do |model|
|
27
31
|
puts "Anonymizing #{model.name}..."
|
28
|
-
model.find_each
|
32
|
+
model.find_each do |record|
|
33
|
+
ActiveRecordAnonymizer::AttributesAnonymizer.new(record).populate
|
34
|
+
end
|
29
35
|
end
|
30
36
|
puts "Anonymize columns for all models done!"
|
31
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_anonymizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keshav Biswa
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -81,11 +81,12 @@ files:
|
|
81
81
|
- README.md
|
82
82
|
- Rakefile
|
83
83
|
- lib/active_record_anonymizer.rb
|
84
|
-
- lib/active_record_anonymizer/
|
84
|
+
- lib/active_record_anonymizer/attributes_anonymizer.rb
|
85
85
|
- lib/active_record_anonymizer/configuration.rb
|
86
86
|
- lib/active_record_anonymizer/encryptor.rb
|
87
87
|
- lib/active_record_anonymizer/extensions.rb
|
88
88
|
- lib/active_record_anonymizer/fake_value.rb
|
89
|
+
- lib/active_record_anonymizer/initiator.rb
|
89
90
|
- lib/active_record_anonymizer/railtie.rb
|
90
91
|
- lib/active_record_anonymizer/version.rb
|
91
92
|
- lib/generators/active_record_anonymizer/USAGE
|