symmetric-encryption icon indicating copy to clipboard operation
symmetric-encryption copied to clipboard

Accepting Multi-Parameter time values for an encrypted field cast to :date (Rails)

Open eyefodder opened this issue 9 years ago • 7 comments

Hi there, with a regular (non-encrypted) date based field, when I use form helpers, I end up with 3 select boxes that are named something like this:

field(1i) #year
field(2i) #month
field(3i) #day

In Active Record, Rails looks at the db column ,and figure out that field can accept a hash and parse that into a date. Check out here and here.

Essentially it passes in to the attribute setter a hash of values:

{1=>1975, 2=> 11, 3=>9} # this would imply a date 9/11/75

It would be great if when I set a field type

attr_encrypted :date_of_birth, type: :date

Then I could hope to see that sort of behavior. I'm not sure if you guys consider this too framework specific, but if you think it's a worthwhile feature, would be glad to figure out a PR for it

eyefodder avatar Aug 31 '16 21:08 eyefodder

I tried this out for regular Active Record models and they do not convert the hash above into a date in the model. In fact after saving the model the Hash date is just lost and returns from the database as nil.

2.3.1 :026 > i = X.new(expire_alert: {1=>1975, 2=> 11, 3=>9})
#<X:0x007fa0aebe2b60> {
     :expire_alert => {
        1 => 1975,
        2 => 11,
        3 => 9
    },
}
2.3.1 :027 > i.save
true
2.3.1 :028 > i.reload
#<X:0x007fa0aebe2b60> {
   :expire_alert => nil,
}

reidmorrison avatar Oct 13 '16 18:10 reidmorrison

I'll take a look into this when I get a moment - just to make sure we are on the same page, in your sample above, X is an ActiveRecord class with an expire_alert field mapping to column type :date right?

eyefodder avatar Oct 13 '16 19:10 eyefodder

Yes, I replaced the model name in the output with X since it is an actual model in our application that has a date column. We are running Rails 4.2. Also removed all other columns from the output.

reidmorrison avatar Oct 13 '16 21:10 reidmorrison

This should work it around:

  def expiry_date=(date)
    if date.is_a? Hash
      super Date.new(*date.values)
    else
      super
    end
  end

PascalSenn avatar Nov 21 '16 10:11 PascalSenn

Sorry - haven't had a chance to do you a minimal example to reproduce yet @reidmorrison, but yes @PascalSenn for working around it I just do:

def some_encrypted_date_field=(value)
  super(hash_to_date(value))
end

edit: We implemented hash_to_date, method shown 2 comments down hash_to_date is the internal function ActiveRecord uses to parse input

eyefodder avatar Nov 21 '16 17:11 eyefodder

@eyefodder This seems not to work for me.

 NameError Exception: undefined local variable or method `hash_to_date' for ...

Couldn't find this method in the codebase neither.

Rails, 4.2.4 which version are you using?

PascalSenn avatar Nov 22 '16 09:11 PascalSenn

ugh - sorry - my bad... So looked a little deeper into how we had implemented. The class ActiveModel::Type::Helpers::AcceptsMultiparameterTime is where they implement the hash to date parsing using a method value_from_multiparameter_assignment

We pretty much lifted that code to implement in a field that is encypted with this method:

#@see ActiveModel::Type::Helpers::AcceptsMultiparameterTime
  def hash_to_date(value)
    return value unless value.is_a?(Hash)
    return value unless value[1] && value[2] && value[3]
    values = value.sort.map(&:last)
    ::Time.send(default_timezone, *values)
  end

eyefodder avatar Nov 22 '16 13:11 eyefodder