Skip to content

Commit cd4292f

Browse files
Merge pull request rails#45543 from jonathanhefner/credentials-secret_key_base-for-new-environment
Generate `secret_key_base` for all new credentials
2 parents 189356b + 915776a commit cd4292f

File tree

5 files changed

+71
-62
lines changed

5 files changed

+71
-62
lines changed

railties/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
* Newly generated per-environment credentials files (e.g.
2+
`config/credentials/production.yml.enc`) now include a `secret_key_base` for
3+
convenience, just as `config/credentials.yml.enc` does.
4+
5+
*Jonathan Hefner*
6+
17
* `--no-*` options now work with the app generator's `--minimal` option, and
28
are both comprehensive and precise. For example:
39

railties/lib/rails/commands/credentials/credentials_command.rb

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,7 @@ def ensure_encryption_key_has_been_added
8282
end
8383

8484
def ensure_credentials_have_been_added
85-
if options[:environment]
86-
encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
87-
else
88-
credentials_generator.add_credentials_file_silently
89-
end
85+
credentials_generator.add_credentials_file
9086
end
9187

9288
def change_credentials_in_system_editor
@@ -122,18 +118,11 @@ def encryption_key_file_generator
122118
Rails::Generators::EncryptionKeyFileGenerator.new
123119
end
124120

125-
def encrypted_file_generator
126-
require "rails/generators"
127-
require "rails/generators/rails/encrypted_file/encrypted_file_generator"
128-
129-
Rails::Generators::EncryptedFileGenerator.new
130-
end
131-
132121
def credentials_generator
133122
require "rails/generators"
134123
require "rails/generators/rails/credentials/credentials_generator"
135124

136-
Rails::Generators::CredentialsGenerator.new
125+
Rails::Generators::CredentialsGenerator.new([content_path, key_path], quiet: true)
137126
end
138127
end
139128
end

railties/lib/rails/generators/rails/app/app_generator.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def credentials
179179
return if options[:pretend] || options[:dummy_app]
180180

181181
require "rails/generators/rails/credentials/credentials_generator"
182-
Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently
182+
Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file
183183
end
184184

185185
def credentials_diff_enroll

railties/lib/rails/generators/rails/credentials/credentials_generator.rb

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,39 @@
77
module Rails
88
module Generators
99
class CredentialsGenerator < Base # :nodoc:
10+
argument :content_path, default: "config/credentials.yml.enc"
11+
argument :key_path, default: "config/master.key"
12+
1013
def add_credentials_file
11-
unless credentials.content_path.exist?
12-
template = credentials_template
14+
in_root do
15+
return if File.exist?(content_path)
1316

14-
say "Adding #{credentials.content_path} to store encrypted credentials."
17+
say "Adding #{content_path} to store encrypted credentials."
1518
say ""
19+
20+
encrypted_file.write(content)
21+
1622
say "The following content has been encrypted with the Rails master key:"
1723
say ""
18-
say template, :on_green
24+
say content, :on_green
1925
say ""
20-
21-
add_credentials_file_silently(template)
22-
2326
say "You can edit encrypted credentials with `bin/rails credentials:edit`."
2427
say ""
2528
end
2629
end
2730

28-
def add_credentials_file_silently(template = nil)
29-
unless credentials.content_path.exist?
30-
credentials.write(credentials_template)
31-
end
32-
end
33-
3431
private
35-
def credentials
32+
def encrypted_file
3633
ActiveSupport::EncryptedConfiguration.new(
37-
config_path: "config/credentials.yml.enc",
38-
key_path: "config/master.key",
34+
config_path: content_path,
35+
key_path: key_path,
3936
env_key: "RAILS_MASTER_KEY",
4037
raise_if_missing_key: true
4138
)
4239
end
4340

44-
def credentials_template
45-
<<~YAML
41+
def content
42+
@content ||= <<~YAML
4643
# aws:
4744
# access_key_id: 123
4845
# secret_access_key: 345

railties/test/commands/credentials_test.rb

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
2222
test "edit credentials" do
2323
# Run twice to ensure credentials can be reread after first edit pass.
2424
2.times do
25-
assert_match(/access_key_id: 123/, run_edit_command)
25+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command
2626
end
2727
end
2828

@@ -36,11 +36,11 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
3636
end
3737

3838
test "edit command does not overwrite by default if credentials already exists" do
39-
run_edit_command(editor: 'ruby -e "File.write ARGV[0], %(api_key: abc)"')
40-
assert_match(/api_key: abc/, run_show_command)
39+
write_credentials "foo: bar"
40+
output = run_edit_command
4141

42-
run_edit_command
43-
assert_match(/api_key: abc/, run_show_command)
42+
assert_match %r/foo: bar/, output
43+
assert_no_match DEFAULT_CREDENTIALS_PATTERN, output
4444
end
4545

4646
test "edit command does not add master key when `RAILS_MASTER_KEY` env specified" do
@@ -49,48 +49,51 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
4949
FileUtils.rm("config/master.key")
5050

5151
switch_env("RAILS_MASTER_KEY", key) do
52-
assert_match(/access_key_id: 123/, run_edit_command)
52+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command
5353
assert_not File.exist?("config/master.key")
5454
end
5555
end
5656
end
5757

5858
test "edit command modifies file specified by environment option" do
59-
assert_match(/access_key_id: 123/, run_edit_command(environment: "production"))
60-
Dir.chdir(app_path) do
61-
assert File.exist?("config/credentials/production.key")
62-
assert File.exist?("config/credentials/production.yml.enc")
63-
end
59+
remove_file "config/credentials.yml.enc"
60+
61+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command(environment: "production")
62+
63+
assert_no_file "config/credentials.yml.enc"
64+
assert_file "config/credentials/production.key"
65+
assert_file "config/credentials/production.yml.enc"
6466
end
6567

6668
test "edit command properly expands environment option" do
67-
assert_match(/access_key_id: 123/, run_edit_command(environment: "prod"))
68-
Dir.chdir(app_path) do
69-
assert File.exist?("config/credentials/production.key")
70-
assert File.exist?("config/credentials/production.yml.enc")
71-
end
69+
remove_file "config/credentials.yml.enc"
70+
71+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command(environment: "prod")
72+
73+
assert_no_file "config/credentials.yml.enc"
74+
assert_file "config/credentials/production.key"
75+
assert_file "config/credentials/production.yml.enc"
7276
end
7377

7478
test "edit command does not raise when an initializer tries to access non-existent credentials" do
7579
app_file "config/initializers/raise_when_loaded.rb", <<-RUBY
7680
Rails.application.credentials.missing_key!
7781
RUBY
7882

79-
assert_match(/access_key_id: 123/, run_edit_command(environment: "qa"))
83+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command(environment: "qa")
8084
end
8185

82-
test "edit command generates template file when the file does not exist" do
83-
FileUtils.rm("#{app_path}/config/credentials.yml.enc")
84-
run_edit_command
86+
test "edit command generates credentials file when it does not exist" do
87+
remove_file "config/credentials.yml.enc"
8588

86-
output = run_show_command
87-
assert_match(/access_key_id: 123/, output)
88-
assert_match(/secret_key_base/, output)
89+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_edit_command
90+
91+
assert_file "config/credentials.yml.enc"
8992
end
9093

9194

9295
test "show credentials" do
93-
assert_match(/access_key_id: 123/, run_show_command)
96+
assert_match DEFAULT_CREDENTIALS_PATTERN, run_show_command
9497
end
9598

9699
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
108111
end
109112

110113
test "show command displays content specified by environment option" do
111-
run_edit_command(environment: "production")
114+
write_credentials "foo: bar", environment: "production"
112115

113-
assert_match(/access_key_id: 123/, run_show_command(environment: "production"))
116+
assert_match %r/foo: bar/, run_show_command(environment: "production")
114117
end
115118

116119
test "show command properly expands environment option" do
117-
run_edit_command(environment: "production")
120+
write_credentials "foo: bar", environment: "production"
118121

119-
output = run_show_command(environment: "prod")
120-
assert_match(/access_key_id: 123/, output)
121-
assert_no_match(/secret_key_base/, output)
122+
assert_match %r/foo: bar/, run_show_command(environment: "prod")
122123
end
123124

124125

@@ -213,6 +214,8 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
213214
end
214215

215216
private
217+
DEFAULT_CREDENTIALS_PATTERN = /access_key_id: 123\n.*secret_key_base: \h{128}\n/m
218+
216219
def run_edit_command(editor: "cat", environment: nil, **options)
217220
switch_env("EDITOR", editor) do
218221
args = environment ? ["--environment", environment] : []
@@ -229,4 +232,18 @@ def run_diff_command(path = nil, enroll: nil, disenroll: nil, **options)
229232
args = [path, ("--enroll" if enroll), ("--disenroll" if disenroll)].compact
230233
rails "credentials:diff", args, **options
231234
end
235+
236+
def write_credentials(content, **options)
237+
switch_env("CONTENT", content) do
238+
run_edit_command(editor: %(ruby -e "File.write ARGV[0], ENV['CONTENT']"), **options)
239+
end
240+
end
241+
242+
def assert_file(relative)
243+
assert File.exist?(app_path(relative)), "Expected file #{relative.inspect} to exist, but it does not"
244+
end
245+
246+
def assert_no_file(relative)
247+
assert_not File.exist?(app_path(relative)), "Expected file #{relative.inspect} to not exist, but it does"
248+
end
232249
end

0 commit comments

Comments
 (0)