mirror of https://github.com/rails/rails
Generate secret_key_base for all new credentials
Currently, when `config/credentials.yml.enc` is generated by `CredentialsGenerator`, it includes a `secret_key_base` for convenience. However, because `config/credentials/#{environment}.yml.enc` files are generated by a different generator (`EncryptedFileGenerator`), they do not include a `secret_key_base`. This commit revises `CredentialsGenerator` to be more generator-like, and changes `rails credentials:edit` to use it for generating both `config/credentials.yml.enc` and `config/credentials/#{environment}.yml.enc` files, thereby always including a `secret_key_base`.
This commit is contained in:
parent
189356bd24
commit
915776ad04
|
@ -1,3 +1,9 @@
|
|||
* Newly generated per-environment credentials files (e.g.
|
||||
`config/credentials/production.yml.enc`) now include a `secret_key_base` for
|
||||
convenience, just as `config/credentials.yml.enc` does.
|
||||
|
||||
*Jonathan Hefner*
|
||||
|
||||
* `--no-*` options now work with the app generator's `--minimal` option, and
|
||||
are both comprehensive and precise. For example:
|
||||
|
||||
|
|
|
@ -82,11 +82,7 @@ module Rails
|
|||
end
|
||||
|
||||
def ensure_credentials_have_been_added
|
||||
if options[:environment]
|
||||
encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
|
||||
else
|
||||
credentials_generator.add_credentials_file_silently
|
||||
end
|
||||
credentials_generator.add_credentials_file
|
||||
end
|
||||
|
||||
def change_credentials_in_system_editor
|
||||
|
@ -122,18 +118,11 @@ module Rails
|
|||
Rails::Generators::EncryptionKeyFileGenerator.new
|
||||
end
|
||||
|
||||
def encrypted_file_generator
|
||||
require "rails/generators"
|
||||
require "rails/generators/rails/encrypted_file/encrypted_file_generator"
|
||||
|
||||
Rails::Generators::EncryptedFileGenerator.new
|
||||
end
|
||||
|
||||
def credentials_generator
|
||||
require "rails/generators"
|
||||
require "rails/generators/rails/credentials/credentials_generator"
|
||||
|
||||
Rails::Generators::CredentialsGenerator.new
|
||||
Rails::Generators::CredentialsGenerator.new([content_path, key_path], quiet: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -179,7 +179,7 @@ module Rails
|
|||
return if options[:pretend] || options[:dummy_app]
|
||||
|
||||
require "rails/generators/rails/credentials/credentials_generator"
|
||||
Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently
|
||||
Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file
|
||||
end
|
||||
|
||||
def credentials_diff_enroll
|
||||
|
|
|
@ -7,42 +7,39 @@ require "active_support/encrypted_configuration"
|
|||
module Rails
|
||||
module Generators
|
||||
class CredentialsGenerator < Base # :nodoc:
|
||||
def add_credentials_file
|
||||
unless credentials.content_path.exist?
|
||||
template = credentials_template
|
||||
argument :content_path, default: "config/credentials.yml.enc"
|
||||
argument :key_path, default: "config/master.key"
|
||||
|
||||
say "Adding #{credentials.content_path} to store encrypted credentials."
|
||||
def add_credentials_file
|
||||
in_root do
|
||||
return if File.exist?(content_path)
|
||||
|
||||
say "Adding #{content_path} to store encrypted credentials."
|
||||
say ""
|
||||
|
||||
encrypted_file.write(content)
|
||||
|
||||
say "The following content has been encrypted with the Rails master key:"
|
||||
say ""
|
||||
say template, :on_green
|
||||
say content, :on_green
|
||||
say ""
|
||||
|
||||
add_credentials_file_silently(template)
|
||||
|
||||
say "You can edit encrypted credentials with `bin/rails credentials:edit`."
|
||||
say ""
|
||||
end
|
||||
end
|
||||
|
||||
def add_credentials_file_silently(template = nil)
|
||||
unless credentials.content_path.exist?
|
||||
credentials.write(credentials_template)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def credentials
|
||||
def encrypted_file
|
||||
ActiveSupport::EncryptedConfiguration.new(
|
||||
config_path: "config/credentials.yml.enc",
|
||||
key_path: "config/master.key",
|
||||
config_path: content_path,
|
||||
key_path: key_path,
|
||||
env_key: "RAILS_MASTER_KEY",
|
||||
raise_if_missing_key: true
|
||||
)
|
||||
end
|
||||
|
||||
def credentials_template
|
||||
<<~YAML
|
||||
def content
|
||||
@content ||= <<~YAML
|
||||
# aws:
|
||||
# access_key_id: 123
|
||||
# secret_access_key: 345
|
||||
|
|
|
@ -22,7 +22,7 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
test "edit credentials" do
|
||||
# Run twice to ensure credentials can be reread after first edit pass.
|
||||
2.times do
|
||||
assert_match(/access_key_id: 123/, run_edit_command)
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,11 +36,11 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test "edit command does not overwrite by default if credentials already exists" do
|
||||
run_edit_command(editor: 'ruby -e "File.write ARGV[0], %(api_key: abc)"')
|
||||
assert_match(/api_key: abc/, run_show_command)
|
||||
write_credentials "foo: bar"
|
||||
output = run_edit_command
|
||||
|
||||
run_edit_command
|
||||
assert_match(/api_key: abc/, run_show_command)
|
||||
assert_match %r/foo: bar/, output
|
||||
assert_no_match DEFAULT_CREDENTIALS_PATTERN, output
|
||||
end
|
||||
|
||||
test "edit command does not add master key when `RAILS_MASTER_KEY` env specified" do
|
||||
|
@ -49,26 +49,30 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
FileUtils.rm("config/master.key")
|
||||
|
||||
switch_env("RAILS_MASTER_KEY", key) do
|
||||
assert_match(/access_key_id: 123/, run_edit_command)
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command
|
||||
assert_not File.exist?("config/master.key")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "edit command modifies file specified by environment option" do
|
||||
assert_match(/access_key_id: 123/, run_edit_command(environment: "production"))
|
||||
Dir.chdir(app_path) do
|
||||
assert File.exist?("config/credentials/production.key")
|
||||
assert File.exist?("config/credentials/production.yml.enc")
|
||||
end
|
||||
remove_file "config/credentials.yml.enc"
|
||||
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command(environment: "production")
|
||||
|
||||
assert_no_file "config/credentials.yml.enc"
|
||||
assert_file "config/credentials/production.key"
|
||||
assert_file "config/credentials/production.yml.enc"
|
||||
end
|
||||
|
||||
test "edit command properly expands environment option" do
|
||||
assert_match(/access_key_id: 123/, run_edit_command(environment: "prod"))
|
||||
Dir.chdir(app_path) do
|
||||
assert File.exist?("config/credentials/production.key")
|
||||
assert File.exist?("config/credentials/production.yml.enc")
|
||||
end
|
||||
remove_file "config/credentials.yml.enc"
|
||||
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command(environment: "prod")
|
||||
|
||||
assert_no_file "config/credentials.yml.enc"
|
||||
assert_file "config/credentials/production.key"
|
||||
assert_file "config/credentials/production.yml.enc"
|
||||
end
|
||||
|
||||
test "edit command does not raise when an initializer tries to access non-existent credentials" do
|
||||
|
@ -76,21 +80,20 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
Rails.application.credentials.missing_key!
|
||||
RUBY
|
||||
|
||||
assert_match(/access_key_id: 123/, run_edit_command(environment: "qa"))
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command(environment: "qa")
|
||||
end
|
||||
|
||||
test "edit command generates template file when the file does not exist" do
|
||||
FileUtils.rm("#{app_path}/config/credentials.yml.enc")
|
||||
run_edit_command
|
||||
test "edit command generates credentials file when it does not exist" do
|
||||
remove_file "config/credentials.yml.enc"
|
||||
|
||||
output = run_show_command
|
||||
assert_match(/access_key_id: 123/, output)
|
||||
assert_match(/secret_key_base/, output)
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command
|
||||
|
||||
assert_file "config/credentials.yml.enc"
|
||||
end
|
||||
|
||||
|
||||
test "show credentials" do
|
||||
assert_match(/access_key_id: 123/, run_show_command)
|
||||
assert_match DEFAULT_CREDENTIALS_PATTERN, run_show_command
|
||||
end
|
||||
|
||||
test "show command raises error when require_master_key is specified and key does not exist" do
|
||||
|
@ -108,17 +111,15 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test "show command displays content specified by environment option" do
|
||||
run_edit_command(environment: "production")
|
||||
write_credentials "foo: bar", environment: "production"
|
||||
|
||||
assert_match(/access_key_id: 123/, run_show_command(environment: "production"))
|
||||
assert_match %r/foo: bar/, run_show_command(environment: "production")
|
||||
end
|
||||
|
||||
test "show command properly expands environment option" do
|
||||
run_edit_command(environment: "production")
|
||||
write_credentials "foo: bar", environment: "production"
|
||||
|
||||
output = run_show_command(environment: "prod")
|
||||
assert_match(/access_key_id: 123/, output)
|
||||
assert_no_match(/secret_key_base/, output)
|
||||
assert_match %r/foo: bar/, run_show_command(environment: "prod")
|
||||
end
|
||||
|
||||
|
||||
|
@ -213,6 +214,8 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
private
|
||||
DEFAULT_CREDENTIALS_PATTERN = /access_key_id: 123\n.*secret_key_base: \h{128}\n/m
|
||||
|
||||
def run_edit_command(editor: "cat", environment: nil, **options)
|
||||
switch_env("EDITOR", editor) do
|
||||
args = environment ? ["--environment", environment] : []
|
||||
|
@ -229,4 +232,18 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
|
|||
args = [path, ("--enroll" if enroll), ("--disenroll" if disenroll)].compact
|
||||
rails "credentials:diff", args, **options
|
||||
end
|
||||
|
||||
def write_credentials(content, **options)
|
||||
switch_env("CONTENT", content) do
|
||||
run_edit_command(editor: %(ruby -e "File.write ARGV[0], ENV['CONTENT']"), **options)
|
||||
end
|
||||
end
|
||||
|
||||
def assert_file(relative)
|
||||
assert File.exist?(app_path(relative)), "Expected file #{relative.inspect} to exist, but it does not"
|
||||
end
|
||||
|
||||
def assert_no_file(relative)
|
||||
assert_not File.exist?(app_path(relative)), "Expected file #{relative.inspect} to not exist, but it does"
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue