attr_masker 0.1.0 → 0.3.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/.github/workflows/tests.yml +91 -0
- data/.gitignore +5 -1
- data/.rubocop.yml +13 -1069
- data/CHANGELOG.adoc +31 -0
- data/Gemfile +5 -0
- data/README.adoc +81 -30
- data/Rakefile +0 -27
- data/attr_masker.gemspec +15 -10
- data/bin/console +14 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +9 -0
- data/gemfiles/Rails-4.2.gemfile +2 -3
- data/gemfiles/Rails-5.0.gemfile +2 -3
- data/gemfiles/Rails-5.1.gemfile +2 -3
- data/gemfiles/Rails-5.2.gemfile +4 -0
- data/gemfiles/Rails-6.0.gemfile +3 -0
- data/gemfiles/Rails-6.1.gemfile +3 -0
- data/gemfiles/Rails-head.gemfile +1 -3
- data/gemfiles/common.gemfile +4 -0
- data/lib/attr_masker.rb +6 -210
- data/lib/attr_masker/attribute.rb +80 -0
- data/lib/attr_masker/error.rb +1 -0
- data/lib/attr_masker/maskers/replacing.rb +20 -3
- data/lib/attr_masker/maskers/simple.rb +20 -5
- data/lib/attr_masker/model.rb +143 -0
- data/lib/attr_masker/performer.rb +56 -17
- data/lib/attr_masker/version.rb +1 -16
- data/lib/tasks/db.rake +13 -4
- data/spec/dummy/app/models/non_persisted_model.rb +2 -0
- data/spec/dummy/config/attr_masker.rb +1 -0
- data/spec/dummy/config/mongoid.yml +33 -0
- data/spec/dummy/config/routes.rb +0 -1
- data/spec/dummy/db/schema.rb +1 -0
- data/spec/features/active_record_spec.rb +97 -0
- data/spec/features/mongoid_spec.rb +36 -0
- data/spec/features/shared_examples.rb +382 -0
- data/spec/spec_helper.rb +26 -3
- data/spec/support/00_control_constants.rb +2 -0
- data/spec/support/10_mongoid_env.rb +9 -0
- data/spec/support/20_combustion.rb +10 -0
- data/spec/support/db_cleaner.rb +13 -2
- data/spec/support/force_config_file_reload.rb +9 -0
- data/spec/support/rake.rb +1 -1
- data/spec/unit/attribute_spec.rb +210 -0
- data/spec/{maskers → unit/maskers}/replacing_spec.rb +0 -0
- data/spec/{maskers → unit/maskers}/simple_spec.rb +2 -2
- data/spec/unit/model_spec.rb +12 -0
- data/spec/unit/rake_task_spec.rb +30 -0
- metadata +139 -32
- data/.travis.yml +0 -32
- data/gemfiles/Rails-4.0.gemfile +0 -5
- data/gemfiles/Rails-4.1.gemfile +0 -5
- data/spec/features_spec.rb +0 -203
- data/spec/support/0_combustion.rb +0 -5
data/CHANGELOG.adoc
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
== Not released yet
|
2
|
+
|
3
|
+
* Drop support for Rails < 4.2
|
4
|
+
* Allow masking attributes which span on more than one column (or field),
|
5
|
+
supersede `column_name` option with `column_names`
|
6
|
+
|
7
|
+
== 0.2.1
|
8
|
+
|
9
|
+
* Bugfix: preload application to find all models
|
10
|
+
* Bugfix: require all dependencies correctly
|
11
|
+
|
12
|
+
== 0.2.0
|
13
|
+
|
14
|
+
* `AttrMasker::Maskers::Simple` is now a class
|
15
|
+
* Added support for Mongoid
|
16
|
+
|
17
|
+
== 0.1.1
|
18
|
+
|
19
|
+
* Mask records disregarding default scope
|
20
|
+
(https://github.com/riboseinc/attr_masker/pull/41[#41])
|
21
|
+
* Major refactoring (extracting `Attribute` and `Model` classes)
|
22
|
+
* Code style improvements (nearly all violations fixed)
|
23
|
+
|
24
|
+
== 0.1
|
25
|
+
|
26
|
+
* First useful version
|
27
|
+
* Rails 4 & 5 compatibility
|
28
|
+
* Callable objects as maskers
|
29
|
+
* Nice progress bar
|
30
|
+
* Built-in maskers: `AttrMasker::Maskers::Replacing`
|
31
|
+
and `AttrMasker::Maskers::SIMPLE`
|
data/Gemfile
CHANGED
data/README.adoc
CHANGED
@@ -3,17 +3,33 @@
|
|
3
3
|
:pygments-style: native
|
4
4
|
:pygments-linenums-mode: inline
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
ifdef::env-github[]
|
7
|
+
image:https://img.shields.io/gem/v/attr_masker[
|
8
|
+
"Gem Version",
|
9
|
+
link="https://rubygems.org/gems/attr_masker"]
|
10
|
+
image:https://img.shields.io/github/workflow/status/riboseinc/attr_masker/Tests[
|
11
|
+
"Build Status",
|
12
|
+
link="https://github.com/riboseinc/attr_masker/actions"]
|
13
|
+
image:https://img.shields.io/codeclimate/maintainability/riboseinc/attr_masker[
|
14
|
+
"Code Climate",
|
15
|
+
link="https://codeclimate.com/github/riboseinc/attr_masker"]
|
16
|
+
image:https://img.shields.io/codecov/c/github/riboseinc/attr_masker[
|
17
|
+
"Test Coverage",
|
18
|
+
link="https://codecov.io/gh/riboseinc/attr_masker"]
|
19
|
+
image:https://img.shields.io/badge/documentation-rdoc-informational[
|
20
|
+
"Documentation on RubyDoc.info",
|
21
|
+
link="https://rubydoc.info/gems/attr_masker"]
|
22
|
+
endif::[]
|
23
|
+
|
24
|
+
Mask ActiveRecord/Mongoid data with ease!
|
9
25
|
|
10
26
|
== Introduction
|
11
27
|
|
12
28
|
This gem is intended to mask sensitive data so that production database dumps
|
13
|
-
can be used in staging or test environments. It works with
|
14
|
-
|
29
|
+
can be used in staging or test environments. It works with Rails 4.2+ and
|
30
|
+
modern Rubies. It supports Active Record and Mongoid models.
|
15
31
|
|
16
|
-
==
|
32
|
+
== Usage instructions
|
17
33
|
|
18
34
|
=== Installation
|
19
35
|
|
@@ -68,33 +84,16 @@ attr_masker :email :unless => ->(record) { ! record.tester_user? }
|
|
68
84
|
attr_masker :first_name, :if => :tester_user?
|
69
85
|
----
|
70
86
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
can be used instead: a lambda, `Method` instance, and more. You can specify it
|
76
|
-
by setting the `:masker` option.
|
77
|
-
|
78
|
-
For instance, you may want to use https://github.com/ffaker/ffaker[ffaker] or
|
79
|
-
https://github.com/skalee/well_read_faker[Well Read Faker] to generate random
|
80
|
-
replacement values:
|
81
|
-
|
82
|
-
[source,ruby]
|
83
|
-
----
|
84
|
-
require "ffaker"
|
85
|
-
|
86
|
-
attr_masker :first_name, :masker => ->(_hash) { FFaker::Name.first_name }
|
87
|
-
----
|
88
|
-
|
89
|
-
A hash is passed as an argument, which includes some information about the
|
90
|
-
record being masked and the attribute value. It can be used to further
|
91
|
-
customize masker's behaviour.
|
87
|
+
The ActiveRecord's `::default_scope` method has no effect on masking. All
|
88
|
+
table records are updated, provided that :if and :unless filters allow that.
|
89
|
+
For example, if you're using a https://github.com/rubysherpas/paranoia[Paranoia]
|
90
|
+
gem to soft-delete your data, records marked as deleted will be masked as well.
|
92
91
|
|
93
92
|
=== Built-in maskers
|
94
93
|
|
95
94
|
Attr Masker comes with several built-in maskers.
|
96
95
|
|
97
|
-
`AttrMasker::Maskers::
|
96
|
+
`AttrMasker::Maskers::Simple`::
|
98
97
|
+
|
99
98
|
Simply replaces any value with the `"(redacted)"`. Only useful for columns
|
100
99
|
containing textual data.
|
@@ -106,7 +105,7 @@ Example:
|
|
106
105
|
[source,ruby]
|
107
106
|
----
|
108
107
|
attr_masker :first_name
|
109
|
-
attr_masker :last_name, :masker => AttrMasker::Maskers::
|
108
|
+
attr_masker :last_name, :masker => AttrMasker::Maskers::Simple.new
|
110
109
|
----
|
111
110
|
+
|
112
111
|
Would set both `first_name` and `last_name` attributes to `"(redacted)"`.
|
@@ -120,7 +119,7 @@ Can be initialized with options.
|
|
120
119
|
|===============================================================================
|
121
120
|
|Name|Default|Description
|
122
121
|
|`replacement`|`"*"`|Replacement string, can be empty.
|
123
|
-
|`alphanum_only`|`false`|When true, only alphanumeric
|
122
|
+
|`alphanum_only`|`false`|When true, only alphanumeric characters are replaced.
|
124
123
|
|===============================================================================
|
125
124
|
+
|
126
125
|
Example:
|
@@ -133,7 +132,59 @@ attr_masker :phone, :masker => rm
|
|
133
132
|
+
|
134
133
|
Would mask "123-456-7890" as "XXX-XXX-XXXX".
|
135
134
|
|
135
|
+
=== Using custom maskers
|
136
|
+
|
137
|
+
Apart from built-in maskers, any object which responds to `#call` can be used,
|
138
|
+
e.g. some lambda or `Method` instance. For instance, you may want to produce
|
139
|
+
unique values basing on other attributes, to mask selectively, or to use
|
140
|
+
tool like https://github.com/skalee/well_read_faker[Well Read Faker] to
|
141
|
+
generate random replacement values:
|
142
|
+
|
143
|
+
[source,ruby]
|
144
|
+
----
|
145
|
+
require "well_read_faker"
|
146
|
+
|
147
|
+
attr_masker :email, masker: ->(model:, **) { "user#{model.id}@example.com" }
|
148
|
+
attr_masker :phone, masker: ->(value:, **) { "******" + value[-3..-1] }
|
149
|
+
attr_masker :bio, masker: ->(**) { WellReadFaker.paragraph }
|
150
|
+
----
|
151
|
+
|
152
|
+
Masker is called with following keyword arguments:
|
153
|
+
|
154
|
+
`value`:: Original value of the field which is about to be masked
|
155
|
+
|
156
|
+
`model`:: Model instance
|
157
|
+
|
158
|
+
`attribute_name`:: Name of the attribute which is about to be masked
|
159
|
+
|
160
|
+
`masking_options`:: Hash of options which were passed in `#attr_masker` call
|
161
|
+
|
162
|
+
This list is likely to be extended in future versions, and that will not be
|
163
|
+
considered a breaking change, therefore it is strongly recommended to always
|
164
|
+
use a splat (`**`) at end of argument list of masker's `#call` method.
|
165
|
+
|
166
|
+
=== Configuration file
|
167
|
+
|
168
|
+
It is also possible to contain all the maskers configuration in one file.
|
169
|
+
Just place it in `config/attr_masker.rb`, and it will be loaded from a Rake
|
170
|
+
task after the application is fully loaded. That means you can re-open classes
|
171
|
+
and add masker definitions there, for example:
|
172
|
+
|
173
|
+
[source,ruby]
|
174
|
+
----
|
175
|
+
# config/attr_masker.rb
|
176
|
+
|
177
|
+
class User
|
178
|
+
attr_masker :first_name, :last_name
|
179
|
+
end
|
180
|
+
|
181
|
+
class Email
|
182
|
+
attr_masker :address, ->(model:, **) { "mail#{model.id}@example.com" }
|
183
|
+
end
|
184
|
+
----
|
185
|
+
|
136
186
|
== Roadmap & TODOs
|
187
|
+
|
137
188
|
- documentation
|
138
189
|
- spec tests
|
139
190
|
- Make the `Rails.env` (in which `db:mask` could be run) configurable
|
data/Rakefile
CHANGED
@@ -1,32 +1,5 @@
|
|
1
1
|
# (c) 2017 Ribose Inc.
|
2
2
|
#
|
3
3
|
|
4
|
-
# require 'rake'
|
5
|
-
# require 'rake/testtask'
|
6
|
-
# require 'rake/rdoctask'
|
7
|
-
|
8
4
|
require "bundler/gem_tasks"
|
9
5
|
load "tasks/db.rake"
|
10
|
-
|
11
|
-
# desc 'Generate documentation for the attr_masker gem.'
|
12
|
-
# Rake::RDocTask.new(:rdoc) do |rdoc|
|
13
|
-
# rdoc.rdoc_dir = 'rdoc'
|
14
|
-
# rdoc.title = 'attr_masker'
|
15
|
-
# rdoc.options << '--line-numbers' << '--inline-source'
|
16
|
-
# rdoc.rdoc_files.include('README*')
|
17
|
-
# rdoc.rdoc_files.include('lib/**/*.rb')
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# if RUBY_VERSION < '1.9.3'
|
21
|
-
# require 'rcov/rcovtask'
|
22
|
-
#
|
23
|
-
# task :rcov do
|
24
|
-
# system "rcov -o coverage/rcov --exclude '^(?!lib)' " + FileList[ 'test/**/*_test.rb' ].join(' ')
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# desc 'Default: run unit tests under rcov.'
|
28
|
-
# task :default => :rcov
|
29
|
-
# else
|
30
|
-
# desc 'Default: run unit tests.'
|
31
|
-
# task :default => :test
|
32
|
-
# end
|
data/attr_masker.gemspec
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# (c) 2017 Ribose Inc.
|
2
2
|
#
|
3
3
|
|
4
|
-
lib = File.expand_path("
|
4
|
+
lib = File.expand_path("lib", __dir__)
|
5
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
6
|
require "attr_masker/version"
|
7
7
|
|
8
8
|
Gem::Specification.new do |gem|
|
9
9
|
gem.name = "attr_masker"
|
10
|
-
gem.version = AttrMasker::
|
10
|
+
gem.version = AttrMasker::VERSION
|
11
11
|
gem.authors = ["Ribose Inc."]
|
12
12
|
gem.email = ["open.source@ribose.com"]
|
13
13
|
gem.homepage = "https://github.com/riboseinc/attr_masker"
|
@@ -17,18 +17,23 @@ Gem::Specification.new do |gem|
|
|
17
17
|
"of certain models by modifying the database."
|
18
18
|
|
19
19
|
gem.files = `git ls-files`.split($/)
|
20
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
20
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
21
|
gem.require_paths = ["lib"]
|
23
22
|
|
24
|
-
gem.add_runtime_dependency("rails", ">= 4.0.0", "<
|
23
|
+
gem.add_runtime_dependency("rails", ">= 4.0.0", "< 7")
|
25
24
|
gem.add_runtime_dependency("ruby-progressbar", "~> 1.8")
|
26
25
|
|
27
|
-
gem.add_development_dependency("bundler", "
|
28
|
-
gem.add_development_dependency("combustion", "~>
|
29
|
-
gem.add_development_dependency("database_cleaner", "~> 1.
|
26
|
+
gem.add_development_dependency("bundler", ">= 1.15")
|
27
|
+
gem.add_development_dependency("combustion", "~> 1.0")
|
28
|
+
gem.add_development_dependency("database_cleaner", "~> 1.8")
|
29
|
+
gem.add_development_dependency("database_cleaner-active_record", "~> 1.8")
|
30
|
+
gem.add_development_dependency("database_cleaner-mongoid", "~> 1.8")
|
31
|
+
# Older versions aren't needed as we don't support Rails < 4
|
32
|
+
gem.add_development_dependency("mongoid", ">= 5")
|
30
33
|
gem.add_development_dependency("pry")
|
31
|
-
gem.add_development_dependency("rspec", "
|
32
|
-
gem.add_development_dependency("rubocop", "~> 0.
|
33
|
-
gem.add_development_dependency("
|
34
|
+
gem.add_development_dependency("rspec", "~> 3.0")
|
35
|
+
gem.add_development_dependency("rubocop", "~> 0.54.0")
|
36
|
+
gem.add_development_dependency("simplecov")
|
37
|
+
gem.add_development_dependency("sqlite3", ">= 1.3.13", "< 2")
|
38
|
+
gem.add_development_dependency("warning", "~> 1.1")
|
34
39
|
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rack/cleanser"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# rubocop:disable all
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake") if File.exists?(Gem.bin_path("rake", "rake"))
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# rubocop:disable all
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# rubocop:disable all
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/setup
ADDED
data/gemfiles/Rails-4.2.gemfile
CHANGED
data/gemfiles/Rails-5.0.gemfile
CHANGED
data/gemfiles/Rails-5.1.gemfile
CHANGED