diff options
19 files changed, 82 insertions, 63 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 1d4b27a0f9..16090e7946 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -7,14 +7,14 @@ *Michael J Coyne* -* Use Capybara registered `:puma` server config. +* Use Capybara registered `:puma` server config. The Capybara registered `:puma` server ensures the puma server is run in process so connection sharing and open request detection work correctly by default. *Thomas Walpole* -* Cookies `:expires` option supports `ActiveSupport::Duration` object. +* Cookies `:expires` option supports `ActiveSupport::Duration` object. cookies[:user_name] = { value: "assain", expires: 1.hour } cookies[:key] = { value: "a yummy cookie", expires: 6.months } @@ -23,7 +23,7 @@ *Assain Jaleel* -* Enforce signed/encrypted cookie expiry server side. +* Enforce signed/encrypted cookie expiry server side. Rails can thwart attacks by malicious clients that don't honor a cookie's expiry. diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c34236d4be..7cae105ddb 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Passing a `Set` to `Relation#where` now behaves the same as passing an + array. + + *Sean Griffin* + * Use given algorithm while removing index from database. Fixes #24190. diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index fe696e0d6e..607d376a08 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -50,20 +50,14 @@ module ActiveRecord end def owner_keys - unless defined?(@owner_keys) - @owner_keys = owners.map do |owner| - owner[owner_key_name] - end - @owner_keys.uniq! - @owner_keys.compact! - end - @owner_keys + @owner_keys ||= owners_by_key.keys end def owners_by_key unless defined?(@owners_by_key) @owners_by_key = owners.each_with_object({}) do |owner, h| - h[convert_key(owner[owner_key_name])] = owner + key = convert_key(owner[owner_key_name]) + h[key] = owner if key end end @owners_by_key diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 63c059e291..d8fc046e10 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -17,13 +17,15 @@ module ActiveRecord # Returns the primary key value. def id sync_with_transaction_state - _read_attribute(self.class.primary_key) if self.class.primary_key + primary_key = self.class.primary_key + _read_attribute(primary_key) if primary_key end # Sets the primary key value. def id=(value) sync_with_transaction_state - _write_attribute(self.class.primary_key, value) if self.class.primary_key + primary_key = self.class.primary_key + _write_attribute(primary_key, value) if primary_key end # Queries the primary key value. diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index b070235684..4077250583 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -58,8 +58,9 @@ module ActiveRecord attr_name.to_s end - name = self.class.primary_key if name == "id".freeze && self.class.primary_key - sync_with_transaction_state if name == self.class.primary_key + primary_key = self.class.primary_key + name = primary_key if name == "id".freeze && primary_key + sync_with_transaction_state if name == primary_key _read_attribute(name, &block) end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 37891ce2ef..bb0ec6a8c3 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -39,8 +39,9 @@ module ActiveRecord attr_name.to_s end - name = self.class.primary_key if name == "id".freeze && self.class.primary_key - sync_with_transaction_state if name == self.class.primary_key + primary_key = self.class.primary_key + name = primary_key if name == "id".freeze && primary_key + sync_with_transaction_state if name == primary_key _write_attribute(name, value) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index c9607df28c..4f0c1890be 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -2,7 +2,7 @@ require_relative "../../migration/join_table" require "active_support/core_ext/string/access" -require "digest" +require "digest/sha2" module ActiveRecord module ConnectionAdapters # :nodoc: diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 5c42414072..be4b169f67 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -13,6 +13,7 @@ module ActiveRecord register_handler(Range, RangeHandler.new(self)) register_handler(Relation, RelationHandler.new) register_handler(Array, ArrayHandler.new(self)) + register_handler(Set, ArrayHandler.new(self)) end def build_from_hash(attributes) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ae1dc35bff..6270533dc2 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1912,6 +1912,19 @@ class RelationTest < ActiveRecord::TestCase end end + test "#where with set" do + david = authors(:david) + mary = authors(:mary) + + authors = Author.where(name: ["David", "Mary"].to_set) + assert_equal [david, mary], authors + end + + test "#where with empty set" do + authors = Author.where(name: Set.new) + assert_empty authors + end + private def custom_post_relation table_alias = Post.arel_table.alias("omg_posts") diff --git a/activestorage/Rakefile b/activestorage/Rakefile index aa71a65f6e..2aa4d2a76f 100644 --- a/activestorage/Rakefile +++ b/activestorage/Rakefile @@ -11,4 +11,6 @@ Rake::TestTask.new do |test| test.warning = false end +task :package + task default: :test diff --git a/activesupport/lib/active_support/security_utils.rb b/activesupport/lib/active_support/security_utils.rb index 51870559ec..b6b31ef140 100644 --- a/activesupport/lib/active_support/security_utils.rb +++ b/activesupport/lib/active_support/security_utils.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "digest" +require "digest/sha2" module ActiveSupport module SecurityUtils diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 678b80516f..3573c3c77b 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -414,7 +414,7 @@ end `find_in_batches` works on model classes, as seen above, and also on relations: ```ruby -Invoice.pending.find_in_batches do |invoice| +Invoice.pending.find_in_batches do |invoices| pending_invoices_export.add_invoices(invoices) end ``` diff --git a/guides/source/security.md b/guides/source/security.md index 9e1dc518d2..a07d583f15 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -152,37 +152,39 @@ In test and development applications get a `secret_key_base` derived from the ap If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. -### Rotating Keys for Encrypted and Signed Cookies +### Rotating Encrypted and Signed Cookies Configurations -It is possible to rotate the `secret_key_base` as well as the salts, -ciphers, and digests used for both encrypted and signed cookies. Rotating -the `secret_key_base` is necessary if the value was exposed or leaked. -It is also useful to rotate this value for other more benign reasons, -such as an employee leaving your organization or changing hosting -environments. +Rotation is ideal for changing cookie configurations and ensuring old cookies +aren't immediately invalid. Your users then have a chance to visit your site, +get their cookie read with an old configuration and have it rewritten with the +new change. The rotation can then be removed once you're comfortable enough +users have had their chance to get their cookies upgraded. -For example to rotate out an old `secret_key_base`, we can define signed and -encrypted rotations as follows: +It's possible to rotate the ciphers and digests used for encrypted and signed cookies. + +For instance to change the digest used for signed cookies from SHA1 to SHA256, +you would first assign the new configuration value: ```ruby -Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| - cookies.rotate :encrypted, secret: Rails.application.credentials.old_secret_key_base - cookies.rotate :signed, secret: Rails.application.credentials.old_secret_key_base -end +Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256" ``` -It's also possible to set up multiple rotations. For instance to use `SHA512` -for signed cookies while rotating out SHA256 and SHA1 digests, we'd do: +Then you'd set up a rotation with the old configuration to keep it alive. ```ruby -Rails.application.config.action_dispatch.signed_cookie_digest = "SHA512" - Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| cookies.rotate :signed, digest: "SHA256" - cookies.rotate :signed, digest: "SHA1" end ``` +Then any written signed cookies will be digested with SHA256. Old cookies +that were written with SHA1 can still be read, and if accessed will be written +with the new digest so they're upgraded and won't be invalid when you remove the +rotation. + +Once users with SHA1 digested signed cookies should no longer have a chance to +have their cookies rewritten, remove the rotation. + While you can setup as many rotations as you'd like it's not common to have many rotations going at any one time. diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ac82ff6633..23fdf03b05 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -69,7 +69,7 @@ module Rails def version_control if !options[:skip_git] && !options[:pretend] - run "git init" + run "git init", capture: options[:quiet] end end @@ -164,7 +164,7 @@ module Rails require_relative "../master_key/master_key_generator" after_bundle do - Rails::Generators::MasterKeyGenerator.new.add_master_key_file + Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]).add_master_key_file end end @@ -174,7 +174,7 @@ module Rails require_relative "../credentials/credentials_generator" after_bundle do - Rails::Generators::CredentialsGenerator.new.add_credentials_file_silently + Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently end end diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index e49d3b39e0..395687974a 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -13,15 +13,15 @@ module Rails unless MASTER_KEY_PATH.exist? key = ActiveSupport::EncryptedFile.generate_key - say "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}" - say "" - say "Save this in a password manager your team can access." - say "" - say "If you lose the key, no one, including you, can access anything encrypted with it." + log "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}" + log "" + log "Save this in a password manager your team can access." + log "" + log "If you lose the key, no one, including you, can access anything encrypted with it." - say "" + log "" create_file MASTER_KEY_PATH, key - say "" + log "" ignore_master_key_file end @@ -31,15 +31,15 @@ module Rails def ignore_master_key_file if File.exist?(".gitignore") unless File.read(".gitignore").include?(key_ignore) - say "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" - say "" + log "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" + log "" append_to_file ".gitignore", key_ignore - say "" + log "" end else - say "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" - say key_ignore, :on_green - say "" + log "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" + log key_ignore, :on_green + log "" end end diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml deleted file mode 100644 index fe80872a16..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml +++ /dev/null @@ -1 +0,0 @@ -# an empty YAML file - any content in here seems to get parsed as a string
\ No newline at end of file diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb deleted file mode 100644 index 1a82a2bdd4..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -# intentionally empty diff --git a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb deleted file mode 100644 index 1a82a2bdd4..0000000000 --- a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -# intentionally empty diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 904e2a5c84..20f593f25c 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -560,6 +560,11 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_match(/run git init/, output) end + def test_quiet_option + output = run_generator [File.join(destination_root, "myapp"), "--quiet"] + assert_empty output + end + def test_application_name_with_spaces path = File.join(destination_root, "foo bar") @@ -737,7 +742,7 @@ class AppGeneratorTest < Rails::Generators::TestCase sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"] @sequence_step ||= 0 - ensure_bundler_first = -> command do + ensure_bundler_first = -> command, options = nil do assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" @sequence_step += 1 end |