secvault 2.5.0 → 2.6.0
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 +4 -4
- data/README.md +2 -71
- data/USAGE_EXAMPLES.md +65 -0
- data/lib/secvault/secrets.rb +4 -3
- data/lib/secvault/version.rb +1 -1
- data/lib/secvault.rb +102 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 804b4325743910209d494c3c81b4f5f0dc8cf89dd802b4b424544923e42cee34
|
4
|
+
data.tar.gz: 6c456cdd91bf12b27ab70922deb6736c42c8afdeb3e91c7173428c791441ff2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82efdd937655a3ba4eaefe8d732df4e2def7fd12a91302b182d1c300f1a17c10031339a53efe14af1f7c23bdfc40bdc44d43f578bc487a4e425551b75169f1c0
|
7
|
+
data.tar.gz: 0bf0eab87edf50ef01b0d5f0989d8403cd2b96795a393504de87875541ef458fbe3342ba2d8d0e8316b252259bbfee00abff8d1cb265bf0d73cdd951e979c27a
|
data/README.md
CHANGED
@@ -7,16 +7,6 @@ Restores the classic Rails `secrets.yml` functionality that was removed in Rails
|
|
7
7
|
- **Rails 7.2+**: Automatic setup
|
8
8
|
- **Rails 8.0+**: Full compatibility
|
9
9
|
|
10
|
-
## ✨ Key Features
|
11
|
-
|
12
|
-
- 🔗 **YAML Anchor/Alias Support**: Use `default: &default` for shared configuration
|
13
|
-
- 🌍 **ERB Interpolation**: Environment variables with type conversion (`ENV['VAR'].to_i`, boolean logic)
|
14
|
-
- 📁 **Multi-File Loading**: Merge multiple YAML files (e.g., base + OAuth + local overrides)
|
15
|
-
- 🔄 **Environment Switching**: Load different environments dynamically
|
16
|
-
- 🛠️ **Development Tools**: Hot-reload secrets without server restart
|
17
|
-
- 🔍 **Utility Methods**: `Secvault.active?` to check integration status
|
18
|
-
- 🏗️ **Flexible Organization**: Feature-based, environment-based, or namespace-based file structures
|
19
|
-
|
20
10
|
## Installation
|
21
11
|
|
22
12
|
```ruby
|
@@ -100,22 +90,11 @@ Secvault.setup_multi_file!([
|
|
100
90
|
])
|
101
91
|
```
|
102
92
|
|
103
|
-
**What this does
|
104
|
-
- ✅ Sets up Rails 7.1 compatibility (calls `setup_backward_compatibility_with_older_rails!`)
|
93
|
+
**What this does:**
|
105
94
|
- ✅ Loads and merges all files in order (later files override earlier ones)
|
106
95
|
- ✅ Handles missing files gracefully
|
107
|
-
- ✅ Adds `reload_secrets!` method in development
|
108
|
-
- ✅ Provides logging (except in production)
|
109
96
|
- ✅ Creates Rails.application.secrets with merged configuration
|
110
97
|
|
111
|
-
**File organization example:**
|
112
|
-
```
|
113
|
-
config/
|
114
|
-
├── secrets.yml # Base application secrets
|
115
|
-
├── secrets.oauth.yml # OAuth providers & external APIs
|
116
|
-
├── secrets.local.yml # Local development overrides (gitignored)
|
117
|
-
```
|
118
|
-
|
119
98
|
**Advanced options:**
|
120
99
|
```ruby
|
121
100
|
# Disable reload helper or logging
|
@@ -160,7 +139,7 @@ dev_secrets = Rails::Secrets.load(env: 'development')
|
|
160
139
|
|
161
140
|
## ERB Features & Type Conversion
|
162
141
|
|
163
|
-
Secvault supports
|
142
|
+
Secvault supports ERB templating with automatic type conversion:
|
164
143
|
|
165
144
|
```yaml
|
166
145
|
production:
|
@@ -225,60 +204,12 @@ else
|
|
225
204
|
end
|
226
205
|
```
|
227
206
|
|
228
|
-
## Usage Examples
|
229
|
-
|
230
|
-
**Basic usage:**
|
231
|
-
```ruby
|
232
|
-
# Access secrets
|
233
|
-
Rails.application.secrets.api_key
|
234
|
-
Rails.application.secrets.database.host
|
235
|
-
Rails.application.secrets.oauth.google.client_id
|
236
|
-
|
237
|
-
# With YAML defaults, you get deep merging:
|
238
|
-
Rails.application.secrets.database.adapter # "postgresql" (from default)
|
239
|
-
Rails.application.secrets.database.host # "localhost" (from environment)
|
240
|
-
```
|
241
|
-
|
242
|
-
**Multi-file merging:**
|
243
|
-
```ruby
|
244
|
-
# Files loaded in order: base → oauth → local
|
245
|
-
# Later files override earlier ones for the same keys
|
246
|
-
# Hash values are deep merged, scalars are replaced
|
247
|
-
|
248
|
-
Rails.application.secrets.api_key # Could be from base or local file
|
249
|
-
Rails.application.secrets.oauth.google # From oauth file
|
250
|
-
Rails.application.secrets.features.debug # From local file override
|
251
|
-
```
|
252
|
-
|
253
207
|
## Security Best Practices
|
254
208
|
|
255
|
-
### ⚠️ Production Security
|
256
209
|
- **Never commit production secrets** to version control
|
257
210
|
- **Use environment variables** in production with ERB: `<%= ENV['SECRET'] %>`
|
258
211
|
- **Use ENV.fetch()** with fallbacks: `<%= ENV.fetch('SECRET', 'default') %>`
|
259
212
|
|
260
|
-
### 📝 File Management
|
261
|
-
- **Add sensitive files** to `.gitignore`:
|
262
|
-
```gitignore
|
263
|
-
config/secrets.yml # If contains sensitive data
|
264
|
-
config/secrets.local.yml # Local development overrides
|
265
|
-
config/secrets.production.yml # If used
|
266
|
-
```
|
267
|
-
|
268
|
-
### 🔑 Recommended Structure
|
269
|
-
```yaml
|
270
|
-
# ✅ GOOD: Base file with safe defaults
|
271
|
-
development:
|
272
|
-
api_key: "safe_dev_key_for_team"
|
273
|
-
|
274
|
-
production:
|
275
|
-
api_key: <%= ENV['API_KEY'] %> # ✅ From environment
|
276
|
-
|
277
|
-
# ❌ BAD: Secrets hardcoded in base file
|
278
|
-
production:
|
279
|
-
api_key: "super_secret_production_key" # ❌ Never do this
|
280
|
-
```
|
281
|
-
|
282
213
|
## License
|
283
214
|
|
284
215
|
MIT License - see [LICENSE](https://opensource.org/licenses/MIT)
|
data/USAGE_EXAMPLES.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Secvault API
|
2
|
+
|
3
|
+
Secvault provides separate control over secrets loading and Rails integration.
|
4
|
+
|
5
|
+
## Core Methods
|
6
|
+
|
7
|
+
### `Secvault.start!(files: [])`
|
8
|
+
|
9
|
+
Loads secrets from YAML files. Returns `true`/`false`.
|
10
|
+
|
11
|
+
- **Default**: Uses `config/secrets.yml` if `files` is empty
|
12
|
+
- **Access**: Secrets available via `Secvault.secrets`
|
13
|
+
- **Non-invasive**: Does not modify `Rails.application.secrets`
|
14
|
+
|
15
|
+
### `Secvault.integrate_with_rails!`
|
16
|
+
|
17
|
+
Replaces `Rails.application.secrets` with Secvault's loaded secrets. Returns `true`/`false`.
|
18
|
+
|
19
|
+
### `Secvault.secrets`
|
20
|
+
|
21
|
+
Access to loaded secrets as `ActiveSupport::OrderedOptions`.
|
22
|
+
|
23
|
+
### Status Methods
|
24
|
+
|
25
|
+
- `Secvault.active?` - Returns `true` if secrets have been loaded
|
26
|
+
- `Secvault.rails_integrated?` - Returns `true` if Rails integration is active
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
### Standalone
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# Load secrets independently
|
34
|
+
Secvault.start!
|
35
|
+
api_key = Secvault.secrets.api_key
|
36
|
+
```
|
37
|
+
|
38
|
+
### With Rails Integration
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# Load and integrate with Rails
|
42
|
+
Secvault.start!
|
43
|
+
Secvault.integrate_with_rails!
|
44
|
+
api_key = Rails.application.secrets.api_key
|
45
|
+
```
|
46
|
+
|
47
|
+
### Multiple Files
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Files are deep-merged in order
|
51
|
+
Secvault.start!(files: [
|
52
|
+
'config/shared_secrets.yml',
|
53
|
+
'config/secrets.yml'
|
54
|
+
])
|
55
|
+
```
|
56
|
+
|
57
|
+
### Error Handling
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
if Secvault.start!(files: ['config/secrets.yml'])
|
61
|
+
if Secvault.integrate_with_rails!
|
62
|
+
# Both operations successful
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
data/lib/secvault/secrets.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/hash/keys"
|
4
|
+
require "active_support/core_ext/hash/deep_merge"
|
4
5
|
require "active_support/ordered_options"
|
5
6
|
require "pathname"
|
6
7
|
require "erb"
|
@@ -89,9 +90,9 @@ module Secvault
|
|
89
90
|
|
90
91
|
secrets ||= {}
|
91
92
|
|
92
|
-
# Merge shared secrets first, then environment-specific
|
93
|
-
all_secrets.
|
94
|
-
all_secrets.
|
93
|
+
# Merge shared secrets first, then environment-specific (using deep merge)
|
94
|
+
all_secrets.deep_merge!(secrets["shared"].deep_symbolize_keys) if secrets["shared"]
|
95
|
+
all_secrets.deep_merge!(secrets[env].deep_symbolize_keys) if secrets[env]
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
data/lib/secvault/version.rb
CHANGED
data/lib/secvault.rb
CHANGED
@@ -57,9 +57,22 @@ module Secvault
|
|
57
57
|
class Error < StandardError; end
|
58
58
|
|
59
59
|
extend self
|
60
|
+
|
61
|
+
# Internal storage for loaded secrets
|
62
|
+
@@loaded_secrets = nil
|
60
63
|
|
61
|
-
#
|
64
|
+
# Access to loaded secrets without Rails integration
|
65
|
+
def secrets
|
66
|
+
@@loaded_secrets || ActiveSupport::OrderedOptions.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if Secvault is currently active (started)
|
62
70
|
def active?
|
71
|
+
@@loaded_secrets != nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check if Secvault is integrated with Rails.application.secrets
|
75
|
+
def rails_integrated?
|
63
76
|
defined?(Rails) && Rails::Secrets == Secvault::RailsSecrets
|
64
77
|
end
|
65
78
|
|
@@ -149,7 +162,39 @@ module Secvault
|
|
149
162
|
end
|
150
163
|
end
|
151
164
|
|
152
|
-
# Load secrets
|
165
|
+
# Load secrets into Secvault.secrets only (no Rails integration)
|
166
|
+
def load_secrets_only!(files, logger: !Rails.env.production?)
|
167
|
+
# Convert strings to Pathname objects and resolve relative to Rails.root
|
168
|
+
file_paths = Array(files).map do |file|
|
169
|
+
file.is_a?(Pathname) ? file : Rails.root.join(file)
|
170
|
+
end
|
171
|
+
|
172
|
+
existing_files = file_paths.select(&:exist?)
|
173
|
+
|
174
|
+
if existing_files.any?
|
175
|
+
# Load and merge all secrets files using Secvault's parser directly
|
176
|
+
merged_secrets = Secvault::Secrets.parse(existing_files, env: Rails.env)
|
177
|
+
|
178
|
+
# Store in Secvault.secrets (ActiveSupport::OrderedOptions for compatibility)
|
179
|
+
@@loaded_secrets = ActiveSupport::OrderedOptions.new
|
180
|
+
@@loaded_secrets.merge!(merged_secrets)
|
181
|
+
|
182
|
+
# Log successful loading
|
183
|
+
if logger
|
184
|
+
file_names = existing_files.map(&:basename)
|
185
|
+
Rails.logger&.info "[Secvault] Loaded #{existing_files.size} files: #{file_names.join(', ')}"
|
186
|
+
Rails.logger&.info "[Secvault] Parsed #{merged_secrets.keys.size} secret keys for #{Rails.env}"
|
187
|
+
end
|
188
|
+
|
189
|
+
true
|
190
|
+
else
|
191
|
+
Rails.logger&.warn "[Secvault] No secrets files found" if logger
|
192
|
+
@@loaded_secrets = ActiveSupport::OrderedOptions.new
|
193
|
+
false
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Load secrets from multiple files and merge them (with Rails integration)
|
153
198
|
def load_multi_file_secrets!(file_paths, logger: !Rails.env.production?)
|
154
199
|
existing_files = file_paths.select(&:exist?)
|
155
200
|
|
@@ -193,6 +238,61 @@ module Secvault
|
|
193
238
|
end
|
194
239
|
end
|
195
240
|
|
241
|
+
# Start Secvault and load secrets (without Rails integration)
|
242
|
+
#
|
243
|
+
# Usage:
|
244
|
+
# Secvault.start! # Uses config/secrets.yml only
|
245
|
+
# Secvault.start!(files: []) # Same as above
|
246
|
+
# Secvault.start!(files: ['path/to/secrets.yml']) # Custom single file
|
247
|
+
# Secvault.start!(files: ['gem.yml', 'app.yml']) # Multiple files
|
248
|
+
#
|
249
|
+
# Access loaded secrets via: Secvault.secrets.your_key
|
250
|
+
# To integrate with Rails.application.secrets, call: Secvault.integrate_with_rails!
|
251
|
+
#
|
252
|
+
# Options:
|
253
|
+
# - files: Array of file paths (String or Pathname). Defaults to ['config/secrets.yml']
|
254
|
+
# - logger: Enable logging (default: true except production)
|
255
|
+
def start!(files: [], logger: !Rails.env.production?)
|
256
|
+
begin
|
257
|
+
# Default to host app's config/secrets.yml if no files specified
|
258
|
+
files_to_load = files.empty? ? ['config/secrets.yml'] : files
|
259
|
+
|
260
|
+
# Load secrets into Secvault.secrets (completely independent of Rails)
|
261
|
+
load_secrets_only!(files_to_load, logger: logger)
|
262
|
+
|
263
|
+
true
|
264
|
+
rescue => e
|
265
|
+
Rails.logger&.error "[Secvault] Failed to start: #{e.message}" if defined?(Rails)
|
266
|
+
false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Integrate loaded secrets with Rails.application.secrets
|
271
|
+
def integrate_with_rails!
|
272
|
+
return false unless @@loaded_secrets
|
273
|
+
|
274
|
+
begin
|
275
|
+
# Set up Rails::Secrets to use Secvault's parser (only when integrating)
|
276
|
+
unless rails_integrated?
|
277
|
+
if defined?(Rails::Secrets)
|
278
|
+
Rails.send(:remove_const, :Secrets)
|
279
|
+
end
|
280
|
+
Rails.const_set(:Secrets, Secvault::RailsSecrets)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Replace Rails.application.secrets with Secvault's loaded secrets
|
284
|
+
Rails.application.define_singleton_method(:secrets) do
|
285
|
+
Secvault.secrets
|
286
|
+
end
|
287
|
+
|
288
|
+
Rails.logger&.info "[Secvault] Integrated with Rails.application.secrets" unless Rails.env.production?
|
289
|
+
true
|
290
|
+
rescue => e
|
291
|
+
Rails.logger&.error "[Secvault] Failed to integrate with Rails: #{e.message}" if defined?(Rails)
|
292
|
+
false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
196
296
|
# Backward compatibility aliases
|
197
297
|
alias_method :setup_rails_71_integration!, :setup_backward_compatibility_with_older_rails!
|
198
298
|
alias_method :setup_multi_files!, :setup_multi_file! # Alternative name
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secvault
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Unnikrishnan KP
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- LICENSE.txt
|
55
55
|
- README.md
|
56
56
|
- Rakefile
|
57
|
+
- USAGE_EXAMPLES.md
|
57
58
|
- lib/secvault.rb
|
58
59
|
- lib/secvault/generators/secrets_generator.rb
|
59
60
|
- lib/secvault/rails_secrets.rb
|