diff --git a/db/migrate/20241017013023_reencrypt_password_scramsha.rb b/db/migrate/20241017013023_reencrypt_password_scramsha.rb index 4595dd7e..8764fd10 100644 --- a/db/migrate/20241017013023_reencrypt_password_scramsha.rb +++ b/db/migrate/20241017013023_reencrypt_password_scramsha.rb @@ -2,6 +2,8 @@ class ReencryptPasswordScramsha < ActiveRecord::Migration[6.1] def up say_with_time('Reencrypting database user password with scram-sha-256') do db_config = ActiveRecord::Base.connection_db_config.configuration_hash + return if db_config[:username].blank? || db_config[:password].blank? + username = db_config[:username] password = connection.raw_connection.encrypt_password(db_config[:password], username, "scram-sha-256") diff --git a/spec/migrations/20241017013023_reencrypt_password_scramsha_spec.rb b/spec/migrations/20241017013023_reencrypt_password_scramsha_spec.rb index 1f293931..5bacb2f7 100644 --- a/spec/migrations/20241017013023_reencrypt_password_scramsha_spec.rb +++ b/spec/migrations/20241017013023_reencrypt_password_scramsha_spec.rb @@ -5,18 +5,31 @@ describe ReencryptPasswordScramsha do migration_context :up do it "Ensures that the user password is stored as scram-sha-256" do - migrate + allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original username = ActiveRecord::Base.connection_db_config.configuration_hash[:username] - users_and_passwords = ActiveRecord::Base.connection.execute <<-SQL - SELECT rolname, rolpassword FROM pg_authid WHERE rolcanlogin; - SQL + expect(ActiveRecord::Base.connection_db_config).to receive(:configuration_hash).exactly(10).times.and_call_original + expect(ActiveRecord::Base.connection_db_config).to receive(:configuration_hash).and_wrap_original do |original_method, *args, &block| + original_method.call(*args, &block).dup.tap { |i| i[:password] ||= "abc" } + end + expect(ActiveRecord::Base.connection).to receive(:execute).with(a_string_matching(/ALTER ROLE #{username} WITH PASSWORD \'SCRAM-SHA-256.*\'\;/)) + + migrate + end + + it "Handles connections with no password" do + allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original + + username = ActiveRecord::Base.connection_db_config.configuration_hash[:username] - record = users_and_passwords.to_a.detect { |i| i["rolname"] == username } + expect(ActiveRecord::Base.connection_db_config).to receive(:configuration_hash).exactly(10).times.and_call_original + expect(ActiveRecord::Base.connection_db_config).to receive(:configuration_hash).and_wrap_original do |original_method, *args, &block| + original_method.call(*args, &block).dup.tap { |i| i.delete(:password) } + end + expect(ActiveRecord::Base.connection).not_to receive(:execute).with(a_string_matching(/ALTER ROLE.*\'\;/)) - expect(record["rolname"]).to eq(username) - expect(record["rolpassword"]).to match(/^SCRAM-SHA-256.*/) + migrate end end end