gdpr_admin 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b019a3a76e13bb0a29a264a2f14aa9979c8a4cf2ef3163014f72f727856d07ca
4
- data.tar.gz: 735b7ac163d0b9a7579f0bb9c26bde48ca2dc5e00996175a31aaa597dae94597
3
+ metadata.gz: 9b1d58ff60fa19dc3b0a1fb067ae2ed0dfc96e8798257989534a39e8600ef878
4
+ data.tar.gz: b199dc1a90a1fab64a6b98bcde51dc656a8943f12381dcdbc275e51147997c92
5
5
  SHA512:
6
- metadata.gz: 14e0c4a02403085c3c1fd5f7b29625f0b8194ab41a374e0f58213aa59f27e5b7e8a4d9477cbb53975fcd331af9d20516cfd3a37ffa6cc6007579ddd72bbba5a7
7
- data.tar.gz: 63d3e8710805b8f339da69a4e93deb501d6bc7734eb21f542c3ca15683114e6c8b57e73d762a2e77294c9b3522e3eb35e5aa79d960a9bf5d6c6ce68081124c4e
6
+ metadata.gz: b7813fc861f53e6d280369e403e9ece16484dfc433e2005b6c6eac739f91694d06169fcf0b60a3b1d1e32f65dcc45a44a8dee5446e89a35b90391070a42c0c96
7
+ data.tar.gz: 02a363b0a84f217aceeb3e19e2d0f53b09d7f5030e3dbf16abae8c92a8dc52cca9138c3fca55b6ce1014f639a915914e910c45e7538d5f24ecf79c005a3abb14
data/README.md CHANGED
@@ -6,12 +6,17 @@
6
6
 
7
7
  [![Build Status](https://github.com/Colex/gdpr_admin/actions/workflows/build.yml/badge.svg)](https://github.com/Colex/gdpr_admin/actions/workflows/build.yml)
8
8
  [![Code Climate](https://codeclimate.com/github/Colex/gdpr_admin.svg)](https://codeclimate.com/github/Colex/gdpr_admin)
9
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/00740133ef1f97181ed6/test_coverage)](https://codeclimate.com/github/Colex/gdpr_admin/test_coverage)
9
10
  [![Gem Version](https://badge.fury.io/rb/gdpr_admin.svg)](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: SyncOrganizationsContactsJob.to_s,
223
+ class: 'GdprAdmin::DataRetentionPoliciesRunnerJob',
159
224
  cron: '0 2 * * *',
160
- name: 'Sync Organization Contacts',
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
- GdprAdmin.config.tenant_adapter.with_tenant(tenant) { process_policies }
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
- policy = policy_class.new(self)
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
- raise SkipDataPolicyError
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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GdprAdmin
4
+ module Helpers
5
+ module DataPolicyHelper
6
+ def skip_data_policy!
7
+ raise SkipDataPolicyError
8
+ end
9
+
10
+ def skip_record!
11
+ raise SkipRecordError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GdprAdmin
4
+ module Helpers
5
+ module ScopeHelper
6
+ def scope_by_date(scope, field = :updated_at)
7
+ scope.where(field => ...request.data_older_than)
8
+ end
9
+ end
10
+ end
11
+ 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
- return if item_fields.nil?
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GdprAdmin
4
+ class SkipRecordError < StandardError; end
5
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  # :nocov:
4
4
  module GdprAdmin
5
- VERSION = '1.2.0'
5
+ VERSION = '1.4.0'
6
6
  end
7
7
  # :nocov:
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.2.0
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-02-16 00:00:00.000000000 Z
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