gdpr_admin 1.2.0 → 1.4.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 +4 -4
- data/README.md +68 -3
- data/app/models/gdpr_admin/request.rb +2 -8
- data/lib/gdpr_admin/application_data_policy.rb +62 -1
- data/lib/gdpr_admin/helpers/data_policy_helper.rb +15 -0
- data/lib/gdpr_admin/helpers/scope_helper.rb +11 -0
- data/lib/gdpr_admin/paper_trail/version_data_policy.rb +10 -7
- data/lib/gdpr_admin/skip_record_error.rb +5 -0
- data/lib/gdpr_admin/version.rb +1 -1
- data/lib/gdpr_admin.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b1d58ff60fa19dc3b0a1fb067ae2ed0dfc96e8798257989534a39e8600ef878
|
4
|
+
data.tar.gz: b199dc1a90a1fab64a6b98bcde51dc656a8943f12381dcdbc275e51147997c92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7813fc861f53e6d280369e403e9ece16484dfc433e2005b6c6eac739f91694d06169fcf0b60a3b1d1e32f65dcc45a44a8dee5446e89a35b90391070a42c0c96
|
7
|
+
data.tar.gz: 02a363b0a84f217aceeb3e19e2d0f53b09d7f5030e3dbf16abae8c92a8dc52cca9138c3fca55b6ce1014f639a915914e910c45e7538d5f24ecf79c005a3abb14
|
data/README.md
CHANGED
@@ -6,12 +6,17 @@
|
|
6
6
|
|
7
7
|
[](https://github.com/Colex/gdpr_admin/actions/workflows/build.yml)
|
8
8
|
[](https://codeclimate.com/github/Colex/gdpr_admin)
|
9
|
+
[](https://codeclimate.com/github/Colex/gdpr_admin/test_coverage)
|
9
10
|
[](http://badge.fury.io/rb/gdpr_admin)
|
10
11
|
|
11
12
|
Rails engine for processing GDPR processes. GDPR Admin offers a simple interface for defining strategies for automating the process of data access and data removal as required by data privacy regulations like GDPR.
|
12
13
|
|
13
14
|
GDPR Admin uses simple Ruby classes, so you're free to code your Data Policies as you see fit. A swiss knife of
|
14
|
-
helper methods are available to make the processes even simpler.
|
15
|
+
helper methods are available to make the processes even simpler. The focus of the gem is to easily implement:
|
16
|
+
|
17
|
+
- [Right of access](https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-of-access/): export a subject's data on request;
|
18
|
+
- [Right to erasure](https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-to-erasure/): remove a subject's personal data on request;
|
19
|
+
- [Storage limitation](https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/principles/storage-limitation/): hold data for as long as required (data retention policies and offboarding tenants);
|
15
20
|
|
16
21
|
## Installation
|
17
22
|
Add this line to your application's Gemfile:
|
@@ -30,6 +35,11 @@ Or install it yourself as:
|
|
30
35
|
$ gem install gdpr_admin
|
31
36
|
```
|
32
37
|
|
38
|
+
Then install the migrations:
|
39
|
+
```bash
|
40
|
+
$ rails gdpr_admin:install:migrations
|
41
|
+
```
|
42
|
+
|
33
43
|
## Usage
|
34
44
|
|
35
45
|
Create your data policies file within `app/gdpr` _(configurable)_ and inherit from `GdprAdmin::ApplicationDataPolicy`.
|
@@ -67,6 +77,26 @@ GdprAdmin::Request.create!(
|
|
67
77
|
|
68
78
|
Creating a request will automatically enqueue the request to be processed in 4 hours - this gives time to cancel an accidental request. You can configure this grace period as desired.
|
69
79
|
|
80
|
+
### Scope Helpers
|
81
|
+
Helpers to be used within the `#scope` method.
|
82
|
+
|
83
|
+
#### `scope_by_date`
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
scope_by_date(scope, field = :updated_at)
|
87
|
+
```
|
88
|
+
|
89
|
+
Automatically scopes the data using the `updated_at` column to match the GDPR Request. You can use a different column by providing
|
90
|
+
a second argument.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class ContactDataPolicy < GdprAdmin::ApplicationDataPolicy
|
94
|
+
def scope
|
95
|
+
scope_by_date(Contact)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
70
100
|
### Anonymization Helpers
|
71
101
|
A set of helper methods are available to make the anonymization even simpler. These are not mandatory, but can help you
|
72
102
|
keep your code cleaner and, better, write less code.
|
@@ -113,6 +143,41 @@ same anonymized value. _(note: different values may also yield the same value)_
|
|
113
143
|
|
114
144
|
To use the built-in anonymizer functions, you need to install the gem `faker`.
|
115
145
|
|
146
|
+
## Data Policy Hooks
|
147
|
+
For advanced use cases, you may install hooks that are run at different stages of the data policy process.
|
148
|
+
|
149
|
+
### `before_process`
|
150
|
+
Will be run before the policy is executed. Calling `skip_data_policy!` will raise `GdprAdmin::SkipDataPolicyError` and
|
151
|
+
stop the execution of the data policy.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
class ContactDataPolicy < GdprAdmin::ApplicationDataPolicy
|
155
|
+
before_process :skip_internal_contacts!
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def skip_internal_contacts!
|
160
|
+
skip_data_policy! if contact.email =~ /.*@company\.com/
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
### `before_process_record`
|
166
|
+
Called before processing a record (either erasing or exporting). Calling `skip_record!` will raise `GdprAdmin::SkipRecordError` and
|
167
|
+
skip processing that particular record.
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
class UserDataPolicy < GdprAdmin::ApplicationDataPolicy
|
171
|
+
before_process :skip_super_admins!
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def skip_super_admins!(user)
|
176
|
+
skip_record! if user.role == 'super_admin'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
116
181
|
## GDPR Request
|
117
182
|
A GDPR Request (`GdprAdmin::Request`) represents a request to remove a subject's data, tenant's data, or export subject data.
|
118
183
|
|
@@ -155,9 +220,9 @@ You can define a repeating job with `sidekiq-cron`:
|
|
155
220
|
|
156
221
|
```ruby
|
157
222
|
Sidekiq::Cron::Job.create(
|
158
|
-
class:
|
223
|
+
class: 'GdprAdmin::DataRetentionPoliciesRunnerJob',
|
159
224
|
cron: '0 2 * * *',
|
160
|
-
name: '
|
225
|
+
name: 'Run Data Retention Policies',
|
161
226
|
queue: 'cron',
|
162
227
|
active_job: true,
|
163
228
|
)
|
@@ -35,7 +35,7 @@ module GdprAdmin
|
|
35
35
|
GdprAdmin.load_data_policies
|
36
36
|
with_lock { processing! }
|
37
37
|
with_lock do
|
38
|
-
|
38
|
+
process_policies
|
39
39
|
completed!
|
40
40
|
end
|
41
41
|
rescue StandardError
|
@@ -63,13 +63,7 @@ module GdprAdmin
|
|
63
63
|
|
64
64
|
def process_policies
|
65
65
|
ApplicationDataPolicy.descendants.each do |policy_class|
|
66
|
-
|
67
|
-
policy.scope.find_each do |record|
|
68
|
-
policy.export(record) if export?
|
69
|
-
policy.erase(record) if erase?
|
70
|
-
end
|
71
|
-
rescue SkipDataPolicyError
|
72
|
-
next
|
66
|
+
policy_class.process(self)
|
73
67
|
end
|
74
68
|
end
|
75
69
|
|
@@ -2,14 +2,34 @@
|
|
2
2
|
|
3
3
|
module GdprAdmin
|
4
4
|
class ApplicationDataPolicy
|
5
|
+
include Helpers::DataPolicyHelper
|
5
6
|
include Helpers::EraseHelper
|
7
|
+
include Helpers::ScopeHelper
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :before_process_hooks, :before_process_record_hooks
|
11
|
+
|
12
|
+
def before_process(method)
|
13
|
+
@before_process_hooks ||= []
|
14
|
+
@before_process_hooks << method
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_process_record(method)
|
18
|
+
@before_process_record_hooks ||= []
|
19
|
+
@before_process_record_hooks << method
|
20
|
+
end
|
21
|
+
|
22
|
+
def process(request)
|
23
|
+
new(request).process
|
24
|
+
end
|
25
|
+
end
|
6
26
|
|
7
27
|
def initialize(request)
|
8
28
|
@request = request
|
9
29
|
end
|
10
30
|
|
11
31
|
def scope
|
12
|
-
|
32
|
+
skip_data_policy!
|
13
33
|
end
|
14
34
|
|
15
35
|
def export(_record)
|
@@ -20,8 +40,49 @@ module GdprAdmin
|
|
20
40
|
raise NotImplementedError
|
21
41
|
end
|
22
42
|
|
43
|
+
def process
|
44
|
+
GdprAdmin.config.tenant_adapter.with_tenant(request.tenant) do
|
45
|
+
run_preprocessors
|
46
|
+
scope.find_each do |record|
|
47
|
+
process_record(record)
|
48
|
+
end
|
49
|
+
rescue SkipDataPolicyError
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
23
54
|
protected
|
24
55
|
|
25
56
|
attr_reader :request
|
57
|
+
|
58
|
+
def process_record(record)
|
59
|
+
run_record_preprocessors(record)
|
60
|
+
export(record) if request.export?
|
61
|
+
erase(record) if request.erase?
|
62
|
+
rescue SkipRecordError
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def run_preprocessors
|
67
|
+
before_process_hooks = self.class.before_process_hooks || []
|
68
|
+
before_process_hooks.each do |hook|
|
69
|
+
call_hook(hook)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def run_record_preprocessors(record)
|
74
|
+
before_process_record_hooks = self.class.before_process_record_hooks || []
|
75
|
+
before_process_record_hooks.each do |hook|
|
76
|
+
call_hook(hook, record)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def call_hook(hook, value = nil)
|
81
|
+
hook = method(hook) unless hook.respond_to?(:call)
|
82
|
+
|
83
|
+
arity = hook.arity
|
84
|
+
args = [value].take(arity).reverse
|
85
|
+
instance_exec(*args, &hook)
|
86
|
+
end
|
26
87
|
end
|
27
88
|
end
|
@@ -9,13 +9,7 @@ module GdprAdmin
|
|
9
9
|
|
10
10
|
def erase(version, item_fields = nil)
|
11
11
|
item_fields ||= infer_item_fields(version)
|
12
|
-
|
13
|
-
|
14
|
-
base_changes = {
|
15
|
-
object: anonymize_version_object(version, item_fields),
|
16
|
-
object_changes: anonymize_version_object_changes(version, item_fields),
|
17
|
-
}.compact
|
18
|
-
erase_fields(version, fields, base_changes)
|
12
|
+
erase_fields(version, fields, base_changes(version, item_fields))
|
19
13
|
end
|
20
14
|
|
21
15
|
def fields
|
@@ -24,6 +18,15 @@ module GdprAdmin
|
|
24
18
|
|
25
19
|
private
|
26
20
|
|
21
|
+
def base_changes(version, item_fields)
|
22
|
+
return {} if item_fields.blank?
|
23
|
+
|
24
|
+
{
|
25
|
+
object: anonymize_version_object(version, item_fields),
|
26
|
+
object_changes: anonymize_version_object_changes(version, item_fields),
|
27
|
+
}.compact
|
28
|
+
end
|
29
|
+
|
27
30
|
def infer_item_fields(version)
|
28
31
|
infer_data_policy_class(version)&.new(request)&.try(:fields)
|
29
32
|
end
|
data/lib/gdpr_admin/version.rb
CHANGED
data/lib/gdpr_admin.rb
CHANGED
@@ -6,7 +6,10 @@ require 'gdpr_admin/configuration'
|
|
6
6
|
require 'gdpr_admin/error'
|
7
7
|
require 'gdpr_admin/invalid_status_error'
|
8
8
|
require 'gdpr_admin/skip_data_policy_error'
|
9
|
+
require 'gdpr_admin/skip_record_error'
|
10
|
+
require 'gdpr_admin/helpers/data_policy_helper'
|
9
11
|
require 'gdpr_admin/helpers/erase_helper'
|
12
|
+
require 'gdpr_admin/helpers/scope_helper'
|
10
13
|
require 'gdpr_admin/application_data_policy'
|
11
14
|
require 'gdpr_admin/paper_trail/version_data_policy'
|
12
15
|
require 'gdpr_admin/tenant_adapters/acts_as_tenant_adapter'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gdpr_admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Colex
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -165,12 +165,15 @@ files:
|
|
165
165
|
- lib/gdpr_admin/configuration.rb
|
166
166
|
- lib/gdpr_admin/engine.rb
|
167
167
|
- lib/gdpr_admin/error.rb
|
168
|
+
- lib/gdpr_admin/helpers/data_policy_helper.rb
|
168
169
|
- lib/gdpr_admin/helpers/erase_helper.rb
|
169
170
|
- lib/gdpr_admin/helpers/field_anonymizer_helper.rb
|
170
171
|
- lib/gdpr_admin/helpers/paper_trail_helper.rb
|
172
|
+
- lib/gdpr_admin/helpers/scope_helper.rb
|
171
173
|
- lib/gdpr_admin/invalid_status_error.rb
|
172
174
|
- lib/gdpr_admin/paper_trail/version_data_policy.rb
|
173
175
|
- lib/gdpr_admin/skip_data_policy_error.rb
|
176
|
+
- lib/gdpr_admin/skip_record_error.rb
|
174
177
|
- lib/gdpr_admin/tenant_adapters/acts_as_tenant_adapter.rb
|
175
178
|
- lib/gdpr_admin/version.rb
|
176
179
|
- lib/tasks/gdpr_admin_tasks.rake
|