obfuscator 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "activerecord"
4
+ gem "faker"
5
+
6
+ group :test do
7
+ gem "rspec"
8
+ gem "sqlite3"
9
+ gem "pry"
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Bryan Woods
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Obfuscator
2
+
3
+ Working from a production database dump can often be helpful for
4
+ debugging strange errors and edge cases, but having potentially
5
+ sensitive user data on a development machine is a dangerous liability.
6
+
7
+ Obfuscator provides a clean, friendly API for obfuscating sensitive
8
+ columns in your Ruby on Rails application's models.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'obfuscator'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install obfuscator
23
+
24
+ ## Usage
25
+
26
+ As a Ruby program:
27
+
28
+ ```ruby
29
+ # Without a format, Obfuscator will fill all of the given columns
30
+ # with dummy data based on the column's SQL type
31
+ Obfuscator.scrub! "Message" do
32
+ # Would generate dummy sentences, paragraphs, and timestamps accordingly
33
+ overwrite :title, :body, :created_at
34
+ end
35
+
36
+ # Currently any format from Faker::Internet
37
+ # (https://github.com/stympy/faker/blob/master/lib/faker/internet.rb)
38
+ # should work for when the generated data needs to be in a specific format
39
+ Obfuscator.scrub! "User" do
40
+ overwrite :login do
41
+ format :user_name
42
+ end
43
+ end
44
+
45
+ Obfuscator.scrub! "Subscriber" do
46
+ overwrite :email_address do
47
+ format :email
48
+ end
49
+ end
50
+ ```
51
+
52
+ Or as a Rake task (TODO: Not finished yet):
53
+
54
+ rake obfuscator:scrub["User", ["login","email"]]
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
63
+
64
+ ## TODO
65
+
66
+ * Get Rake task fully working
67
+ * Move away from iterating through all records and calling
68
+ update_attributes. Better: finding in batches. Best: Generating a
69
+ giant update_all.
70
+ * Definite jankiness with the Dsl (especially that you can't pass
71
+ multiple overwrite/format blocks in one parent block currently)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,47 @@
1
+ module Obfuscator
2
+ module Dsl
3
+ module ClassMethods
4
+ def scrub!(model, &block)
5
+ instance_eval(&block)
6
+
7
+ obfuscator = Obfuscator::Generic.new
8
+
9
+ obfuscator.scrub!(model, columns)
10
+ end
11
+
12
+ def overwrite(*columns, &block)
13
+ if block_given?
14
+ @columns = columns
15
+
16
+ return instance_eval(&block)
17
+ end
18
+
19
+ if columns.length == 1
20
+ @columns.push(columns.first)
21
+ else
22
+ @columns.push(columns)
23
+ end
24
+ end
25
+
26
+ def columns
27
+ if @columns.is_a?(Array)
28
+ @columns.flatten.uniq
29
+ else
30
+ @columns
31
+ end
32
+ end
33
+
34
+ def format(format)
35
+ @columns = { columns.first => format }
36
+ end
37
+ end
38
+
39
+ def self.included(base)
40
+ base.extend(ClassMethods)
41
+
42
+ base.module_eval do
43
+ @columns = []
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ module Obfuscator
2
+ class Generic
3
+ class UnkownObfuscationTypeError < StandardError; end
4
+
5
+ attr_accessor :model, :columns
6
+
7
+ def scrub!(model_name = "User", columns = [])
8
+ @model = model_name.singularize.constantize
9
+
10
+ if columns.is_a?(Hash)
11
+ store_types_from_column_values(columns)
12
+ @columns = columns.keys
13
+ else
14
+ @columns = columns
15
+ end
16
+
17
+ return unless @columns.any? and @columns.map!(&:to_s)
18
+
19
+ scrub_all_records!
20
+ end
21
+
22
+ private
23
+
24
+ def model_columns_contain_given?
25
+ (@model.columns_hash.keys | @columns).any?
26
+ end
27
+
28
+ def store_types_from_column_values(columns)
29
+ columns.each do |key, value|
30
+ instance_variable_set("@#{key}_type", value)
31
+ end
32
+ end
33
+
34
+ def columns_with_obfuscated_values_hash
35
+ result_hash = {}
36
+
37
+ @columns.each do |column|
38
+ type = instance_variable_get("@#{column}_type")
39
+
40
+ if type.present?
41
+ if Faker::Internet.respond_to?(type)
42
+ result_hash[column] = Faker::Internet.send(type)
43
+ @result = result_hash
44
+ else
45
+ raise UnkownObfuscationTypeError.new("[#{type}] is an unknown type")
46
+ end
47
+ else
48
+ derive_value_from_type(@model.columns_hash[column].type)
49
+
50
+ @result = Hash[@columns.map { |key| [key, @value] }]
51
+ end
52
+ end
53
+
54
+ @result
55
+ end
56
+
57
+ def derive_value_from_type(sql_type)
58
+ default = Faker::Lorem.sentence
59
+
60
+ case sql_type
61
+ when :string
62
+ @value = default
63
+ when :text
64
+ @value = Faker::Lorem.paragraph
65
+ when :integer
66
+ @value = Obfuscator::Utilities.random_number(10)
67
+ when :boolean
68
+ @value = Obfuscator::Utilities.random_boolean
69
+ when :datetime
70
+ @value = Obfuscator::Utilities.random_date
71
+ else
72
+ @value = default
73
+ end
74
+ end
75
+
76
+ def scrub_all_records!
77
+ @model.all.each do |m|
78
+ attributes = model_columns_contain_given? ?
79
+ columns_with_obfuscated_values_hash : {}
80
+
81
+ m.update_attributes(attributes)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,9 @@
1
+ module Obfuscator
2
+ class Railtie < Rails::Railtie
3
+ railtie_name :obfuscator
4
+
5
+ rake_tasks do
6
+ load "tasks/obfuscator.rake"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Obfuscator
2
+ class Utilities
3
+ class << self
4
+ def random_number(limit)
5
+ rand(limit)
6
+ end
7
+
8
+ def random_boolean
9
+ [true, false].sample
10
+ end
11
+
12
+ def random_date
13
+ month, day, year = rand(12) + 1, rand(28) + 1, (1980..2012).to_a.sample
14
+
15
+ Date.parse("#{day}/#{month}/#{year}")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Obfuscator
2
+ VERSION = "0.0.1"
3
+ end
data/lib/obfuscator.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "obfuscator/version"
2
+ require "obfuscator/utilities"
3
+ require "obfuscator/generic"
4
+ require "obfuscator/dsl"
5
+ require "obfuscator/railtie" if defined?(Rails)
6
+
7
+ module Obfuscator
8
+ include Dsl
9
+ end
@@ -0,0 +1,12 @@
1
+ namespace :obfuscator do
2
+ desc "Scrub sensitive data in provided model's columns"
3
+
4
+ task :scrub, [:model, :columns] => :environment do |task, arguments|
5
+ model = arguments[:model]
6
+ columns = arguments[:columns]
7
+
8
+ obfuscator = Obfuscator::Generic.new
9
+
10
+ obfuscator.scrub!(model, columns)
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/obfuscator/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Bryan Woods"]
6
+ gem.email = ["bryanwoods4e@gmail.com"]
7
+ gem.description = "A clean, friendly API for obfuscating slightly sensitive data from a Rails application"
8
+ gem.summary = "Obfuscate provides an easy way to quickly overwrite slightly sensitive data from a Rails application for debugging or developing off a database dump"
9
+ gem.homepage = "http://github.com/bryanwoods/obfuscator"
10
+ gem.licenses = ["MIT"]
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "obfuscator"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Obfuscator::VERSION
18
+ gem.add_dependency "activerecord"
19
+ gem.add_dependency "faker"
20
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Obfuscator::Dsl do
4
+ before(:each) do
5
+ Obfuscator::Dsl.instance_variable_set(:@columns, [])
6
+
7
+ User.create!(
8
+ login: "login",
9
+ email: "email@example.com",
10
+ password: "pword",
11
+ birthdate: 20.years.ago
12
+ )
13
+
14
+ User.create!(
15
+ login: "login2",
16
+ email: "email2@example.com",
17
+ password: "pword"
18
+ )
19
+ end
20
+
21
+ describe ".scrub!" do
22
+ it "Scrubs the model with the columns given in a block" do
23
+ Obfuscator::Generic.any_instance.should_receive(:scrub!).
24
+ with("User", [:login, :email])
25
+
26
+ Obfuscator.scrub!("User") do
27
+ overwrite :login
28
+ overwrite :email
29
+ end
30
+ end
31
+ end
32
+
33
+ describe ".overwrite" do
34
+ context "given one column" do
35
+ it "stores the given column in an array" do
36
+ Obfuscator.overwrite(:login)
37
+ Obfuscator.overwrite(:email)
38
+ Obfuscator.overwrite(:birth_date)
39
+
40
+ Obfuscator.send(:columns).should == [:login, :email, :birth_date]
41
+ end
42
+
43
+ context "given more than one column" do
44
+ it "stores the given columns in an array" do
45
+ Obfuscator.overwrite(:login, :email, :birth_date)
46
+
47
+ Obfuscator.send(:columns).should == [:login, :email, :birth_date]
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ describe ".format" do
54
+ it "sets the type of the column to the given format" do
55
+ Obfuscator::Generic.any_instance.should_receive(:scrub!).
56
+ with("User", { login: :user_name })
57
+
58
+ Obfuscator.scrub!("User") do
59
+ overwrite :login do
60
+ format :user_name
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ describe Obfuscator::Generic do
4
+ before(:each) do
5
+ User.create!(
6
+ login: "login",
7
+ email: "email@example.com",
8
+ password: "pword",
9
+ birthdate: 20.years.ago
10
+ )
11
+
12
+ User.create!(
13
+ login: "login2",
14
+ email: "email2@example.com",
15
+ password: "pword"
16
+ )
17
+ end
18
+
19
+ let(:obfuscator) { Obfuscator::Generic.new }
20
+ let(:first_user) { User.first }
21
+ let(:last_user) { User.last }
22
+
23
+ describe ".scrub!" do
24
+ context "given a model" do
25
+ it "assigns @model to the given model name" do
26
+ obfuscator.scrub!("Person")
27
+ obfuscator.model.should == Person
28
+ end
29
+ end
30
+
31
+ context "not given a model" do
32
+ it "defaults to 'User'" do
33
+ obfuscator.scrub!
34
+ obfuscator.model.should == User
35
+ end
36
+ end
37
+
38
+ context "given an array of columns" do
39
+ it "updates the given columns for each record" do
40
+ first_user.login.should == "login"
41
+ last_user.login.should == "login2"
42
+
43
+ obfuscator.scrub!("User", [:login])
44
+
45
+ first_user.reload.login.should_not == "login"
46
+ last_user.reload.login.should_not == "login2"
47
+ end
48
+
49
+ it "obfuscates the given columns with dummy data" do
50
+ Faker::Lorem.should_receive(:sentence).any_number_of_times.
51
+ and_return("The quick brown fox")
52
+
53
+ obfuscator.scrub!("User", [:email])
54
+
55
+ first_user.reload.email.should == "The quick brown fox"
56
+ end
57
+
58
+ context "given a text column" do
59
+ let(:paragraph) do
60
+ "A slightly long section of text, one that would perhaps be too
61
+ long to be stored as a string and might therefore be better stored
62
+ as a text blizzle bluzzle."
63
+ end
64
+
65
+ it "obfuscates the column with a dummy paragraph" do
66
+ Faker::Lorem.should_receive(:paragraph).any_number_of_times.
67
+ and_return(paragraph)
68
+
69
+ obfuscator.scrub!("User", [:bio])
70
+
71
+ last_user.reload.bio.should == paragraph
72
+ end
73
+ end
74
+
75
+ context "given an integer column" do
76
+ it "obfuscates the column with a dummy number" do
77
+ Obfuscator::Utilities.should_receive(:random_number).
78
+ any_number_of_times.with(10)
79
+
80
+ obfuscator.scrub!("User", [:id])
81
+ end
82
+ end
83
+
84
+ context "given a boolean column" do
85
+ let!(:person) { Person.create!(good_looking: true) }
86
+
87
+ it "obfuscates the column with a random true or false" do
88
+ Obfuscator::Utilities.should_receive(:random_boolean).
89
+ and_return(false)
90
+
91
+ obfuscator.scrub!("Person", [:good_looking])
92
+
93
+ person.reload.good_looking.should be_false
94
+ end
95
+ end
96
+
97
+ context "given a datetime column" do
98
+ it "obfuscates the column with a random date" do
99
+ Obfuscator::Utilities.stub(:random_date).any_number_of_times.
100
+ and_return(1.year.ago.beginning_of_day)
101
+
102
+ obfuscator.scrub!("User", [:birthdate])
103
+
104
+ first_user.reload.birthdate.should == 1.year.ago.beginning_of_day
105
+ end
106
+ end
107
+
108
+ context "given a string column" do
109
+ context "not given a type parameter" do
110
+ it "obfuscates the column with a dummy sentence" do
111
+ Faker::Lorem.should_receive(:sentence).any_number_of_times.
112
+ and_return("The quick brown fox")
113
+
114
+ obfuscator.scrub!("User", [:login])
115
+
116
+ first_user.reload.login.should == "The quick brown fox"
117
+ end
118
+
119
+ it "generates unique dummy sentences" do
120
+ obfuscator.scrub!("User", [:login])
121
+
122
+ first_user.reload.login.should_not == last_user.reload.login
123
+ end
124
+ end
125
+
126
+ context "given a type parameter" do
127
+ context "given valid type parameters" do
128
+ it "generates a plausible user_name" do
129
+ Faker::Internet.should_receive(:user_name).
130
+ any_number_of_times.
131
+ and_return("bryanawesome")
132
+
133
+ Faker::Internet.should_receive(:email).
134
+ any_number_of_times.
135
+ and_return("bryanawesome@example.com")
136
+
137
+ obfuscator.scrub!("User", { login: :user_name, email: :email })
138
+ end
139
+
140
+ context "given an invalid type parameter" do
141
+ it "raises an UnkownObfuscationTypeError" do
142
+ expect { obfuscator.scrub!("User", { login: :invalid_type }) }.
143
+ to raise_error(
144
+ Obfuscator::Generic::UnkownObfuscationTypeError
145
+ )
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ context "not given an array of columns" do
154
+ it "returns nil" do
155
+ obfuscator.scrub!("User").should be_nil
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Obfuscator::Utilities do
4
+ describe ".random_number" do
5
+ it "returns a random number" do
6
+ 0.upto(9).to_a.should include(Obfuscator::Utilities.random_number(10))
7
+ end
8
+ end
9
+
10
+ describe ".random_boolean" do
11
+ it "randomly returns true or false" do
12
+ [true, false].should include(Obfuscator::Utilities.random_boolean)
13
+ end
14
+ end
15
+
16
+ describe ".random_date" do
17
+ it "returns a random date" do
18
+ Obfuscator::Utilities.random_date.should be_kind_of(Date)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rspec'
5
+ require 'sqlite3'
6
+ require 'active_record'
7
+ require 'faker'
8
+ require 'pry'
9
+
10
+ require 'obfuscator'
11
+
12
+ RSpec.configure do |config|
13
+ end
14
+
15
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
16
+
17
+ ActiveRecord::Schema.define do
18
+ self.verbose = false
19
+
20
+ create_table :users, force: true do |t|
21
+ t.string :email
22
+ t.string :login
23
+ t.string :password
24
+ t.text :bio
25
+ t.datetime :birthdate
26
+ end
27
+
28
+ create_table :people, force: true do |t|
29
+ t.string :email
30
+ t.string :login
31
+ t.string :password
32
+ t.boolean :good_looking
33
+ end
34
+ end
35
+
36
+ class User < ActiveRecord::Base; end
37
+ class Person < ActiveRecord::Base; end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: obfuscator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bryan Woods
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70183958259900 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70183958259900
25
+ - !ruby/object:Gem::Dependency
26
+ name: faker
27
+ requirement: &70183958259480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70183958259480
36
+ description: A clean, friendly API for obfuscating slightly sensitive data from a
37
+ Rails application
38
+ email:
39
+ - bryanwoods4e@gmail.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - .rspec
46
+ - Gemfile
47
+ - LICENSE
48
+ - README.md
49
+ - Rakefile
50
+ - lib/obfuscator.rb
51
+ - lib/obfuscator/dsl.rb
52
+ - lib/obfuscator/generic.rb
53
+ - lib/obfuscator/railtie.rb
54
+ - lib/obfuscator/utilities.rb
55
+ - lib/obfuscator/version.rb
56
+ - lib/tasks/obfuscator.rake
57
+ - obfuscator.gemspec
58
+ - spec/obfuscator/dsl_spec.rb
59
+ - spec/obfuscator/generic_spec.rb
60
+ - spec/obfuscator/utilities_spec.rb
61
+ - spec/spec_helper.rb
62
+ homepage: http://github.com/bryanwoods/obfuscator
63
+ licenses:
64
+ - MIT
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.16
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Obfuscate provides an easy way to quickly overwrite slightly sensitive data
87
+ from a Rails application for debugging or developing off a database dump
88
+ test_files:
89
+ - spec/obfuscator/dsl_spec.rb
90
+ - spec/obfuscator/generic_spec.rb
91
+ - spec/obfuscator/utilities_spec.rb
92
+ - spec/spec_helper.rb