mongo_masker 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f217269ec66fb6a591ab9892b713668ef49cd761030f0f1daf3fb8f806c928f5
4
+ data.tar.gz: c3720d536bd94c8adc2c2a5fbd07446ebb54e9a192d34e93b0be7fea36ee34f3
5
+ SHA512:
6
+ metadata.gz: 16e4d2ca4b1b9ef37005beebe1bfb44431b8ba4bfdd93758c15f55178042da1f1ccee353cec9f70823ddc7928d6619b1c0c32a82a13f42ceb95824ef0e5c8e2a
7
+ data.tar.gz: cd977e2df4ecc121785d3c28158020883f895f0922b3973c21de401c54e0f6937f52d0655af6b26d3ccf6bc84d3641968b4a41972cbe22e98a30a20cb15da71e
data/.dockerignore ADDED
@@ -0,0 +1 @@
1
+ /data/
data/.gitignore ADDED
@@ -0,0 +1,53 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+
52
+ # import data
53
+ /data/
data/Dockerfile ADDED
@@ -0,0 +1,12 @@
1
+ FROM ruby:2.6
2
+
3
+ ENV LANG C.UTF-8
4
+
5
+ WORKDIR /masking
6
+
7
+ ADD Gemfile /masking/Gemfile
8
+ ADD Gemfile.lock /masking/Gemfile.lock
9
+
10
+ RUN bundle install
11
+
12
+ COPY . .
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'ffaker'
4
+ gem 'mongo'
5
+ gem 'irb'
6
+ gem 'thor'
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ bson (4.4.2)
5
+ ffaker (2.10.0)
6
+ irb (1.0.0)
7
+ mongo (2.7.0)
8
+ bson (>= 4.4.2, < 5.0.0)
9
+ thor (0.20.3)
10
+
11
+ PLATFORMS
12
+ ruby
13
+
14
+ DEPENDENCIES
15
+ ffaker
16
+ irb
17
+ mongo
18
+ thor
19
+
20
+ BUNDLED WITH
21
+ 1.17.1
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Mongodb-data-masking
2
+ Masking your data in mongodb
3
+
4
+ # Usage
5
+ ```
6
+ bundle
7
+ ruby masker.rb mask mask.yml
8
+ ```
9
+
10
+ # Example mask.yml
11
+ ```yaml
12
+ version: 1
13
+ db_url: mongodb://mongodb:27017/development
14
+ models:
15
+ - name: users
16
+ condition:
17
+ email:
18
+ "$not": !ruby/regexp '/@basicinc\.jp$/'
19
+ fields:
20
+ email: FFaker::Internet.safe_email
21
+ - name: users
22
+ fields:
23
+ reset_password_token: String.new
24
+ confirmation_token: String.new
25
+ - name: sitesconta
26
+ fields:
27
+ title: FFaker::NameJA.name
28
+ description: FFaker::LoremJA.sentence
29
+ domain: FFaker::Internet.domain_name
30
+ external_service: :external_services
31
+ - name: external_services
32
+ fields:
33
+ _type: "'ExternalService'"
34
+ facebook: nil
35
+ google: nil
36
+ - name: contacts
37
+ condition:
38
+ site_id:
39
+ "$ne": BSON::ObjectId('12312345346456456456sdff')
40
+ delete: true
41
+ ```
data/bin/mongo_masker ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mongo_masker'
4
+ require 'thor'
5
+
6
+ class MongoMasker::Cli < Thor
7
+ include Thor::Actions
8
+
9
+ desc 'mask MASKER_FILE', 'Mask database'
10
+ def mask(masker_file)
11
+ masker = MongoMasker::Main.new(masker_file)
12
+ masker.mask
13
+ end
14
+ end
15
+
16
+ MongoMasker::Cli.start
@@ -0,0 +1,20 @@
1
+ version: '3'
2
+ services:
3
+ mongodb:
4
+ image: mongo:3.4.2
5
+ volumes:
6
+ - mongo-data:/data/db
7
+ - ./data:/tmp/data
8
+
9
+ app:
10
+ build: ./
11
+ links:
12
+ - mongodb
13
+ volumes:
14
+ - ./lib:/masking/lib
15
+ - ./bin:/masking/bin
16
+ stdin_open: true
17
+ tty: true
18
+
19
+ volumes:
20
+ mongo-data:
@@ -0,0 +1,141 @@
1
+ require 'yaml'
2
+ require 'ffaker'
3
+ require 'mongo'
4
+
5
+ Mongo::Logger.logger.level = Logger::FATAL
6
+
7
+ module MongoMasker
8
+ class Main
9
+ def initialize(config = nil)
10
+ configure config unless config.nil?
11
+ @sequence = 0
12
+ end
13
+
14
+ def db
15
+ @db ||= Mongo::Client.new(@config['db_url'] || 'mongodb://mongodb:27017/development')
16
+ end
17
+
18
+ def mask(config = nil)
19
+ configure config unless config.nil?
20
+ raise 'Please provide mask' if @config.nil?
21
+
22
+ track_time do
23
+ @config['models'].each do |model|
24
+ mask_document model
25
+ end
26
+ puts 'Done!' unless @config['silent']
27
+ end
28
+ end
29
+
30
+ def configure(config)
31
+ @config = config.is_a?(String) ? load_from_yaml(config) : config
32
+ end
33
+
34
+ def seq
35
+ @sequence += 1
36
+ end
37
+
38
+ private
39
+
40
+ def track_time
41
+ start_at = Time.now
42
+ yield
43
+ finish_at = Time.now
44
+ puts "Elapsed time: #{format_time_diff(start_at, finish_at)}" unless @config['silent']
45
+ end
46
+
47
+ def format_time_diff(start_at, finish_at)
48
+ output = Time.at(finish_at - start_at).strftime '%H hours %M minutes %S seconds'
49
+ output.gsub(/^0+ hours /, '').gsub(/^0+ minutes /, '')
50
+ end
51
+
52
+ def mask_document(model)
53
+ scope = prepair_scope model
54
+
55
+ if model['delete']
56
+ delete_documents scope, model
57
+ else
58
+ mask_each_document scope, model
59
+ end
60
+ rescue StandardError => e
61
+ puts "\nCan't mask #{model['name']}" unless @config['silent']
62
+ raise e
63
+ ensure
64
+ puts '' unless @config['silent']
65
+ end
66
+
67
+ def delete_documents(scope, model)
68
+ puts "Deleting #{model['name']}"
69
+ scope.delete_many
70
+ end
71
+
72
+ def mask_each_document(scope, model)
73
+ total = scope.count()
74
+
75
+ scope.each_with_index do |document, index|
76
+ print "Masking #{model['name']} (#{index + 1}/#{total})\r" unless @config['silent']
77
+ mask = create_mask model['fields']
78
+
79
+ apply_mask(document, mask)
80
+ db[model['name']].find({_id: document['_id']}).update_one(document)
81
+ end
82
+ end
83
+
84
+ def prepair_scope(model)
85
+ scope = db[model['name']]
86
+ scope.find(parse_condition(model['condition']))
87
+ end
88
+
89
+ def parse_condition condition
90
+ if condition.is_a?(String) && condition.match?(/^BSON::ObjectId\('[A-Za-z0-9]+'\)$/)
91
+ return eval(condition)
92
+ end
93
+
94
+ if condition.is_a?(Hash)
95
+ condition.each do |op, value|
96
+ condition[op] = parse_condition value
97
+ end
98
+ return condition
99
+ end
100
+
101
+ if condition.is_a?(Array)
102
+ return condition.map do |value|
103
+ parse_condition value
104
+ end
105
+ end
106
+
107
+ condition
108
+ end
109
+
110
+ def create_mask(fields)
111
+ mask = {}
112
+ fields.each do |field, value|
113
+ if value.is_a?(Symbol)
114
+ sub_mask = create_mask @config['models'].find{|model| model['name'] == value.to_s}['fields']
115
+ mask[field] = sub_mask
116
+ next
117
+ end
118
+
119
+ mask[field] = evalute_field_value(value)
120
+ end
121
+ mask
122
+ end
123
+
124
+ def apply_mask(document, mask)
125
+ mask = mask.reject do |field, _value|
126
+ document[field].nil?
127
+ end
128
+ document.update(mask)
129
+ end
130
+
131
+ def evalute_field_value(value)
132
+ eval(value)
133
+ rescue StandardError
134
+ raise "Can't eval `#{value}`"
135
+ end
136
+
137
+ def load_from_yaml(config_path)
138
+ YAML.load_file config_path
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mongo_masker'
3
+ s.version = '1.0.0'
4
+ s.date = '2019-02-19'
5
+ s.summary = 'Mongo masker'
6
+ s.description = 'Masking production data mongodb for testing/development'
7
+ s.authors = ['Clicia Scarlet']
8
+ s.email = ['pnvduc@gmail.com']
9
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
10
+ s.bindir = 'bin'
11
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
12
+ s.require_paths = ['lib']
13
+ s.homepage = 'https://github.com/basicinc/Masker'
14
+ s.license = 'MIT'
15
+
16
+ s.add_runtime_dependency 'thor'
17
+ s.add_runtime_dependency 'mongo'
18
+ s.add_runtime_dependency 'ffaker'
19
+ s.add_runtime_dependency 'irb'
20
+
21
+ s.add_development_dependency 'bundler'
22
+ s.add_development_dependency 'rake'
23
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo_masker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Clicia Scarlet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mongo
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ffaker
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: irb
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Masking production data mongodb for testing/development
98
+ email:
99
+ - pnvduc@gmail.com
100
+ executables:
101
+ - mongo_masker
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".dockerignore"
106
+ - ".gitignore"
107
+ - Dockerfile
108
+ - Gemfile
109
+ - Gemfile.lock
110
+ - README.md
111
+ - bin/mongo_masker
112
+ - docker-compose.yml
113
+ - lib/mongo_masker.rb
114
+ - mongo_masker.gemspec
115
+ homepage: https://github.com/basicinc/Masker
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.7.6
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Mongo masker
139
+ test_files: []