rails_eu_gdpr 0.0.4
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 +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +106 -0
- data/Rakefile +22 -0
- data/app/assets/config/eu_gdpr_manifest.js +2 -0
- data/app/assets/javascripts/eu_gdpr.js +1 -0
- data/app/assets/javascripts/eu_gdpr/application.js +13 -0
- data/app/assets/javascripts/eu_gdpr/application/keep.js +62 -0
- data/app/assets/stylesheets/eu_gdpr/application.css +15 -0
- data/app/builders/eu_gdpr/personal_data_hash_builder.rb +47 -0
- data/app/concerns/model/eu_gdpr/personal_data_concern.rb +36 -0
- data/app/controllers/eu_gdpr/application_controller.rb +5 -0
- data/app/controllers/eu_gdpr/privacy_policies_controller.rb +10 -0
- data/app/helpers/eu_gdpr/application_helper.rb +13 -0
- data/app/jobs/eu_gdpr/application_job.rb +4 -0
- data/app/mailers/eu_gdpr/application_mailer.rb +6 -0
- data/app/models/eu_gdpr/application_record.rb +5 -0
- data/app/models/eu_gdpr/personal_data.rb +57 -0
- data/app/resolvers/eu_gdpr/privacy_policy_resolver.rb +4 -0
- data/app/views/eu_gdpr/cookies/_consent_banner.html.erb +40 -0
- data/app/views/layouts/eu_gdpr/application.html.erb +16 -0
- data/config/initializers/add_personal_data_attributes_to_logging_filter.rb +3 -0
- data/config/initializers/assets.rb +3 -0
- data/config/initializers/enforce_ssl.rb +3 -0
- data/config/initializers/inject_models.rb +7 -0
- data/config/initializers/show_status.rb +15 -0
- data/config/locales/de.yml +7 -0
- data/config/locales/en.yml +7 -0
- data/config/routes.rb +21 -0
- data/lib/active_model/model.rb +98 -0
- data/lib/eu_gdpr.rb +10 -0
- data/lib/eu_gdpr/configuration.rb +37 -0
- data/lib/eu_gdpr/engine.rb +5 -0
- data/lib/eu_gdpr/personal_data_registry.rb +38 -0
- data/lib/eu_gdpr/version.rb +3 -0
- data/lib/generators/eu_gdpr/install/install_generator.rb +17 -0
- data/lib/generators/eu_gdpr/install/templates/initializer.rb +46 -0
- data/lib/generators/eu_gdpr/install/templates/routes.source +2 -0
- data/lib/rails_eu_gdpr.rb +1 -0
- data/lib/tasks/eu_gdpr_tasks.rake +4 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NGY1MzY5NWYyM2Y0YmEwMjQ3YmRiMjkzMGYxYjk0ZDRlODQzOGNmYQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NjI2MTI4OWRiOWUwY2ZiZTRmZDliZDY2MzQ2NDdkMzVmZmUxOWUxNg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ODkxNjhhMDI5Yjg5ODQ1MzVmMjM4MzQzOGI1MDg1MGQ2NDIzMDY0MTgwM2E5
|
10
|
+
OGFhYTExYzI0ZTI4ZTE5NzIwYTBlOTcyZTVhMGI5YWY5NjQ2NzYzYjAyOGJh
|
11
|
+
MWUyODFkZDdlNzZjZTlmZTdiZmQzMzY0MGRjYjJhOTBmYzgwYmY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OGJlYzE3ZDc5NzcxNmQ0MDlhN2RlNjFiYzk1YjFlOWI4MjVjMTRiNGRjZDI4
|
14
|
+
MWE3NWE3NTMxNTk1ODlmMzA4MjNkMTU2ZTFlMDI3YTI1MTc5ZTdjMWU0Mzcz
|
15
|
+
YmQ2ODQzOWI1ZGZkYjRjZmRjYTFlNGQwYmZkMjdmZmU2MjczODg=
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018 Roberto Vasquez Angel
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# EuGdpr
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
|
6
|
+
## Displaying the eu cookie banner
|
7
|
+
|
8
|
+
# app/assets/javascripts/application.js
|
9
|
+
//= require eu_gdpr
|
10
|
+
|
11
|
+
# app/controllers/application_controller.rb
|
12
|
+
helper EuGdpr::ApplicationHelper
|
13
|
+
|
14
|
+
# app/views/layouts/application.html.erb
|
15
|
+
<%= render_cookie_consent_banner(link: eu_gdpr.privacy_policy_path) %>
|
16
|
+
|
17
|
+
## Registering personal data
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
EuGdpr.personal_data(Ecm::UserArea::User, log_removals: true, forget_with: :anonymization) do |u|
|
21
|
+
u.attribute(:email, anonymize_with: :scrambler)
|
22
|
+
u.attribute(:firstname, anonymize_with: :scrambler)
|
23
|
+
u.attribute(:lastname, anonymize_with: :scrambler)
|
24
|
+
u.attribute(:last_ip, anonymize_with: :nullifier)
|
25
|
+
u.association(:posts) do |p|
|
26
|
+
p.attribute(:title)
|
27
|
+
p.attribute(:body)
|
28
|
+
p.association(:gallery) do |g|
|
29
|
+
g.attribute(:name)
|
30
|
+
g.association(:pictures) do |p|
|
31
|
+
p.attribute(:title)
|
32
|
+
p.attribute(:asset) { |r| r.base64_encoded_asset }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
EuGdpr.personal_data(Ecm::Contact::ContactRequest, log_removals: true, forget_with: :deletion) do |r|
|
41
|
+
r.attribute(:firstname)
|
42
|
+
r.attribute(:lastname)
|
43
|
+
r.attribute(:title)
|
44
|
+
r.attribute(:body)
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
## How do I show the structure of registered personal data?
|
49
|
+
|
50
|
+
- EuGdpr.personal_data.each do |pd|
|
51
|
+
%h2= pd.root
|
52
|
+
= ap(pd.to_hash.as_json).html_safe
|
53
|
+
|
54
|
+
## Features
|
55
|
+
|
56
|
+
* Checks for SSL in production
|
57
|
+
* Adds sensible attribute log filtering (customizable)
|
58
|
+
* EU Cookie Message
|
59
|
+
|
60
|
+
## Installation
|
61
|
+
Add this line to your application's Gemfile:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
gem 'eu_gdpr'
|
65
|
+
```
|
66
|
+
|
67
|
+
And then execute:
|
68
|
+
```bash
|
69
|
+
$ bundle
|
70
|
+
```
|
71
|
+
|
72
|
+
Or install it yourself as:
|
73
|
+
```bash
|
74
|
+
$ gem install eu_gdpr
|
75
|
+
```
|
76
|
+
|
77
|
+
Add the initializer:
|
78
|
+
|
79
|
+
```bash
|
80
|
+
$ rails g eu_gdpr:install
|
81
|
+
```
|
82
|
+
|
83
|
+
## Upgrading to 0.0.3
|
84
|
+
|
85
|
+
Remove config.privacy_policy_defaults from config/initializers/eu_gdpr.rb as this options is not needed anymore.
|
86
|
+
|
87
|
+
## Contributing
|
88
|
+
Contribution directions go here.
|
89
|
+
|
90
|
+
## License
|
91
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
92
|
+
|
93
|
+
## TODO
|
94
|
+
|
95
|
+
* Personal Data Export
|
96
|
+
* Right to Forget
|
97
|
+
* Add cookie consent levels (i.e.required, marketing, etc.)
|
98
|
+
|
99
|
+
## Unsorted notes
|
100
|
+
|
101
|
+
Model::Gdpr::PersonalDataConcern#gdpr_forget!
|
102
|
+
Model::Gdpr::PersonalDataConcern#gdpr_export(format: :json)
|
103
|
+
|
104
|
+
Gdpr::Anonymizer::Base
|
105
|
+
Gdpr::Anonymizer::Scrambler
|
106
|
+
Gdpr::Anonymizer::Nullifier
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'EuGdpr'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./eu_gdpr/application
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree ./application
|
@@ -0,0 +1,62 @@
|
|
1
|
+
//= require js.cookie
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
var windowIsTurbolinked = 'Turbolinks' in window;
|
5
|
+
|
6
|
+
var cookiesEu = {
|
7
|
+
init: function() {
|
8
|
+
var cookiesEuOKButton = document.querySelector('.js-cookies-eu-ok');
|
9
|
+
|
10
|
+
if (cookiesEuOKButton) {
|
11
|
+
this.addListener(cookiesEuOKButton);
|
12
|
+
// clear turbolinks cache so cookie banner does not reappear
|
13
|
+
windowIsTurbolinked && window.Turbolinks.clearCache();
|
14
|
+
}
|
15
|
+
},
|
16
|
+
|
17
|
+
addListener: function(target) {
|
18
|
+
// Support for IE < 9
|
19
|
+
if (target.attachEvent) {
|
20
|
+
target.attachEvent('onclick', this.setCookie);
|
21
|
+
} else {
|
22
|
+
target.addEventListener('click', this.setCookie, false);
|
23
|
+
}
|
24
|
+
},
|
25
|
+
|
26
|
+
setCookie: function() {
|
27
|
+
Cookies.set('cookie_eu_consented', true, { path: '/', expires: 365 });
|
28
|
+
|
29
|
+
var container = document.querySelector('.js-cookies-eu');
|
30
|
+
container.parentNode.removeChild(container);
|
31
|
+
}
|
32
|
+
};
|
33
|
+
|
34
|
+
(function() {
|
35
|
+
function eventName(fallback) {
|
36
|
+
return windowIsTurbolinked ? 'turbolinks:load' : fallback
|
37
|
+
}
|
38
|
+
|
39
|
+
var isCalled = false;
|
40
|
+
|
41
|
+
function isReady() {
|
42
|
+
// return early when cookiesEu.init has been called AND Turbolinks is NOT included
|
43
|
+
// when Turbolinks is included cookiesEu.init has to be called on every page load
|
44
|
+
if (isCalled && !windowIsTurbolinked) {
|
45
|
+
return
|
46
|
+
}
|
47
|
+
isCalled = true;
|
48
|
+
|
49
|
+
cookiesEu.init();
|
50
|
+
}
|
51
|
+
|
52
|
+
if (document.addEventListener) {
|
53
|
+
return document.addEventListener(eventName('DOMContentLoaded'), isReady, false);
|
54
|
+
}
|
55
|
+
|
56
|
+
// Old browsers IE < 9
|
57
|
+
if (window.addEventListener) {
|
58
|
+
window.addEventListener(eventName('load'), isReady, false);
|
59
|
+
} else if (window.attachEvent) {
|
60
|
+
window.attachEvent(eventName('onload'), isReady);
|
61
|
+
}
|
62
|
+
})();
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
class PersonalDataHashBuilder
|
3
|
+
include ActiveModel::Model
|
4
|
+
attr_accessor :root, :options, :block
|
5
|
+
|
6
|
+
def initialize(attributes, options = {})
|
7
|
+
super(attributes)
|
8
|
+
@builder_options = options
|
9
|
+
invoke_block
|
10
|
+
end
|
11
|
+
|
12
|
+
def attribute(name, options = {})
|
13
|
+
if with_options?
|
14
|
+
hash[:attributes][name] = { :options => options }
|
15
|
+
else
|
16
|
+
hash[:attributes] << name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def association(name, options = {}, &block)
|
21
|
+
if with_options?
|
22
|
+
hash[:associations][name] = { :options => options, :attributes => EuGdpr::PersonalDataHashBuilder.new(:block => block).hash }
|
23
|
+
else
|
24
|
+
hash[name] = EuGdpr::PersonalDataHashBuilder.new({ :block => block}, @builder_options).hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def hash
|
29
|
+
if with_options?
|
30
|
+
@hash ||= { :root => root, :options => options, :attributes => {}, :associations => {} }
|
31
|
+
else
|
32
|
+
@hash ||= { :attributes => [] }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def invoke_block
|
39
|
+
self.block.call(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def with_options?
|
43
|
+
@builder_options[:with_options] = true if @builder_options[:with_options].nil?
|
44
|
+
@builder_options[:with_options]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Model
|
2
|
+
module EuGdpr
|
3
|
+
module PersonalDataConcern
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
# Backport class_methods method.
|
7
|
+
if Rails.version < '4.2'
|
8
|
+
def self.class_methods(&class_methods_module_definition)
|
9
|
+
mod = const_defined?(:ClassMethods) ?
|
10
|
+
const_get(:ClassMethods) :
|
11
|
+
const_set(:ClassMethods, Module.new)
|
12
|
+
|
13
|
+
mod.module_eval(&class_methods_module_definition)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class_methods do
|
18
|
+
def personal_data_attributes=(attribute_names)
|
19
|
+
@personal_data_attributes = attribute_names
|
20
|
+
end
|
21
|
+
|
22
|
+
def personal_data_attributes
|
23
|
+
@personal_data_attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
def gdpr_export_options=(options)
|
27
|
+
@gdpr_export_options = options
|
28
|
+
end
|
29
|
+
|
30
|
+
def gdpr_export_options
|
31
|
+
@gdpr_export_options ||= {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
class PrivacyPoliciesController < EuGdpr::Configuration.base_controller.constantize
|
3
|
+
if Gem.loaded_specs["ecm_cms"].present? || Gem.loaded_specs["ecm_cms2"].present?
|
4
|
+
prepend_view_path ::EuGdpr::PrivacyPolicyResolver.instance unless view_paths.include?(::EuGdpr::PrivacyPolicyResolver.instance)
|
5
|
+
end
|
6
|
+
|
7
|
+
def show
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
module ApplicationHelper
|
3
|
+
def render_cookie_consent_banner(options = {})
|
4
|
+
return unless EuGdpr::Configuration.enable_cookie_consent_banner
|
5
|
+
|
6
|
+
options.reverse_merge!(:link => eu_gdpr.privacy_policy_path)
|
7
|
+
|
8
|
+
unless url_for() == eu_gdpr.privacy_policy_path
|
9
|
+
render('eu_gdpr/cookies/consent_banner', options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
class PersonalData
|
3
|
+
include ActiveModel::Model
|
4
|
+
|
5
|
+
attr_accessor :root, :options, :block
|
6
|
+
|
7
|
+
def to_hash_with_options
|
8
|
+
@hash ||= build_hash_with_options!
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_hash_with_options!
|
12
|
+
EuGdpr::PersonalDataHashBuilder.new({ :root => root, :options => options, :block => block }, { :with_options => true }).hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
@hash ||= build_hash!
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_hash!
|
20
|
+
EuGdpr::PersonalDataHashBuilder.new({ :root => root, :options => options, :block => block }, { :with_options => false }).hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
EuGdpr::PersonalDataRegistry.all
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.count
|
28
|
+
EuGdpr::PersonalDataRegistry.count
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.attribute_names
|
32
|
+
[:root, :options, :block]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.find(id)
|
36
|
+
all.find {|x| x.root == id.gsub('-', '/').camelize }
|
37
|
+
end
|
38
|
+
|
39
|
+
def id
|
40
|
+
root.underscore.gsub('/', '-')
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_param
|
44
|
+
id
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
[self.class.model_name.human, root_model_human].compact.join(' - ')
|
49
|
+
end
|
50
|
+
|
51
|
+
def root_model_human
|
52
|
+
return if root.nil?
|
53
|
+
root.constantize.model_name.human
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<div id="cookies-eu-modal" class="modal fade cookies-eu js-cookies-eu" tabindex="-1" role="dialog">
|
2
|
+
<div class="modal-dialog" role="document">
|
3
|
+
<div class="modal-content">
|
4
|
+
<div class="modal-header">
|
5
|
+
<h4 class="modal-title">Information über Cookies auf dieser Website.</h4>
|
6
|
+
</div>
|
7
|
+
<div class="modal-body">
|
8
|
+
<div class="row">
|
9
|
+
<div class="col-lg-12 span6">
|
10
|
+
<p>Cookies helfen uns bei der Bereitstellung unserer Dienste. Durch die Nutzung unserer Dienste erklären Sie sich damit einverstanden, dass wir Cookies setzen.</p>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
<div class="row">
|
14
|
+
<div class="col-lg-6 span6">
|
15
|
+
<h5>Erforderliche Cookies</h5>
|
16
|
+
<p>Diese Cookies sind für die Kernfunktionen der Website erforderlich und werden automatisch aktiviert, wenn Sie diese Website nutzen.</p>
|
17
|
+
<p>
|
18
|
+
<% if defined?(link).present? %>
|
19
|
+
<a href="<%= link %>" class="btn btn-secondary btn-default cookies-eu-link" target="<%= defined?(target).present? ? target : '' %>">Mehr erfahren</a>
|
20
|
+
<% end %>
|
21
|
+
</p>
|
22
|
+
</div>
|
23
|
+
<div class="col-lg-6 span12">
|
24
|
+
<h5>Gestattete Funktionalität</h5>
|
25
|
+
<p>Speichern die Anmeldeinformationen und sorgen für sichere Anmeldung</p>
|
26
|
+
<p>Speichern Ihren Aufgaben- oder Transaktionsfortschritt</p>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<div class="modal-footer">
|
30
|
+
<button type="button" class="btn btn-primary cookies-eu-ok js-cookies-eu-ok" data-dismiss="modal">Ich stimme zu</button>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<% if cookies && cookies['cookie_eu_consented'] != 'true' %>
|
37
|
+
<script>
|
38
|
+
$('#cookies-eu-modal').modal({ backdrop: 'static', keyboard: false })
|
39
|
+
</script>
|
40
|
+
<% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Eu gdpr</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag "eu_gdpr/application", media: "all" %>
|
9
|
+
<%= javascript_include_tag "eu_gdpr/application" %>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<%= yield %>
|
14
|
+
|
15
|
+
</body>
|
16
|
+
</html>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
Rails.application.config.to_prepare do
|
2
|
+
EuGdpr::Configuration.personal_data_root_classes.call.each do |model, options|
|
3
|
+
model.send(:include, Model::EuGdpr::PersonalDataConcern)
|
4
|
+
model.personal_data_attributes = options[:personal_data_attributes]
|
5
|
+
model.gdpr_export_options = { :only => options[:personal_data_attributes] }
|
6
|
+
end
|
7
|
+
end if EuGdpr::Configuration.personal_data_root_classes.respond_to?(:call)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Rails.application.config.after_initialize do
|
2
|
+
if EuGdpr::Configuration.enforce_ssl?
|
3
|
+
puts "[EU GDPR] Traffic encryption check (via force_ssl) => [OK]"
|
4
|
+
else
|
5
|
+
puts "[EU GDPR] Warning: Traffic encryption check (via force_ssl) => [FALSE]"
|
6
|
+
end
|
7
|
+
|
8
|
+
if EuGdpr::Configuration.enable_cookie_consent_banner?
|
9
|
+
puts "[EU GDPR] Cookie consent banner enabled? => [OK]"
|
10
|
+
else
|
11
|
+
puts "[EU GDPR] Warning: Cookie consent banner enabled? => [FALSE]"
|
12
|
+
end
|
13
|
+
|
14
|
+
puts "[EU GDPR] Filtered personal data attributes => #{(EuGdpr::Configuration.filter_personal_data_attributes || []).join(", ")}"
|
15
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
EuGdpr::Engine.routes.draw do
|
2
|
+
if Gem.loaded_specs["route_translator"].present?
|
3
|
+
localized do
|
4
|
+
resource :privacy_policy, :only => [:show]
|
5
|
+
scope :eu_gdpr_engine do
|
6
|
+
end
|
7
|
+
end
|
8
|
+
elsif Gem.loaded_specs["i18n_routing"].present?
|
9
|
+
localized(I18n.available_locales) do
|
10
|
+
scope "/:i18n_locale", :constraints => {:i18n_locale => /#{I18n.available_locales.join('|')}/} do
|
11
|
+
resource :privacy_policy, :only => [:show]
|
12
|
+
scope :eu_gdpr_engine do
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
else
|
17
|
+
resource :privacy_policy, :only => [:show]
|
18
|
+
scope :eu_gdpr_engine do
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
|
3
|
+
# == Active Model Basic Model
|
4
|
+
#
|
5
|
+
# Includes the required interface for an object to interact with
|
6
|
+
# <tt>ActionPack</tt>, using different <tt>ActiveModel</tt> modules.
|
7
|
+
# It includes model name introspections, conversions, translations and
|
8
|
+
# validations. Besides that, it allows you to initialize the object with a
|
9
|
+
# hash of attributes, pretty much like <tt>ActiveRecord</tt> does.
|
10
|
+
#
|
11
|
+
# A minimal implementation could be:
|
12
|
+
#
|
13
|
+
# class Person
|
14
|
+
# include ActiveModel::Model
|
15
|
+
# attr_accessor :name, :age
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# person = Person.new(name: 'bob', age: '18')
|
19
|
+
# person.name # => 'bob'
|
20
|
+
# person.age # => 18
|
21
|
+
#
|
22
|
+
# Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
|
23
|
+
# to return +false+, which is the most common case. You may want to override
|
24
|
+
# it in your class to simulate a different scenario:
|
25
|
+
#
|
26
|
+
# class Person
|
27
|
+
# include ActiveModel::Model
|
28
|
+
# attr_accessor :id, :name
|
29
|
+
#
|
30
|
+
# def persisted?
|
31
|
+
# self.id == 1
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# person = Person.new(id: 1, name: 'bob')
|
36
|
+
# person.persisted? # => true
|
37
|
+
#
|
38
|
+
# Also, if for some reason you need to run code on <tt>initialize</tt>, make
|
39
|
+
# sure you call +super+ if you want the attributes hash initialization to
|
40
|
+
# happen.
|
41
|
+
#
|
42
|
+
# class Person
|
43
|
+
# include ActiveModel::Model
|
44
|
+
# attr_accessor :id, :name, :omg
|
45
|
+
#
|
46
|
+
# def initialize(attributes={})
|
47
|
+
# super
|
48
|
+
# @omg ||= true
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# person = Person.new(id: 1, name: 'bob')
|
53
|
+
# person.omg # => true
|
54
|
+
#
|
55
|
+
# For more detailed information on other functionalities available, please
|
56
|
+
# refer to the specific modules included in <tt>ActiveModel::Model</tt>
|
57
|
+
# (see below).
|
58
|
+
module Model
|
59
|
+
def self.included(base) #:nodoc:
|
60
|
+
base.class_eval do
|
61
|
+
extend ActiveModel::Naming
|
62
|
+
extend ActiveModel::Translation
|
63
|
+
include ActiveModel::Validations
|
64
|
+
include ActiveModel::Conversion
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Initializes a new model with the given +params+.
|
69
|
+
#
|
70
|
+
# class Person
|
71
|
+
# include ActiveModel::Model
|
72
|
+
# attr_accessor :name, :age
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# person = Person.new(name: 'bob', age: '18')
|
76
|
+
# person.name # => "bob"
|
77
|
+
# person.age # => 18
|
78
|
+
def initialize(params={})
|
79
|
+
params.each do |attr, value|
|
80
|
+
# self.public_send("#{attr}=", value)
|
81
|
+
self.send("#{attr}=", value)
|
82
|
+
end if params
|
83
|
+
end
|
84
|
+
|
85
|
+
# Indicates if the model is persisted. Default is +false+.
|
86
|
+
#
|
87
|
+
# class Person
|
88
|
+
# include ActiveModel::Model
|
89
|
+
# attr_accessor :id, :name
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# person = Person.new(id: 1, name: 'bob')
|
93
|
+
# person.persisted? # => false
|
94
|
+
def persisted?
|
95
|
+
false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/eu_gdpr.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
module Configuration
|
3
|
+
def configure
|
4
|
+
yield self
|
5
|
+
end
|
6
|
+
|
7
|
+
mattr_accessor(:base_controller) { Proc.new {{}} }
|
8
|
+
mattr_accessor(:personal_data_root_classes) { Proc.new {{}} }
|
9
|
+
mattr_accessor(:filter_personal_data_attributes) { [] }
|
10
|
+
mattr_accessor(:enforce_ssl) { true }
|
11
|
+
mattr_accessor(:enable_cookie_consent_banner) { true }
|
12
|
+
|
13
|
+
def personal_data
|
14
|
+
@personal_data ||= ::EuGdpr::PersonalDataRegistry.instance
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.enforce_ssl?
|
18
|
+
enforce_ssl
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.enable_cookie_consent_banner?
|
22
|
+
enable_cookie_consent_banner
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.privacy_policy_defaults_for(locale)
|
26
|
+
privacy_policy_defaults[locale.to_sym]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.privacy_policy_available_for(locale)
|
30
|
+
EuGdpr::PrivacyPolicy.where(:locale => locale).any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.filtered_log_parameters
|
34
|
+
Rails.application.config.filter_parameters
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
class PersonalDataRegistry
|
3
|
+
extend ActiveModel::Translation
|
4
|
+
extend ActiveModel::Naming
|
5
|
+
|
6
|
+
attr_accessor :personal_data
|
7
|
+
|
8
|
+
delegate :first, :last, :[], :each, :map, :collect, :to => :personal_data
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@personal_data = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.instance
|
15
|
+
@@instance
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.all
|
19
|
+
instance.personal_data
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.count
|
23
|
+
instance.personal_data.size
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.attribute_names
|
27
|
+
[:personal_data]
|
28
|
+
end
|
29
|
+
|
30
|
+
def register(root, options, &block)
|
31
|
+
self.personal_data << PersonalData.new(:root => root, :options => options, :block => block)
|
32
|
+
end
|
33
|
+
|
34
|
+
@@instance = EuGdpr::PersonalDataRegistry.new
|
35
|
+
|
36
|
+
private_class_method :new
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module EuGdpr
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
desc 'Installs the initializer and routes'
|
5
|
+
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def generate_initializer
|
9
|
+
copy_file 'initializer.rb', 'config/initializers/eu_gdpr.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_routes
|
13
|
+
route File.read(File.join(File.expand_path('../templates', __FILE__), 'routes.source'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
Rails.application.config.to_prepare do
|
2
|
+
EuGdpr.configure do |config|
|
3
|
+
# Set the base controller
|
4
|
+
#
|
5
|
+
# Default: config.base_controller = 'FrontendController'
|
6
|
+
#
|
7
|
+
config.base_controller = '::Frontend::ApplicationController'
|
8
|
+
|
9
|
+
# Add these attributes to the rails logging filter
|
10
|
+
#
|
11
|
+
# default: config.filter_personal_data_attributes = [:email, :firstname, :lastname, :birthdate]
|
12
|
+
#
|
13
|
+
config.filter_personal_data_attributes = [:email, :firstname, :lastname, :birthdate]
|
14
|
+
|
15
|
+
# If set to true and force_ssl is not set to true in production it will raise
|
16
|
+
# an exception when trying to boot rails.
|
17
|
+
#
|
18
|
+
# default: config.enforce_ssl = true
|
19
|
+
#
|
20
|
+
config.enforce_ssl = true
|
21
|
+
|
22
|
+
# Enables or disables the cookie message.
|
23
|
+
#
|
24
|
+
# default: config.enable_cookie_consent_banner = true
|
25
|
+
#
|
26
|
+
config.enable_cookie_consent_banner = true
|
27
|
+
|
28
|
+
# config.personal_data.register('User', log_removals: true, forget_with: :anonymization) do |u|
|
29
|
+
# u.attribute(:email, anonymize_with: :scrambler)
|
30
|
+
# u.attribute(:firstname, anonymize_with: :scrambler)
|
31
|
+
# u.attribute(:lastname, anonymize_with: :scrambler)
|
32
|
+
# u.attribute(:last_ip, anonymize_with: :nullifier)
|
33
|
+
# u.association(:posts) do |p|
|
34
|
+
# p.attribute(:title)
|
35
|
+
# p.attribute(:body)
|
36
|
+
# p.association(:gallery) do |g|
|
37
|
+
# g.attribute(:name)
|
38
|
+
# g.association(:pictures) do |p|
|
39
|
+
# p.attribute(:title)
|
40
|
+
# p.attribute(:asset) { |r| r.base64_encoded_asset }
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'eu_gdpr'
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_eu_gdpr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roberto Vasquez Angel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: awesome_print
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cookies_eu
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- roberto@vasquez-angel.de
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- MIT-LICENSE
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- app/assets/config/eu_gdpr_manifest.js
|
80
|
+
- app/assets/javascripts/eu_gdpr.js
|
81
|
+
- app/assets/javascripts/eu_gdpr/application.js
|
82
|
+
- app/assets/javascripts/eu_gdpr/application/keep.js
|
83
|
+
- app/assets/stylesheets/eu_gdpr/application.css
|
84
|
+
- app/builders/eu_gdpr/personal_data_hash_builder.rb
|
85
|
+
- app/concerns/model/eu_gdpr/personal_data_concern.rb
|
86
|
+
- app/controllers/eu_gdpr/application_controller.rb
|
87
|
+
- app/controllers/eu_gdpr/privacy_policies_controller.rb
|
88
|
+
- app/helpers/eu_gdpr/application_helper.rb
|
89
|
+
- app/jobs/eu_gdpr/application_job.rb
|
90
|
+
- app/mailers/eu_gdpr/application_mailer.rb
|
91
|
+
- app/models/eu_gdpr/application_record.rb
|
92
|
+
- app/models/eu_gdpr/personal_data.rb
|
93
|
+
- app/resolvers/eu_gdpr/privacy_policy_resolver.rb
|
94
|
+
- app/views/eu_gdpr/cookies/_consent_banner.html.erb
|
95
|
+
- app/views/layouts/eu_gdpr/application.html.erb
|
96
|
+
- config/initializers/add_personal_data_attributes_to_logging_filter.rb
|
97
|
+
- config/initializers/assets.rb
|
98
|
+
- config/initializers/enforce_ssl.rb
|
99
|
+
- config/initializers/inject_models.rb
|
100
|
+
- config/initializers/show_status.rb
|
101
|
+
- config/locales/de.yml
|
102
|
+
- config/locales/en.yml
|
103
|
+
- config/routes.rb
|
104
|
+
- lib/active_model/model.rb
|
105
|
+
- lib/eu_gdpr.rb
|
106
|
+
- lib/eu_gdpr/configuration.rb
|
107
|
+
- lib/eu_gdpr/engine.rb
|
108
|
+
- lib/eu_gdpr/personal_data_registry.rb
|
109
|
+
- lib/eu_gdpr/version.rb
|
110
|
+
- lib/generators/eu_gdpr/install/install_generator.rb
|
111
|
+
- lib/generators/eu_gdpr/install/templates/initializer.rb
|
112
|
+
- lib/generators/eu_gdpr/install/templates/routes.source
|
113
|
+
- lib/rails_eu_gdpr.rb
|
114
|
+
- lib/tasks/eu_gdpr_tasks.rake
|
115
|
+
homepage: https://github.com/robotex82/rails_eu_gdpr
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata: {}
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ! '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 2.4.3
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: Simple EU GDPR (DSGVO) compliance for rails applications.
|
139
|
+
test_files: []
|