Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ It is recommended that you implement a strategy to insure that you do not mix th
attr_encrypted :ssn, key: :encryption_key, v2_gcm_iv: is_decrypting?(:ssn)

def is_decrypting?(attribute)
encrypted_attributes[attribute][:operation] == :decrypting
attr_encrypted_encrypted_attributes[attribute][:operation] == :decrypting
end
end

Expand Down
1 change: 0 additions & 1 deletion attr_encrypted.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Gem::Specification.new do |s|
s.homepage = 'http://github.com/attr-encrypted/attr_encrypted'
s.license = 'MIT'

s.has_rdoc = false
s.rdoc_options = ['--line-numbers', '--inline-source', '--main', 'README.rdoc']

s.require_paths = ['lib']
Expand Down
32 changes: 16 additions & 16 deletions lib/attr_encrypted.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def self.extended(base) # :nodoc:
base.class_eval do
include InstanceMethods
attr_writer :attr_encrypted_options
@attr_encrypted_options, @encrypted_attributes = {}, {}
@attr_encrypted_options, @attr_encrypted_encrypted_attributes = {}, {}
end
end

Expand Down Expand Up @@ -173,7 +173,7 @@ def attr_encrypted(*attributes)
value.respond_to?(:empty?) ? !value.empty? : !!value
end

encrypted_attributes[attribute.to_sym] = options.merge(attribute: encrypted_attribute_name)
attr_encrypted_encrypted_attributes[attribute.to_sym] = options.merge(attribute: encrypted_attribute_name)
end
end

Expand Down Expand Up @@ -223,7 +223,7 @@ def attr_encrypted_default_options
# User.attr_encrypted?(:name) # false
# User.attr_encrypted?(:email) # true
def attr_encrypted?(attribute)
encrypted_attributes.has_key?(attribute.to_sym)
attr_encrypted_encrypted_attributes.has_key?(attribute.to_sym)
end

# Decrypts a value for the attribute specified
Expand All @@ -236,7 +236,7 @@ def attr_encrypted?(attribute)
#
# email = User.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
def decrypt(attribute, encrypted_value, options = {})
options = encrypted_attributes[attribute.to_sym].merge(options)
options = attr_encrypted_encrypted_attributes[attribute.to_sym].merge(options)
if options[:if] && !options[:unless] && not_empty?(encrypted_value)
encrypted_value = encrypted_value.unpack(options[:encode]).first if options[:encode]
value = options[:encryptor].send(options[:decrypt_method], options.merge!(value: encrypted_value))
Expand All @@ -262,7 +262,7 @@ def decrypt(attribute, encrypted_value, options = {})
#
# encrypted_email = User.encrypt(:email, 'test@example.com')
def encrypt(attribute, value, options = {})
options = encrypted_attributes[attribute.to_sym].merge(options)
options = attr_encrypted_encrypted_attributes[attribute.to_sym].merge(options)
if options[:if] && !options[:unless] && (options[:allow_empty_value] || not_empty?(value))
value = options[:marshal] ? options[:marshaler].send(options[:dump_method], value) : value.to_s
encrypted_value = options[:encryptor].send(options[:encrypt_method], options.merge!(value: value))
Expand All @@ -286,9 +286,9 @@ def not_empty?(value)
# attr_encrypted :email, key: 'my secret key'
# end
#
# User.encrypted_attributes # { email: { attribute: 'encrypted_email', key: 'my secret key' } }
def encrypted_attributes
@encrypted_attributes ||= superclass.encrypted_attributes.dup
# User.attr_encrypted_encrypted_attributes # { email: { attribute: 'encrypted_email', key: 'my secret key' } }
def attr_encrypted_encrypted_attributes
@attr_encrypted_encrypted_attributes ||= superclass.attr_encrypted_encrypted_attributes.dup
end

# Forwards calls to :encrypt_#{attribute} or :decrypt_#{attribute} to the corresponding encrypt or decrypt method
Expand Down Expand Up @@ -326,8 +326,8 @@ module InstanceMethods
# @user = User.new('some-secret-key')
# @user.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
def decrypt(attribute, encrypted_value)
encrypted_attributes[attribute.to_sym][:operation] = :decrypting
encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(encrypted_value)
attr_encrypted_encrypted_attributes[attribute.to_sym][:operation] = :decrypting
attr_encrypted_encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(encrypted_value)
self.class.decrypt(attribute, encrypted_value, evaluated_attr_encrypted_options_for(attribute))
end

Expand All @@ -347,18 +347,18 @@ def decrypt(attribute, encrypted_value)
# @user = User.new('some-secret-key')
# @user.encrypt(:email, 'test@example.com')
def encrypt(attribute, value)
encrypted_attributes[attribute.to_sym][:operation] = :encrypting
encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(value)
attr_encrypted_encrypted_attributes[attribute.to_sym][:operation] = :encrypting
attr_encrypted_encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(value)
self.class.encrypt(attribute, value, evaluated_attr_encrypted_options_for(attribute))
end

# Copies the class level hash of encrypted attributes with virtual attribute names as keys
# and their corresponding options as values to the instance
#
def encrypted_attributes
@encrypted_attributes ||= begin
def attr_encrypted_encrypted_attributes
@attr_encrypted_encrypted_attributes ||= begin
duplicated= {}
self.class.encrypted_attributes.map { |key, value| duplicated[key] = value.dup }
self.class.attr_encrypted_encrypted_attributes.map { |key, value| duplicated[key] = value.dup }
duplicated
end
end
Expand All @@ -368,7 +368,7 @@ def encrypted_attributes
# Returns attr_encrypted options evaluated in the current object's scope for the attribute specified
def evaluated_attr_encrypted_options_for(attribute)
evaluated_options = Hash.new
attributes = encrypted_attributes[attribute.to_sym]
attributes = attr_encrypted_encrypted_attributes[attribute.to_sym]
attribute_option_value = attributes[:attribute]

[:if, :unless, :value_present, :allow_empty_value].each do |option|
Expand Down
12 changes: 6 additions & 6 deletions lib/attr_encrypted/adapters/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def self.extended(base) # :nodoc:
alias_method :reload_without_attr_encrypted, :reload
def reload(*args, &block)
result = reload_without_attr_encrypted(*args, &block)
self.class.encrypted_attributes.keys.each do |attribute_name|
self.class.attr_encrypted_encrypted_attributes.keys.each do |attribute_name|
instance_variable_set("@#{attribute_name}", nil)
end
result
Expand All @@ -27,8 +27,8 @@ class << self
def perform_attribute_assignment(method, new_attributes, *args)
return if new_attributes.blank?

send method, new_attributes.reject { |k, _| self.class.encrypted_attributes.key?(k.to_sym) }, *args
send method, new_attributes.reject { |k, _| !self.class.encrypted_attributes.key?(k.to_sym) }, *args
send method, new_attributes.reject { |k, _| self.class.attr_encrypted_encrypted_attributes.key?(k.to_sym) }, *args
send method, new_attributes.reject { |k, _| !self.class.attr_encrypted_encrypted_attributes.key?(k.to_sym) }, *args
end
private :perform_attribute_assignment

Expand All @@ -54,7 +54,7 @@ def attr_encrypted(*attrs)
options = attrs.extract_options!
attr = attrs.pop
attribute attr if ::ActiveRecord::VERSION::STRING >= "5.1.0"
options.merge! encrypted_attributes[attr]
options.merge! attr_encrypted_encrypted_attributes[attr]

define_method("#{attr}_was") do
attribute_was(attr)
Expand Down Expand Up @@ -122,10 +122,10 @@ def method_missing_with_attr_encrypted(method, *args, &block)
if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
attribute_names = match.captures.last.split('_and_')
attribute_names.each_with_index do |attribute, index|
if attr_encrypted?(attribute) && encrypted_attributes[attribute.to_sym][:mode] == :single_iv_and_salt
if attr_encrypted?(attribute) && attr_encrypted_encrypted_attributes[attribute.to_sym][:mode] == :single_iv_and_salt
args[index] = send("encrypt_#{attribute}", args[index])
warn "DEPRECATION WARNING: This feature will be removed in the next major release."
attribute_names[index] = encrypted_attributes[attribute.to_sym][:attribute]
attribute_names[index] = attr_encrypted_encrypted_attributes[attribute.to_sym][:attribute]
end
end
method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
Expand Down
10 changes: 5 additions & 5 deletions test/active_record_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class Account < ActiveRecord::Base
attr_encrypted :password, key: :password_encryption_key

def encrypting?(attr)
encrypted_attributes[attr][:operation] == :encrypting
attr_encrypted_encrypted_attributes[attr][:operation] == :encrypting
end

def password_encryption_key
Expand Down Expand Up @@ -279,14 +279,14 @@ def test_should_allow_proc_based_mode
@person = PersonWithProcMode.create(email: 'test@example.com', credentials: 'password123')

# Email is :per_attribute_iv_and_salt
assert_equal @person.class.encrypted_attributes[:email][:mode].class, Proc
assert_equal @person.class.encrypted_attributes[:email][:mode].call, :per_attribute_iv_and_salt
assert_equal @person.class.attr_encrypted_encrypted_attributes[:email][:mode].class, Proc
assert_equal @person.class.attr_encrypted_encrypted_attributes[:email][:mode].call, :per_attribute_iv_and_salt
refute_nil @person.encrypted_email_salt
refute_nil @person.encrypted_email_iv

# Credentials is :single_iv_and_salt
assert_equal @person.class.encrypted_attributes[:credentials][:mode].class, Proc
assert_equal @person.class.encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
assert_equal @person.class.attr_encrypted_encrypted_attributes[:credentials][:mode].class, Proc
assert_equal @person.class.attr_encrypted_encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
assert_nil @person.encrypted_credentials_salt
assert_nil @person.encrypted_credentials_iv
end
Expand Down
30 changes: 15 additions & 15 deletions test/attr_encrypted_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ def setup
@iv = SecureRandom.random_bytes(12)
end

def test_should_store_email_in_encrypted_attributes
assert User.encrypted_attributes.include?(:email)
def test_should_store_email_in_attr_encrypted_encrypted_attributes
assert User.attr_encrypted_encrypted_attributes.include?(:email)
end

def test_should_not_store_salt_in_encrypted_attributes
refute User.encrypted_attributes.include?(:salt)
def test_should_not_store_salt_in_attr_encrypted_encrypted_attributes
refute User.attr_encrypted_encrypted_attributes.include?(:salt)
end

def test_attr_encrypted_should_return_true_for_email
assert User.attr_encrypted?('email')
end

def test_attr_encrypted_should_not_use_the_same_attribute_name_for_two_attributes_in_the_same_line
refute_equal User.encrypted_attributes[:email][:attribute], User.encrypted_attributes[:without_encoding][:attribute]
refute_equal User.attr_encrypted_encrypted_attributes[:email][:attribute], User.attr_encrypted_encrypted_attributes[:without_encoding][:attribute]
end

def test_attr_encrypted_should_return_false_for_salt
Expand Down Expand Up @@ -154,7 +154,7 @@ def test_should_decrypt_email
def test_should_decrypt_email_when_reading
@user = User.new
assert_nil @user.email
options = @user.encrypted_attributes[:email]
options = @user.attr_encrypted_encrypted_attributes[:email]
iv = @user.send(:generate_iv, options[:algorithm])
encoded_iv = [iv].pack(options[:encode_iv])
salt = SecureRandom.random_bytes
Expand Down Expand Up @@ -222,8 +222,8 @@ def test_should_use_options_found_in_the_attr_encrypted_options_attribute
assert_equal encrypted, @user.crypted_password_test
end

def test_should_inherit_encrypted_attributes
assert_equal [User.encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, Admin.encrypted_attributes.keys.collect { |key| key.to_s }.sort
def test_should_inherit_attr_encrypted_encrypted_attributes
assert_equal [User.attr_encrypted_encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, Admin.attr_encrypted_encrypted_attributes.keys.collect { |key| key.to_s }.sort
end

def test_should_inherit_attr_encrypted_options
Expand All @@ -233,7 +233,7 @@ def test_should_inherit_attr_encrypted_options

def test_should_not_inherit_unrelated_attributes
assert SomeOtherClass.attr_encrypted_options.empty?
assert SomeOtherClass.encrypted_attributes.empty?
assert SomeOtherClass.attr_encrypted_encrypted_attributes.empty?
end

def test_should_evaluate_a_symbol_option
Expand Down Expand Up @@ -304,7 +304,7 @@ def test_should_encrypt_empty_with_truthy_allow_empty_value_option
end

def test_should_work_with_aliased_attr_encryptor
assert User.encrypted_attributes.include?(:aliased)
assert User.attr_encrypted_encrypted_attributes.include?(:aliased)
end

def test_should_always_reset_options
Expand Down Expand Up @@ -385,8 +385,8 @@ def test_should_decrypt_second_record
end

def test_should_specify_the_default_algorithm
assert YetAnotherClass.encrypted_attributes[:email][:algorithm]
assert_equal YetAnotherClass.encrypted_attributes[:email][:algorithm], 'aes-256-gcm'
assert YetAnotherClass.attr_encrypted_encrypted_attributes[:email][:algorithm]
assert_equal YetAnotherClass.attr_encrypted_encrypted_attributes[:email][:algorithm], 'aes-256-gcm'
end

def test_should_not_encode_iv_when_encode_iv_is_false
Expand Down Expand Up @@ -469,14 +469,14 @@ def test_should_not_by_default_generate_iv_when_attribute_is_empty
assert_nil user.encrypted_with_true_if_iv
end

def test_encrypted_attributes_state_is_not_shared
def test_attr_encrypted_encrypted_attributes_state_is_not_shared
user = User.new
user.ssn = '123456789'

another_user = User.new

assert_equal :encrypting, user.encrypted_attributes[:ssn][:operation]
assert_nil another_user.encrypted_attributes[:ssn][:operation]
assert_equal :encrypting, user.attr_encrypted_encrypted_attributes[:ssn][:operation]
assert_nil another_user.attr_encrypted_encrypted_attributes[:ssn][:operation]
end

def test_should_not_by_default_generate_key_when_attribute_is_empty
Expand Down
18 changes: 9 additions & 9 deletions test/legacy_attr_encrypted_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ def self.call(object)

class LegacyAttrEncryptedTest < Minitest::Test

def test_should_store_email_in_encrypted_attributes
assert LegacyUser.encrypted_attributes.include?(:email)
def test_should_store_email_in_attr_encrypted_encrypted_attributes
assert LegacyUser.attr_encrypted_encrypted_attributes.include?(:email)
end

def test_should_not_store_salt_in_encrypted_attributes
assert !LegacyUser.encrypted_attributes.include?(:salt)
def test_should_not_store_salt_in_attr_encrypted_encrypted_attributes
assert !LegacyUser.attr_encrypted_encrypted_attributes.include?(:salt)
end

def test_attr_encrypted_should_return_true_for_email
assert LegacyUser.attr_encrypted?('email')
end

def test_attr_encrypted_should_not_use_the_same_attribute_name_for_two_attributes_in_the_same_line
refute_equal LegacyUser.encrypted_attributes[:email][:attribute], LegacyUser.encrypted_attributes[:without_encoding][:attribute]
refute_equal LegacyUser.attr_encrypted_encrypted_attributes[:email][:attribute], LegacyUser.attr_encrypted_encrypted_attributes[:without_encoding][:attribute]
end

def test_attr_encrypted_should_return_false_for_salt
Expand Down Expand Up @@ -200,8 +200,8 @@ def test_should_use_options_found_in_the_attr_encrypted_options_attribute
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'LegacyUser', insecure_mode: true, algorithm: 'aes-256-cbc'), @user.crypted_password_test
end

def test_should_inherit_encrypted_attributes
assert_equal [LegacyUser.encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, LegacyAdmin.encrypted_attributes.keys.collect { |key| key.to_s }.sort
def test_should_inherit_attr_encrypted_encrypted_attributes
assert_equal [LegacyUser.attr_encrypted_encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, LegacyAdmin.attr_encrypted_encrypted_attributes.keys.collect { |key| key.to_s }.sort
end

def test_should_inherit_attr_encrypted_options
Expand All @@ -211,7 +211,7 @@ def test_should_inherit_attr_encrypted_options

def test_should_not_inherit_unrelated_attributes
assert LegacySomeOtherClass.attr_encrypted_options.empty?
assert LegacySomeOtherClass.encrypted_attributes.empty?
assert LegacySomeOtherClass.attr_encrypted_encrypted_attributes.empty?
end

def test_should_evaluate_a_symbol_option
Expand Down Expand Up @@ -268,7 +268,7 @@ def test_should_not_encrypt_with_true_unless
end

def test_should_work_with_aliased_attr_encryptor
assert LegacyUser.encrypted_attributes.include?(:aliased)
assert LegacyUser.attr_encrypted_encrypted_attributes.include?(:aliased)
end

def test_should_always_reset_options
Expand Down