tidus 1.0.1 → 1.0.2
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 +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.
|