tidus 1.0.7 → 1.2.1
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 +5 -5
- data/lib/tasks/views.rake +11 -3
- data/lib/tidus.rb +2 -2
- data/lib/tidus/query.rb +12 -1
- data/lib/tidus/strategies/ean_anonymizer.rb +6 -0
- data/lib/tidus/strategies/postgresql/ean_anonymizer.rb +147 -0
- data/lib/tidus/version.rb +1 -1
- metadata +15 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c0788d5eac4c060fa79f55b57512f970ebc1919994532fba9d5fcd0857db0bcd
|
4
|
+
data.tar.gz: a30b93c68023dda2caaef0c71ca0ea52c521a2bc2c39ca15f98b9a1e66e5300c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06a983ac2815a1178f8b53dcdb4a219fa0322f8e2de32d8af2071f57dffac1c292534de65f74f231e9705c3b3eaca12aeff69f2c642d9261c0beff74c640993f
|
7
|
+
data.tar.gz: 612b24c8414c4721af928321f9f057e5d933b2dc629a6fd16c73fa7c7544bf72ff16acc664cb0683eef2afd681e73e6d17a8960dc375bdad094918e845b9a647
|
data/lib/tasks/views.rake
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
+
skip_tables = [
|
2
|
+
'schema_migrations',
|
3
|
+
'ar_internal_metadata',
|
4
|
+
]
|
5
|
+
|
1
6
|
namespace :db do
|
2
7
|
desc "Clears all the views which are currently existing"
|
3
8
|
task :clear_views do
|
4
9
|
Rails.application.eager_load! if defined?(Rails)
|
10
|
+
|
5
11
|
ActiveRecord::Base.descendants.each do |c|
|
6
|
-
next if c.table_name
|
12
|
+
next if skip_tables.include?(c.table_name)
|
13
|
+
|
7
14
|
puts "Clearing view '#{c.view_name}' for table '#{c.table_name}'"
|
8
15
|
|
9
16
|
c.clear_view
|
@@ -13,8 +20,9 @@ namespace :db do
|
|
13
20
|
desc "Generates all the views for the models"
|
14
21
|
task :generate_views do
|
15
22
|
Rails.application.eager_load! if defined?(Rails)
|
23
|
+
|
16
24
|
ActiveRecord::Base.descendants.each do |c|
|
17
|
-
next if c.table_name
|
25
|
+
next if skip_tables.include?(c.table_name) || c.skip_anonymization?
|
18
26
|
|
19
27
|
if ActiveRecord::Base.connection.table_exists? c.table_name
|
20
28
|
puts "Generating view '#{c.view_name}' for table '#{c.table_name}'"
|
@@ -34,4 +42,4 @@ end
|
|
34
42
|
|
35
43
|
Rake::Task["db:rollback"].enhance do
|
36
44
|
Rake::Task["db:generate_views"].invoke
|
37
|
-
end
|
45
|
+
end
|
data/lib/tidus.rb
CHANGED
@@ -9,5 +9,5 @@ require "tidus/anonymization"
|
|
9
9
|
require "tidus/strategies/base_selector"
|
10
10
|
Dir["#{File.dirname(__FILE__)}/tidus/strategies/**/*.rb"].each { |f| require f }
|
11
11
|
|
12
|
-
load "active_record/railties/databases.rake"
|
13
|
-
load "tasks/views.rake"
|
12
|
+
load "active_record/railties/databases.rake" if defined?(Rails)
|
13
|
+
load "tasks/views.rake"
|
data/lib/tidus/query.rb
CHANGED
@@ -2,8 +2,19 @@
|
|
2
2
|
|
3
3
|
module Tidus
|
4
4
|
module Query
|
5
|
+
def create_view_query_part
|
6
|
+
case connection.instance_values['config'][:adapter].to_s.downcase
|
7
|
+
when 'postgresql'
|
8
|
+
return 'CREATE OR REPLACE VIEW'
|
9
|
+
when 'sqlite3'
|
10
|
+
return 'CREATE VIEW IF NOT EXISTS'
|
11
|
+
else
|
12
|
+
return 'CREATE VIEW'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
def create_query
|
6
|
-
"
|
17
|
+
"#{create_view_query_part} #{view_name} AS " +
|
7
18
|
"SELECT #{view_columns.join(', ')} " +
|
8
19
|
"FROM #{table_name}"
|
9
20
|
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Tidus
|
2
|
+
module Postgresql
|
3
|
+
class EanAnonymizer
|
4
|
+
BASE_MAPPING = '0123456789'.freeze
|
5
|
+
DEFAULT_MAPPING_START = 1.freeze
|
6
|
+
|
7
|
+
def self.anonymize(table_name, column_name, options = {})
|
8
|
+
mapping_snippet = build_mapped_digit_snippet(column_name, options)
|
9
|
+
|
10
|
+
query = <<-SQL
|
11
|
+
(
|
12
|
+
SELECT
|
13
|
+
string_agg(new_digits.digit::TEXT, ''::TEXT)
|
14
|
+
FROM (
|
15
|
+
(
|
16
|
+
SELECT
|
17
|
+
pos,
|
18
|
+
digit
|
19
|
+
FROM (
|
20
|
+
#{mapping_snippet}
|
21
|
+
) AS where_sub
|
22
|
+
ORDER BY pos ASC
|
23
|
+
)
|
24
|
+
UNION ALL
|
25
|
+
(
|
26
|
+
SELECT
|
27
|
+
LENGTH(#{column_name}::TEXT) AS pos,
|
28
|
+
(
|
29
|
+
10
|
30
|
+
-
|
31
|
+
(
|
32
|
+
(
|
33
|
+
SELECT
|
34
|
+
SUM(digit)
|
35
|
+
FROM (
|
36
|
+
#{mapping_snippet}
|
37
|
+
) AS where_sub
|
38
|
+
WHERE pos % 2 = LENGTH(#{column_name}::TEXT) % 2
|
39
|
+
)
|
40
|
+
+
|
41
|
+
(
|
42
|
+
SELECT
|
43
|
+
SUM(digit)
|
44
|
+
FROM (
|
45
|
+
#{mapping_snippet}
|
46
|
+
) AS where_sub
|
47
|
+
WHERE pos % 2 = (LENGTH(#{column_name}::TEXT) - 1) % 2
|
48
|
+
) * 3
|
49
|
+
) % 10
|
50
|
+
) % 10 AS digit
|
51
|
+
)
|
52
|
+
) new_digits
|
53
|
+
)
|
54
|
+
SQL
|
55
|
+
|
56
|
+
return query.gsub!(/\n/, '').gsub!(/\ +/, ' ')
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generates a new mapping if no cache_key is given
|
60
|
+
# Generates a new mapping if the cache_key is unknown
|
61
|
+
# Reuses the found mapping if the cache_key is known
|
62
|
+
def self.retrieve_mapping(cache_key)
|
63
|
+
@cached_mappings ||= {}
|
64
|
+
mapping = @cached_mappings[cache_key]
|
65
|
+
|
66
|
+
if mapping == nil
|
67
|
+
mapping = {
|
68
|
+
base: BASE_MAPPING,
|
69
|
+
replacement: BASE_MAPPING.split('').shuffle.join('')
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
if cache_key != nil
|
74
|
+
@cached_mappings[cache_key] = mapping
|
75
|
+
end
|
76
|
+
|
77
|
+
return mapping
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.build_mapped_digit_snippet(column_name, options)
|
81
|
+
substrings = build_digit_mapping(column_name, options)
|
82
|
+
|
83
|
+
return <<-SQL
|
84
|
+
SELECT
|
85
|
+
*
|
86
|
+
FROM (
|
87
|
+
SELECT
|
88
|
+
ROW_NUMBER() over () AS pos,
|
89
|
+
digit::INT
|
90
|
+
FROM (
|
91
|
+
SELECT
|
92
|
+
REGEXP_SPLIT_TO_TABLE(
|
93
|
+
#{substrings.join(' || ')},
|
94
|
+
''
|
95
|
+
) AS digit
|
96
|
+
) AS sub
|
97
|
+
) AS sub
|
98
|
+
WHERE pos < LENGTH(#{column_name}::TEXT)
|
99
|
+
SQL
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.build_digit_mapping(column_name, options)
|
103
|
+
mapping = retrieve_mapping(options[:cache_key])
|
104
|
+
|
105
|
+
options[:start] ||= DEFAULT_MAPPING_START
|
106
|
+
|
107
|
+
# reduce length by 1 to take away the check digit
|
108
|
+
total_length = "LENGTH(#{column_name}::TEXT) - 1"
|
109
|
+
length = options[:length] || total_length
|
110
|
+
|
111
|
+
substrings = []
|
112
|
+
|
113
|
+
if options[:start] != DEFAULT_MAPPING_START
|
114
|
+
substrings << build_substring_snippet(
|
115
|
+
column_name,
|
116
|
+
DEFAULT_MAPPING_START,
|
117
|
+
options[:start] - DEFAULT_MAPPING_START
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
translate_part = <<-SNIPPET
|
122
|
+
TRANSLATE(
|
123
|
+
#{build_substring_snippet(column_name, options[:start], length)},
|
124
|
+
'#{mapping[:base]}',
|
125
|
+
'#{mapping[:replacement]}'
|
126
|
+
)
|
127
|
+
SNIPPET
|
128
|
+
|
129
|
+
substrings << translate_part.gsub(/\n/, '').gsub(/\ +/, ' ')
|
130
|
+
|
131
|
+
if options[:length] != nil
|
132
|
+
substrings << build_substring_snippet(
|
133
|
+
column_name,
|
134
|
+
"#{options[:start]} + #{options[:length]}",
|
135
|
+
"#{total_length} - #{options[:start]}"
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
return substrings
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.build_substring_snippet(column_name, start, length)
|
143
|
+
return "SUBSTRING(#{column_name}::TEXT, #{start}, #{length})"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
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.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schoknecht
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -16,42 +16,42 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '13.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '13.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '3.9'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '3.9'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: sqlite3
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.
|
47
|
+
version: '1.4'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
54
|
+
version: '1.4'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: activerecord
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
version: '3.2'
|
69
69
|
description: Creates views which allow anonymization of database tables.
|
70
70
|
email:
|
71
|
-
- tobias.schoknecht@
|
71
|
+
- tobias.schoknecht@viafintech.com
|
72
72
|
executables: []
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
@@ -79,10 +79,12 @@ files:
|
|
79
79
|
- lib/tidus/query.rb
|
80
80
|
- lib/tidus/strategies/base_selector.rb
|
81
81
|
- lib/tidus/strategies/cond_anonymizer.rb
|
82
|
+
- lib/tidus/strategies/ean_anonymizer.rb
|
82
83
|
- lib/tidus/strategies/email_anonymizer.rb
|
83
84
|
- lib/tidus/strategies/null_anonymizer.rb
|
84
85
|
- lib/tidus/strategies/overlay_anonymizer.rb
|
85
86
|
- lib/tidus/strategies/postgresql/cond_anonymizer.rb
|
87
|
+
- lib/tidus/strategies/postgresql/ean_anonymizer.rb
|
86
88
|
- lib/tidus/strategies/postgresql/email_anonymizer.rb
|
87
89
|
- lib/tidus/strategies/postgresql/null_anonymizer.rb
|
88
90
|
- lib/tidus/strategies/postgresql/overlay_anonymizer.rb
|
@@ -99,7 +101,7 @@ homepage: ''
|
|
99
101
|
licenses:
|
100
102
|
- MIT
|
101
103
|
metadata: {}
|
102
|
-
post_install_message:
|
104
|
+
post_install_message:
|
103
105
|
rdoc_options: []
|
104
106
|
require_paths:
|
105
107
|
- lib
|
@@ -114,9 +116,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
116
|
- !ruby/object:Gem::Version
|
115
117
|
version: '0'
|
116
118
|
requirements: []
|
117
|
-
|
118
|
-
|
119
|
-
signing_key:
|
119
|
+
rubygems_version: 3.0.6
|
120
|
+
signing_key:
|
120
121
|
specification_version: 4
|
121
122
|
summary: Gem for creating anonymization views.
|
122
123
|
test_files: []
|