Question

I have a Rails 4 application and I am using the attr_encrypted gem to store SSN in an encrypted form in the DB.

What I am trying to do is only display the last 4 digits when editing / showing the form. So essentially once the SSN has been saved, users can't see the full SSN in the form. So I've tried doing the following,

class PaymentData < ActiveRecord::Base
  attr_encrypted :ssn, key: 'secret'

  alias_method :org_ssn, :ssn
  def ssn
    org_ssn.reverse # just a way of testing if this works
  end
end

What I am seeing that the on the show form, which uses <%= @payment_data.ssn %>, it works fine. But on my edit form which is using <%= f.input :ssn %>, the edit field is prepopulated with the full SSN.

Note: I am using the simple_form gem for my forms, but if I use the basic, <%= f.text_field :ssn %> syntax, I see the same full SSN.

What am I doing wrong here?

Was it helpful?

Solution

This is what I ended up doing,

def obfuscated_ssn
  return ssn unless ssn && ssn.size > 3
  ssn.last(4).rjust(ssn.length, '*')
end

def obfuscated_ssn=(ssn)
  self.ssn = ssn unless ssn.include?('*')
end

This is a bit hacky, but it displays the SSN with a redacted string, but then doesn't save a string with the redacted bits, *, to keep bad data from getting the database.

OTHER TIPS

I suggest something different.

Keep your obfuscated_ssn method from your answer, it makes sense to have it in the model. However overriding the setter like this... well I'd go a different way. What I don't really like, is that you are doing validations in the setter. I checked the gem page but couldn't find anything related to validation, but normally your model would include validation rules like

validates_length_of :ssn, :minimum => 5, :maximum => 20, :allow_blank => true

I don't know if the ssn gem is clever enough to run the validations only on non-encrypted data though...

But anyway here's how I would tweak your form/controller :

Your form

you would need to figure out how to display one field and submit to a completely different one

I suggest changing the default value shown by the field (Ruby doc for text_field :)

f.text_field(:ssn, :value => f.object.obfuscated_ssn)

Now I'm not sure about the behavior you want. Is the user supposed to always write the ssn field? Or do you want to leave it unchanged in case the user doesn't enter anything ? If so I'd put some checks on your controller

Controller

def model_params
  if params[:your_model][:ssn] == @model.obfuscated_ssn
    params[:your_model].delete(:ssn) # Make sure your model doesn't get updated with obfuscated ssn
  end
  params.require(:your_model).permit(:ssn, etc.)
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top