masking 1.1.0 → 1.1.2.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +9 -4
- data/.github/workflows/acceptance_test_mariadb.yml +57 -1
- data/.github/workflows/acceptance_test_mysql.yml +1 -1
- data/.rubocop.yml +7 -1
- data/.rubocop_todo.yml +105 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +19 -0
- data/CODEOWNERS +1 -0
- data/Dockerfile +5 -3
- data/Dockerfile.ghcr +10 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +67 -51
- data/README.md +38 -26
- data/acceptance/expected_error_result.txt +2 -0
- data/acceptance/expected_query_result.txt +36 -30
- data/acceptance/import_dumpfile.sql +3 -1
- data/acceptance/masking.yml +2 -0
- data/acceptance/run_test.sh +18 -6
- data/docker-compose/mariadb1010.yml +17 -0
- data/docker-compose/mariadb1011.yml +17 -0
- data/docker-compose/mariadb105.yml +17 -0
- data/docker-compose/mariadb106.yml +17 -0
- data/docker-compose/mariadb107.yml +17 -0
- data/docker-compose/mariadb108.yml +17 -0
- data/docker-compose/mariadb109.yml +17 -0
- data/lib/masking/cli/error_message.rb +16 -9
- data/lib/masking/config/target_columns/column.rb +19 -5
- data/lib/masking/config/target_columns/method/string_binary_distinctor.rb +3 -3
- data/lib/masking/config/target_columns/method/type/base.rb +25 -0
- data/lib/masking/config/target_columns/method/type/binary.rb +19 -0
- data/lib/masking/config/target_columns/method/type/boolean.rb +27 -0
- data/lib/masking/config/target_columns/method/type/date.rb +28 -0
- data/lib/masking/config/target_columns/method/type/extension/ignore_null.rb +24 -0
- data/lib/masking/config/target_columns/method/type/float.rb +19 -0
- data/lib/masking/config/target_columns/method/type/integer.rb +19 -0
- data/lib/masking/config/target_columns/method/{null.rb → type/null.rb} +5 -5
- data/lib/masking/config/target_columns/method/type/string.rb +37 -0
- data/lib/masking/config/target_columns/method/type/time.rb +26 -0
- data/lib/masking/config/target_columns/method.rb +14 -10
- data/lib/masking/insert_statement/sql_builder.rb +2 -2
- data/lib/masking/insert_statement.rb +1 -1
- data/lib/masking/sql_dump_line.rb +1 -0
- data/lib/masking/version.rb +1 -1
- data/masking.gemspec +1 -18
- metadata +29 -184
- data/lib/masking/cli/error_messages.yml +0 -10
- data/lib/masking/config/target_columns/method/binary.rb +0 -23
- data/lib/masking/config/target_columns/method/boolean.rb +0 -29
- data/lib/masking/config/target_columns/method/date.rb +0 -30
- data/lib/masking/config/target_columns/method/float.rb +0 -23
- data/lib/masking/config/target_columns/method/integer.rb +0 -23
- data/lib/masking/config/target_columns/method/string.rb +0 -33
- data/lib/masking/config/target_columns/method/time.rb +0 -28
@@ -1,33 +1,39 @@
|
|
1
1
|
*************************** 1. row ***************************
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
binary_or_blob: \x92
|
2
|
+
id: 1
|
3
|
+
string: anonymized string
|
4
|
+
email: anonymized+1@example.com
|
5
|
+
integer: 12345
|
6
|
+
float: 123.45
|
7
|
+
boolean: 1
|
8
|
+
null: NULL
|
9
|
+
date: 2018-08-24
|
10
|
+
time: 2018-08-24 15:54:06
|
11
|
+
binary_or_blob: \x92
|
12
|
+
nullable_string: string1
|
13
|
+
nullable_integer: NULL
|
12
14
|
*************************** 2. row ***************************
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
binary_or_blob: \x92\x92
|
15
|
+
id: 2
|
16
|
+
string: anonymized string
|
17
|
+
email: anonymized+2@example.com
|
18
|
+
integer: 12345
|
19
|
+
float: 123.45
|
20
|
+
boolean: 1
|
21
|
+
null: NULL
|
22
|
+
date: 2018-08-24
|
23
|
+
time: 2018-08-24 15:54:06
|
24
|
+
binary_or_blob: \x92\x92
|
25
|
+
nullable_string: NULL
|
26
|
+
nullable_integer: 33333
|
23
27
|
*************************** 3. row ***************************
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
binary_or_blob: NULL
|
28
|
+
id: 3
|
29
|
+
string: anonymized string
|
30
|
+
email: anonymized+3@example.com
|
31
|
+
integer: 12345
|
32
|
+
float: 123.45
|
33
|
+
boolean: 1
|
34
|
+
null: NULL
|
35
|
+
date: 2018-08-24
|
36
|
+
time: 2018-08-24 15:54:06
|
37
|
+
binary_or_blob: NULL
|
38
|
+
nullable_string: string3
|
39
|
+
nullable_integer: 33333
|
@@ -33,6 +33,8 @@ CREATE TABLE `users` (
|
|
33
33
|
`date` date DEFAULT NULL,
|
34
34
|
`time` timestamp NULL DEFAULT NULL,
|
35
35
|
`binary_or_blob` binary(11) DEFAULT NULL,
|
36
|
+
`nullable_string` varchar(20) DEFAULT '',
|
37
|
+
`nullable_integer` int(11) DEFAULT NULL,
|
36
38
|
PRIMARY KEY (`id`)
|
37
39
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
|
38
40
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
@@ -43,7 +45,7 @@ CREATE TABLE `users` (
|
|
43
45
|
|
44
46
|
LOCK TABLES `users` WRITE;
|
45
47
|
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
46
|
-
INSERT INTO `users` (`id`, `string`, `email`, `integer`, `float`, `boolean`, `null`, `date`, `time`, `binary_or_blob`) VALUES (1,'example@exa','test@example.com',1245,2.1,1,-321,NULL,NULL,_binary '\\x92\0\0\0\0\0\0\0'),(2,'あいうえお','invalid@email',0,-23.4422,0,321,NULL,NULL,_binary '\\x92\\x92\0\0\0'),(3,'+-l;a*&^%$','chikahiro@test.com',-1,21.2321,NULL,-231321,'2019-10-31','2019-10-31 16:27:21',NULL);
|
48
|
+
INSERT INTO `users` (`id`, `string`, `email`, `integer`, `float`, `boolean`, `null`, `date`, `time`, `binary_or_blob`, `nullable_string`, `nullable_integer`) VALUES (1,'example@exa','test@example.com',1245,2.1,1,-321,NULL,NULL,_binary '\\x92\0\0\0\0\0\0\0','abcde',NULL),(2,'あいうえお','invalid@email',0,-23.4422,0,321,NULL,NULL,_binary '\\x92\\x92\0\0\0',NULL,12),(3,'+-l;a*&^%$','chikahiro@test.com',-1,21.2321,NULL,-231321,'2019-10-31','2019-10-31 16:27:21',NULL,'abc123',-123);
|
47
49
|
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
48
50
|
UNLOCK TABLES;
|
49
51
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
data/acceptance/masking.yml
CHANGED
@@ -7,6 +7,8 @@ users:
|
|
7
7
|
'null': NULL
|
8
8
|
date: 2018-08-24
|
9
9
|
time: 2018-08-24 15:54:06
|
10
|
+
nullable_string?: string%{n}
|
11
|
+
nullable_integer?: 33333
|
10
12
|
## TODO: something not working well with binary...
|
11
13
|
# binary_or_blob: !binary |
|
12
14
|
# R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
|
data/acceptance/run_test.sh
CHANGED
@@ -10,16 +10,28 @@ MYSQL_DBNAME=${MYSQL_DBNAME:-mydb}
|
|
10
10
|
|
11
11
|
FILEDIR="$( cd "$( dirname "$0" )" && pwd )"
|
12
12
|
|
13
|
-
|
13
|
+
# clear tmp file
|
14
14
|
rm "$FILEDIR"/tmp/* || echo 'no tmp file'
|
15
15
|
|
16
|
-
|
16
|
+
# import database
|
17
17
|
mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" < "$FILEDIR/import_dumpfile.sql"
|
18
18
|
|
19
|
-
|
19
|
+
# masking & restore
|
20
20
|
mysqldump -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" --complete-insert | exe/masking -c "$FILEDIR/masking.yml" > "$FILEDIR/tmp/masking_dumpfile.sql"
|
21
21
|
mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" < "$FILEDIR/tmp/masking_dumpfile.sql"
|
22
|
-
|
23
|
-
## compare
|
22
|
+
## compare the result
|
24
23
|
mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" -e 'SELECT * FROM users ORDER BY id;' --vertical > "$FILEDIR/tmp/query_result.txt"
|
25
|
-
diff "$FILEDIR/
|
24
|
+
diff "$FILEDIR/expected_query_result.txt" "$FILEDIR/tmp/query_result.txt" || (echo 'test failed' && exit 1)
|
25
|
+
|
26
|
+
# test errors
|
27
|
+
set +e
|
28
|
+
## without masking.yml
|
29
|
+
mysqldump -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" --complete-insert | exe/masking -c "$FILEDIR/no_file.yml" 2>> "$FILEDIR/tmp/errors.txt" 1> /dev/null
|
30
|
+
## without `--complete-insert``
|
31
|
+
mysqldump -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" | exe/masking -c "$FILEDIR/masking.yml" 2>> "$FILEDIR/tmp/errors.txt" 1> /dev/null
|
32
|
+
set -e
|
33
|
+
### compare the result
|
34
|
+
diff "$FILEDIR/expected_error_result.txt" "$FILEDIR/tmp/errors.txt" || (echo 'error output test failed' && exit 1)
|
35
|
+
|
36
|
+
echo 'test passed!'
|
37
|
+
exit 0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb1010
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb1010
|
10
|
+
|
11
|
+
mariadb1010:
|
12
|
+
image: mariadb:10.10
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb1011
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb1011
|
10
|
+
|
11
|
+
mariadb1011:
|
12
|
+
image: mariadb:10.11
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb105
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb105
|
10
|
+
|
11
|
+
mariadb105:
|
12
|
+
image: mariadb:10.5
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb106
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb106
|
10
|
+
|
11
|
+
mariadb106:
|
12
|
+
image: mariadb:10.6
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb107
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb107
|
10
|
+
|
11
|
+
mariadb107:
|
12
|
+
image: mariadb:10.7
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb108
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb108
|
10
|
+
|
11
|
+
mariadb108:
|
12
|
+
image: mariadb:10.8
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
build:
|
6
|
+
target: with-mysql-client
|
7
|
+
depends_on:
|
8
|
+
- mariadb109
|
9
|
+
entrypoint: docker-compose/wait-for-mysql.sh mariadb109
|
10
|
+
|
11
|
+
mariadb109:
|
12
|
+
image: mariadb:10.9
|
13
|
+
environment:
|
14
|
+
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
15
|
+
MYSQL_USER: mysqluser
|
16
|
+
MYSQL_PASSWORD: password
|
17
|
+
MYSQL_DATABASE: mydb
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'pathname'
|
4
3
|
require 'erb'
|
5
4
|
require 'ostruct'
|
5
|
+
require 'masking/errors'
|
6
6
|
|
7
7
|
module Masking
|
8
8
|
class Cli
|
@@ -19,19 +19,26 @@ module Masking
|
|
19
19
|
|
20
20
|
attr_reader :error_class
|
21
21
|
|
22
|
-
YAML_FILE_PATH = Pathname('lib/masking/cli/error_messages.yml')
|
23
|
-
|
24
|
-
def error_messages
|
25
|
-
@error_messages = YAML.safe_load(YAML_FILE_PATH.read)
|
26
|
-
end
|
27
|
-
|
28
22
|
def error_message(keyword_args)
|
29
23
|
ERB.new(
|
30
|
-
|
24
|
+
ERROR_MESSAGES.fetch(error_class.to_s)
|
31
25
|
).result(
|
32
|
-
OpenStruct.new(keyword_args).instance_eval { binding }
|
26
|
+
OpenStruct.new(keyword_args).instance_eval { binding } # rubocop:disable Style/OpenStructUse
|
33
27
|
)
|
34
28
|
end
|
29
|
+
|
30
|
+
ERROR_MESSAGES = {
|
31
|
+
'Masking::Error::ConfigFileDoesNotExist' =>
|
32
|
+
'ERROR: config file (<%= config_file_path %>) does not exist',
|
33
|
+
'Masking::Error::ConfigFileIsNotFile' =>
|
34
|
+
'ERROR: config file (<%= config_file_path %>) is not file',
|
35
|
+
'Masking::Error::ConfigFileIsNotValidYaml' =>
|
36
|
+
'ERROR: config file (<%= config_file_path %>) is not valid yaml format',
|
37
|
+
'Masking::Error::ConfigFileContainsNullAsColumnName' =>
|
38
|
+
'ERROR: config file (<%= config_file_path %>) is not valid, column name contains `null`',
|
39
|
+
'Masking::Error::InsertStatementParseError' =>
|
40
|
+
'ERROR: cannot parse SQL dump file. you may forget to put `--complete-insert` option in mysqldump?'
|
41
|
+
}.freeze
|
35
42
|
end
|
36
43
|
end
|
37
44
|
end
|
@@ -6,22 +6,36 @@ module Masking
|
|
6
6
|
class Config
|
7
7
|
class TargetColumns
|
8
8
|
class Column
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :table_name, :method_value, :method
|
10
10
|
|
11
11
|
def initialize(name, table_name:, method_value:)
|
12
12
|
raise ColumnNameIsNil if name.nil?
|
13
13
|
|
14
|
-
@
|
15
|
-
@table_name
|
16
|
-
@method_value
|
17
|
-
@method
|
14
|
+
@original_name = name.to_sym
|
15
|
+
@table_name = table_name.to_sym
|
16
|
+
@method_value = method_value
|
17
|
+
@method = Method.new(method_value, ignore_null: ignore_null?)
|
18
18
|
end
|
19
19
|
|
20
20
|
def ==(other)
|
21
21
|
name == other.name && table_name == other.table_name && method_value == other.method_value
|
22
22
|
end
|
23
23
|
|
24
|
+
def name
|
25
|
+
@name ||= ignore_null? ? original_name.to_s.chomp(IGNORE_NULL_SUFFIX).to_sym : original_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def ignore_null?
|
29
|
+
@ignore_null ||= original_name.to_s.end_with?(IGNORE_NULL_SUFFIX)
|
30
|
+
end
|
31
|
+
|
24
32
|
class ColumnNameIsNil < StandardError; end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :original_name
|
37
|
+
|
38
|
+
IGNORE_NULL_SUFFIX = '?'
|
25
39
|
end
|
26
40
|
end
|
27
41
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'masking/config/target_columns/method/binary'
|
4
|
-
require 'masking/config/target_columns/method/string'
|
3
|
+
require 'masking/config/target_columns/method/type/binary'
|
4
|
+
require 'masking/config/target_columns/method/type/string'
|
5
5
|
|
6
6
|
module Masking
|
7
7
|
class Config
|
@@ -10,7 +10,7 @@ module Masking
|
|
10
10
|
module StringBinaryDistinctor
|
11
11
|
class << self
|
12
12
|
def new(value)
|
13
|
-
binary?(value) ? Binary.new(value) : String.new(value)
|
13
|
+
binary?(value) ? Type::Binary.new(value) : Type::String.new(value)
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masking
|
4
|
+
class Config
|
5
|
+
class TargetColumns
|
6
|
+
class Method
|
7
|
+
module Type
|
8
|
+
class Base
|
9
|
+
def initialize(value)
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(_sql_value)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
|
5
|
+
module Masking
|
6
|
+
class Config
|
7
|
+
class TargetColumns
|
8
|
+
class Method
|
9
|
+
module Type
|
10
|
+
class Binary < Base
|
11
|
+
def call(_sql_value)
|
12
|
+
"_binary '#{value}'".b
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
|
5
|
+
module Masking
|
6
|
+
class Config
|
7
|
+
class TargetColumns
|
8
|
+
class Method
|
9
|
+
module Type
|
10
|
+
class Boolean < Base
|
11
|
+
def call(_sql_value)
|
12
|
+
boolean_format.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# NOTE: 11.1.1 Numeric Type Overview, chapter BOOL, BOOLEAN
|
18
|
+
# https://dev.mysql.com/doc/refman/8.0/en/numeric-type-overview.html
|
19
|
+
def boolean_format
|
20
|
+
value ? 1 : 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
module Masking
|
7
|
+
class Config
|
8
|
+
class TargetColumns
|
9
|
+
class Method
|
10
|
+
module Type
|
11
|
+
class Date < Base
|
12
|
+
def call(_sql_value)
|
13
|
+
"'#{date_format}'"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
FORMAT = '%Y-%m-%d'
|
19
|
+
|
20
|
+
def date_format
|
21
|
+
value.strftime(FORMAT)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masking
|
4
|
+
class Config
|
5
|
+
class TargetColumns
|
6
|
+
class Method
|
7
|
+
module Type
|
8
|
+
module Extension
|
9
|
+
module IgnoreNull
|
10
|
+
def call(sql_value)
|
11
|
+
if sql_value == 'NULL'
|
12
|
+
sequence! if respond_to?(:sequence!, true)
|
13
|
+
return 'NULL'
|
14
|
+
end
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
|
5
|
+
module Masking
|
6
|
+
class Config
|
7
|
+
class TargetColumns
|
8
|
+
class Method
|
9
|
+
module Type
|
10
|
+
class Float < Base
|
11
|
+
def call(_sql_value)
|
12
|
+
value.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
|
5
|
+
module Masking
|
6
|
+
class Config
|
7
|
+
class TargetColumns
|
8
|
+
class Method
|
9
|
+
module Type
|
10
|
+
class Integer < Base
|
11
|
+
def call(_sql_value)
|
12
|
+
value.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
|
5
|
+
module Masking
|
6
|
+
class Config
|
7
|
+
class TargetColumns
|
8
|
+
class Method
|
9
|
+
module Type
|
10
|
+
class String < Base
|
11
|
+
def initialize(value)
|
12
|
+
super(value)
|
13
|
+
@sequence = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(_sql_value)
|
17
|
+
sequence!
|
18
|
+
"'#{output}'".b
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
SEQUENTIAL_NUMBER_PLACEHOLDER = '%{n}' # rubocop:disable Style/FormatStringToken
|
24
|
+
|
25
|
+
def output
|
26
|
+
value.sub(SEQUENTIAL_NUMBER_PLACEHOLDER, @sequence.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
def sequence!
|
30
|
+
@sequence += 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'masking/config/target_columns/method/type/base'
|
4
|
+
|
5
|
+
module Masking
|
6
|
+
class Config
|
7
|
+
class TargetColumns
|
8
|
+
class Method
|
9
|
+
module Type
|
10
|
+
class Time < Base
|
11
|
+
def call(_sql_value)
|
12
|
+
"'#{time_format}'"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
FORMAT = '%Y-%m-%d %H:%M:%S'
|
18
|
+
def time_format
|
19
|
+
value.strftime(FORMAT)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'forwardable'
|
5
|
-
|
5
|
+
require 'masking/config/target_columns/method/type/extension/ignore_null'
|
6
|
+
require 'masking/config/target_columns/method/string_binary_distinctor'
|
7
|
+
Dir[Pathname(__FILE__).dirname.join('method/type/*.rb').to_s].sort.each(&method(:require))
|
6
8
|
|
7
9
|
module Masking
|
8
10
|
class Config
|
@@ -10,8 +12,10 @@ module Masking
|
|
10
12
|
class Method
|
11
13
|
extend Forwardable
|
12
14
|
|
13
|
-
def initialize(method)
|
14
|
-
@method_type = mapping(method.class).new(method)
|
15
|
+
def initialize(method, ignore_null: false)
|
16
|
+
@method_type = mapping(method.class).new(method).tap do |obj|
|
17
|
+
obj.singleton_class.prepend(Type::Extension::IgnoreNull) if ignore_null
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
def_delegator :@method_type, :call
|
@@ -21,13 +25,13 @@ module Masking
|
|
21
25
|
# rubocop:disable Layout/HashAlignment
|
22
26
|
MAPPING = {
|
23
27
|
::String => StringBinaryDistinctor,
|
24
|
-
::Integer => Integer,
|
25
|
-
::Float => Float,
|
26
|
-
::Date => Date,
|
27
|
-
::Time => Time,
|
28
|
-
::TrueClass => Boolean,
|
29
|
-
::FalseClass => Boolean,
|
30
|
-
::NilClass => Null
|
28
|
+
::Integer => Type::Integer,
|
29
|
+
::Float => Type::Float,
|
30
|
+
::Date => Type::Date,
|
31
|
+
::Time => Type::Time,
|
32
|
+
::TrueClass => Type::Boolean,
|
33
|
+
::FalseClass => Type::Boolean,
|
34
|
+
::NilClass => Type::Null
|
31
35
|
}.freeze
|
32
36
|
# rubocop:enable Layout/HashAlignment
|
33
37
|
|