Skip to content

Commit ce9d2a1

Browse files
Merge pull request rails#45544 from jonathanhefner/credentials-custom-templates
Support custom credentials templates
2 parents cd4292f + d7a4c91 commit ce9d2a1

File tree

7 files changed

+57
-25
lines changed

7 files changed

+57
-25
lines changed

railties/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
* In-app custom credentials templates are now supported. When a credentials
2+
file does not exist, `rails credentials:edit` will now try to use
3+
`lib/templates/rails/credentials/credentials.yml.tt` to generate the
4+
credentials file, before falling back to the default template.
5+
6+
This allows e.g. an open-source Rails app (which would not include encrypted
7+
credentials files in its repo) to include a credentials template, so that
8+
users who install the app will get a custom pre-filled credentials file when
9+
they run `rails credentials:edit`.
10+
11+
*Jonathan Hefner*
12+
113
* Newly generated per-environment credentials files (e.g.
214
`config/credentials/production.yml.enc`) now include a `secret_key_base` for
315
convenience, just as `config/credentials.yml.enc` does.

railties/lib/rails/application.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,11 @@ def migration_railties # :nodoc:
493493
ordered_railties.flatten - [self]
494494
end
495495

496+
def load_generators(app = self) # :nodoc:
497+
app.ensure_generator_templates_added
498+
super
499+
end
500+
496501
# Eager loads the application code.
497502
def eager_load!
498503
Rails.autoloaders.each(&:eager_load)
@@ -582,6 +587,11 @@ def validate_secret_key_base(secret_key_base)
582587
end
583588
end
584589

590+
def ensure_generator_templates_added
591+
configured_paths = config.generators.templates
592+
configured_paths.unshift(*(paths["lib/templates"].existent - configured_paths))
593+
end
594+
585595
private
586596
def generate_development_secret
587597
if secrets.secret_key_base.nil?

railties/lib/rails/application/finisher.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module Finisher
1111
include Initializable
1212

1313
initializer :add_generator_templates do
14-
config.generators.templates.unshift(*paths["lib/templates"].existent)
14+
ensure_generator_templates_added
1515
end
1616

1717
initializer :setup_main_autoloader do

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

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def help
2828
def edit
2929
extract_environment_option_from_argument(default_environment: nil)
3030
require_application!
31+
load_generators
3132

3233
ensure_editor_available(command: "bin/rails credentials:edit") || (return)
3334

@@ -77,12 +78,15 @@ def credentials
7778
end
7879

7980
def ensure_encryption_key_has_been_added
81+
require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
82+
encryption_key_file_generator = Rails::Generators::EncryptionKeyFileGenerator.new
8083
encryption_key_file_generator.add_key_file(key_path)
8184
encryption_key_file_generator.ignore_key_file(key_path)
8285
end
8386

8487
def ensure_credentials_have_been_added
85-
credentials_generator.add_credentials_file
88+
require "rails/generators/rails/credentials/credentials_generator"
89+
Rails::Generators::CredentialsGenerator.new([content_path, key_path], quiet: true).invoke_all
8690
end
8791

8892
def change_credentials_in_system_editor
@@ -110,20 +114,6 @@ def key_path
110114
def extract_environment_from_path(path)
111115
available_environments.find { |env| path.include? env } if path.end_with?(".yml.enc")
112116
end
113-
114-
def encryption_key_file_generator
115-
require "rails/generators"
116-
require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
117-
118-
Rails::Generators::EncryptionKeyFileGenerator.new
119-
end
120-
121-
def credentials_generator
122-
require "rails/generators"
123-
require "rails/generators/rails/credentials/credentials_generator"
124-
125-
Rails::Generators::CredentialsGenerator.new([content_path, key_path], quiet: true)
126-
end
127117
end
128118
end
129119
end

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def add_credentials_file
1717
say "Adding #{content_path} to store encrypted credentials."
1818
say ""
1919

20-
encrypted_file.write(content)
20+
content = render_template_to_encrypted_file
2121

2222
say "The following content has been encrypted with the Rails master key:"
2323
say ""
@@ -38,15 +38,20 @@ def encrypted_file
3838
)
3939
end
4040

41-
def content
42-
@content ||= <<~YAML
43-
# aws:
44-
# access_key_id: 123
45-
# secret_access_key: 345
41+
def secret_key_base
42+
@secret_key_base ||= SecureRandom.hex(64)
43+
end
44+
45+
def render_template_to_encrypted_file
46+
content = nil
47+
48+
encrypted_file.change do |tmp_path|
49+
template("credentials.yml", tmp_path, force: true, verbose: false) do |rendered|
50+
content = rendered
51+
end
52+
end
4653

47-
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
48-
secret_key_base: #{SecureRandom.hex(64)}
49-
YAML
54+
content
5055
end
5156
end
5257
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# aws:
2+
# access_key_id: 123
3+
# secret_access_key: 345
4+
5+
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
6+
secret_key_base: <%= secret_key_base %>

railties/test/commands/credentials_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
9191
assert_file "config/credentials.yml.enc"
9292
end
9393

94+
test "edit command can use custom template to generate credentials file" do
95+
app_file "lib/templates/rails/credentials/credentials.yml.tt", <<~ERB
96+
provides_secret_key_base: <%= [secret_key_base] == [secret_key_base].compact %>
97+
ERB
98+
remove_file "config/credentials.yml.enc"
99+
100+
assert_match %r/provides_secret_key_base: true/, run_edit_command
101+
end
102+
94103

95104
test "show credentials" do
96105
assert_match DEFAULT_CREDENTIALS_PATTERN, run_show_command

0 commit comments

Comments
 (0)