ravioli 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: