New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrap active_record_encryption.configuration on an after initialize #48520
Wrap active_record_encryption.configuration on an after initialize #48520
Conversation
This commit changes the active_record_encryption.configuration to be set on an after initialize block. After PR rails#44873, we can configure now hash_digest_class that will be used on ActiveRecord::Encryption. But the docs said that we could change it via an initializer by setting Rails.application.config.active_record.encryption.hash_digest_class. This wasn't working because the railtie "active_record_encryption.configuration" was being set before the initializer was loaded. This changes this behave to configure the active_record encryption after the initialize was loaded.
if ActiveRecord::Encryption.config.add_to_filter_parameters | ||
ActiveRecord::Encryption.install_auto_filtered_parameters_hook(app) | ||
# Filtered params | ||
if ActiveRecord::Encryption.config.add_to_filter_parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jorgemanrubia the current failure https://buildkite.com/rails/rails/builds/97281#0188d44a-def3-4a39-9f69-1cfd4ab8d4cd/1089-1099 is happening because now ActiveRecord::Encryption.install_auto_filtered_parameters_hook
is invoked after the initialize, but the code for install_auto_filtered_parameters_hook
is a hook that relies on ActiveRecord::Encryption.on_encrypted_attribute_declared
and the attributes were declared before the config.after_initialize do |app|
block is run.
is there an easy way that I can get all encrypted classes / attributes so I can run this block:
filter = [("#{klass.model_name.element}" if klass.name), encrypted_attribute_name.to_s].compact.join(".")
unless excluded_from_filter_parameters?(filter)
app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
klass.filter_attributes += [encrypted_attribute_name]
end
on the after initialize by Getting all encrypted attributes / classes instead to rely on the hook?
My first thought was to keep the hook, change install_auto_filtered_parameters_hook
to instead of filtering attribute to only add that the klass / attribute was encrypted and then on an after_initialize I really modify the filtering. But I want to see your thoughts on this.
Also another options was to run
ActiveRecord::Encryption.configure \
primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
deterministic_key: app.credentials.dig(:active_record_encryption, :deterministic_key),
key_derivation_salt: app.credentials.dig(:active_record_encryption, :key_derivation_salt),
**config.active_record.encryption
# Filtered params
if ActiveRecord::Encryption.config.add_to_filter_parameters
ActiveRecord::Encryption.install_auto_filtered_parameters_hook(app)
end
both on the non after_initialize / after_initialize block. But this looks weird for me 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jorgemanrubia I made a POC of this suggestion:
Here is a version using a hash, which required me two loops to setup the attributes and this which I made with an array to avoid this two loops.
what are your thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor comments but the patch looks good @duduribeiro, thanks!
@@ -9,6 +9,7 @@ module Configurable | |||
included do | |||
mattr_reader :config, default: Config.new | |||
mattr_accessor :encrypted_attribute_declaration_listeners | |||
mattr_reader :encrypted_attributes, default: Concurrent::Array.new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the hash version better you refer at 95b3e01.
Also, I'd consider renaming :encrypted_attributes
to entrypted_attributes_by_class
to clarify a little bit what's contained there.
railties/CHANGELOG.md
Outdated
@@ -1,3 +1,14 @@ | |||
* Wrap active_record_encryption.configuration on an after initialize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the first line here, instead of describing the internals of the change itself, I'd try to clarify what's being fixed here.
Example: Support configuring the digest class in Active Record encryption via initializer.
@@ -4331,6 +4331,16 @@ def new(app); self; end | |||
assert_match(/Cannot assign to `load_defaults`, it is a configuration method/, error.message) | |||
end | |||
|
|||
test "allows initializer to set active_record_encryption.configuration" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great call adding a test here 👏
Also, the PR description could contain |
the failing test https://buildkite.com/rails/rails/builds/97291 is the one that is also failing on main branch https://buildkite.com/rails/rails/builds/97274 |
@jorgemanrubia I will not do this because #48204 is something that is still happening with me and I will investigate why. I added a reproduction on a rails 7.0.5 to 7.1.0 app here #48204 (comment) and will try to investigate more but we can merge this PR as it is related with another thing. This PR fixes the problem where the initializer couldn't set the thanks |
|
||
ActiveSupport.on_load(:active_record) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ActiveSupport.on_load(:active_record)
should only run during eager loading phase, that is after_initialize
, so this change isn't necessary. Why do you need to do this?
Why don't you move the ActiveRecord::Encryption.configure
call to inside ActiveSupport.on_load(:active_record) do
? I don't think we need to use after_initialize
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @rafaelfranca
thanks for the tip! We are moving this PR to #48530, which also fixes the initialization problem.
about setting ActiveRecord::Encryption.configure on ActiveSupport.on_load(:active_record)
, I found that on on my app my ActiveRecord was loaded before my initializers got executed. See this and this for the @jorgemanrubia thoughts.
It it possible that some initializer in this app is loading an Active Record model? That will trigger the AR encryption initialization logic. application.rb runs before any initializer runs, so the fact that it works makes me suspect that something is triggering that initialization before the new defaults' initializer is running.
After some debugging I found that when I use https://github.com/basecamp/name_of_person gem it loads the active_record before running any initializer. See this
So I think @jorgemanrubia will rely on after_initialize
too on his new PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a bug in your application and in the name_of_person gem that should be fixed. Rails should not change its architecture because of bugs in other libraries.
Motivation / Background
This commit changes the active_record_encryption.configuration to be set on an after initialize block. After PR #44873, we can configure now hash_digest_class that will be used on ActiveRecord::Encryption. But the docs said that we could change it via an initializer by setting
Rails.application.config.active_record.encryption.hash_digest_class. This wasn't working because the railtie
"active_record_encryption.configuration" was being set before the initializer was loaded. This changes this behave to configure the active_record encryption after the initialize was loaded.
Additional information
I found this while debugging #48204 and I noticed that the PR #44873 added an option to configure the hash_class used to encrypt the attribute by setting the
ActiveRecord::Encryption.config.hash_digest_class
on the initializer. But while debugging my issue, I found that the code to use this hash_digest_class wasn't using the different hash_digest_class that I set and I noticed that the ActiveRecord::Encryption was only configured only at theactive_record
railtie before the custom initializers I have.and my
new_framework_defaults_7_1
was loaded after that.Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]