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.
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: []