diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..8145f56b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: CI + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby-version: ['2.7', '3.0', '3.1'] + rails-version: ['5.1.1', '5.2.0', '6.0.0', '6.1.0'] + exclude: + - ruby-version: 3.0 + rails-version: 5.1.1 + - ruby-version: 3.1 + rails-version: 5.1.1 + - ruby-version: 3.0 + rails-version: 5.2.0 + - ruby-version: 3.1 + rails-version: 5.2.0 + env: + ACTIVERECORD: ${{ matrix.rails-version }} + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - name: Run tests + run: bundle exec rake diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 00000000..0d47d339 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +attr_encrypted diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f75b2ede..00000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -sudo: false -language: ruby -cache: bundler -rvm: - - 2.0 - - 2.1 - - 2.2.2 - - 2.3.0 - - 2.4.0 - - 2.5.0 - - rbx -env: - - ACTIVERECORD=3.0.0 - - ACTIVERECORD=3.1.0 - - ACTIVERECORD=3.2.0 - - ACTIVERECORD=4.0.0 - - ACTIVERECORD=4.1.0 - - ACTIVERECORD=4.2.0 - - ACTIVERECORD=5.0.0 - - ACTIVERECORD=5.1.1 -matrix: - exclude: - - rvm: 2.0 - env: ACTIVERECORD=5.0.0 - - rvm: 2.0 - env: ACTIVERECORD=5.1.1 - - rvm: 2.1 - env: ACTIVERECORD=5.0.0 - - rvm: 2.1 - env: ACTIVERECORD=5.1.1 - - rvm: 2.4.0 - env: ACTIVERECORD=3.0.0 - - rvm: 2.4.0 - env: ACTIVERECORD=3.1.0 - - rvm: 2.4.0 - env: ACTIVERECORD=3.2.0 - - rvm: 2.4.0 - env: ACTIVERECORD=4.0.0 - - rvm: 2.4.0 - env: ACTIVERECORD=4.1.0 - - rvm: 2.5.0 - env: ACTIVERECORD=3.0.0 - - rvm: 2.5.0 - env: ACTIVERECORD=3.1.0 - - rvm: 2.5.0 - env: ACTIVERECORD=3.2.0 - - rvm: 2.5.0 - env: ACTIVERECORD=4.0.0 - - rvm: 2.5.0 - env: ACTIVERECORD=4.1.0 - - rvm: rbx - env: ACTIVERECORD=5.0.0 - - rvm: rbx - env: ACTIVERECORD=5.1.1 - allow_failures: - - rvm: rbx - fast_finish: true -addons: - code_climate: - repo_token: a90435ed4954dd6e9f3697a20c5bc3754f67d94703f870e8fc7b00f69f5b2d06 diff --git a/README.md b/README.md index 74853f1d..b0f097ce 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ # 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) + +[![Build Status](https://github.com/KentaaNL/attr_encrypted/actions/workflows/test.yml/badge.svg)](https://github.com/KentaaNL/attr_encrypted/actions) 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`. + +Forked from [attr-encrypted/attr_encrypted](https://github.com/attr-encrypted/attr_encrypted) with the following fixes: +* Optional update encrypted attributes only when values changed (#1) +* Fix concurrency problem (#2) +* Support ActiveRecord 5.2, 6.0 and 6.1 (#3, #6, #7) +* Rename encrypt/decrypt methods (#8) ## Installation Add attr_encrypted to your gemfile: ```ruby - gem "attr_encrypted", "~> 3.0.0" + gem "attr_encrypted", github: "KentaaNL/attr_encrypted" ``` Then install the gem: @@ -22,7 +29,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 @@ -145,7 +152,8 @@ The following are the default options used by `attr_encrypted`: decrypt_method: 'decrypt', mode: :per_attribute_iv, algorithm: 'aes-256-gcm', - allow_empty_value: false + allow_empty_value: false, + update_unchanged: true ``` All of the aforementioned options are explained in depth below. @@ -322,6 +330,16 @@ You may want to encrypt empty strings or nil so as to not reveal which records a end ``` +### The `:update_unchanged` option + +You may want to only update changed attributes each time the record is saved. + +```ruby + class User + attr_encrypted :email, key: 'some secret key', marshal: true, update_unchanged: false + end +``` + ## ORMs @@ -357,7 +375,7 @@ NOTE: This only works if all records are encrypted with the same encryption key __NOTE: This feature is deprecated and will be removed in the next major release.__ -### DataMapper and Sequel +### Sequel #### Default options diff --git a/attr_encrypted.gemspec b/attr_encrypted.gemspec index 41f96ed9..9631d6d9 100644 --- a/attr_encrypted.gemspec +++ b/attr_encrypted.gemspec @@ -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'] @@ -38,10 +37,10 @@ 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('sequel') + s.add_development_dependency('pry-byebug') if RUBY_VERSION < '2.1.0' s.add_development_dependency('nokogiri', '< 1.7.0') s.add_development_dependency('public_suffix', '< 3.0.0') @@ -50,19 +49,12 @@ Gem::Specification.new do |s| 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') + s.add_development_dependency('sqlite3', '~> 1.4.0', '>= 1.4') end - s.add_development_dependency('dm-sqlite-adapter') 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/ - - s.post_install_message = "\n\n\nWARNING: Several insecure default options and features were deprecated in attr_encrypted v2.0.0.\n -Additionally, there was a bug in Encryptor v2.0.0 that insecurely encrypted data when using an AES-*-GCM algorithm.\n -This bug was fixed but introduced breaking changes between v2.x and v3.x.\n -Please see the README for more information regarding upgrading to attr_encrypted v3.0.0.\n\n\n" - end diff --git a/lib/attr_encrypted.rb b/lib/attr_encrypted.rb index 3895b721..675cbf08 100644 --- a/lib/attr_encrypted.rb +++ b/lib/attr_encrypted.rb @@ -52,7 +52,7 @@ def self.extended(base) # :nodoc: # string instead of just 'true'. See # http://www.ruby-doc.org/core/classes/Array.html#M002245 # for more encoding directives. - # Defaults to false unless you're using it with ActiveRecord, DataMapper, or Sequel. + # Defaults to false unless you're using it with ActiveRecord or Sequel. # # encode_iv: Defaults to true. @@ -104,6 +104,9 @@ def self.extended(base) # :nodoc: # allow_empty_value: Attributes which have nil or empty string values will not be encrypted unless this option # has a truthy value. # + # update_unchanged: Attributes which have unchanged values will be encrypted again on each update. + # Defaults to true. + # # You can specify your own default options # # class User @@ -158,12 +161,14 @@ def attr_encrypted(*attributes) end define_method(attribute) do - instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", decrypt(attribute, send(encrypted_attribute_name))) + instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", decrypt_attribute(attribute, send(encrypted_attribute_name))) end define_method("#{attribute}=") do |value| - send("#{encrypted_attribute_name}=", encrypt(attribute, value)) - instance_variable_set("@#{attribute}", value) + if should_update_encrypted_attribute?(attribute, value) + send("#{encrypted_attribute_name}=", encrypt_attribute(attribute, value)) + instance_variable_set("@#{attribute}", value) + end end define_method("#{attribute}?") do @@ -204,6 +209,7 @@ def attr_encrypted_default_options mode: :per_attribute_iv, algorithm: 'aes-256-gcm', allow_empty_value: false, + update_unchanged: true } end @@ -232,8 +238,8 @@ def attr_encrypted?(attribute) # attr_encrypted :email # end # - # email = User.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING') - def decrypt(attribute, encrypted_value, options = {}) + # email = User.decrypt_attribute(:email, 'SOME_ENCRYPTED_EMAIL_STRING') + def decrypt_attribute(attribute, encrypted_value, options = {}) options = 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] @@ -258,8 +264,8 @@ def decrypt(attribute, encrypted_value, options = {}) # attr_encrypted :email # end # - # encrypted_email = User.encrypt(:email, 'test@example.com') - def encrypt(attribute, value, options = {}) + # encrypted_email = User.encrypt_attribute(:email, 'test@example.com') + def encrypt_attribute(attribute, value, options = {}) options = 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 @@ -301,7 +307,11 @@ def encrypted_attributes # User.encrypt_email('SOME_ENCRYPTED_EMAIL_STRING') def method_missing(method, *arguments, &block) if method.to_s =~ /^((en|de)crypt)_(.+)$/ && attr_encrypted?($3) - send($1, $3, *arguments) + if $1 == 'encrypt' + send(:encrypt_attribute, $3, *arguments) + else + send(:decrypt_attribute, $3, *arguments) + end else super end @@ -322,11 +332,11 @@ module InstanceMethods # end # # @user = User.new('some-secret-key') - # @user.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING') - def decrypt(attribute, encrypted_value) + # @user.decrypt_attribute(:email, 'SOME_ENCRYPTED_EMAIL_STRING') + def decrypt_attribute(attribute, encrypted_value) encrypted_attributes[attribute.to_sym][:operation] = :decrypting 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)) + self.class.decrypt_attribute(attribute, encrypted_value, evaluated_attr_encrypted_options_for(attribute)) end # Encrypts a value for the attribute specified using options evaluated in the current object's scope @@ -343,22 +353,36 @@ def decrypt(attribute, encrypted_value) # end # # @user = User.new('some-secret-key') - # @user.encrypt(:email, 'test@example.com') - def encrypt(attribute, value) + # @user.encrypt_attribute(:email, 'test@example.com') + def encrypt_attribute(attribute, value) encrypted_attributes[attribute.to_sym][:operation] = :encrypting encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(value) - self.class.encrypt(attribute, value, evaluated_attr_encrypted_options_for(attribute)) + self.class.encrypt_attribute(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 ||= self.class.encrypted_attributes.dup + @encrypted_attributes ||= begin + duplicated= {} + self.class.encrypted_attributes.map { |key, value| duplicated[key] = value.dup } + duplicated + end end protected + # Determine if unchanged attribute needs to be updated again + def should_update_encrypted_attribute?(attribute, value) + if encrypted_attributes[attribute.to_sym][:update_unchanged] + return true + else + old_value = instance_variable_get("@#{attribute}") + return old_value.nil? || old_value != value + end + end + # 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 diff --git a/lib/attr_encrypted/adapters/active_record.rb b/lib/attr_encrypted/adapters/active_record.rb index a22108e4..8bf241f4 100644 --- a/lib/attr_encrypted/adapters/active_record.rb +++ b/lib/attr_encrypted/adapters/active_record.rb @@ -60,11 +60,11 @@ def attr_encrypted(*attrs) if ::ActiveRecord::VERSION::STRING >= "4.1" define_method("#{attr}_changed?") do |options = {}| - attribute_changed?(attr, options) + attribute_changed?(attr, **options) end else define_method("#{attr}_changed?") do - attribute_changed?(attr) + attribute_changed?(attr) end end @@ -73,6 +73,22 @@ def attr_encrypted(*attrs) end define_method("#{attr}_with_dirtiness=") do |value| + ## + # In ActiveRecord 5.2+, due to changes to the way virtual + # attributes are handled, @attributes[attr].value is nil which + # breaks attribute_was. Setting it here returns us to the expected + # behavior. + if ::ActiveRecord::VERSION::STRING >= "5.2" + # This is needed support attribute_was before a record has + # been saved + if ::ActiveRecord::VERSION::STRING < "6.0" + set_attribute_was(attr, __send__(attr)) if value != __send__(attr) + end + # This is needed to support attribute_was after a record has + # been saved + @attributes.write_from_user(attr.to_s, value) if value != __send__(attr) + end + ## attribute_will_change!(attr) if value != __send__(attr) __send__("#{attr}_without_dirtiness=", value) end diff --git a/lib/attr_encrypted/adapters/data_mapper.rb b/lib/attr_encrypted/adapters/data_mapper.rb deleted file mode 100644 index d918f312..00000000 --- a/lib/attr_encrypted/adapters/data_mapper.rb +++ /dev/null @@ -1,22 +0,0 @@ -if defined?(DataMapper) - module AttrEncrypted - module Adapters - module DataMapper - def self.extended(base) # :nodoc: - class << base - alias_method :included_without_attr_encrypted, :included - alias_method :included, :included_with_attr_encrypted - end - end - - def included_with_attr_encrypted(base) - included_without_attr_encrypted(base) - base.extend AttrEncrypted - base.attr_encrypted_options[:encode] = true - end - end - end - end - - DataMapper::Resource.extend AttrEncrypted::Adapters::DataMapper -end \ No newline at end of file diff --git a/test/active_record_test.rb b/test/active_record_test.rb index 89134548..3d0f70c8 100644 --- a/test/active_record_test.rb +++ b/test/active_record_test.rb @@ -124,7 +124,6 @@ class Address < ActiveRecord::Base end class ActiveRecordTest < Minitest::Test - def setup drop_all_tables create_tables @@ -202,21 +201,49 @@ def test_should_create_was_predicate 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 + if ::ActiveRecord::VERSION::STRING < "6.0" + 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 + end + + # ActiveRecord 5.2 specific methods + if ::ActiveRecord::VERSION::STRING >= "5.2" + def test_should_create_will_save_change_to_predicate + person = Person.create!(email: 'test@example.com') + refute person.will_save_change_to_email? + person.email = 'test@example.com' + refute person.will_save_change_to_email? + person.email = 'test2@example.com' + assert person.will_save_change_to_email? + end + + def test_should_create_saved_change_to_predicate + person = Person.create!(email: 'test@example.com') + assert person.saved_change_to_email? + person.reload + person.email = 'test@example.com' + refute person.saved_change_to_email? + person.email = nil + refute person.saved_change_to_email? + person.email = 'test2@example.com' + refute person.saved_change_to_email? + person.save + assert person.saved_change_to_email? + end end if ::ActiveRecord::VERSION::STRING > "4.0" diff --git a/test/attr_encrypted_test.rb b/test/attr_encrypted_test.rb index 3fc131b1..780250d4 100644 --- a/test/attr_encrypted_test.rb +++ b/test/attr_encrypted_test.rb @@ -29,6 +29,7 @@ class User attr_encrypted :with_false_unless, :key => SECRET_KEY, :unless => false, mode: :per_attribute_iv_and_salt attr_encrypted :with_if_changed, :key => SECRET_KEY, :if => :should_encrypt attr_encrypted :with_allow_empty_value, key: SECRET_KEY, allow_empty_value: true, marshal: true + attr_encrypted :with_unchanged_false, key: SECRET_KEY, update_unchanged: false attr_encryptor :aliased, :key => SECRET_KEY @@ -379,7 +380,7 @@ def test_should_decrypt_second_record @user2 = User.new @user2.email = 'test@example.com' - assert_equal 'test@example.com', @user1.decrypt(:email, @user1.encrypted_email) + assert_equal 'test@example.com', @user1.decrypt_attribute(:email, @user1.encrypted_email) end def test_should_specify_the_default_algorithm @@ -466,4 +467,51 @@ def test_should_not_by_default_generate_iv_when_attribute_is_empty user.with_true_if = nil assert_nil user.encrypted_with_true_if_iv end + + def test_should_not_generate_iv_if_same_value_when_option_is_false + user = User.new + assert_nil user.encrypted_with_unchanged_false_iv + user.with_unchanged_false = 'thing@thing.com' + old_value = user.encrypted_with_unchanged_false_iv + refute_nil(old_value) + user.with_unchanged_false = 'thing@thing.com' + assert_equal old_value, user.encrypted_with_unchanged_false_iv + end + + def test_should_generate_iv_if_same_value_when_option_is_true + user = User.new + assert_nil user.encrypted_email_iv + user.email = 'thing@thing.com' + refute_nil(old_value = user.encrypted_email_iv) + user.email = 'thing@thing.com' + refute_equal old_value, user.encrypted_email_iv + end + + def test_should_not_update_iv_if_same_value_when_option_is_false + user = User.new + user.with_unchanged_false = 'thing@thing.com' + old_encrypted_with_unchanged_false_iv = user.encrypted_with_unchanged_false_iv + refute_nil old_encrypted_with_unchanged_false_iv + user.with_unchanged_false = 'thing@thing.com' + assert_equal old_encrypted_with_unchanged_false_iv, user.encrypted_with_unchanged_false_iv + end + + def test_should_not_update_iv_if_same_value_when_option_is_true + user = User.new(email: 'thing@thing.com') + old_encrypted_email_iv = user.encrypted_email_iv + refute_nil old_encrypted_email_iv + user.email = 'thing@thing.com' + refute_nil user.encrypted_email_iv + refute_equal old_encrypted_email_iv, user.encrypted_email_iv + end + + def test_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] + end end diff --git a/test/data_mapper_test.rb b/test/data_mapper_test.rb deleted file mode 100644 index b01ccbd2..00000000 --- a/test/data_mapper_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -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_data_mapper_test.rb b/test/legacy_data_mapper_test.rb deleted file mode 100644 index eab5a23e..00000000 --- a/test/legacy_data_mapper_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -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/test_helper.rb b/test/test_helper.rb index ad6ef3bd..ad9dd2f8 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,6 +1,7 @@ require 'simplecov' require 'simplecov-rcov' -require "codeclimate-test-reporter" +require 'codeclimate-test-reporter' +require 'pry-byebug' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( [ @@ -24,7 +25,6 @@ end require 'active_record' -require 'data_mapper' require 'digest/sha2' require 'sequel' ActiveSupport::Deprecation.behavior = :raise