1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Raise an error only when require_master_key is specified

To prevent errors from being raise in environments where credentials
is unnecessary.

Context: https://github.com/rails/rails/issues/31283#issuecomment-348801489

Fixes #31283
This commit is contained in:
yuuji.yaginuma 2017-12-05 21:41:19 +09:00
parent daf15f58b9
commit 35373219c9
13 changed files with 94 additions and 27 deletions

View file

@ -11,8 +11,9 @@ module ActiveSupport
delegate :[], :fetch, to: :config
delegate_missing_to :options
def initialize(config_path:, key_path:, env_key:)
super content_path: config_path, key_path: key_path, env_key: env_key
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
super content_path: config_path, key_path: key_path,
env_key: env_key, raise_if_missing_key: raise_if_missing_key
end
# Allow a config to be started without a file present

View file

@ -26,11 +26,11 @@ module ActiveSupport
end
attr_reader :content_path, :key_path, :env_key
attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
def initialize(content_path:, key_path:, env_key:)
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
@content_path, @key_path = Pathname.new(content_path), Pathname.new(key_path)
@env_key = env_key
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
end
def key
@ -38,7 +38,7 @@ module ActiveSupport
end
def read
if content_path.exist?
if !key.nil? && content_path.exist?
decrypt content_path.binread
else
raise MissingContentError, content_path
@ -93,7 +93,7 @@ module ActiveSupport
end
def handle_missing_key
raise MissingKeyError, key_path: key_path, env_key: env_key
raise MissingKeyError, key_path: key_path, env_key: env_key if raise_if_missing_key
end
end
end

View file

@ -10,8 +10,10 @@ class EncryptedConfigurationTest < ActiveSupport::TestCase
@credentials_key_path = File.join(Dir.tmpdir, "master.key")
File.write(@credentials_key_path, ActiveSupport::EncryptedConfiguration.generate_key)
@credentials = ActiveSupport::EncryptedConfiguration.new \
config_path: @credentials_config_path, key_path: @credentials_key_path, env_key: "RAILS_MASTER_KEY"
@credentials = ActiveSupport::EncryptedConfiguration.new(
config_path: @credentials_config_path, key_path: @credentials_key_path,
env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true
)
end
teardown do

View file

@ -12,8 +12,9 @@ class EncryptedFileTest < ActiveSupport::TestCase
@key_path = File.join(Dir.tmpdir, "content.txt.key")
File.write(@key_path, ActiveSupport::EncryptedFile.generate_key)
@encrypted_file = ActiveSupport::EncryptedFile.new \
content_path: @content_path, key_path: @key_path, env_key: "CONTENT_KEY"
@encrypted_file = ActiveSupport::EncryptedFile.new(
content_path: @content_path, key_path: @key_path, env_key: "CONTENT_KEY", raise_if_missing_key: true
)
end
teardown do
@ -47,4 +48,12 @@ class EncryptedFileTest < ActiveSupport::TestCase
assert_equal "#{@content} and went by the lake", @encrypted_file.read
end
test "raise MissingKeyError when key is missing" do
assert_raise(ActiveSupport::EncryptedFile::MissingKeyError) do
ActiveSupport::EncryptedFile.new(
content_path: @content_path, key_path: "", env_key: "", raise_if_missing_key: true
).read
end
end
end

View file

@ -472,7 +472,8 @@ module Rails
ActiveSupport::EncryptedConfiguration.new(
config_path: Rails.root.join(path),
key_path: Rails.root.join(key_path),
env_key: env_key
env_key: env_key,
raise_if_missing_key: config.require_master_key
)
end

View file

@ -16,7 +16,8 @@ module Rails
:ssl_options, :public_file_server,
:session_options, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
:read_encrypted_secrets, :log_level, :content_security_policy_report_only
:read_encrypted_secrets, :log_level, :content_security_policy_report_only,
:require_master_key
attr_reader :encoding, :api_only
@ -56,6 +57,7 @@ module Rails
@read_encrypted_secrets = false
@content_security_policy = nil
@content_security_policy_report_only = false
@require_master_key = false
end
def load_defaults(target_version)

View file

@ -33,8 +33,7 @@ module Rails
def show
require_application_and_environment!
say Rails.application.credentials.read.presence ||
"No credentials have been added yet. Use bin/rails credentials:edit to change that."
say Rails.application.credentials.read.presence || missing_credentials_message
end
private
@ -67,6 +66,14 @@ module Rails
Rails::Generators::CredentialsGenerator.new
end
def missing_credentials_message
if Rails.application.credentials.key.nil?
"Missing master key to decrypt credentials. See bin/rails credentials:help"
else
"No credentials have been added yet. Use bin/rails credentials:edit to change that."
end
end
end
end
end

View file

@ -37,9 +37,9 @@ module Rails
def show(file_path)
require_application_and_environment!
encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
say Rails.application.encrypted(file_path, key_path: options[:key]).read.presence ||
"File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that."
say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: options[:key], file_path: file_path)
end
private
@ -72,6 +72,14 @@ module Rails
Rails::Generators::EncryptedFileGenerator.new
end
def missing_encrypted_message(key:, key_path:, file_path:)
if key.nil?
"Missing '#{key_path}' to decrypt data. See bin/rails encrypted:help"
else
"File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that."
end
end
end
end
end

View file

@ -36,7 +36,8 @@ module Rails
ActiveSupport::EncryptedConfiguration.new(
config_path: "config/credentials.yml.enc",
key_path: "config/master.key",
env_key: "RAILS_MASTER_KEY"
env_key: "RAILS_MASTER_KEY",
raise_if_missing_key: true
)
end

View file

@ -24,7 +24,7 @@ module Rails
def add_encrypted_file_silently(file_path, key_path, template = encrypted_file_template)
unless File.exist?(file_path)
setup = { content_path: file_path, key_path: key_path, env_key: "RAILS_MASTER_KEY" }
setup = { content_path: file_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true }
ActiveSupport::EncryptedFile.new(setup).write(template)
end
end

View file

@ -707,6 +707,14 @@ module ApplicationTests
assert_match(/Missing.*RAILS_MASTER_KEY/, error)
end
test "credentials does not raise error when require_master_key is false and master key does not exist" do
remove_file "config/master.key"
add_to_config "config.require_master_key = false"
app "development"
assert_not app.credentials.secret_key_base
end
test "protect from forgery is the default in a new app" do
make_basic_app

View file

@ -26,10 +26,6 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
end
end
test "show credentials" do
assert_match(/access_key_id: 123/, run_show_command)
end
test "edit command does not add master key to gitignore when already exist" do
run_edit_command
@ -47,6 +43,24 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
assert_match(/api_key: abc/, run_show_command)
end
test "show credentials" do
assert_match(/access_key_id: 123/, run_show_command)
end
test "show command raise error when require_master_key is specified and key does not exist" do
remove_file "config/master.key"
add_to_config "config.require_master_key = true"
assert_match(/Missing encryption key to decrypt file with/, run_show_command(allow_failure: true))
end
test "show command does not raise error when require_master_key is false and master key does not exist" do
remove_file "config/master.key"
add_to_config "config.require_master_key = false"
assert_match(/Missing master key to decrypt credentials/, run_show_command)
end
private
def run_edit_command(editor: "cat")
switch_env("EDITOR", editor) do
@ -54,7 +68,7 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
end
end
def run_show_command
rails "credentials:show"
def run_show_command(**options)
rails "credentials:show", **options
end
end

View file

@ -52,6 +52,20 @@ class Rails::Command::EncryptedCommandTest < ActiveSupport::TestCase
assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key"))
end
test "show command raise error when require_master_key is specified and key does not exist" do
add_to_config "config.require_master_key = true"
assert_match(/Missing encryption key to decrypt file with/,
run_show_command("config/tokens.yml.enc", key: "unexist.key", allow_failure: true))
end
test "show command does not raise error when require_master_key is false and master key does not exist" do
remove_file "config/master.key"
add_to_config "config.require_master_key = false"
assert_match(/Missing 'config\/master\.key' to decrypt data/, run_show_command("config/tokens.yml.enc"))
end
test "won't corrupt encrypted file when passed wrong key" do
run_edit_command("config/tokens.yml.enc", key: "config/tokens.key")
@ -68,8 +82,8 @@ class Rails::Command::EncryptedCommandTest < ActiveSupport::TestCase
end
end
def run_show_command(file, key: nil)
rails "encrypted:show", prepare_args(file, key)
def run_show_command(file, key: nil, **options)
rails "encrypted:show", prepare_args(file, key), **options
end
def prepare_args(file, key)