tidus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14edabf3b34700e5d9ab484afd54af8348604833
4
+ data.tar.gz: 7df5e0603faff6661bebeb09ead1dd3442c1c8f2
5
+ SHA512:
6
+ metadata.gz: 02cde5036ec4facd91e9ac0b4dc1e3dc97394a25ffd745ea31d070475b33223be0d9268fd5fb40ecfd6d5fe3a8dc059cb6a0d5042660fe6434b7de2b43c26480
7
+ data.tar.gz: c5c883a7aeefd19482c566cdaf367956362f17e013856320b9db3066cba8c03b8ba9fed314169b8689a973218912ff04088ff9e0a2ce3075a5e1730067a88e55
@@ -0,0 +1,43 @@
1
+ namespace :db do
2
+ desc "Clears all the views which are currently existing"
3
+ task :clear_views do
4
+ Rails.application.eager_load! if defined?(Rails)
5
+ ActiveRecord::Base.descendants.each do |c|
6
+ next if c.table_name == "schema_migrations"
7
+ puts "Clearing view '#{c.view_name}' for table '#{c.table_name}'"
8
+
9
+ ActiveRecord::Base.connection.execute(
10
+ "DROP VIEW IF EXISTS #{c.view_name}"
11
+ )
12
+ end
13
+ end
14
+
15
+ desc "Generates all the views for the models"
16
+ task :generate_views do
17
+ Rails.application.eager_load! if defined?(Rails)
18
+ ActiveRecord::Base.descendants.each do |c|
19
+ next if c.table_name == "schema_migrations"
20
+
21
+ if ActiveRecord::Base.connection.table_exists? c.table_name
22
+ puts "Generating view '#{c.view_name}' for table '#{c.table_name}'"
23
+
24
+ ActiveRecord::Base.connection.execute(
25
+ "CREATE VIEW #{c.view_name} AS " +
26
+ "(SELECT #{c.view_columns.join(', ')} " +
27
+ "FROM #{c.table_name})"
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ Rake::Task["db:migrate"].enhance ["db:clear_views"]
35
+ Rake::Task["db:rollback"].enhance ["db:clear_views"]
36
+
37
+ Rake::Task["db:migrate"].enhance do
38
+ Rake::Task["db:generate_views"].invoke
39
+ end
40
+
41
+ Rake::Task["db:rollback"].enhance do
42
+ Rake::Task["db:generate_views"].invoke
43
+ end
@@ -0,0 +1,63 @@
1
+ module Tidus
2
+ module Anonymization
3
+ def view_postfix
4
+ @view_postfix || "anonymized"
5
+ end
6
+
7
+ def view_postfix=(val)
8
+ @view_postfix = val
9
+ end
10
+
11
+ def view_name
12
+ @view_name || "#{table_name}_#{view_postfix}"
13
+ end
14
+
15
+ def view_name=(val)
16
+ @view_name = val
17
+ end
18
+
19
+ def view_columns
20
+ @view_columns ||= {}
21
+ default_view_columns.merge(@view_columns)
22
+ .map{ |k,v| ["#{v} AS #{k}"] }
23
+ .flatten
24
+ end
25
+
26
+ def default_view_columns
27
+ defaults = {}
28
+ column_names.each do |column|
29
+ defaults[column.to_sym] = "#{table_name}.#{column}"
30
+ end
31
+ defaults
32
+ end
33
+
34
+ def anonymizes(*attributes)
35
+ @view_columns ||= {}
36
+
37
+ options = attributes.extract_options!.dup
38
+ columns = attributes - [options]
39
+
40
+ raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
41
+ raise ArgumentError, "You need to supply a strategy" if options[:strategy].blank?
42
+
43
+ columns.each do |column|
44
+ key = options[:strategy].to_s.camelize
45
+
46
+ begin
47
+ if key.include?('::')
48
+ klass = key.constantize
49
+ else
50
+ klass = const_get("ActiveRecordAnonymize::#{key}Anonymizer")
51
+ end
52
+ rescue NameError
53
+ raise ArgumentError, "Unknown anonymizer: '#{key}'"
54
+ end
55
+
56
+ @view_columns[column.to_sym] = klass.anonymize(table_name, column, options)
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ ActiveRecord::Base.extend Tidus::Anonymization
@@ -0,0 +1,44 @@
1
+ module Tidus
2
+ class CondAnonymizer
3
+ def self.anonymize(table_name, column_name, options = {})
4
+ adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
5
+ case adapter
6
+ when "postgresql"
7
+ name = "#{table_name}.#{column_name}"
8
+
9
+ type = options[:result_type] || "text"
10
+ default = options[:default].nil? ? "#{name}::#{type}" : "'#{options[:default]}'::#{type}"
11
+
12
+ if options[:conditions].blank?
13
+ raise "Missing option :conditions for CondAnonymizer on #{name}"
14
+ elsif options[:conditions].kind_of?(Array)
15
+ conditions = options[:conditions]
16
+ else
17
+ conditions = [options[:conditions]]
18
+ end
19
+
20
+ command = "CASE "
21
+
22
+ conditions.each do |cond|
23
+ raise ":column for condition must be set" if cond[:column].blank?
24
+ raise ":value for condition must be set" if cond[:value].nil?
25
+ raise ":result for condition must be set" if cond[:result].nil?
26
+ cond_column = cond[:column]
27
+ cond_value = cond[:value]
28
+ cond_type = cond[:type] || "text"
29
+ comparator = cond[:comparator] || "="
30
+ cond_result = cond[:result]
31
+
32
+ command += "WHEN ((#{table_name}.#{cond_column})::#{cond_type} #{comparator} " +
33
+ "'#{cond_value}'::#{cond_type}) THEN '#{cond_result}'::#{type} "
34
+ end
35
+
36
+ command += "ELSE #{default} END"
37
+
38
+ return command
39
+ else
40
+ raise "#{self.name} not implemented for #{adapter}"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ module Tidus
2
+ class EmailAnonymizer
3
+ def self.anonymize(table_name, column_name, options = {})
4
+ adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
5
+ case adapter
6
+ when "postgresql"
7
+ name = "#{table_name}.#{column_name}"
8
+ options[:length] ||= 15
9
+
10
+ return "CASE WHEN ((#{name})::text ~~ '%@%'::text) " +
11
+ "THEN (((\"left\"(md5((#{name})::text), #{options[:length]}) || '@'::text) " +
12
+ "|| split_part((#{name})::text, '@'::text, 2)))::character varying " +
13
+ "ELSE #{name} END"
14
+ else
15
+ raise "#{self.name} not implemented for #{adapter}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,13 @@
1
+ module Tidus
2
+ class NullAnonymizer
3
+ def self.anonymize(table_name, column_name, options = {})
4
+ adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
5
+ case adapter
6
+ when "postgresql"
7
+ return "NULL::unknown"
8
+ else
9
+ raise "#{self.name} not implemented for #{adapter}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module Tidus
2
+ class OverlayAnonymizer
3
+ def self.anonymize(table_name, column_name, options = {})
4
+ adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
5
+ case adapter
6
+ when "postgresql"
7
+ name = "#{table_name}.#{column_name}"
8
+
9
+ raise "Missing option :start for OverlayAnonymizer on #{name}" if options[:start].blank?
10
+ raise "Missing option :length for OverlayAnonymizer on #{name}" if options[:length].blank?
11
+
12
+ overlay_char = options[:char] || "X"
13
+ overlay = overlay_char * options[:length]
14
+ return "\"overlay\"((#{name})::text, " +
15
+ "'#{overlay}'::text, #{options[:start]})"
16
+ else
17
+ raise "#{self.name} not implemented for #{adapter}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Tidus
2
+ class StaticAnonymizer
3
+ def self.anonymize(table_name, column_name, options = {})
4
+ adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
5
+ case adapter
6
+ when "postgresql"
7
+ raise "Missing option :value for StaticAnonymizer on #{table_name}.#{column_name}" if options[:value].blank?
8
+
9
+ return "'#{options[:value]}'"
10
+ else
11
+ raise "#{self.name} not implemented for #{adapter}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -0,0 +1,28 @@
1
+ module Tidus
2
+ class TextAnonymizer
3
+ def self.anonymize(table_name, column_name, options = {})
4
+ base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZCßüäöÜÄÖ"
5
+ adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
6
+ case adapter
7
+ when "postgresql"
8
+ return "translate((#{table_name}.#{column_name})::text, " +
9
+ "'#{base}'::text, '#{generate_mapping(base)}'::text)"
10
+ else
11
+ raise "#{self.name} not implemented for #{adapter}"
12
+ end
13
+ end
14
+
15
+ private
16
+ def self.generate_mapping(base)
17
+ result = ""
18
+ base.split("").each do |letter|
19
+ if letter == letter.upcase
20
+ result += ("A".."Z").to_a.shuffle.first
21
+ else
22
+ result += ("a".."z").to_a.shuffle.first
23
+ end
24
+ end
25
+ result
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Tidus
2
+ VERSION = "1.0.0"
3
+ end
data/lib/tidus.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "rake"
2
+ require "active_record"
3
+
4
+ require "tidus/version"
5
+ require "tidus/anonymization"
6
+ require "tidus/strategies/cond_anonymizer.rb"
7
+ require "tidus/strategies/email_anonymizer.rb"
8
+ require "tidus/strategies/null_anonymizer.rb"
9
+ require "tidus/strategies/overlay_anonymizer.rb"
10
+ require "tidus/strategies/static_anonymizer.rb"
11
+ require "tidus/strategies/text_anonymizer.rb"
12
+
13
+ load "active_record/railties/databases.rake"
14
+ load "tasks/views.rake"
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tidus
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Schoknecht
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 10.0.4
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 10.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.14.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.14.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.10
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.10
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ description: Creates views which allow anonymization of database tables.
70
+ email:
71
+ - tobias.schoknecht@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/tasks/views.rake
77
+ - lib/tidus.rb
78
+ - lib/tidus/anonymization.rb
79
+ - lib/tidus/strategies/cond_anonymizer.rb
80
+ - lib/tidus/strategies/email_anonymizer.rb
81
+ - lib/tidus/strategies/null_anonymizer.rb
82
+ - lib/tidus/strategies/overlay_anonymizer.rb
83
+ - lib/tidus/strategies/static_anonymizer.rb
84
+ - lib/tidus/strategies/text_anonymizer.rb
85
+ - lib/tidus/version.rb
86
+ homepage: ''
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.4.5
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Gem for creating anonymization views.
110
+ test_files: []