attr_masker 0.1.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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