forget_that 0.1.0
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 +7 -0
- data/.byebug_history +37 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +117 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +97 -0
- data/README.md +137 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/anonymization_config.yml +17 -0
- data/forget_that.gemspec +40 -0
- data/lib/forget_that.rb +19 -0
- data/lib/forget_that/record.rb +18 -0
- data/lib/forget_that/service.rb +83 -0
- data/lib/forget_that/version.rb +3 -0
- data/lib/generators/forget_that/install_generator.rb +31 -0
- data/lib/generators/forget_that/templates/migration.rb.tt +7 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c4c089033e63cc13c348be8e0fc634dc9270d835b1427faffcfce82f744fce33
|
4
|
+
data.tar.gz: ccf7e04335ecc2af7e89742d0e3046c1d2fa04f063f1a45d74de3dade8526d71
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84525558e0101333d1c38b59a79b48230043dafac018c729f4340e34f1f4b7063b2936b72f82d738abcc1db3e054cfbfadae35312a2e0d733b9505768272ef14
|
7
|
+
data.tar.gz: 0c5e4546c6f65dcbc70d02a9179258e4d1cb0f200e8f4b54cb0787d6b2cddfb3d1b4e0d8c7d499de903d2b6c968f056001fded5c8599882d5b590b5894b07cba
|
data/.byebug_history
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
exit
|
2
|
+
self.default_scoped.object_id
|
3
|
+
self.object_id
|
4
|
+
self
|
5
|
+
exit
|
6
|
+
klass.for_anonymization.to_sql
|
7
|
+
klass.where(created_at: (Time.now - 2000.years)..(Time.now), anonymized: 0)
|
8
|
+
klass.for_anonymization.to_sql
|
9
|
+
klass.where(created_at: (Time.now - 2000.years)..(Time.now), anonymized: 0)
|
10
|
+
klass.where(created_at: (Time.now - 2000.years)..(Time.now), anonymized: 1)
|
11
|
+
klass.where(created_at: (Time.now - 2000.years)..(Time.now), anonymized: true)
|
12
|
+
klass.where(created_at: (Time.now - 2000.years)..(Time.now))
|
13
|
+
klass.where(created_at: (Time.now - 2.years)..(Time.now - 90.days))
|
14
|
+
klass.where(created_at: (Time.now - 2.years)..(Time.now))
|
15
|
+
klass.where(anonymized: false, created_at: (Time.now - 2.years)..(Time.now))
|
16
|
+
klass.where(anonymized: false, created_at: (Time.now - 2000.years)..(Time.now))
|
17
|
+
klass.where(anonymized: false, created_at: (Time.now - 2000.years)..(retention_threshold.call))
|
18
|
+
end)
|
19
|
+
where(anonymized: false, created_at: (Time.now - 2000.years)..(retention_threshold.call))
|
20
|
+
scope :for_anonymization, (lambda do
|
21
|
+
klass.for_anonymization.to_sql
|
22
|
+
klass.all
|
23
|
+
klass.where(anonymizeed: true).to_sql
|
24
|
+
klass.where(anonymized: 'true').to_sql
|
25
|
+
klass.where(anonymized: true).to_sql
|
26
|
+
klass.where(anonymized: 'true')
|
27
|
+
klass.where(anonymized: true)
|
28
|
+
klass.where(anonymized: true).to_sql
|
29
|
+
klass.where(anonymized: false).to_sql
|
30
|
+
klass.where(anonymized: false)
|
31
|
+
klass.where(id: 1)
|
32
|
+
klass.where()id: 1
|
33
|
+
klass.where(anonymized: false)
|
34
|
+
klass.for_anonymization
|
35
|
+
klass.for:anonymization
|
36
|
+
klass.all
|
37
|
+
klass
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
Style/Documentation:
|
2
|
+
Enabled: false
|
3
|
+
Style/MixinUsage:
|
4
|
+
Exclude:
|
5
|
+
- 'bin/setup'
|
6
|
+
- 'bin/update'
|
7
|
+
Metrics/LineLength:
|
8
|
+
Max: 120
|
9
|
+
IgnoredPatterns: ['(\A|\s)#']
|
10
|
+
|
11
|
+
Metrics/MethodLength:
|
12
|
+
Max: 68
|
13
|
+
|
14
|
+
Metrics/ClassLength:
|
15
|
+
Max: 258
|
16
|
+
Exclude:
|
17
|
+
- 'app/components/consumer/factories/persist_leasing_request.rb'
|
18
|
+
|
19
|
+
Metrics/ModuleLength:
|
20
|
+
Max: 212
|
21
|
+
|
22
|
+
Metrics/ParameterLists:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/MethodMissingSuper:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/MissingRespondToMissing:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Style/NumericLiteralPrefix:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Naming/VariableNumber:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Naming/PredicateName:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/DoubleNegation:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/EmptyMethod:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/StringLiteralsInInterpolation:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
# Nodody really wants this. We are using Ruby! ` return ` as direct call should be used as less as possible
|
50
|
+
Style/GuardClause:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Style/FormatStringToken:
|
54
|
+
Exclude:
|
55
|
+
- spec/**/*
|
56
|
+
|
57
|
+
Style/SafeNavigation:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
Metrics/AbcSize:
|
61
|
+
Max: 99
|
62
|
+
Exclude:
|
63
|
+
- spec/**/*
|
64
|
+
|
65
|
+
Metrics/CyclomaticComplexity:
|
66
|
+
Max: 13
|
67
|
+
|
68
|
+
Metrics/PerceivedComplexity:
|
69
|
+
Max: 14
|
70
|
+
|
71
|
+
Documentation:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
Layout/MultilineHashBraceLayout:
|
75
|
+
EnforcedStyle: symmetrical
|
76
|
+
|
77
|
+
Layout/AlignHash:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
Layout/AlignParameters:
|
81
|
+
Enabled: false
|
82
|
+
|
83
|
+
Metrics/BlockLength:
|
84
|
+
Enabled: false
|
85
|
+
|
86
|
+
# TODO: This is just a quickfix, we need to find out what this *really* does.
|
87
|
+
Lint/SafeNavigationChain:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
# Allows to create getter and setter methods with get_ and set_
|
91
|
+
Naming/AccessorMethodName:
|
92
|
+
Enabled: false
|
93
|
+
|
94
|
+
Naming/MemoizedInstanceVariableName:
|
95
|
+
Enabled: false
|
96
|
+
|
97
|
+
Lint/AmbiguousBlockAssociation:
|
98
|
+
Exclude:
|
99
|
+
- "spec/**/*"
|
100
|
+
|
101
|
+
Lint/AmbiguousOperator:
|
102
|
+
Enabled: true
|
103
|
+
|
104
|
+
AllCops:
|
105
|
+
DisplayCopNames: true
|
106
|
+
TargetRubyVersion: 2.5.0
|
107
|
+
CacheRootDirectory: tmp/test-results
|
108
|
+
Exclude:
|
109
|
+
- db/**/*
|
110
|
+
- bin/**
|
111
|
+
- utils/**/*
|
112
|
+
- Guardfile
|
113
|
+
- config/**/*
|
114
|
+
- node_modules/**/*
|
115
|
+
- vendor/**/*
|
116
|
+
- Gemfile
|
117
|
+
- Gemfile.lock
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
forget_that (0.1.0)
|
5
|
+
activerecord (>= 5)
|
6
|
+
activesupport
|
7
|
+
pg
|
8
|
+
railties (>= 5)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
actionpack (6.0.2.1)
|
14
|
+
actionview (= 6.0.2.1)
|
15
|
+
activesupport (= 6.0.2.1)
|
16
|
+
rack (~> 2.0, >= 2.0.8)
|
17
|
+
rack-test (>= 0.6.3)
|
18
|
+
rails-dom-testing (~> 2.0)
|
19
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
20
|
+
actionview (6.0.2.1)
|
21
|
+
activesupport (= 6.0.2.1)
|
22
|
+
builder (~> 3.1)
|
23
|
+
erubi (~> 1.4)
|
24
|
+
rails-dom-testing (~> 2.0)
|
25
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
26
|
+
activemodel (6.0.2.1)
|
27
|
+
activesupport (= 6.0.2.1)
|
28
|
+
activerecord (6.0.2.1)
|
29
|
+
activemodel (= 6.0.2.1)
|
30
|
+
activesupport (= 6.0.2.1)
|
31
|
+
activesupport (6.0.2.1)
|
32
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
33
|
+
i18n (>= 0.7, < 2)
|
34
|
+
minitest (~> 5.1)
|
35
|
+
tzinfo (~> 1.1)
|
36
|
+
zeitwerk (~> 2.2)
|
37
|
+
builder (3.2.4)
|
38
|
+
concurrent-ruby (1.1.5)
|
39
|
+
crass (1.0.5)
|
40
|
+
diff-lcs (1.3)
|
41
|
+
erubi (1.9.0)
|
42
|
+
i18n (1.7.0)
|
43
|
+
concurrent-ruby (~> 1.0)
|
44
|
+
loofah (2.4.0)
|
45
|
+
crass (~> 1.0.2)
|
46
|
+
nokogiri (>= 1.5.9)
|
47
|
+
method_source (0.9.2)
|
48
|
+
mini_portile2 (2.4.0)
|
49
|
+
minitest (5.13.0)
|
50
|
+
nokogiri (1.10.7)
|
51
|
+
mini_portile2 (~> 2.4.0)
|
52
|
+
pg (1.2.1)
|
53
|
+
rack (2.0.8)
|
54
|
+
rack-test (1.1.0)
|
55
|
+
rack (>= 1.0, < 3)
|
56
|
+
rails-dom-testing (2.0.3)
|
57
|
+
activesupport (>= 4.2.0)
|
58
|
+
nokogiri (>= 1.6)
|
59
|
+
rails-html-sanitizer (1.3.0)
|
60
|
+
loofah (~> 2.3)
|
61
|
+
railties (6.0.2.1)
|
62
|
+
actionpack (= 6.0.2.1)
|
63
|
+
activesupport (= 6.0.2.1)
|
64
|
+
method_source
|
65
|
+
rake (>= 0.8.7)
|
66
|
+
thor (>= 0.20.3, < 2.0)
|
67
|
+
rake (10.5.0)
|
68
|
+
rspec (3.9.0)
|
69
|
+
rspec-core (~> 3.9.0)
|
70
|
+
rspec-expectations (~> 3.9.0)
|
71
|
+
rspec-mocks (~> 3.9.0)
|
72
|
+
rspec-core (3.9.1)
|
73
|
+
rspec-support (~> 3.9.1)
|
74
|
+
rspec-expectations (3.9.0)
|
75
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
76
|
+
rspec-support (~> 3.9.0)
|
77
|
+
rspec-mocks (3.9.1)
|
78
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
79
|
+
rspec-support (~> 3.9.0)
|
80
|
+
rspec-support (3.9.2)
|
81
|
+
thor (1.0.1)
|
82
|
+
thread_safe (0.3.6)
|
83
|
+
tzinfo (1.2.6)
|
84
|
+
thread_safe (~> 0.1)
|
85
|
+
zeitwerk (2.2.2)
|
86
|
+
|
87
|
+
PLATFORMS
|
88
|
+
ruby
|
89
|
+
|
90
|
+
DEPENDENCIES
|
91
|
+
bundler (~> 2.0)
|
92
|
+
forget_that!
|
93
|
+
rake (~> 10.0)
|
94
|
+
rspec (~> 3.0)
|
95
|
+
|
96
|
+
BUNDLED WITH
|
97
|
+
2.0.2
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# ForgetThat
|
2
|
+
|
3
|
+
ForgetThat is a tool to take care of critical data in your database. It replaces the critical pieces of data with anonymized data, according to pre-set per-application policy.
|
4
|
+
|
5
|
+
## Important notice
|
6
|
+
|
7
|
+
When misconfigured and/or misused this gem can effectively wipe important data from the database. Be responsible and test before running on production data.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'forget_that'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
## Configuration
|
22
|
+
|
23
|
+
Before gem could be used, a config in `config/anonymization_config.yml` in must be created:
|
24
|
+
|
25
|
+
```YAML
|
26
|
+
config:
|
27
|
+
retention_time:
|
28
|
+
value: 90
|
29
|
+
unit: 'days'
|
30
|
+
|
31
|
+
schema:
|
32
|
+
table1:
|
33
|
+
name: 'Peter'
|
34
|
+
table2:
|
35
|
+
phone: '%{random_phone}'
|
36
|
+
```
|
37
|
+
|
38
|
+
Pay attention that default placeholders are `random_date`, `hex_string`, `random_phone`, `fake_personal_id_number`, `random_amount`. You can add your own placeholders by supplying them to initializer ([see below](#custom_placeholders))
|
39
|
+
|
40
|
+
## Database migration
|
41
|
+
|
42
|
+
After you created a config you can generate migration that adds anonymization metadata to corresponding tables:
|
43
|
+
|
44
|
+
$ rails g forget_that:install
|
45
|
+
|
46
|
+
Do not forget to run the migration:
|
47
|
+
|
48
|
+
$ rails db:migrate
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
In order to run the service, you can create an instance and use the `call` method:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
ForgetThat::Service.new.call
|
56
|
+
```
|
57
|
+
|
58
|
+
Calling that will find records older then `retention_time` in your configured tables and replace the configured fields with configured values.
|
59
|
+
If some of the placeholders are not supplied, or some tables do not contain `anonymization` flag the error will be raised.
|
60
|
+
|
61
|
+
### Sidekiq
|
62
|
+
|
63
|
+
The case gem is used originally is data anonymization with accordance to data protection regulations in EU. Reducing the amount of sensitive information, after transactions are complete is the safest bet when it comes to data security.
|
64
|
+
|
65
|
+
This can be achieved through setting up a `sidekiq` worker:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class AnonymizeCustomerData
|
69
|
+
include Sidekiq::Worker
|
70
|
+
|
71
|
+
sidekiq_options retry: 10
|
72
|
+
|
73
|
+
def perform
|
74
|
+
Rails.logger.info('[AnonymizeCustomerData.perform] start')
|
75
|
+
|
76
|
+
ForgetThat::Service.new.call
|
77
|
+
|
78
|
+
Rails.logger.info('[AnonymizeCustomerData.perform] done')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
Then you can use tool like `sidekiq-cron` in order to schedule it.
|
84
|
+
|
85
|
+
### Rake-task for using production data locally
|
86
|
+
|
87
|
+
Another use might be when a developer dumps a production database in order to play with it locally.
|
88
|
+
|
89
|
+
To be on the safe side and to not compromise sensitive data, the gem might be configured in the following way:
|
90
|
+
|
91
|
+
```YAML
|
92
|
+
config:
|
93
|
+
retention_time:
|
94
|
+
value: 0
|
95
|
+
unit: 'seconds'
|
96
|
+
|
97
|
+
schema:
|
98
|
+
# your anonymization schema
|
99
|
+
```
|
100
|
+
|
101
|
+
Then gem can be invoked from the rake-task. It is your responsibility to ensure that it never runs on production.
|
102
|
+
|
103
|
+
### Custom placeholders
|
104
|
+
|
105
|
+
The default placeholders are `random_date`, `hex_string`, `random_phone`, `fake_personal_id_number`, `random_amount`. In some cases this might not be enough or behaviour might not be desireable. In that case you can supply `anonymizers` hash.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
ForgetThat::Service.new(
|
109
|
+
anonymizers: {
|
110
|
+
foobar: -> { 'Foo' + 'Bar' }
|
111
|
+
}
|
112
|
+
).call
|
113
|
+
```
|
114
|
+
|
115
|
+
Each member of this hash must be a zero-arity lambda that returns a string value.
|
116
|
+
|
117
|
+
If the key in the hash matches one of the pre-defined placeholders, the pre-defined placeholder will be overridden by the new one.
|
118
|
+
|
119
|
+
After anonymizer was supplied with the lambda, it can be used in the config.
|
120
|
+
|
121
|
+
```YAML
|
122
|
+
# ...
|
123
|
+
|
124
|
+
schema:
|
125
|
+
users:
|
126
|
+
name: 'Peter %{foobar}' #results in the "name" column of table "users" filled with "Peter FooBar"
|
127
|
+
```
|
128
|
+
|
129
|
+
## Development
|
130
|
+
|
131
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
132
|
+
|
133
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
134
|
+
|
135
|
+
## Contributing
|
136
|
+
|
137
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/vehiculum-berlin/forget_that.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "forget_that"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
config:
|
2
|
+
retention_time:
|
3
|
+
value: 90
|
4
|
+
unit: 'days'
|
5
|
+
|
6
|
+
schema:
|
7
|
+
addresses:
|
8
|
+
city: 'Kyteż'
|
9
|
+
zip_code: '99999'
|
10
|
+
street: 'Sesame Street'
|
11
|
+
street_number: '123'
|
12
|
+
lived_since: '%{random_date}'
|
13
|
+
|
14
|
+
bank_accounts:
|
15
|
+
bic: '123456789'
|
16
|
+
iban: '123456789'
|
17
|
+
bank_name: 'ACME INC.'
|
data/forget_that.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'forget_that/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'forget_that'
|
9
|
+
spec.version = ForgetThat::VERSION
|
10
|
+
spec.authors = ['Stan Pankov']
|
11
|
+
spec.email = ['s.pankov@vehiculum.de']
|
12
|
+
|
13
|
+
spec.summary = "Strip your customers' data from db"
|
14
|
+
spec.description = 'Easily setup and run anonymization policies'
|
15
|
+
spec.homepage = 'https://github.com/vehiculum-berlin'
|
16
|
+
|
17
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
18
|
+
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/vehiculum-berlin/forget_that'
|
21
|
+
spec.metadata["changelog_uri"] = 'https://github.com/vehiculum-berlin/forget_that'
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
end
|
28
|
+
spec.bindir = 'exe'
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ['lib']
|
31
|
+
|
32
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
33
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
35
|
+
|
36
|
+
spec.add_runtime_dependency 'activerecord', '>= 5'
|
37
|
+
spec.add_runtime_dependency 'activesupport'
|
38
|
+
spec.add_runtime_dependency 'pg'
|
39
|
+
spec.add_runtime_dependency 'railties', '>= 5'
|
40
|
+
end
|
data/lib/forget_that.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forget_that/version'
|
4
|
+
require 'forget_that/service'
|
5
|
+
require 'forget_that/record'
|
6
|
+
|
7
|
+
module ForgetThat
|
8
|
+
class InvalidConfigError < StandardError; end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_writer :logger
|
12
|
+
|
13
|
+
def logger
|
14
|
+
@logger ||= Logger.new($stdout).tap do |log|
|
15
|
+
log.progname = self.name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
module ForgetThat
|
5
|
+
class Record < ActiveRecord::Base
|
6
|
+
retention_threshold = (
|
7
|
+
-> { YAML.load_file('config/anonymization_config.yml') } >>
|
8
|
+
->(config) { config.dig('config', 'retention_time') } >>
|
9
|
+
->(retention_params) { retention_params['value'].send(retention_params['unit']) } >>
|
10
|
+
->(retention_time) { Time.current - retention_time }
|
11
|
+
)
|
12
|
+
|
13
|
+
scope :for_anonymization, (lambda do
|
14
|
+
where(anonymized: false)
|
15
|
+
.where('created_at < ?', retention_threshold.call)
|
16
|
+
end)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForgetThat
|
4
|
+
class Service
|
5
|
+
def initialize(options = {})
|
6
|
+
@custom_anonymizers = options[:anonymizers] || {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
raise InvalidConfigError unless valid_config?
|
11
|
+
|
12
|
+
config.each do |table, columns|
|
13
|
+
klass = Class.new(ForgetThat::Record) { self.table_name = table }
|
14
|
+
records_hash = klass
|
15
|
+
.for_anonymization
|
16
|
+
.pluck(:id)
|
17
|
+
.map { |id| [id, populate_records_hash(columns)] }
|
18
|
+
.to_h
|
19
|
+
klass.update(records_hash.keys, records_hash.values)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def anonymizers
|
24
|
+
{
|
25
|
+
random_date: -> { Time.now - [*1..10**4].sample.days },
|
26
|
+
hex_string: -> { SecureRandom.hex(5) },
|
27
|
+
random_phone: -> { "+49#{[*0..10].map { SecureRandom.random_number(10) } .join}" },
|
28
|
+
fake_personal_id_number: -> { "DE#{[*0..10].map { SecureRandom.random_number(10) }.join}" },
|
29
|
+
random_amount: -> { [*1..80].sample * [*900..1100].sample }
|
30
|
+
}.merge(@custom_anonymizers)
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid_config?
|
34
|
+
return false unless valid_anonymizer_set?
|
35
|
+
return false unless valid_database_schema?
|
36
|
+
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid_anonymizer_set?
|
41
|
+
available = anonymizers.stringify_keys.keys
|
42
|
+
required = config
|
43
|
+
.map { |_key, value| value.map { |_key, val| val } }
|
44
|
+
.flatten
|
45
|
+
.map { |value| value.to_s.scan(/%{(\w*)\.?.*}/) }
|
46
|
+
.flatten
|
47
|
+
.uniq
|
48
|
+
return true if (required - available).empty?
|
49
|
+
|
50
|
+
ForgetThat.logger.error("Anonymizers #{(required - available).join(', ')} are not defined")
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def valid_database_schema?
|
55
|
+
if config
|
56
|
+
.keys
|
57
|
+
.map { |table| ActiveRecord::Base.connection.columns(table).map(&:name).include? 'anonymized' }
|
58
|
+
.reduce { |a, b| a && b }
|
59
|
+
true
|
60
|
+
else
|
61
|
+
ForgetThat.logger.error('Some of the tables in your database do not contain `anonymized` flag')
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def populate_records_hash(columns)
|
69
|
+
columns
|
70
|
+
.map { |key, value| [key, value.to_s % generate_anonymized_values] }
|
71
|
+
.to_h
|
72
|
+
.merge('anonymized' => true)
|
73
|
+
end
|
74
|
+
|
75
|
+
def generate_anonymized_values
|
76
|
+
anonymizers.map { |key, value| [key, value.call] }.to_h
|
77
|
+
end
|
78
|
+
|
79
|
+
def config
|
80
|
+
YAML.load_file('config/anonymization_config.yml').dig('schema')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/active_record'
|
4
|
+
|
5
|
+
module ForgetThat
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
include ActiveRecord::Generators::Migration
|
9
|
+
source_root File.join(__dir__, 'templates')
|
10
|
+
|
11
|
+
def copy_migration
|
12
|
+
migration_template 'migration.rb', 'db/migrate/install_forget_that.rb', migration_version: migration_version
|
13
|
+
end
|
14
|
+
|
15
|
+
def migration_version
|
16
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
17
|
+
end
|
18
|
+
|
19
|
+
def used_tables
|
20
|
+
YAML
|
21
|
+
.load_file('config/anonymization_config.yml')
|
22
|
+
.dig('schema')
|
23
|
+
.keys
|
24
|
+
.map { |table| [table, ActiveRecord::Base.connection.columns(table).map(&:name).include?('anonymized')] }
|
25
|
+
.to_h
|
26
|
+
.reject { |_key, value| value }
|
27
|
+
.keys
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: forget_that
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stan Pankov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activerecord
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pg
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: railties
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '5'
|
111
|
+
description: Easily setup and run anonymization policies
|
112
|
+
email:
|
113
|
+
- s.pankov@vehiculum.de
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".byebug_history"
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rspec"
|
121
|
+
- ".rubocop.yml"
|
122
|
+
- ".travis.yml"
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- bin/console
|
128
|
+
- bin/setup
|
129
|
+
- config/anonymization_config.yml
|
130
|
+
- forget_that.gemspec
|
131
|
+
- lib/forget_that.rb
|
132
|
+
- lib/forget_that/record.rb
|
133
|
+
- lib/forget_that/service.rb
|
134
|
+
- lib/forget_that/version.rb
|
135
|
+
- lib/generators/forget_that/install_generator.rb
|
136
|
+
- lib/generators/forget_that/templates/migration.rb.tt
|
137
|
+
homepage: https://github.com/vehiculum-berlin
|
138
|
+
licenses: []
|
139
|
+
metadata:
|
140
|
+
allowed_push_host: https://rubygems.org
|
141
|
+
homepage_uri: https://github.com/vehiculum-berlin
|
142
|
+
source_code_uri: https://github.com/vehiculum-berlin/forget_that
|
143
|
+
changelog_uri: https://github.com/vehiculum-berlin/forget_that
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubygems_version: 3.0.6
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: Strip your customers' data from db
|
163
|
+
test_files: []
|