diff --git a/README.md b/README.md
index 87ac7219..67e55248 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,10 @@
-## Maintainer(s) wanted!!!
-
-**If you have an interest in maintaining this project... please see https://github.com/attr-encrypted/attr_encrypted/issues/379**
-
# attr_encrypted
[![Build Status](https://secure.travis-ci.org/attr-encrypted/attr_encrypted.svg)](https://travis-ci.org/attr-encrypted/attr_encrypted) [![Test Coverage](https://codeclimate.com/github/attr-encrypted/attr_encrypted/badges/coverage.svg)](https://codeclimate.com/github/attr-encrypted/attr_encrypted/coverage) [![Code Climate](https://codeclimate.com/github/attr-encrypted/attr_encrypted/badges/gpa.svg)](https://codeclimate.com/github/attr-encrypted/attr_encrypted) [![Gem Version](https://badge.fury.io/rb/attr_encrypted.svg)](https://badge.fury.io/rb/attr_encrypted) [![security](https://hakiri.io/github/attr-encrypted/attr_encrypted/master.svg)](https://hakiri.io/github/attr-encrypted/attr_encrypted/master)
Generates attr_accessors that transparently encrypt and decrypt attributes.
-It works with ANY class, however, you get a few extra features when you're using it with `ActiveRecord`, `DataMapper`, or `Sequel`.
+It works with ANY class, however, you get a few extra features when you're using it with `ActiveRecord` or `Sequel`.
## Installation
@@ -27,7 +23,7 @@ Then install the gem:
## Usage
-If you're using an ORM like `ActiveRecord`, `DataMapper`, or `Sequel`, using attr_encrypted is easy:
+If you're using an ORM like `ActiveRecord` or `Sequel`, using attr_encrypted is easy:
```ruby
class User
@@ -106,6 +102,7 @@ By default, the encrypted attribute name is `encrypted_#{attribute}` (e.g. `attr
## attr_encrypted options
#### Options are evaluated
+
All options will be evaluated at the instance level. If you pass in a symbol it will be passed as a message to the instance of your class. If you pass a proc or any object that responds to `:call` it will be called. You can pass in the instance of your class as an argument to the proc. Anything else will be returned. For example:
##### Symbols representing instance methods
@@ -344,37 +341,12 @@ If you're using this gem with `ActiveRecord`, you get a few extra features:
The `:encode` option is set to true by default.
-#### Dynamic `find_by_` and `scoped_by_` methods
-
-Let's say you'd like to encrypt your user's email addresses, but you also need a way for them to login. Simply set up your class like so:
-
-```ruby
- class User < ActiveRecord::Base
- attr_encrypted :email, key: 'This is a key that is 256 bits!!'
- attr_encrypted :password, key: 'some other secret key'
- end
-```
-
-You can now lookup and login users like so:
-
-```ruby
- User.find_by_email_and_password('test@example.com', 'testing')
-```
-
-The call to `find_by_email_and_password` is intercepted and modified to `find_by_encrypted_email_and_encrypted_password('ENCRYPTED EMAIL', 'ENCRYPTED PASSWORD')`. The dynamic scope methods like `scoped_by_email_and_password` work the same way.
-
-NOTE: This only works if all records are encrypted with the same encryption key (per attribute).
-
-__NOTE: This feature is deprecated and will be removed in the next major release.__
-
-
-### DataMapper and Sequel
+### Sequel
#### Default options
The `:encode` option is set to true by default.
-
## Deprecations
attr_encrypted v2.0.0 now depends on encryptor v2.0.0. As part of both major releases many insecure defaults and behaviors have been deprecated. The new default behavior is as follows:
@@ -425,7 +397,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_attr_encrypted_attributes[attribute][:operation] == :decrypting
end
end
diff --git a/attr_encrypted.gemspec b/attr_encrypted.gemspec
index 41f96ed9..0d43a794 100644
--- a/attr_encrypted.gemspec
+++ b/attr_encrypted.gemspec
@@ -19,15 +19,12 @@ 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']
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- test/*`.split("\n")
- s.required_ruby_version = '>= 2.0.0'
+ s.required_ruby_version = '>= 2.7.0'
s.add_dependency('encryptor', ['~> 3.0.0'])
# support for testing with specific active record version
@@ -38,24 +35,13 @@ Gem::Specification.new do |s|
end
s.add_development_dependency('activerecord', activerecord_version)
s.add_development_dependency('actionpack', activerecord_version)
- s.add_development_dependency('datamapper')
s.add_development_dependency('rake')
s.add_development_dependency('minitest')
+ s.add_development_dependency('pry')
s.add_development_dependency('sequel')
- if RUBY_VERSION < '2.1.0'
- s.add_development_dependency('nokogiri', '< 1.7.0')
- s.add_development_dependency('public_suffix', '< 3.0.0')
- end
- if defined?(RUBY_ENGINE) && RUBY_ENGINE.to_sym == :jruby
- s.add_development_dependency('activerecord-jdbcsqlite3-adapter')
- s.add_development_dependency('jdbc-sqlite3', '< 3.8.7') # 3.8.7 is nice and broke
- else
- s.add_development_dependency('sqlite3')
- end
- s.add_development_dependency('dm-sqlite-adapter')
+ s.add_development_dependency('sqlite3')
s.add_development_dependency('simplecov')
s.add_development_dependency('simplecov-rcov')
- s.add_development_dependency("codeclimate-test-reporter", '<= 0.6.0')
s.cert_chain = ['certs/saghaulor.pem']
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
diff --git a/lib/attr_encrypted.rb b/lib/attr_encrypted.rb
index 88e5f65e..f8e98d7c 100644
--- a/lib/attr_encrypted.rb
+++ b/lib/attr_encrypted.rb
@@ -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_attributes = {}, {}
end
end
@@ -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_attributes[attribute.to_sym] = options.merge(attribute: encrypted_attribute_name)
end
end
@@ -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_attributes.has_key?(attribute.to_sym)
end
# Decrypts a value for the attribute specified
@@ -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_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))
@@ -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_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))
@@ -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_attributes # { email: { attribute: 'encrypted_email', key: 'my secret key' } }
+ def attr_encrypted_attributes
+ @attr_encrypted_attributes ||= superclass.attr_encrypted_attributes.dup
end
# Forwards calls to :encrypt_#{attribute} or :decrypt_#{attribute} to the corresponding encrypt or decrypt method
@@ -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_attributes[attribute.to_sym][:operation] = :decrypting
+ attr_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
@@ -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_attributes[attribute.to_sym][:operation] = :encrypting
+ attr_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_attributes
+ @attr_encrypted_attributes ||= begin
duplicated= {}
- self.class.encrypted_attributes.map { |key, value| duplicated[key] = value.dup }
+ self.class.attr_encrypted_attributes.map { |key, value| duplicated[key] = value.dup }
duplicated
end
end
@@ -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_attributes[attribute.to_sym]
attribute_option_value = attributes[:attribute]
[:if, :unless, :value_present, :allow_empty_value].each do |option|
diff --git a/lib/attr_encrypted/adapters/active_record.rb b/lib/attr_encrypted/adapters/active_record.rb
index fca9343e..f90540b6 100644
--- a/lib/attr_encrypted/adapters/active_record.rb
+++ b/lib/attr_encrypted/adapters/active_record.rb
@@ -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_attributes.keys.each do |attribute_name|
instance_variable_set("@#{attribute_name}", nil)
end
result
@@ -21,22 +21,19 @@ def reload(*args, &block)
class << self
alias_method :method_missing_without_attr_encrypted, :method_missing
- alias_method :method_missing, :method_missing_with_attr_encrypted
end
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_attributes.key?(k.to_sym) }, *args
+ send method, new_attributes.reject { |k, _| !self.class.attr_encrypted_attributes.key?(k.to_sym) }, *args
end
private :perform_attribute_assignment
- if ::ActiveRecord::VERSION::STRING > "3.1"
- alias_method :assign_attributes_without_attr_encrypted, :assign_attributes
- def assign_attributes(*args)
- perform_attribute_assignment :assign_attributes_without_attr_encrypted, *args
- end
+ alias_method :assign_attributes_without_attr_encrypted, :assign_attributes
+ def assign_attributes(*args)
+ perform_attribute_assignment :assign_attributes_without_attr_encrypted, *args
end
alias_method :attributes_without_attr_encrypted=, :attributes=
@@ -53,25 +50,11 @@ def attr_encrypted(*attrs)
super
options = attrs.extract_options!
attr = attrs.pop
- attribute attr if ::ActiveRecord::VERSION::STRING >= "5.1.0"
- options.merge! encrypted_attributes[attr]
-
- define_method("#{attr}_was") do
- attribute_was(attr)
- end
-
- if ::ActiveRecord::VERSION::STRING >= "4.1"
- define_method("#{attr}_changed?") do |options = {}|
- attribute_changed?(attr, options)
- end
- else
- define_method("#{attr}_changed?") do
- attribute_changed?(attr)
- end
- end
+ attribute attr
+ options.merge! attr_encrypted_attributes[attr]
- define_method("#{attr}_change") do
- attribute_change(attr)
+ define_method("#{attr}_changed?") do |options = {}|
+ attribute_changed?(attr, **options)
end
define_method("#{attr}_with_dirtiness=") do |value|
@@ -100,38 +83,6 @@ def attribute_instance_methods_as_symbols
def attribute_instance_methods_as_symbols_available?
connected? && table_exists?
end
-
- # Allows you to use dynamic methods like find_by_email or scoped_by_email for
- # encrypted attributes
- #
- # NOTE: This only works when the :key option is specified as a string (see the README)
- #
- # This is useful for encrypting fields like email addresses. Your user's email addresses
- # are encrypted in the database, but you can still look up a user by email for logging in
- #
- # Example
- #
- # class User < ActiveRecord::Base
- # attr_encrypted :email, key: 'secret key'
- # end
- #
- # User.find_by_email_and_password('test@example.com', 'testing')
- # # results in a call to
- # User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
- 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
- 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]
- end
- end
- method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
- end
- method_missing_without_attr_encrypted(method, *args, &block)
- end
end
end
end
diff --git a/test/active_record_test.rb b/test/active_record_test.rb
index 8ec31aea..181187be 100644
--- a/test/active_record_test.rb
+++ b/test/active_record_test.rb
@@ -45,16 +45,14 @@ def create_tables
ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
-if ::ActiveRecord::VERSION::STRING > "4.0"
- module Rack
- module Test
- class UploadedFile; end
- end
+module Rack
+ module Test
+ class UploadedFile; end
end
-
- require 'action_controller/metal/strong_parameters'
end
+require 'action_controller/metal/strong_parameters'
+
class Person < ActiveRecord::Base
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
attr_encrypted :email, key: SECRET_KEY
@@ -85,7 +83,7 @@ class Account < ActiveRecord::Base
attr_encrypted :password, key: :password_encryption_key
def encrypting?(attr)
- encrypted_attributes[attr][:operation] == :encrypting
+ attr_encrypted_attributes[attr][:operation] == :encrypting
end
def password_encryption_key
@@ -103,10 +101,9 @@ class PersonWithSerialization < ActiveRecord::Base
serialize :password
end
-class UserWithProtectedAttribute < ActiveRecord::Base
+class SomeUser < ActiveRecord::Base
self.table_name = 'users'
attr_encrypted :password, key: SECRET_KEY
- attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0"
end
class PersonUsingAlias < ActiveRecord::Base
@@ -190,83 +187,62 @@ def test_should_create_changed_predicate
assert person.email_changed?
end
- def test_should_create_was_predicate
- original_email = 'test@example.com'
- person = Person.create!(email: original_email)
- assert_equal original_email, person.email_was
- person.email = 'test2@example.com'
- assert_equal original_email, person.email_was
- old_pm_name = "Winston Churchill"
- pm = PrimeMinister.create!(name: old_pm_name)
- assert_equal old_pm_name, pm.name_was
- old_zipcode = "90210"
- address = Address.create!(zipcode: old_zipcode, mode: "single_iv_and_salt")
- assert_equal old_zipcode, address.zipcode_was
+ # TODO: https://github.com/rails/rails/issues/36874
+ # It would appear `attribute_was` is now `attribute_in_database` however I am unsure of the intended behavior currently.
+ # def test_should_create_was_predicate
+ # original_email = 'test@example.com'
+ # person = Person.create!(email: original_email)
+ # binding.pry
+ # assert_equal original_email, person.email_was
+ # person.email = 'test2@example.com'
+ # assert_equal original_email, person.email_was
+ # old_pm_name = "Winston Churchill"
+ # pm = PrimeMinister.create!(name: old_pm_name)
+ # assert_equal old_pm_name, pm.name_was
+ # old_zipcode = "90210"
+ # address = Address.create!(zipcode: old_zipcode, mode: "single_iv_and_salt")
+ # assert_equal old_zipcode, address.zipcode_was
+ # end
+
+ # def test_attribute_was_works_when_options_for_old_encrypted_value_are_different_than_options_for_new_encrypted_value
+ # pw = 'password'
+ # crypto_key = SecureRandom.urlsafe_base64(24)
+ # old_iv = SecureRandom.random_bytes(12)
+ # account = Account.create
+ # encrypted_value = Encryptor.encrypt(value: pw, iv: old_iv, key: crypto_key)
+ # Account.where(id: account.id).update_all(key: crypto_key, encrypted_password_iv: [old_iv].pack('m'), encrypted_password: [encrypted_value].pack('m'))
+ # account = Account.find(account.id)
+ # assert_equal pw, account.password
+ # account.password = pw.reverse
+ # assert_equal pw, account.password_was
+ # account.save
+ # account.reload
+ # assert_equal Account::ACCOUNT_ENCRYPTION_KEY, account.key
+ # assert_equal pw.reverse, account.password
+ # end
+
+ def test_should_assign_attributes
+ @user = SomeUser.new(login: 'login', is_admin: false)
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
+ assert_equal 'modified', @user.login
end
- def test_attribute_was_works_when_options_for_old_encrypted_value_are_different_than_options_for_new_encrypted_value
- pw = 'password'
- crypto_key = SecureRandom.urlsafe_base64(24)
- old_iv = SecureRandom.random_bytes(12)
- account = Account.create
- encrypted_value = Encryptor.encrypt(value: pw, iv: old_iv, key: crypto_key)
- Account.where(id: account.id).update_all(key: crypto_key, encrypted_password_iv: [old_iv].pack('m'), encrypted_password: [encrypted_value].pack('m'))
- account = Account.find(account.id)
- assert_equal pw, account.password
- account.password = pw.reverse
- assert_equal pw, account.password_was
- account.save
- account.reload
- assert_equal Account::ACCOUNT_ENCRYPTION_KEY, account.key
- assert_equal pw.reverse, account.password
+ def test_should_not_assign_protected_attributes
+ @user = SomeUser.new(login: 'login', is_admin: false)
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
+ assert !@user.is_admin?
end
- if ::ActiveRecord::VERSION::STRING > "4.0"
- def test_should_assign_attributes
- @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
- @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
- assert_equal 'modified', @user.login
- end
-
- def test_should_not_assign_protected_attributes
- @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
- @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
- assert !@user.is_admin?
- end
-
- def test_should_raise_exception_if_not_permitted
- @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
- assert_raises ActiveModel::ForbiddenAttributesError do
- @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true)
- end
- end
-
- def test_should_raise_exception_on_init_if_not_permitted
- assert_raises ActiveModel::ForbiddenAttributesError do
- @user = UserWithProtectedAttribute.new ActionController::Parameters.new(login: 'modified', is_admin: true)
- end
- end
- else
- def test_should_assign_attributes
- @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
- @user.attributes = { login: 'modified', is_admin: true }
- assert_equal 'modified', @user.login
- end
-
- def test_should_not_assign_protected_attributes
- @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
- @user.attributes = { login: 'modified', is_admin: true }
- assert !@user.is_admin?
+ def test_should_raise_exception_if_not_permitted
+ @user = SomeUser.new(login: 'login', is_admin: false)
+ assert_raises ActiveModel::ForbiddenAttributesError do
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true)
end
+ end
- def test_should_assign_protected_attributes
- @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
- if ::ActiveRecord::VERSION::STRING > "3.1"
- @user.send(:assign_attributes, { login: 'modified', is_admin: true }, without_protection: true)
- else
- @user.send(:attributes=, { login: 'modified', is_admin: true }, false)
- end
- assert @user.is_admin?
+ def test_should_raise_exception_on_init_if_not_permitted
+ assert_raises ActiveModel::ForbiddenAttributesError do
+ @user = SomeUser.new ActionController::Parameters.new(login: 'modified', is_admin: true)
end
end
@@ -279,23 +255,21 @@ 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_attributes[:email][:mode].class, Proc
+ assert_equal @person.class.attr_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_attributes[:credentials][:mode].class, Proc
+ assert_equal @person.class.attr_encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
assert_nil @person.encrypted_credentials_salt
assert_nil @person.encrypted_credentials_iv
end
- if ::ActiveRecord::VERSION::STRING > "3.1"
- def test_should_allow_assign_attributes_with_nil
- @person = Person.new
- assert_nil(@person.assign_attributes nil)
- end
+ def test_should_allow_assign_attributes_with_nil
+ @person = Person.new
+ assert_nil(@person.assign_attributes nil)
end
def test_that_alias_encrypts_column
diff --git a/test/attr_encrypted_test.rb b/test/attr_encrypted_test.rb
index 84cb130a..793fe756 100644
--- a/test/attr_encrypted_test.rb
+++ b/test/attr_encrypted_test.rb
@@ -82,12 +82,12 @@ 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_attributes
+ assert User.attr_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_attributes
+ refute User.attr_encrypted_attributes.include?(:salt)
end
def test_attr_encrypted_should_return_true_for_email
@@ -95,7 +95,7 @@ def test_attr_encrypted_should_return_true_for_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_attributes[:email][:attribute], User.attr_encrypted_attributes[:without_encoding][:attribute]
end
def test_attr_encrypted_should_return_false_for_salt
@@ -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_attributes[:email]
iv = @user.send(:generate_iv, options[:algorithm])
encoded_iv = [iv].pack(options[:encode_iv])
salt = SecureRandom.random_bytes
@@ -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_attributes
+ assert_equal [User.attr_encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, Admin.attr_encrypted_attributes.keys.collect { |key| key.to_s }.sort
end
def test_should_inherit_attr_encrypted_options
@@ -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_attributes.empty?
end
def test_should_evaluate_a_symbol_option
@@ -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_attributes.include?(:aliased)
end
def test_should_always_reset_options
@@ -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_attributes[:email][:algorithm]
+ assert_equal YetAnotherClass.attr_encrypted_attributes[:email][:algorithm], 'aes-256-gcm'
end
def test_should_not_encode_iv_when_encode_iv_is_false
@@ -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_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_attributes[:ssn][:operation]
+ assert_nil another_user.attr_encrypted_attributes[:ssn][:operation]
end
def test_should_not_by_default_generate_key_when_attribute_is_empty
diff --git a/test/data_mapper_test.rb b/test/data_mapper_test.rb
deleted file mode 100644
index 3fb284a6..00000000
--- a/test/data_mapper_test.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'test_helper'
-
-DataMapper.setup(:default, 'sqlite3::memory:')
-
-class Client
- include DataMapper::Resource
-
- property :id, Serial
- property :encrypted_email, String
- property :encrypted_email_iv, String
- property :encrypted_email_salt, String
-
- property :encrypted_credentials, Text
- property :encrypted_credentials_iv, Text
- property :encrypted_credentials_salt, Text
-
- self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
-
- attr_encrypted :email, :key => SECRET_KEY
- attr_encrypted :credentials, :key => SECRET_KEY, :marshal => true
-
- def initialize(attrs = {})
- super attrs
- self.credentials ||= { :username => 'example', :password => 'test' }
- end
-end
-
-DataMapper.auto_migrate!
-
-class DataMapperTest < Minitest::Test
-
- def setup
- Client.all.each(&:destroy)
- end
-
- def test_should_encrypt_email
- @client = Client.new :email => 'test@example.com'
- assert @client.save
- refute_nil @client.encrypted_email
- refute_equal @client.email, @client.encrypted_email
- assert_equal @client.email, Client.first.email
- end
-
- def test_should_marshal_and_encrypt_credentials
- @client = Client.new
- assert @client.save
- refute_nil @client.encrypted_credentials
- refute_equal @client.credentials, @client.encrypted_credentials
- assert_equal @client.credentials, Client.first.credentials
- assert Client.first.credentials.is_a?(Hash)
- end
-
- def test_should_encode_by_default
- assert Client.attr_encrypted_options[:encode]
- end
-
-end
diff --git a/test/legacy_active_record_test.rb b/test/legacy_active_record_test.rb
deleted file mode 100644
index 80231857..00000000
--- a/test/legacy_active_record_test.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-# frozen_string_literal: true
-
-# -*- encoding: utf-8 -*-
-require_relative 'test_helper'
-
-ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
-
-def create_people_table
- ActiveRecord::Schema.define(:version => 1) do
- create_table :legacy_people do |t|
- t.string :encrypted_email
- t.string :password
- t.string :encrypted_credentials
- t.string :salt
- end
- end
-end
-
-# The table needs to exist before defining the class
-create_people_table
-
-ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
-
-class LegacyPerson < ActiveRecord::Base
- self.attr_encrypted_options[:insecure_mode] = true
- self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
- self.attr_encrypted_options[:mode] = :single_iv_and_salt
-
- attr_encrypted :email, :key => 'a secret key'
- attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => 'some private key', insecure_mode: true, algorithm: 'aes-256-cbc') }, :marshal => true
-
- ActiveSupport::Deprecation.silenced = true
- def after_initialize; end
- ActiveSupport::Deprecation.silenced = false
-
- after_initialize :initialize_salt_and_credentials
-
- protected
-
- def initialize_salt_and_credentials
- self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(5)).to_s)
- self.credentials ||= { :username => 'example', :password => 'test' }
- rescue ActiveRecord::MissingAttributeError
- end
-end
-
-class LegacyPersonWithValidation < LegacyPerson
- validates_presence_of :email
- validates_uniqueness_of :encrypted_email
-end
-
-class LegacyActiveRecordTest < Minitest::Test
-
- def setup
- drop_all_tables
- create_people_table
- end
-
- def test_should_decrypt_with_correct_encoding
- if defined?(Encoding)
- @person = LegacyPerson.create :email => 'test@example.com'
- assert_equal 'UTF-8', LegacyPerson.first.email.encoding.name
- end
- end
-
- def test_should_encrypt_email
- @person = LegacyPerson.create :email => 'test@example.com'
- refute_nil @person.encrypted_email
- refute_equal @person.email, @person.encrypted_email
- assert_equal @person.email, LegacyPerson.first.email
- end
-
- def test_should_marshal_and_encrypt_credentials
- @person = LegacyPerson.create
- refute_nil @person.encrypted_credentials
- refute_equal @person.credentials, @person.encrypted_credentials
- assert_equal @person.credentials, LegacyPerson.first.credentials
- end
-
- def test_should_find_by_email
- @person = LegacyPerson.create(:email => 'test@example.com')
- assert_equal @person, LegacyPerson.find_by_email('test@example.com')
- end
-
- def test_should_find_by_email_and_password
- LegacyPerson.create(:email => 'test@example.com', :password => 'invalid')
- @person = LegacyPerson.create(:email => 'test@example.com', :password => 'test')
- assert_equal @person, LegacyPerson.find_by_email_and_password('test@example.com', 'test')
- end
-
- def test_should_scope_by_email
- @person = LegacyPerson.create(:email => 'test@example.com')
- assert_equal @person, LegacyPerson.scoped_by_email('test@example.com').first rescue NoMethodError
- end
-
- def test_should_scope_by_email_and_password
- LegacyPerson.create(:email => 'test@example.com', :password => 'invalid')
- @person = LegacyPerson.create(:email => 'test@example.com', :password => 'test')
- assert_equal @person, LegacyPerson.scoped_by_email_and_password('test@example.com', 'test').first rescue NoMethodError
- end
-
- def test_should_encode_by_default
- assert LegacyPerson.attr_encrypted_options[:encode]
- end
-
- def test_should_validate_presence_of_email
- @person = LegacyPersonWithValidation.new
- assert !@person.valid?
- assert !@person.errors[:email].empty? || @person.errors.on(:email)
- end
-
- def test_should_validate_uniqueness_of_email
- @person = LegacyPersonWithValidation.new :email => 'test@example.com'
- assert @person.save
- @person2 = LegacyPersonWithValidation.new :email => @person.email
- assert !@person2.valid?
- assert !@person2.errors[:encrypted_email].empty? || @person2.errors.on(:encrypted_email)
- end
-
-end
diff --git a/test/legacy_attr_encrypted_test.rb b/test/legacy_attr_encrypted_test.rb
index 875086d2..608a8197 100644
--- a/test/legacy_attr_encrypted_test.rb
+++ b/test/legacy_attr_encrypted_test.rb
@@ -57,12 +57,12 @@ 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_attributes
+ assert LegacyUser.attr_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_attributes
+ assert !LegacyUser.attr_encrypted_attributes.include?(:salt)
end
def test_attr_encrypted_should_return_true_for_email
@@ -70,7 +70,7 @@ def test_attr_encrypted_should_return_true_for_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_attributes[:email][:attribute], LegacyUser.attr_encrypted_attributes[:without_encoding][:attribute]
end
def test_attr_encrypted_should_return_false_for_salt
@@ -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_attributes
+ assert_equal [LegacyUser.attr_encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, LegacyAdmin.attr_encrypted_attributes.keys.collect { |key| key.to_s }.sort
end
def test_should_inherit_attr_encrypted_options
@@ -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_attributes.empty?
end
def test_should_evaluate_a_symbol_option
@@ -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_attributes.include?(:aliased)
end
def test_should_always_reset_options
diff --git a/test/legacy_data_mapper_test.rb b/test/legacy_data_mapper_test.rb
deleted file mode 100644
index 03916dd9..00000000
--- a/test/legacy_data_mapper_test.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'test_helper'
-
-DataMapper.setup(:default, 'sqlite3::memory:')
-
-class LegacyClient
- include DataMapper::Resource
- self.attr_encrypted_options[:insecure_mode] = true
- self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
- self.attr_encrypted_options[:mode] = :single_iv_and_salt
-
- property :id, Serial
- property :encrypted_email, String
- property :encrypted_credentials, Text
- property :salt, String
-
- attr_encrypted :email, :key => 'a secret key', mode: :single_iv_and_salt
- attr_encrypted :credentials, :key => Proc.new { |client| Encryptor.encrypt(:value => client.salt, :key => 'some private key', insecure_mode: true, algorithm: 'aes-256-cbc') }, :marshal => true, mode: :single_iv_and_salt
-
- def initialize(attrs = {})
- super attrs
- self.salt ||= Digest::SHA1.hexdigest((Time.now.to_i * rand(5)).to_s)
- self.credentials ||= { :username => 'example', :password => 'test' }
- end
-end
-
-DataMapper.auto_migrate!
-
-class LegacyDataMapperTest < Minitest::Test
-
- def setup
- LegacyClient.all.each(&:destroy)
- end
-
- def test_should_encrypt_email
- @client = LegacyClient.new :email => 'test@example.com'
- assert @client.save
- refute_nil @client.encrypted_email
- refute_equal @client.email, @client.encrypted_email
- assert_equal @client.email, LegacyClient.first.email
- end
-
- def test_should_marshal_and_encrypt_credentials
- @client = LegacyClient.new
- assert @client.save
- refute_nil @client.encrypted_credentials
- refute_equal @client.credentials, @client.encrypted_credentials
- assert_equal @client.credentials, LegacyClient.first.credentials
- assert LegacyClient.first.credentials.is_a?(Hash)
- end
-
- def test_should_encode_by_default
- assert LegacyClient.attr_encrypted_options[:encode]
- end
-
-end
diff --git a/test/run.sh b/test/run.sh
index 7e9b777e..3543bad4 100755
--- a/test/run.sh
+++ b/test/run.sh
@@ -1,12 +1,21 @@
-#!/usr/bin/env sh -e
+#!/usr/bin/env bash
-for RUBY in 1.9.3 2.0.0 2.1 2.2
+set -e
+
+for RUBY in 2.7.6 3.0.4
do
- for RAILS in 2.3.8 3.0.0 3.1.0 3.2.0 4.0.0 4.1.0 4.2.0
+ for ACTIVERECORD in 6.1 7.0
do
- if [[ $RUBY -gt 1.9.3 && $RAILS -lt 4.0.0 ]]; then
- continue
- fi
- RBENV_VERSION=$RUBY ACTIVERECORD=$RAILS bundle && bundle exec rake
+ echo ">>> Testing with Ruby ${RUBY} and ActiveRecord ${ACTIVERECORD}."
+ export RBENV_VERSION=$RUBY
+ export ACTIVERECORD=$ACTIVERECORD
+
+ rbenv install $RUBY --skip-existing
+ bundle install
+ bundle check
+ bundle exec rake test
+ rm Gemfile.lock
+ echo ">>> Finished testing with Ruby ${RUBY} and ActiveRecord ${ACTIVERECORD}."
done
done
+
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index c352a138..070651cc 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -2,13 +2,12 @@
require 'simplecov'
require 'simplecov-rcov'
-require "codeclimate-test-reporter"
+require 'pry'
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
[
SimpleCov::Formatter::HTMLFormatter,
- SimpleCov::Formatter::RcovFormatter,
- CodeClimate::TestReporter::Formatter
+ SimpleCov::Formatter::RcovFormatter
]
)
@@ -16,8 +15,6 @@
add_filter 'test'
end
-CodeClimate::TestReporter.start
-
require 'minitest/autorun'
# Rails 4.0.x pins to an old minitest
@@ -26,7 +23,6 @@
end
require 'active_record'
-require 'data_mapper'
require 'digest/sha2'
require 'sequel'
ActiveSupport::Deprecation.behavior = :raise
@@ -35,11 +31,7 @@
$:.unshift(File.dirname(__FILE__))
require 'attr_encrypted'
-DB = if defined?(RUBY_ENGINE) && RUBY_ENGINE.to_sym == :jruby
- Sequel.jdbc('jdbc:sqlite::memory:')
-else
- Sequel.sqlite
-end
+DB = Sequel.sqlite
# The :after_initialize hook was removed in Sequel 4.0
# and had been deprecated for a while before that:
@@ -55,6 +47,6 @@ def base64_encoding_regex
def drop_all_tables
connection = ActiveRecord::Base.connection
- tables = (ActiveRecord::VERSION::MAJOR >= 5 ? connection.data_sources : connection.tables)
+ tables = connection.data_sources
tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
end