Support NoSQL databases in generator
It would be awesome if, in the generator, you could specify an optional arg that let you generate for a NoSQL database (like Mongoid). So instead of creating a database migration, it would just add the appropriate fields to the model file.
That's a great idea. I don't know how well attr_encrypted plays with Mongoid, but I'll investigate when I have some free-time later.
We have a fork at http://github.com/tinfoil/attr_encrypted that patches in Mongoid support. So long as you have the proper fields declared, it should work with something like:
field :encrypted_otp_secret, :type => String
field :encrypted_otp_secret_iv, :type => String
field :encrypted_otp_secret_salt, :type => String
attr_encrypted :otp_secret, :key => ENV['OTP_SECRET_DB_KEY']
@bsedat I'm using your fork of attr_encrypted for my rails 3 app using mongoid. I'm trying to get this set up, but nothing is getting saved to the encrypted fields when I set the otp_secret and save the user.
Here's an example from the console:
1.9.3-head :003 > u.otp_secret = User.generate_otp_secret
=> "u7n3t3fmn3q2rwnduvcwrpzplkp6nhxgv7fnerytmzym46rk2him74l2hhti4kfijhp7w6fmwqvhnqxzbcparznop62edmivcyiy7q5hdsak3v62smptmvahhipb25sc"
1.9.3-head :004 > u.save!
=> true
1.9.3-head :006 > u.inspect
=> "#<User _id: 531a3cdcc8eb1cd895000008, created_at: 2014-03-07 21:40:44 UTC, updated_at: 2014-10-24 21:50:51 UTC, invited_by_type: nil, invited_by_field: nil, invited_by_id: nil, email: \"FAKEFAKEFAKE\", encrypted_password: \"BLAHBLAHBLAH\", authentication_token: \"WAHWAHWAH\", [...bunch of stuff], encrypted_otp_secret: nil, encrypted_otp_secret_iv: nil, encrypted_otp_secret_salt: nil, active: false, [...bunch of other stuff]>"
Any ideas why the otp fields aren't saving?
I've tried all the combinations of fields that I can think of on my user model after reading several other attr_encrypted issues
Here's the last thing I tried, which is maximum fields:
## Two-Factor
attr_accessible :otp_attempt
field :otp_required_for_login, type:Boolean, default:true ## TODO remove this--should not be on by default
field :otp_secret, type:String
field :otp_secret_iv, type:String
field :otp_secret_salt, type:String
field :encrypted_otp_secret, type:String
field :encrypted_otp_secret_iv, type:String
field :encrypted_otp_secret_salt, type:String
attr_encrypted :otp_secret, :key => 'super_secret_two_factor_key', :encode => true, :mode => :per_attribute_iv_and_salt
attr_encrypted :otp_secret_iv, :key => 'super_secret_two_factor_key', :encode => true, :mode => :per_attribute_iv_and_salt
attr_encrypted :otp_secret_salt, :key => 'super_secret_two_factor_key', :encode => true, :mode => :per_attribute_iv_and_salt
I got things to save by explicitly calling update_attribute, but now when I try to access the unencrypted fields I get "OpenSSL::Cipher::CipherError: bad decrypt"
I couldn't get this working with rails 3 & mongoid. Interestingly, I did get attr_encrypted to work on an arbitrary other field, but I could never get the otp_secret to be decrypted correctly. I decided to use https://github.com/Houdini/two_factor_authentication instead.
I override some methods in my model and this work with mongoid. BTW: i use standard attr_encrypted(not fork).
def encrypted_otp_secret=(value)
self.write_attribute(:encrypted_otp_secret, value.force_encoding("ISO-8859-1").encode("UTF-8"))
end
def encrypted_otp_secret
if self.read_attribute(:encrypted_otp_secret).nil?
super
else
self.read_attribute(:encrypted_otp_secret).encode("ISO-8859-1").force_encoding('ASCII-8BIT')
end
end
P.S. But this not clear way, i think.
Is there a solution for this? It does not work with mongoid (5.1.6).... I tried everything from here, still receiving nil...
@JIucToyxuu can you explain it bit more. probably with gist ?
@QuinnWilton any update on this issue ?
@JIucToyxuu is right, but I'll expound. attr_encrypted converts the otp_secret into raw byte data that Mongoid refuses to write into a String field:
user = User.find(id)
user.otp_secret = User.generate_otp_secret
user.save!
user.encrypted_otp_secret
=> "z\xD5\xCEK\x05?Q\x8C\aY\xBB\xF1\x01\x92\x12u\xC24~F\x04\x1C=\x8D\x11\b\xCB\x11,B\x94\xCBj6(\xE4\x8B\x85\x0E\xD0"
User.find(id).encrypted_otp_secret
=> nil
My solution, instead of playing with force_encoding is to write Base64 to the database instead:
# app/models/concerns/base64_encoded.rb
module Base64Encoded
def base64_encoded(*attributes)
attributes.each do |attribute|
define_method :"#{attribute}=" do |value|
value = Base64.encode64(value) if value
self.write_attribute(attribute, value)
end
define_method attribute do
value = self.read_attribute(attribute)
value = Base64.decode64(value) if value
value
end
end
end
end
extend Base64Encoded
field :encrypted_otp_secret, type: String
field :encrypted_otp_secret_iv, type: String
field :encrypted_otp_secret_salt, type: String
field :consumed_timestep, type: Integer
field :otp_required_for_login, type: Boolean, default: false
# ensure all encrypted attributes are base64 encoded
# so Mongoid can write them into a String field
base64_encoded :encrypted_otp_secret
base64_encoded :encrypted_otp_secret_iv
base64_encoded :encrypted_otp_secret_salt
I have an error coming from devise_two_factor/models/two_factor_authenticatable.rb:10: undefined method encrypts
Do you know how to get this right?
I've run into the same problem but cannot offer you a solution other than suggesting you stick to v4.1.1.
The upgrade to v5.0.0 made devise-two-factor rely on active record's encrypts method. This was intentional as it's explicitly mentioned in the repo's README ("Devise-Two-Factor uses ActiveRecord encrypted attributes")
tl;dr v5.0.0 adds activerecord as an undeclared dependency, making it incompatible with any app that uses a NoSQL solution like mongoid over activerecord.