reidmorrison / symmetric-encryption

Symmetric Encryption for Ruby Projects using OpenSSL

Home Page:https://logger.rocketjob.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Require active record causes trouble when it is not in use

Irto opened this issue · comments

Environment

Provide at least:

  • Ruby Version: 2.4.2
  • Symmetric Encryption: 4.1.1
  • Rails 4.2
  • Full Stack Trace, if an exception is being raised.

Behaviour

After update to version 4.1.1 from 4.0 we are receiving an error from another gem, money-rails. The lib started to raise an error that do not allow us to run our app, the error is a simple exception: ActiveRecord::ConnectionNotEstablished. It is expected in our app since it do not have an configured SQL database, we are just using mongo through Mongoid.

The error begin to happen when the line require 'active_record' is added to the file lib/symmetric_encryption.rb. When in any rails app it forces to load the active record and trigger the hooks in ActiveSupport that could execute unexpected behaviours, as in money-rails in this file: https://github.com/RubyMoney/money-rails/blob/master/lib/money-rails/hooks.rb line 21

Important to say that the rails gem adds the active record as dependency, then you will be able to require it in any rails app, even when it was not configured.

Also, I open this issue here, and not in money-rails since that they have this file unmodified for the last 2 years, and newest change are in symmetric-encryption.

So guys, I hope I could describe well the problem and we can discuss a way to solve this and I have the update version of the lib.

The previous behavior relied on Rails being loaded before Symmetric Encryption was loaded, which is not always true. This change is now required since more and more people are using RuboCop that is changing the order of the Gemfile which broke the previously brittle Symmetric Encryption load code.
Suggest you fix older gems that are brittle and incorrectly rely on Gem load order.

@reidmorrison this issue may be unrelated to money-rails or other gems which rely on gem load order. While gems like money-rails (and searchkick) may trigger the ActiveRecord::ConnectionNotEstablished error in established applications running without activerecord, the very same error can be observed inside a brand new mongoid based rails application.

I pushed https://github.com/williamatodd/mongoid_example_app to supplement this closed report by creating a new app without activerecord, adding symmetric_encryption to the gemfile, generating the configuration file, and generating a scaffold for a Product model with a single attribute of name:string. If you have the opportunity to clone this app, you may be able to recreate the error by running bin/rake test after the requisite bundling and db setup.

FWIW, to rule out the possibility of Gemfile ordering and rubocop autocorrect issues, I experimented with multiple gemfile orders and ran through the same experiment before and after rubocop autocorrection. Some of these experimental gemfiles are located in this gist https://gist.github.com/williamatodd/ea301a51ade4768a51a322d2e71eeb05 for your review.

Regardless of the whether we are able to resolve the underlying issue, which I attest could be a shared delusion, I appreciate your efforts in maintaining this gem and thank you for your time.

mongoid_connection_error

Thank you, the sample application was a big help. Using the sample application it appears to be working with the new changes in master. Can you give it a try against master and see if it also works for you?

A recent PR #118 by @cmrd-senya allows bypassing all the Rails extensions that can help with issues like these.

@reidmorrison, ad31ba3 did not fix the issue on the sample app when attempting the workflow outlined above. #119 should take care of it provided that you're amenable to lazily loading the ORM adapters.

Hello.

We just doing this with @cmrd-senya in rails 5:

class EncryptedStringType < ActiveRecord::Type::String

  def deserialize(value)
    CloudEncryptor.decrypt(value) if value
  end

  def serialize(value)
    CloudEncryptor.encrypt(value) if value
  end

end

ActiveRecord::Type.register(:encrypted_string, EncryptedStringType)

In model:

attribute :encrypted_data, :encrypted_string

Encryptor itself:

require 'symmetric-encryption/core'

class CloudEncryptor

  class << self

    def encrypt(plaintext)
      SymmetricKeyManager.ensure_cipher_presence!
      SymmetricEncryption.encrypt(plaintext, random_iv: true)
    end

    def decrypt(ciphertext)
      SymmetricKeyManager.ensure_cipher_presence!
      SymmetricEncryption.decrypt(ciphertext)
    end

  end

end

SymmetricKeyManager.ensure_cipher_presence! just our implementation, which load SymmetricEncryption.cipher with "failover": trying AWS KMS -> if it down, try different AWS region -> if all down -> trying GCP. Also good, that we initialize lazy by ActiveRecord::Type::String and not influence on rails load.

BTW, @reidmorrison we can try to add "failover" functionality later to gem, if somebody interesting in this. Main issue, that we wrote rake tasks to generate from one master key all encrypted keys from different providers (looks like here we need add this in cli).

Using the sample app with master results in the following errors, but the ActiveRecord issues appear to be resolved:

Error:
ProductsControllerTest#test_should_update_product:
NoMethodError: undefined method `products' for #<ProductsControllerTest:0x00007f98f3392c98>
    test/controllers/products_controller_test.rb:5:in `block in <class:ProductsControllerTest>'

Published v4.2.0 that also includes the fix to lazy load AR and Mongoid extensions.