I’m working with a Rails application that uses the attr_encrypted gem to handle encryption for sensitive data. Previously, we only stored the last 4 digits of the SSN. We’re migrating to store the full SSN while keeping the last 4 digits accessible as ssn_last_four for legacy compatibility.
class Person < ApplicationRecord
prepend SsnLastFourConcern
attr_encrypted :ssn, key: 'some_secret_key'
attr_encrypted :ssn, key: 'some_secret_key'
end
and I have the concern:
module SsnLastFourConcern
extend ActiveSupport::Concern
def ssn_last_four
return ssn[-4..] if ssn?
super
end
def ssn=(value)
self.ssn_last_four = value[-4..] if value.present? && value.length == 9
super(value)
end
end
The issue is that calling super(value) in the overridden ssn= setter leads to an infinite loop. But calling the super in ssn_last_four it works as expected
The workaround could be to use the before_save callback on ssn_last_four and make sure it reflects SSN. Also, by overriding the getter, the rest of the app will work properly, but I want to make sure that in case someone gets encrypted data from the column and manually decrypts it, he still gets the valid data.
EDIT: I do not override the ssn_last_four setter.
EDIT: Removed extend ActiveSupport::Concern as @engineersmnky suggested
Here is the stack trace: SystemStackError: stack level too deep from ruby-2.7.8/gems/activemodel-6.0.4/lib/active_model/attribute_set.rb:33:in `key?'
self.ssn_last_fourin the ssn setter? that's where you are getting your loopssn_last_four=setter.ActiveSupport::Concernsince you don't use any part of the Concern interface.