1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
# frozen_string_literal: true
require "isolation/abstract_unit"
require "env_helpers"
require "rails/command"
require "rails/commands/credentials/credentials_command"
require "fileutils"
require "tempfile"
class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation, EnvHelpers
setup { build_app }
teardown { teardown_app }
test "edit without editor gives hint" do
run_edit_command(editor: "").tap do |output|
assert_match "No $EDITOR to open file in", output
assert_match "rails credentials:edit", output
end
end
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)
end
end
test "edit command does not add master key to gitignore when already exist" do
run_edit_command
Dir.chdir(app_path) do
gitignore = File.read(".gitignore")
assert_equal 1, gitignore.scan(%r|config/master\.key|).length
end
end
test "edit command does not overwrite by default if credentials already exists" do
run_edit_command(editor: "eval echo api_key: abc >")
assert_match(/api_key: abc/, run_show_command)
run_edit_command
assert_match(/api_key: abc/, run_show_command)
end
test "edit command does not add master key when `RAILS_MASTER_KEY` env specified" do
Dir.chdir(app_path) do
key = IO.binread("config/master.key").strip
FileUtils.rm("config/master.key")
switch_env("RAILS_MASTER_KEY", key) do
assert_match(/access_key_id: 123/, 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
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
end
test "edit command does not raise when an initializer tries to access non-existent credentials" do
app_file "config/initializers/raise_when_loaded.rb", <<-RUBY
Rails.application.credentials.missing_key!
RUBY
assert_match(/access_key_id: 123/, 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
output = run_show_command
assert_match(/access_key_id: 123/, output)
assert_match(/secret_key_base/, output)
end
test "edit ask the user to opt in to pretty credentials" do
assert_match(/Would you like to make the credentials diff from git/, run_edit_command)
end
test "edit doesn't ask the user to opt in to pretty credentials when alreasy asked" do
app_file("tmp/rails_pretty_credentials", "")
assert_no_match(/Would you like to make the credentials diff from git/, run_edit_command)
end
test "edit doesn't ask the user to opt in when user already opted in" do
content = <<~EOM
[diff "rails_credentials"]
textconv = bin/rails credentials:show
EOM
app_file(".git/config", content)
assert_no_match(/Would you like to make the credentials diff from git/, run_edit_command)
end
test "edit ask the user to opt in to pretty credentials, user accepts" do
file = Tempfile.open("credentials_test")
file.write("y")
file.rewind
run_edit_command(stdin: file.path)
git_attributes = app_path(".gitattributes")
expected = <<~EOM
config/credentials/*.yml.enc diff=rails_credentials
config/credentials.yml.enc diff=rails_credentials
EOM
assert(File.exist?(git_attributes))
assert_equal(expected, File.read(git_attributes))
Dir.chdir(app_path) do
assert_equal("bin/rails credentials:show\n", `git config --get 'diff.rails_credentials.textconv'`)
end
ensure
file.close!
end
test "edit ask the user to opt in to pretty credentials, user refuses" do
file = Tempfile.open("credentials_test")
file.write("n")
file.rewind
run_edit_command(stdin: file.path)
git_attributes = app_path(".gitattributes")
assert_not(File.exist?(git_attributes))
ensure
file.close!
end
test "show credentials" do
assert_match(/access_key_id: 123/, run_show_command)
end
test "show command when argument is provided (from git diff left file)" do
run_edit_command(environment: "development")
assert_match(/access_key_id: 123/, run_show_command("config/credentials/development.yml.enc"))
end
test "show command when argument is provided (from git diff right file)" do
run_edit_command(environment: "development")
dir = Dir.mktmpdir
file_path = File.join(dir, "KnAM4a_development.yml.enc")
file_content = File.read(app_path("config", "credentials", "development.yml.enc"))
File.write(file_path, file_content)
assert_match(/access_key_id: 123/, run_show_command(file_path))
ensure
FileUtils.rm_rf(dir)
end
test "show command when argument is provided (git diff) and filename is the master credentials" do
assert_match(/access_key_id: 123/, run_show_command("config/credentials.yml.enc"))
end
test "show command when argument is provided (git diff) and master key is not available" do
remove_file "config/master.key"
raw_content = File.read(app_path("config", "credentials.yml.enc"))
assert_match(raw_content, run_show_command("config/credentials.yml.enc"))
end
test "show command when argument is provided (git diff) return the raw encrypted content in an error occurs" do
run_edit_command(environment: "development")
dir = Dir.mktmpdir
file_path = File.join(dir, "20190807development.yml.enc")
file_content = File.read(app_path("config", "credentials", "development.yml.enc"))
File.write(file_path, file_content)
assert_match(file_content, run_show_command(file_path))
ensure
FileUtils.rm_rf(dir)
end
test "show command raises 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 'config\/master\.key' to decrypt credentials/, run_show_command)
end
test "show command displays content specified by environment option" do
run_edit_command(environment: "production")
assert_match(/access_key_id: 123/, run_show_command(environment: "production"))
end
test "show command properly expands environment option" do
run_edit_command(environment: "production")
output = run_show_command(environment: "prod")
assert_match(/access_key_id: 123/, output)
assert_no_match(/secret_key_base/, output)
end
private
def run_edit_command(editor: "cat", environment: nil, **options)
switch_env("EDITOR", editor) do
args = environment ? ["--environment", environment] : []
rails "credentials:edit", args, **options
end
end
def run_show_command(path = nil, environment: nil, **options)
args = environment ? ["--environment", environment] : []
args.unshift(path)
rails "credentials:show", args, **options
end
end
|