tidus 1.0.0
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 +7 -0
- data/lib/tasks/views.rake +43 -0
- data/lib/tidus/anonymization.rb +63 -0
- data/lib/tidus/strategies/cond_anonymizer.rb +44 -0
- data/lib/tidus/strategies/email_anonymizer.rb +21 -0
- data/lib/tidus/strategies/null_anonymizer.rb +13 -0
- data/lib/tidus/strategies/overlay_anonymizer.rb +21 -0
- data/lib/tidus/strategies/static_anonymizer.rb +17 -0
- data/lib/tidus/strategies/text_anonymizer.rb +28 -0
- data/lib/tidus/version.rb +3 -0
- data/lib/tidus.rb +14 -0
- metadata +110 -0
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
|
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: []
|