Skip to content

Commit

Permalink
Merge pull request rails#50606 from maximerety/active-record-encrypti…
Browse files Browse the repository at this point in the history
…on-eager-load

[Fix rails#50604] Restore compatibility of Active Record Encryption configs with eager loading mode
  • Loading branch information
eileencodes authored and maximerety committed Feb 13, 2024
1 parent 8fda5f8 commit cee5d5b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 10 deletions.
6 changes: 6 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
* Fix an issue where `ActiveRecord::Encryption` configurations are not ready before the loading
of Active Record models, when an application is eager loaded. As a result, encrypted attributes
could be misconfigured in some cases.

*Maxime Réty*

* Properly synchronize `Mysql2Adapter#active?` and `TrilogyAdapter#active?`

As well as `disconnect!` and `verify!`.
Expand Down
17 changes: 7 additions & 10 deletions activerecord/lib/active_record/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -378,23 +378,20 @@ class Railtie < Rails::Railtie # :nodoc:
end

initializer "active_record_encryption.configuration" do |app|
auto_filtered_parameters = ActiveRecord::Encryption::AutoFilteredParameters.new(app)

config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
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
**app.config.active_record.encryption

auto_filtered_parameters = ActiveRecord::Encryption::AutoFilteredParameters.new(app)
auto_filtered_parameters.enable if ActiveRecord::Encryption.config.add_to_filter_parameters

ActiveSupport.on_load(:active_record) do
# Support extended queries for deterministic attributes and validations
if ActiveRecord::Encryption.config.extend_queries
ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
ActiveRecord::Encryption::ExtendedDeterministicUniquenessValidator.install_support
end
# Support extended queries for deterministic attributes and validations
if ActiveRecord::Encryption.config.extend_queries
ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
ActiveRecord::Encryption::ExtendedDeterministicUniquenessValidator.install_support
end
end

Expand Down
75 changes: 75 additions & 0 deletions railties/test/application/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ def recording
end
end

class ::MyCustomKeyProvider
attr_reader :primary_key
def initialize(primary_key); @primary_key = primary_key; end
end

class ::MyOldKeyProvider; end

module ApplicationTests
class ConfigurationTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
Expand Down Expand Up @@ -3849,6 +3856,74 @@ class Post < ActiveRecord::Base
assert_not_includes ActiveRecord::Base.filter_attributes, :content
end

test "ActiveRecord::Encryption.config is ready for encrypted attributes when app is lazy loaded" do
add_to_config <<-RUBY
config.enable_reloading = false
config.eager_load = false
RUBY

app_file "config/initializers/active_record.rb", <<-RUBY
Rails.application.config.active_record.encryption.primary_key = "dummy_key"
Rails.application.config.active_record.encryption.previous = [ { key_provider: MyOldKeyProvider.new } ]
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define(version: 1) do
create_table :posts do |t|
t.string :content
end
end
ActiveRecord::Base.connection.schema_cache.add("posts")
RUBY

app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
encrypts :content, key_provider: MyCustomKeyProvider.new(ActiveRecord::Encryption.config.primary_key)
end
RUBY

app "development"

assert_kind_of ::MyOldKeyProvider, Post.attribute_types["content"].previous_schemes.first.key_provider
assert_kind_of ::MyCustomKeyProvider, Post.attribute_types["content"].scheme.key_provider
assert_equal "dummy_key", Post.attribute_types["content"].scheme.key_provider.primary_key
end

test "ActiveRecord::Encryption.config is ready for encrypted attributes when app is eager loaded" do
add_to_config <<-RUBY
config.enable_reloading = false
config.eager_load = true
RUBY

app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
encrypts :content, key_provider: MyCustomKeyProvider.new(ActiveRecord::Encryption.config.primary_key)
end
RUBY

app_file "config/initializers/active_record.rb", <<-RUBY
Rails.application.config.active_record.encryption.primary_key = "dummy_key"
Rails.application.config.active_record.encryption.previous = [ { key_provider: MyOldKeyProvider.new } ]
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define(version: 1) do
create_table :posts do |t|
t.string :content
end
end
ActiveRecord::Base.connection.schema_cache.add("posts")
RUBY

app "production"

assert_kind_of ::MyOldKeyProvider, Post.attribute_types["content"].previous_schemes.first&.key_provider
assert_kind_of ::MyCustomKeyProvider, Post.attribute_types["content"].scheme.key_provider
assert_equal "dummy_key", Post.attribute_types["content"].scheme.key_provider.primary_key
end

test "ActiveStorage.routes_prefix can be configured via config.active_storage.routes_prefix" do
app_file "config/environments/development.rb", <<-RUBY
Rails.application.configure do
Expand Down

0 comments on commit cee5d5b

Please sign in to comment.