start encoding candidate emails - completes #57
This commit is contained in:
parent
8269bb9e5c
commit
229ebf1380
@ -4,6 +4,8 @@ class Candidate < ApplicationRecord
|
|||||||
has_many :answers
|
has_many :answers
|
||||||
belongs_to :recruiter, class_name: "User"
|
belongs_to :recruiter, class_name: "User"
|
||||||
|
|
||||||
|
serialize :email, CryptSerializer
|
||||||
|
|
||||||
before_validation(:generate_test_hash, on: :create)
|
before_validation(:generate_test_hash, on: :create)
|
||||||
|
|
||||||
validates_presence_of :recruiter_id
|
validates_presence_of :recruiter_id
|
||||||
|
46
app/services/crypt_serializer.rb
Normal file
46
app/services/crypt_serializer.rb
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
require 'openssl'
|
||||||
|
require 'base64'
|
||||||
|
|
||||||
|
class CryptSerializer
|
||||||
|
attr_reader :cipher
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# pulling from DB - return plain value
|
||||||
|
def load value
|
||||||
|
new.decrypt value
|
||||||
|
end
|
||||||
|
|
||||||
|
# saving to DB - return encrypted value
|
||||||
|
def dump value
|
||||||
|
new.encrypt value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@cipher = OpenSSL::Cipher::AES.new(128, :CBC)
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypt(value)
|
||||||
|
unless value.is_a?(String)
|
||||||
|
raise "Attribute was supposed to be a `String`, but was instead a `#{value.class}`"
|
||||||
|
end
|
||||||
|
|
||||||
|
return value if value.nil?
|
||||||
|
|
||||||
|
cipher.encrypt
|
||||||
|
parts = [cipher.random_key, cipher.random_iv, cipher.update(value) + cipher.final]
|
||||||
|
|
||||||
|
Base64.urlsafe_encode64 Marshal.dump(parts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrypt(value)
|
||||||
|
return value if value.nil?
|
||||||
|
|
||||||
|
parts = Marshal.load Base64.urlsafe_decode64(value)
|
||||||
|
cipher.decrypt
|
||||||
|
cipher.key = parts[0]
|
||||||
|
cipher.iv = parts[1]
|
||||||
|
|
||||||
|
cipher.update(parts[2]) + cipher.final
|
||||||
|
end
|
||||||
|
end
|
26
db/migrate/20160826200610_encode_candidate_emails.rb
Normal file
26
db/migrate/20160826200610_encode_candidate_emails.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
class EncodeCandidateEmails < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
sql = "select id, email from candidates;"
|
||||||
|
candidates = ActiveRecord::Base.connection.execute(sql).to_h
|
||||||
|
|
||||||
|
candidates.each do |id, email|
|
||||||
|
sql = if base64?(email)
|
||||||
|
# going down - decrypt
|
||||||
|
"UPDATE candidates set email = '#{CryptSerializer.load email}' WHERE id = #{id};"
|
||||||
|
else
|
||||||
|
# going up - encrypt emails
|
||||||
|
"UPDATE candidates set email = '#{CryptSerializer.dump email}' WHERE id = #{id};"
|
||||||
|
end
|
||||||
|
ActiveRecord::Base.connection.execute(sql)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def base64? string
|
||||||
|
Base64.urlsafe_decode64 string
|
||||||
|
true
|
||||||
|
rescue ArgumentError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20160824183159) do
|
ActiveRecord::Schema.define(version: 20160826200610) do
|
||||||
|
|
||||||
create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
t.integer "candidate_id"
|
t.integer "candidate_id"
|
||||||
|
16
test/fixtures/candidates.yml
vendored
16
test/fixtures/candidates.yml
vendored
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
roy: # Roy should have started, and is ready for a reminder
|
roy: # Roy should have started, and is ready for a reminder
|
||||||
name: Roy Cruz
|
name: Roy Cruz
|
||||||
email: roy.cruz@mailinator.com
|
email: <%= CryptSerializer.dump 'roy.cruz@mailinator.com' %>
|
||||||
experience: 0-3
|
experience: 0-3
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
@ -12,7 +12,7 @@ roy: # Roy should have started, and is ready for a reminder
|
|||||||
|
|
||||||
gillian: # Gillian has not begun the test
|
gillian: # Gillian has not begun the test
|
||||||
name: Gillian Anderson
|
name: Gillian Anderson
|
||||||
email: gillian.anderson@mailinator.com
|
email: <%= CryptSerializer.dump 'gillian.anderson@mailinator.com' %>
|
||||||
experience: 4-6
|
experience: 4-6
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
@ -22,7 +22,7 @@ gillian: # Gillian has not begun the test
|
|||||||
|
|
||||||
martha: # Martha has not begun the test
|
martha: # Martha has not begun the test
|
||||||
name: Martha Watts
|
name: Martha Watts
|
||||||
email: martha.watts@mailinator.com
|
email: <%= CryptSerializer.dump 'martha.watts@mailinator.com' %>
|
||||||
experience: 4-6
|
experience: 4-6
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
@ -32,7 +32,7 @@ martha: # Martha has not begun the test
|
|||||||
|
|
||||||
dawn: # Dawn has completed, and been reminded, but not submitted the test
|
dawn: # Dawn has completed, and been reminded, but not submitted the test
|
||||||
name: Dawn Hopkins
|
name: Dawn Hopkins
|
||||||
email: dawn.hopkins@mailinator.com
|
email: <%= CryptSerializer.dump 'dawn.hopkins@mailinator.com' %>
|
||||||
experience: 0-2
|
experience: 0-2
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
@ -42,7 +42,7 @@ dawn: # Dawn has completed, and been reminded, but not submitted the test
|
|||||||
|
|
||||||
peggy: # Peggy has completed, and been reminded, but not submitted the test
|
peggy: # Peggy has completed, and been reminded, but not submitted the test
|
||||||
name: Peggy Blisters
|
name: Peggy Blisters
|
||||||
email: peggy.blisters@mailinator.com
|
email: <%= CryptSerializer.dump 'peggy.blisters@mailinator.com' %>
|
||||||
experience: 0-2
|
experience: 0-2
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
@ -52,7 +52,7 @@ peggy: # Peggy has completed, and been reminded, but not submitted the test
|
|||||||
|
|
||||||
richard: # Richard has completed AND submitted the test
|
richard: # Richard has completed AND submitted the test
|
||||||
name: Richard Burns
|
name: Richard Burns
|
||||||
email: richard.burns@mailinator.com
|
email: <%= CryptSerializer.dump 'richard.burns@mailinator.com' %>
|
||||||
experience: 15+
|
experience: 15+
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
@ -62,10 +62,10 @@ richard: # Richard has completed AND submitted the test
|
|||||||
|
|
||||||
juan: # Juan has chosen "finish later" for live coders
|
juan: # Juan has chosen "finish later" for live coders
|
||||||
name: Juan Campbell
|
name: Juan Campbell
|
||||||
email: juan.campbell@mailinator.com
|
email: <%= CryptSerializer.dump 'juan.campbell@mailinator.com' %>
|
||||||
experience: 15+
|
experience: 15+
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
reminded: true
|
reminded: true
|
||||||
test_hash: qKQo0l4dyol
|
test_hash: <%= CryptSerializer.dump 'qKQo0l4dyol
|
||||||
|
@ -9,4 +9,18 @@ class CandidateTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
assert candidate.test_hash.present?
|
assert candidate.test_hash.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "should encrypt emails" do
|
||||||
|
email = 'test@mailinator.com'
|
||||||
|
candidate = Candidate.create(name: 'new name',
|
||||||
|
email: email,
|
||||||
|
experience: '0-3',
|
||||||
|
recruiter_id: users(:recruiter).id,
|
||||||
|
quiz_id: quizzes(:fed).id)
|
||||||
|
|
||||||
|
sql = "select email from candidates where id = #{candidate.id};"
|
||||||
|
enc_email = ActiveRecord::Base.connection.execute(sql).first.first
|
||||||
|
|
||||||
|
refute_equal email, enc_email
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
26
test/services/crypt_serializer_test.rb
Normal file
26
test/services/crypt_serializer_test.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class CryptSerializerTest < ActiveSupport::TestCase
|
||||||
|
test "should generate marshaled array" do
|
||||||
|
string = "some string to encrypt"
|
||||||
|
encrypted = CryptSerializer.dump string
|
||||||
|
ar = Marshal.load(Base64.urlsafe_decode64(encrypted))
|
||||||
|
|
||||||
|
assert_instance_of Array, ar
|
||||||
|
assert_equal 3, ar.count
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should encrypt and dencrypt" do
|
||||||
|
string = "test@string.email"
|
||||||
|
encrypted = CryptSerializer.dump string
|
||||||
|
decrypted = CryptSerializer.load encrypted
|
||||||
|
|
||||||
|
assert_equal string, decrypted
|
||||||
|
end
|
||||||
|
|
||||||
|
test "must raise RuntimeError" do
|
||||||
|
assert_raises RuntimeError do
|
||||||
|
CryptSerializer.dump nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user