Class: MyObfuscate

Inherits:
Object
  • Object
show all
Defined in:
lib/my_obfuscate.rb,
lib/my_obfuscate/mysql.rb,
lib/my_obfuscate/version.rb,
lib/my_obfuscate/sql_server.rb

Overview

Class for obfuscating MySQL dumps. This can parse mysqldump outputs when using the -c option, which includes column names in the insert statements.

Defined Under Namespace

Classes: Mysql, SqlServer

Constant Summary collapse

NUMBER_CHARS =
"1234567890"
USERNAME_CHARS =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" + NUMBER_CHARS
SENSIBLE_CHARS =
USERNAME_CHARS + '+-=[{]}/?|!@#$%^&*()`~'
VERSION =
"0.3.6"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(configuration = {}) ⇒ MyObfuscate

Make a new MyObfuscate object. Pass in a configuration structure to define how the obfuscation should be performed. See the README.rdoc file for more information.



15
16
17
# File 'lib/my_obfuscate.rb', line 15

def initialize(configuration = {})
  @config = configuration
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



7
8
9
# File 'lib/my_obfuscate.rb', line 7

def config
  @config
end

#database_typeObject

Returns the value of attribute database_type.



7
8
9
# File 'lib/my_obfuscate.rb', line 7

def database_type
  @database_type
end

#fail_on_unspecified_columnsObject

Returns the value of attribute fail_on_unspecified_columns.



7
8
9
# File 'lib/my_obfuscate.rb', line 7

def fail_on_unspecified_columns
  @fail_on_unspecified_columns
end

#globally_kept_columnsObject

Returns the value of attribute globally_kept_columns.



7
8
9
# File 'lib/my_obfuscate.rb', line 7

def globally_kept_columns
  @globally_kept_columns
end

Class Method Details

.apply_table_config(row, table_config, columns) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/my_obfuscate.rb', line 83

def self.apply_table_config(row, table_config, columns)
  return row unless table_config.is_a?(Hash)
  row_hash = row_as_hash(row, columns)

  table_config.each do |column, definition|
    index = columns.index(column)
    
    definition = { :type => definition } if definition.is_a?(Symbol)

    if definition.has_key?(:unless)
      unless_check = make_conditional_method(definition[:unless], index, row)

      next if unless_check.call(row_hash)
    end


    if definition.has_key?(:if)
      if_check = make_conditional_method(definition[:if], index, row)

      next unless if_check.call(row_hash)
    end

    if definition[:skip_regexes]
      next if definition[:skip_regexes].any? {|regex| row[index] =~ regex}
    end

    row[index.to_i] = case definition[:type]
      when :email
        clean_quotes(Faker::Internet.email)
      when :string
        random_string(definition[:length] || 30, definition[:chars] || SENSIBLE_CHARS)
      when :lorem
        clean_bad_whitespace(clean_quotes(Faker::Lorem.sentences(definition[:number] || 1).join(".  ")))
      when :name
        clean_quotes(Faker::Name.name)
      when :first_name
        clean_quotes(Faker::Name.first_name)
      when :last_name
        clean_quotes(Faker::Name.last_name)
      when :address
        clean_quotes("#{Faker::Address.street_address}\\n#{Faker::Address.city}, #{Faker::Address.state_abbr} #{Faker::Address.zip_code}")
      when :street_address
        clean_bad_whitespace(clean_quotes(Faker::Address.street_address))
      when :city
        clean_quotes(Faker::Address.city)
      when :state
        Faker::Address.state_abbr
      when :zip_code
        Faker::Address.zip_code
      when :phone
        Faker::PhoneNumber.phone_number
      when :company
        clean_bad_whitespace(clean_quotes(Faker::Company.name))
      when :ipv4
        Faker::Internet.ip_v4_address
      when :ipv6
        Faker::Internet.ip_v6_address
      when :url
        clean_bad_whitespace(Faker::Internet.url)
      when :integer
        random_integer(definition[:between] || (0..1000)).to_s
      when :fixed
        if definition[:one_of]
          definition[:one_of][(rand * definition[:one_of].length).to_i]
        else
          definition[:string].is_a?(Proc) ? definition[:string].call(row_hash) : definition[:string]
        end
      when :null
        nil
      when :keep
        row[index]
      else
        $stderr.puts "Keeping a column value by providing an unknown type (#{definition[:type]}) is deprecated.  Use :keep instead."
        row[index]
    end
  end
  row
end

.make_conditional_method(conditional_method, index, row) ⇒ Object



72
73
74
75
76
77
78
79
80
81
# File 'lib/my_obfuscate.rb', line 72

def self.make_conditional_method(conditional_method, index, row)
  if conditional_method.is_a?(Symbol)
    if conditional_method == :blank
      conditional_method = lambda { |row_hash| row[index].nil? || row[index] == '' }
    elsif conditional_method == :nil
      conditional_method = lambda { |row_hash| row[index].nil? }
    end
  end
  conditional_method
end

.random_integer(between) ⇒ Object



162
163
164
# File 'lib/my_obfuscate.rb', line 162

def self.random_integer(between)
  (between.min + (between.max - between.min) * rand).round
end

.random_string(length_or_range, chars) ⇒ Object



166
167
168
169
170
171
172
# File 'lib/my_obfuscate.rb', line 166

def self.random_string(length_or_range, chars)
  length_or_range = (length_or_range..length_or_range) if length_or_range.is_a?(Fixnum)
  times = random_integer(length_or_range)
  out = ""
  times.times { out << chars[rand * chars.length] }
  out
end

.row_as_hash(row, columns) ⇒ Object



68
69
70
# File 'lib/my_obfuscate.rb', line 68

def self.row_as_hash(row, columns)
  columns.zip(row).inject({}) {|m, (name, value)| m[name] = value; m}
end

Instance Method Details

#check_for_defined_columns_not_in_table(table_name, columns) ⇒ Object



174
175
176
177
178
179
180
181
182
# File 'lib/my_obfuscate.rb', line 174

def check_for_defined_columns_not_in_table(table_name, columns)
  missing_columns = config[table_name].keys - columns
  unless missing_columns.length == 0
    error_message = missing_columns.map do |missing_column|
      "Column '#{missing_column}' could not be found in table '#{table_name}', please fix your obfuscator config."
    end.join("\n")
    raise RuntimeError.new(error_message)
  end
end

#check_for_table_columns_not_in_definition(table_name, columns) ⇒ Object



184
185
186
187
188
189
190
191
192
# File 'lib/my_obfuscate.rb', line 184

def check_for_table_columns_not_in_definition(table_name, columns)
  missing_columns = columns - (config[table_name].keys + (globally_kept_columns || []).map {|i| i.to_sym}).uniq
  unless missing_columns.length == 0
    error_message = missing_columns.map do |missing_column|
      "Column '#{missing_column}' defined in table '#{table_name}', but not found in table definition, please fix your obfuscator config."
    end.join("\n")
    raise RuntimeError.new(error_message)
  end
end

#database_helperObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/my_obfuscate.rb', line 23

def database_helper
  if @database_helper.nil?
    if @database_type == :sql_server
      @database_helper = SqlServer.new
    else
      @database_helper = Mysql.new
    end
  end

  @database_helper
end

#fail_on_unspecified_columns?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/my_obfuscate.rb', line 19

def fail_on_unspecified_columns?
  @fail_on_unspecified_columns
end

#obfuscate(input_io, output_io) ⇒ Object

Read an input stream and dump out an obfuscated output stream. These streams could be StringIO objects, Files, or STDIN and STDOUT.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/my_obfuscate.rb', line 37

def obfuscate(input_io, output_io)

  # We assume that every INSERT INTO line occupies one line in the file, with no internal linebreaks.
  input_io.each do |line|
    if table_data = database_helper.parse_insert_statement(line)
      table_name = table_data[:table_name]
      columns = table_data[:column_names]
      if config[table_name]
        output_io.puts obfuscate_bulk_insert_line(line, table_name, columns)
      else
        $stderr.puts "Deprecated: #{table_name} was not specified in the config.  A future release will cause this to be an error.  Please specify the table definition or set it to :keep."
        output_io.write line
      end
    else
      output_io.write line
    end
  end
end

#obfuscate_bulk_insert_line(line, table_name, columns) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/my_obfuscate.rb', line 194

def obfuscate_bulk_insert_line(line, table_name, columns)
  table_config = config[table_name]
  if table_config == :truncate
    ""
  elsif table_config == :keep
    line
  else
    check_for_defined_columns_not_in_table(table_name, columns)
    check_for_table_columns_not_in_definition(table_name, columns) if fail_on_unspecified_columns?
    # Note: Remember to SQL escape strings in what you pass back.
    reassembling_each_insert(line, table_name, columns) do |row|
      MyObfuscate.apply_table_config(row, table_config, columns)
    end
  end
end

#reassembling_each_insert(line, table_name, columns) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/my_obfuscate.rb', line 56

def reassembling_each_insert(line, table_name, columns)
  output = database_helper.rows_to_be_inserted(line).map do |sub_insert|
    result = yield(sub_insert)
    result = result.map do |i|
      database_helper.make_valid_value_string(i)
    end
    result = result.join(",")
    "(" + result + ")"
  end.join(",")
  database_helper.make_insert_statement(table_name, columns, output)
end