rails_eu_gdpr 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +106 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/eu_gdpr_manifest.js +2 -0
  6. data/app/assets/javascripts/eu_gdpr.js +1 -0
  7. data/app/assets/javascripts/eu_gdpr/application.js +13 -0
  8. data/app/assets/javascripts/eu_gdpr/application/keep.js +62 -0
  9. data/app/assets/stylesheets/eu_gdpr/application.css +15 -0
  10. data/app/builders/eu_gdpr/personal_data_hash_builder.rb +47 -0
  11. data/app/concerns/model/eu_gdpr/personal_data_concern.rb +36 -0
  12. data/app/controllers/eu_gdpr/application_controller.rb +5 -0
  13. data/app/controllers/eu_gdpr/privacy_policies_controller.rb +10 -0
  14. data/app/helpers/eu_gdpr/application_helper.rb +13 -0
  15. data/app/jobs/eu_gdpr/application_job.rb +4 -0
  16. data/app/mailers/eu_gdpr/application_mailer.rb +6 -0
  17. data/app/models/eu_gdpr/application_record.rb +5 -0
  18. data/app/models/eu_gdpr/personal_data.rb +57 -0
  19. data/app/resolvers/eu_gdpr/privacy_policy_resolver.rb +4 -0
  20. data/app/views/eu_gdpr/cookies/_consent_banner.html.erb +40 -0
  21. data/app/views/layouts/eu_gdpr/application.html.erb +16 -0
  22. data/config/initializers/add_personal_data_attributes_to_logging_filter.rb +3 -0
  23. data/config/initializers/assets.rb +3 -0
  24. data/config/initializers/enforce_ssl.rb +3 -0
  25. data/config/initializers/inject_models.rb +7 -0
  26. data/config/initializers/show_status.rb +15 -0
  27. data/config/locales/de.yml +7 -0
  28. data/config/locales/en.yml +7 -0
  29. data/config/routes.rb +21 -0
  30. data/lib/active_model/model.rb +98 -0
  31. data/lib/eu_gdpr.rb +10 -0
  32. data/lib/eu_gdpr/configuration.rb +37 -0
  33. data/lib/eu_gdpr/engine.rb +5 -0
  34. data/lib/eu_gdpr/personal_data_registry.rb +38 -0
  35. data/lib/eu_gdpr/version.rb +3 -0
  36. data/lib/generators/eu_gdpr/install/install_generator.rb +17 -0
  37. data/lib/generators/eu_gdpr/install/templates/initializer.rb +46 -0
  38. data/lib/generators/eu_gdpr/install/templates/routes.source +2 -0
  39. data/lib/rails_eu_gdpr.rb +1 -0
  40. data/lib/tasks/eu_gdpr_tasks.rake +4 -0
  41. metadata +139 -0
@@ -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=
@@ -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.
@@ -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
@@ -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,2 @@
1
+ //= link_directory ../javascripts/eu_gdpr .js
2
+ //= link_directory ../stylesheets/eu_gdpr .css
@@ -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,5 @@
1
+ module EuGdpr
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery :with => :exception
4
+ end
5
+ 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,4 @@
1
+ module EuGdpr
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end if Rails.version >= '4.2'
@@ -0,0 +1,6 @@
1
+ module EuGdpr
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default :from => 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module EuGdpr
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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,4 @@
1
+ module EuGdpr
2
+ class PrivacyPolicyResolver < Ecm::Cms::PageResolver
3
+ end
4
+ end if Gem.loaded_specs["ecm_cms2"].present?
@@ -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,3 @@
1
+ Rails.application.config.after_initialize do
2
+ Rails.application.config.filter_parameters += EuGdpr::Configuration.filter_personal_data_attributes
3
+ end if EuGdpr::Configuration.filter_personal_data_attributes.is_a?(Array)
@@ -0,0 +1,3 @@
1
+ Rails.application.config.assets.precompile += %w(
2
+ eu_gdpr.js
3
+ )
@@ -0,0 +1,3 @@
1
+ if Rails.env.production? && EuGdpr::Configuration.enforce_ssl?
2
+ raise "[EU GDPR] Legal regulations require enabled ssl (via force_ssl), but force_ssl is not set to true for production environment." unless Rails.application.config.force_ssl == true
3
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ de:
2
+ # i18n_routing
3
+ resource:
4
+ privacy_policy: datenschutzerklaerung
5
+ # route_translator
6
+ routes:
7
+ privacy_policy: datenschutzerklaerung
@@ -0,0 +1,7 @@
1
+ en:
2
+ # i18n_routing
3
+ resource:
4
+ privacy_policy: privacy-policy
5
+ # route_translator
6
+ routes:
7
+ privacy_policy: privacy-policy
@@ -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
@@ -0,0 +1,10 @@
1
+ require "awesome_print"
2
+ require "cookies_eu"
3
+ require "eu_gdpr/configuration"
4
+ require "eu_gdpr/engine"
5
+ require "active_model/model" if Rails::VERSION::MAJOR < 4
6
+ require "eu_gdpr/personal_data_registry"
7
+
8
+ module EuGdpr
9
+ extend Configuration
10
+ end
@@ -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,5 @@
1
+ module EuGdpr
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace EuGdpr
4
+ end
5
+ 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,3 @@
1
+ module EuGdpr
2
+ VERSION = '0.0.4'.freeze
3
+ 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,2 @@
1
+
2
+ mount EuGdpr::Engine, :at => '/'
@@ -0,0 +1 @@
1
+ require 'eu_gdpr'
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :eu_gdpr do
3
+ # # Task goes here
4
+ # end
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: []