ravioli 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 572198c31192d87b63d13d1e672813f7c9e37c9cc3a9f9e17622b5f86c882b34
4
- data.tar.gz: 626a0d7ec6f697f00fabc011884fefec7fed2c0a55141638f3096a657fcb0a82
3
+ metadata.gz: 8f4bb7ba56c32614b3b2be61de6c94648e3ff2fbb54692f84c56bbacdbb1db4e
4
+ data.tar.gz: bafd93a46a25071127c09c19231af5545749ea18b63eaa94d7ac2e67e2642fdd
5
5
  SHA512:
6
- metadata.gz: cfb0f8416dd26cb6150a6d0c3cb93a908fd75d7571ccbccc138f5141ff5b888d75a90cfe9dd3438748519f6078ba300875600aa0b3f7cb980ab426c3e2223330
7
- data.tar.gz: e1316a4be7b03f9825f7324f70798c2c41ce937b04da209fc2df59a3fa4d1730efb94de12f74b53636ef6069faa27564bc9388951b7ed29f26b1ab73ed4f1400
6
+ metadata.gz: f2b59aa2b29243d15c43a0262442576eeedb7c2b5d651341e184b771f277c598c62844dfc0b9f3d234ce9206a80ecd62286d352f31a4ea08db6439f64c5ccac8
7
+ data.tar.gz: a691095131f8babd604bf1a6c5a6aaacf4b137c46dc9adc1cd824ffabe2b2c1f75657e8b082defee4b911c5a37695d8955184ffe5d6c2f593cadc336ffdc92eb
data/README.md CHANGED
@@ -216,7 +216,7 @@ Ravioli will then check for [encrypted credentials](https://guides.rubyonrails.o
216
216
  2. Then, it loads and applies `config/credentials/RAILS_ENV.yml.enc` over top of what it has already loaded
217
217
  3. Finally, IF `Rails.config.staging?` IS TRUE, it loads and applies `config/credentials/staging.yml.enc`
218
218
 
219
- This allows you to use your secure credentials stores without duplicating information; you can simply layer environment-specific values over top of
219
+ This allows you to use your secure credentials stores without duplicating information; you can simply layer environment-specific values over top of a "root" `config/credentials.yml.enc` file.
220
220
 
221
221
  ### All put together, it does this:
222
222
 
@@ -417,47 +417,47 @@ staging:
417
417
 
418
418
  ### Encryption keys in ENV
419
419
 
420
- Because Ravioli merges environment-specific credentials over top of the root credentials file, you'll need to provide encryption keys for two (or, if you have a staging setup, three) different files in ENV vars. As such, Ravioli looks for decryption keys in a fallback-specific way. Here's where it looks for each file:
420
+ Here are a few facts about credentials in Rails and how they're deployed:
421
421
 
422
- <table><thead><tr><th>File</th><th>First it tries...</th><th>Then it tries...</th></tr></thead><tbody><tr><td>
422
+ 1. Rails assumes you want to use the file that matches your environment, if it exists (e.g. `RAILS_ENV=production` will look for `config/credentials/production.yml.enc`)
423
+ 2. Rails does _not_ support environment-specfic keys, but it _does_ now aggressively loads credentials at boot time.
423
424
 
424
- `config/credentials.yml.enc`
425
+ **This means `RAILS_MASTER_KEY` MUST be the decryption key for your environment-specific credential file, if one exists.**
425
426
 
426
- </td><td>
427
+ But, because Ravioli merges environment-specific credentials over top of the root credentials file, you'll need to provide encryption keys for two (or, if you have a staging setup, three) different files in ENV vars. As such, Ravioli looks for decryption keys in a way that mirrors Rails' assumptions, but allows progressive layering of credentials.
427
428
 
428
- `ENV["RAILS_BASE_KEY"]`
429
+ Here are a few examples
429
430
 
430
- </td><td>
431
+ <table><thead><tr><th>File</th><th>First it tries...</th><th>Then it tries...</th></tr></thead><tbody>
431
432
 
432
- `ENV["RAILS_MASTER_KEY"]`
433
+ <tr>
434
+ <td><code>config/credentials.yml.enc</code></td>
435
+ <td><code>ENV["RAILS_MASTER_KEY"]</code></td>
436
+ <td><code>ENV["RAILS_ROOT_KEY"]</code></td>
437
+ </tr>
433
438
 
434
- </td></tr><tr><td>
439
+ <tr>
440
+ <td><code>config/credentials/#{RAILS_ENV}.yml.enc</code></td>
441
+ <td><code>ENV["RAILS_MASTER_KEY"]</code></td>
442
+ <td><code>ENV["RAILS_#{RAILS_ENV}_KEY"]</code></td>
443
+ </tr>
435
444
 
436
- `config/credentials/production.yml.enc`
437
445
 
438
- </td><td>
446
+ <tr>
447
+ <td><code>config/credentials/staging.yml.enc</code></td>
448
+ <td><code>ENV["RAILS_MASTER_KEY"]</code></td>
449
+ <td><code>ENV["RAILS_STAGING_KEY"]</code></td>
450
+ </tr>
439
451
 
440
- `ENV["RAILS_PRODUCTION_KEY"]`
452
+ </tbody></table>
441
453
 
442
- </td><td>
443
-
444
- `ENV["RAILS_MASTER_KEY"]`
445
-
446
- </td></tr><tr><td>
447
-
448
- `config/credentials/staging.yml.enc` (only if running on staging)
449
-
450
- </td><td>
451
-
452
- `ENV["RAILS_STAGING_KEY"]`
453
-
454
- </td><td>
455
-
456
- `ENV["RAILS_MASTER_KEY"]`
454
+ Credentials are loaded in that order, too, so that you can have a base setup on `config/credentials.yml.enc`, overlay that with production-specific stuff from `config/credentials/production.yml.enc`, and then short-circuit or redirect some stuff in `config/credentials/staging.yml.enc` for staging environments.
457
455
 
458
- </td></tr></tbody></table>
456
+ #### TLDR:
459
457
 
460
- Credentials are loaded in that order, too, so that you can have a base setup on `config/credentials.yml.enc`, overlay that with production-specific stuff from `config/credentials/production.yml.enc`, and then short-circuit or redirect some stuff in `config/credentials/staging.yml.enc` for staging environments.
458
+ 1. Set `RAILS_MASTER_KEY` to the key for your specific environment
459
+ 2. Set `RAILS_STAGING_KEY` to the key for your staging credentials (if deploying to staging AND you have staging-specific credentials)
460
+ 3. Set `RAILS_ROOT_KEY` to the key for your root credentials (if you have anything in `config/credentials.yml.enc`)
461
461
 
462
462
  ## License
463
463
 
@@ -79,17 +79,10 @@ module Ravioli
79
79
  #
80
80
  # @param is_staging [boolean, #present?] whether or not the current environment is considered a staging environment
81
81
  def add_staging_flag!(is_staging = Rails.env.production? && ENV["STAGING"].present?)
82
+ require_relative "./staging_inquirer"
82
83
  is_staging = is_staging.present?
83
84
  configuration.staging = is_staging
84
- Rails.env.class_eval <<-EOC, __FILE__, __LINE__ + 1
85
- def staging?
86
- config = Rails.try(:config)
87
- return false unless config&.is_a?(Ravioli::Configuration)
88
-
89
- config.staging?
90
- end
91
- EOC
92
- is_staging
85
+ Rails.env.class.prepend Ravioli::StagingInquirer
93
86
  end
94
87
 
95
88
  # Iterates through the config directory (including nested folders) and
@@ -104,14 +97,28 @@ module Ravioli
104
97
 
105
98
  # Loads Rails encrypted credentials that it can. Checks for corresponding private key files, or ENV vars based on the {Ravioli::Builder credentials preadmlogic}
106
99
  def auto_load_credentials!
107
- # Load the base config
108
- load_credentials(key_path: "config/master.key", env_name: "base")
109
-
110
- # Load any environment-specific configuration on top of it
111
- load_credentials("config/credentials/#{Rails.env}", key_path: "config/credentials/#{Rails.env}.key", env_name: "master")
100
+ # Load the root config (supports using the master key or `RAILS_ROOT_KEY`)
101
+ load_credentials(
102
+ key_path: "config/master.key",
103
+ env_names: %w[master root],
104
+ )
105
+
106
+ # Load any environment-specific configuration on top of it. Since Rails will try
107
+ # `RAILS_MASTER_KEY` from the environment, we assume the same
108
+ load_credentials(
109
+ "config/credentials/#{Rails.env}",
110
+ key_path: "config/credentials/#{Rails.env}.key",
111
+ env_names: ["master"],
112
+ )
112
113
 
113
114
  # Apply staging configuration on top of THAT, if need be
114
- load_credentials("config/credentials/staging", key_path: "config/credentials/staging.key") if configuration.staging?
115
+ if configuration.staging?
116
+ load_credentials(
117
+ "config/credentials/staging",
118
+ env_names: %w[staging master],
119
+ key_path: "config/credentials/staging.key",
120
+ )
121
+ end
115
122
  end
116
123
 
117
124
  # When the builder is done working, lock the configuration and return it
@@ -140,11 +147,30 @@ module Ravioli
140
147
  end
141
148
 
142
149
  # Load secure credentials using a key either from a file or the ENV
143
- def load_credentials(path = "credentials", key_path: path, env_name: path.split("/").last)
144
- credentials = parse_credentials(path, env_name: env_name, key_path: key_path)
145
- configuration.append(credentials) if credentials.present?
146
- rescue => error
147
- warn "Could not decrypt `#{path}.yml.enc' with key file `#{key_path}' or `ENV[\"#{env_name}\"]'", error, critical: false
150
+ def load_credentials(path = "credentials", key_path: path, env_names: path.split("/").last)
151
+ error = nil
152
+ env_names = Array(env_names).map { |env_name| parse_env_name(env_name) }
153
+ env_names.each do |env_name|
154
+ credentials = parse_credentials(path, env_name: env_name, key_path: key_path)
155
+ if credentials.present?
156
+ configuration.append(credentials)
157
+ return credentials
158
+ end
159
+ rescue => e
160
+ error = e
161
+ end
162
+
163
+ if error
164
+ attempted_names = ["key file `#{key_path}'"]
165
+ attempted_names.push(*env_names.map { |env_name| "`ENV[\"#{env_name}\"]'" })
166
+ attempted_names = attempted_names.to_sentence(two_words_connector: " or ", last_word_connector: ", or ")
167
+ warn(
168
+ "Could not decrypt `#{path}.yml.enc' with #{attempted_names}",
169
+ error,
170
+ critical: false,
171
+ )
172
+ end
173
+
148
174
  {}
149
175
  end
150
176
 
@@ -233,13 +259,17 @@ module Ravioli
233
259
  {name => config}
234
260
  end
235
261
 
262
+ def parse_env_name(env_name)
263
+ env_name = env_name.to_s
264
+ env_name.match?(/^RAILS_/) ? env_name : "RAILS_#{env_name.upcase}_KEY"
265
+ end
266
+
236
267
  def parse_credentials(path, key_path: path, env_name: path.split("/").last)
237
268
  @current_credentials = path
238
- env_name = env_name.to_s
239
- env_name = "RAILS_#{env_name.upcase}_KEY" unless env_name.upcase == env_name
269
+ env_name = parse_env_name(env_name)
240
270
  key_path = path_to_config_file_path(key_path, extnames: "key", quiet: true)
241
- options = {key_path: key_path}
242
- options[:env_key] = ENV[env_name].present? ? env_name : SecureRandom.hex(6)
271
+ options = {key_path: key_path.to_s}
272
+ options[:env_key] = ENV[env_name].present? ? env_name : "__RAVIOLI__#{SecureRandom.hex(6)}"
243
273
 
244
274
  path = path_to_config_file_path(path, extnames: "yml.enc")
245
275
  (Rails.application.encrypted(path, **options)&.config || {}).tap do
@@ -288,7 +318,7 @@ module Ravioli
288
318
  if @strict && critical
289
319
  raise BuildError.new(message, error)
290
320
  else
291
- Rails.logger.warn(message) if defined? Rails
321
+ Rails.logger.try(:warn, message) if defined? Rails
292
322
  $stderr.write message # rubocop:disable Rails/Output
293
323
  end
294
324
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ravioli
4
+ # A module that we mix in to the `Rails.env` inquirer class to add some extra staging-related
5
+ # metadata
6
+ module StagingInquirer
7
+ # Add a `name` method to `Rails.env` that will return "staging" for staging environments, and
8
+ # otherwise the string's value
9
+ def name
10
+ staging? ? "staging" : to_s
11
+ end
12
+
13
+ # Add a `strict:` keyword to reduce `Rails.env.production && !Rails.env.staging` calls
14
+ def production?(strict: false)
15
+ is_production = super()
16
+ return is_production unless strict && is_production
17
+
18
+ is_production && !staging?
19
+ end
20
+
21
+ # Override staging inquiries to check against the current configuration
22
+ def staging?
23
+ Rails.try(:config)&.staging?
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ravioli
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ravioli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flip Sasser
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-28 00:00:00.000000000 Z
11
+ date: 2021-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -271,6 +271,7 @@ files:
271
271
  - lib/ravioli/builder.rb
272
272
  - lib/ravioli/configuration.rb
273
273
  - lib/ravioli/engine.rb
274
+ - lib/ravioli/staging_inquirer.rb
274
275
  - lib/ravioli/version.rb
275
276
  homepage: https://github.com/flipsasser/ravioli
276
277
  licenses: