tidus 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/tasks/views.rake +2 -8
- data/lib/tidus.rb +5 -6
- data/lib/tidus/anonymization.rb +6 -2
- data/lib/tidus/query.rb +23 -0
- data/lib/tidus/strategies/base_selector.rb +17 -0
- data/lib/tidus/strategies/cond_anonymizer.rb +3 -41
- data/lib/tidus/strategies/email_anonymizer.rb +3 -16
- data/lib/tidus/strategies/null_anonymizer.rb +3 -10
- data/lib/tidus/strategies/overlay_anonymizer.rb +3 -18
- data/lib/tidus/strategies/postgresql/cond_anonymizer.rb +40 -0
- data/lib/tidus/strategies/postgresql/email_anonymizer.rb +15 -0
- data/lib/tidus/strategies/postgresql/null_anonymizer.rb +9 -0
- data/lib/tidus/strategies/postgresql/overlay_anonymizer.rb +17 -0
- data/lib/tidus/strategies/postgresql/text_anonymizer.rb +25 -0
- data/lib/tidus/strategies/sqlite3/null_anonymizer.rb +9 -0
- data/lib/tidus/strategies/static_anonymizer.rb +5 -8
- data/lib/tidus/strategies/text_anonymizer.rb +3 -25
- data/lib/tidus/version.rb +1 -1
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16d22e9b2bf400af04c3c38e0d230a19134edf46
|
4
|
+
data.tar.gz: c8c0389b398d87c9b440efc68eb2586263137241
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2633f5e90d829abb8e8a598302f54f5dc22def128e1b74d48b94300992fb3e80f85008f456d1906b40c36edef3e5400bb13035eeffca2c6e873854745d59eb48
|
7
|
+
data.tar.gz: 11cdfd7de96c1d7ad031d452b8c5c9f100e6dfbaeba35a5524bc709f86b083a6e9c7720f04b70ff931fc22f7ff87f2c5950d260e95d581a735689f062b8fe242
|
data/lib/tasks/views.rake
CHANGED
@@ -6,9 +6,7 @@ namespace :db do
|
|
6
6
|
next if c.table_name == "schema_migrations"
|
7
7
|
puts "Clearing view '#{c.view_name}' for table '#{c.table_name}'"
|
8
8
|
|
9
|
-
|
10
|
-
"DROP VIEW IF EXISTS #{c.view_name}"
|
11
|
-
)
|
9
|
+
c.clear_view
|
12
10
|
end
|
13
11
|
end
|
14
12
|
|
@@ -21,11 +19,7 @@ namespace :db do
|
|
21
19
|
if ActiveRecord::Base.connection.table_exists? c.table_name
|
22
20
|
puts "Generating view '#{c.view_name}' for table '#{c.table_name}'"
|
23
21
|
|
24
|
-
|
25
|
-
"CREATE VIEW #{c.view_name} AS " +
|
26
|
-
"(SELECT #{c.view_columns.join(', ')} " +
|
27
|
-
"FROM #{c.table_name})"
|
28
|
-
)
|
22
|
+
c.create_view
|
29
23
|
end
|
30
24
|
end
|
31
25
|
end
|
data/lib/tidus.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require "rake"
|
2
4
|
require "active_record"
|
3
5
|
|
4
6
|
require "tidus/version"
|
7
|
+
require "tidus/query"
|
5
8
|
require "tidus/anonymization"
|
6
|
-
require "tidus/strategies/
|
7
|
-
|
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"
|
9
|
+
require "tidus/strategies/base_selector"
|
10
|
+
Dir["#{File.dirname(__FILE__)}/tidus/strategies/**/*.rb"].each { |f| require f }
|
12
11
|
|
13
12
|
load "active_record/railties/databases.rake"
|
14
13
|
load "tasks/views.rake"
|
data/lib/tidus/anonymization.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Tidus
|
2
4
|
module Anonymization
|
5
|
+
include Tidus::Query
|
6
|
+
|
3
7
|
def view_postfix
|
4
8
|
@view_postfix || "anonymized"
|
5
9
|
end
|
@@ -37,8 +41,8 @@ module Tidus
|
|
37
41
|
options = attributes.extract_options!.dup
|
38
42
|
columns = attributes - [options]
|
39
43
|
|
40
|
-
raise ArgumentError, "
|
41
|
-
raise ArgumentError, "
|
44
|
+
raise ArgumentError, "Must have at least one attribute" if attributes.empty?
|
45
|
+
raise ArgumentError, "Must have a strategy" if options[:strategy].blank?
|
42
46
|
|
43
47
|
columns.each do |column|
|
44
48
|
key = options[:strategy].to_s.camelize
|
data/lib/tidus/query.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Tidus
|
4
|
+
module Query
|
5
|
+
def create_query
|
6
|
+
"CREATE VIEW #{view_name} AS " +
|
7
|
+
"SELECT #{view_columns.join(', ')} " +
|
8
|
+
"FROM #{table_name}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_view
|
12
|
+
connection.execute(create_query)
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear_query
|
16
|
+
"DROP VIEW IF EXISTS #{view_name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear_view
|
20
|
+
connection.execute(clear_query)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Tidus
|
4
|
+
class BaseSelector
|
5
|
+
def self.anonymize(table_name, column_name, options = {})
|
6
|
+
adapter = ActiveRecord::Base.connection.instance_values["config"][:adapter]
|
7
|
+
|
8
|
+
begin
|
9
|
+
klass = Kernel.const_get("Tidus::#{adapter.camelize}::#{self.name.demodulize}")
|
10
|
+
klass.anonymize(table_name, column_name, options)
|
11
|
+
rescue NameError
|
12
|
+
raise "#{self.name} not implemented for #{adapter}"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,44 +1,6 @@
|
|
1
|
-
|
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 "
|
1
|
+
# encoding: utf-8
|
21
2
|
|
22
|
-
|
23
|
-
|
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
|
3
|
+
module Tidus
|
4
|
+
class CondAnonymizer < Tidus::BaseSelector
|
43
5
|
end
|
44
6
|
end
|
@@ -1,20 +1,7 @@
|
|
1
|
-
|
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
|
1
|
+
# encoding: utf-8
|
9
2
|
|
10
|
-
|
11
|
-
|
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
|
3
|
+
module Tidus
|
4
|
+
class EmailAnonymizer < Tidus::BaseSelector
|
18
5
|
end
|
19
6
|
end
|
20
7
|
|
@@ -1,13 +1,6 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
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
|
4
|
+
class NullAnonymizer < Tidus::BaseSelector
|
12
5
|
end
|
13
6
|
end
|
@@ -1,21 +1,6 @@
|
|
1
|
-
|
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?
|
1
|
+
# encoding: utf-8
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
return "\"overlay\"((#{name})::text, " +
|
15
|
-
"'#{overlay}'::text, #{options[:start]})"
|
16
|
-
else
|
17
|
-
raise "#{self.name} not implemented for #{adapter}"
|
18
|
-
end
|
19
|
-
end
|
3
|
+
module Tidus
|
4
|
+
class OverlayAnonymizer < Tidus::BaseSelector
|
20
5
|
end
|
21
6
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Tidus
|
2
|
+
module Postgresql
|
3
|
+
class CondAnonymizer
|
4
|
+
def self.anonymize(table_name, column_name, options = {})
|
5
|
+
name = "#{table_name}.#{column_name}"
|
6
|
+
|
7
|
+
type = options[:result_type] || "text"
|
8
|
+
default = options[:default].nil? ? "#{name}::#{type}" : "'#{options[:default]}'::#{type}"
|
9
|
+
|
10
|
+
if options[:conditions].blank?
|
11
|
+
raise "Missing option :conditions for CondAnonymizer on #{name}"
|
12
|
+
elsif options[:conditions].kind_of?(Array)
|
13
|
+
conditions = options[:conditions]
|
14
|
+
else
|
15
|
+
conditions = [options[:conditions]]
|
16
|
+
end
|
17
|
+
|
18
|
+
command = "CASE "
|
19
|
+
|
20
|
+
conditions.each do |cond|
|
21
|
+
raise ":column for condition must be set" if cond[:column].blank?
|
22
|
+
raise ":value for condition must be set" if cond[:value].nil?
|
23
|
+
raise ":result for condition must be set" if cond[:result].nil?
|
24
|
+
cond_column = cond[:column]
|
25
|
+
cond_value = cond[:value]
|
26
|
+
cond_type = cond[:type] || "text"
|
27
|
+
comparator = cond[:comparator] || "="
|
28
|
+
cond_result = cond[:result]
|
29
|
+
|
30
|
+
command += "WHEN ((#{table_name}.#{cond_column})::#{cond_type} #{comparator} " +
|
31
|
+
"'#{cond_value}'::#{cond_type}) THEN '#{cond_result}'::#{type} "
|
32
|
+
end
|
33
|
+
|
34
|
+
command += "ELSE #{default} END"
|
35
|
+
|
36
|
+
return command
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Tidus
|
2
|
+
module Postgresql
|
3
|
+
class EmailAnonymizer
|
4
|
+
def self.anonymize(table_name, column_name, options = {})
|
5
|
+
name = "#{table_name}.#{column_name}"
|
6
|
+
options[:length] ||= 15
|
7
|
+
|
8
|
+
return "CASE WHEN ((#{name})::text ~~ '%@%'::text) " +
|
9
|
+
"THEN (((\"left\"(md5((#{name})::text), #{options[:length]}) || '@'::text) " +
|
10
|
+
"|| split_part((#{name})::text, '@'::text, 2)))::character varying " +
|
11
|
+
"ELSE #{name} END"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Tidus
|
2
|
+
module Postgresql
|
3
|
+
class OverlayAnonymizer
|
4
|
+
def self.anonymize(table_name, column_name, options = {})
|
5
|
+
name = "#{table_name}.#{column_name}"
|
6
|
+
|
7
|
+
raise "Missing option :start for OverlayAnonymizer on #{name}" if options[:start].blank?
|
8
|
+
raise "Missing option :length for OverlayAnonymizer on #{name}" if options[:length].blank?
|
9
|
+
|
10
|
+
overlay_char = options[:char] || "X"
|
11
|
+
overlay = overlay_char * options[:length]
|
12
|
+
return "\"overlay\"((#{name})::text, " +
|
13
|
+
"'#{overlay}'::text, #{options[:start]})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tidus
|
2
|
+
module Postgresql
|
3
|
+
class TextAnonymizer
|
4
|
+
def self.anonymize(table_name, column_name, options = {})
|
5
|
+
base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZCßüäöÜÄÖ"
|
6
|
+
return "translate((#{table_name}.#{column_name})::text, " +
|
7
|
+
"'#{base}'::text, '#{generate_mapping(base)}'::text)"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def self.generate_mapping(base)
|
12
|
+
upper = ("A".."Z").to_a
|
13
|
+
lower = ("a".."z").to_a
|
14
|
+
result = base.split("").map do |letter|
|
15
|
+
if letter == letter.upcase
|
16
|
+
upper.sample
|
17
|
+
else
|
18
|
+
lower.sample
|
19
|
+
end
|
20
|
+
end
|
21
|
+
result.join("")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,15 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Tidus
|
2
4
|
class StaticAnonymizer
|
3
5
|
def self.anonymize(table_name, column_name, options = {})
|
4
|
-
|
5
|
-
|
6
|
-
when "postgresql"
|
7
|
-
raise "Missing option :value for StaticAnonymizer on #{table_name}.#{column_name}" if options[:value].blank?
|
6
|
+
raise "Missing option :value for StaticAnonymizer on #{table_name}.#{column_name}" if options[:value].blank?
|
7
|
+
type = options[:type] || "unknown"
|
8
8
|
|
9
|
-
|
10
|
-
else
|
11
|
-
raise "#{self.name} not implemented for #{adapter}"
|
12
|
-
end
|
9
|
+
return "'#{options[:value]}'::#{type}"
|
13
10
|
end
|
14
11
|
end
|
15
12
|
end
|
@@ -1,28 +1,6 @@
|
|
1
|
-
|
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
|
1
|
+
# encoding: utf-8
|
14
2
|
|
15
|
-
|
16
|
-
|
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
|
3
|
+
module Tidus
|
4
|
+
class TextAnonymizer < Tidus::BaseSelector
|
27
5
|
end
|
28
6
|
end
|
data/lib/tidus/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tidus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schoknecht
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -76,10 +76,18 @@ files:
|
|
76
76
|
- lib/tasks/views.rake
|
77
77
|
- lib/tidus.rb
|
78
78
|
- lib/tidus/anonymization.rb
|
79
|
+
- lib/tidus/query.rb
|
80
|
+
- lib/tidus/strategies/base_selector.rb
|
79
81
|
- lib/tidus/strategies/cond_anonymizer.rb
|
80
82
|
- lib/tidus/strategies/email_anonymizer.rb
|
81
83
|
- lib/tidus/strategies/null_anonymizer.rb
|
82
84
|
- lib/tidus/strategies/overlay_anonymizer.rb
|
85
|
+
- lib/tidus/strategies/postgresql/cond_anonymizer.rb
|
86
|
+
- lib/tidus/strategies/postgresql/email_anonymizer.rb
|
87
|
+
- lib/tidus/strategies/postgresql/null_anonymizer.rb
|
88
|
+
- lib/tidus/strategies/postgresql/overlay_anonymizer.rb
|
89
|
+
- lib/tidus/strategies/postgresql/text_anonymizer.rb
|
90
|
+
- lib/tidus/strategies/sqlite3/null_anonymizer.rb
|
83
91
|
- lib/tidus/strategies/static_anonymizer.rb
|
84
92
|
- lib/tidus/strategies/text_anonymizer.rb
|
85
93
|
- lib/tidus/version.rb
|
@@ -103,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
111
|
version: '0'
|
104
112
|
requirements: []
|
105
113
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.4.
|
114
|
+
rubygems_version: 2.4.6
|
107
115
|
signing_key:
|
108
116
|
specification_version: 4
|
109
117
|
summary: Gem for creating anonymization views.
|