localvault 1.6.2 → 1.8.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.
@@ -1,5 +1,6 @@
1
1
  require "json"
2
2
  require "shellwords"
3
+ require_relative "env_projection"
3
4
 
4
5
  module LocalVault
5
6
  # Encrypted key-value store backed by a single JSON blob.
@@ -143,44 +144,15 @@ module LocalVault
143
144
  # @example
144
145
  # vault.export_env(project: "myapp")
145
146
  # # => "export DB_URL=postgres%3A//..."
146
- def export_env(project: nil, on_skip: nil)
147
- secrets = all
148
- if project
149
- group = secrets[project]
150
- return "" unless group.is_a?(Hash)
151
- group.filter_map do |k, v|
152
- if shell_safe_key?(k)
153
- "export #{k}=#{Shellwords.escape(v.to_s)}"
154
- else
155
- on_skip&.call(k)
156
- nil
157
- end
158
- end.join("\n")
159
- else
160
- secrets.flat_map do |k, v|
161
- if v.is_a?(Hash)
162
- unless shell_safe_key?(k)
163
- on_skip&.call(k)
164
- next []
165
- end
166
- v.filter_map do |sk, sv|
167
- if shell_safe_key?(sk)
168
- "export #{k.upcase}__#{sk}=#{Shellwords.escape(sv.to_s)}"
169
- else
170
- on_skip&.call("#{k}.#{sk}")
171
- nil
172
- end
173
- end
174
- else
175
- if shell_safe_key?(k)
176
- ["export #{k}=#{Shellwords.escape(v.to_s)}"]
177
- else
178
- on_skip&.call(k)
179
- []
180
- end
181
- end
182
- end.join("\n")
183
- end
147
+ def export_env(project: nil, on_skip: nil, only: nil, except: nil, map: nil, profile: nil)
148
+ EnvProjection.entries(all,
149
+ project: project,
150
+ only: only,
151
+ except: except,
152
+ map: map,
153
+ profile: profile,
154
+ on_skip: on_skip
155
+ ).map { |entry| "export #{entry.env_name}=#{Shellwords.escape(entry.value)}" }.join("\n")
184
156
  end
185
157
 
186
158
  # Returns a flat hash suitable for env injection.
@@ -195,40 +167,16 @@ module LocalVault
195
167
  # @example
196
168
  # vault.env_hash(project: "myapp")
197
169
  # # => {"DB_URL" => "postgres://...", "SECRET" => "abc"}
198
- def env_hash(project: nil, on_skip: nil)
199
- secrets = all
200
- if project
201
- group = secrets[project]
202
- return {} unless group.is_a?(Hash)
203
- group.each_with_object({}) do |(k, v), h|
204
- if shell_safe_key?(k)
205
- h[k] = v.to_s
206
- else
207
- on_skip&.call(k)
208
- end
209
- end
210
- else
211
- secrets.each_with_object({}) do |(k, v), h|
212
- if v.is_a?(Hash)
213
- unless shell_safe_key?(k)
214
- on_skip&.call(k)
215
- next
216
- end
217
- v.each do |sk, sv|
218
- if shell_safe_key?(sk)
219
- h["#{k.upcase}__#{sk}"] = sv.to_s
220
- else
221
- on_skip&.call("#{k}.#{sk}")
222
- end
223
- end
224
- else
225
- if shell_safe_key?(k)
226
- h[k] = v.to_s
227
- else
228
- on_skip&.call(k)
229
- end
230
- end
231
- end
170
+ def env_hash(project: nil, on_skip: nil, only: nil, except: nil, map: nil, profile: nil)
171
+ EnvProjection.entries(all,
172
+ project: project,
173
+ only: only,
174
+ except: except,
175
+ map: map,
176
+ profile: profile,
177
+ on_skip: on_skip
178
+ ).each_with_object({}) do |entry, hash|
179
+ hash[entry.env_name] = entry.value
232
180
  end
233
181
  end
234
182
 
@@ -0,0 +1,92 @@
1
+ require "base64"
2
+
3
+ module LocalVault
4
+ module VaultResolver
5
+ Result = Struct.new(:vault, :active_vault, :active_vault_source, :session_vault, keyword_init: true)
6
+
7
+ def self.resolve(name = nil)
8
+ active_vault, source = active_vault_name(name)
9
+ session_name, session_key = session_credentials
10
+
11
+ if session_name == active_vault && session_key
12
+ vault = build_vault(active_vault, session_key)
13
+ return Result.new(vault: vault, active_vault: active_vault, active_vault_source: source, session_vault: session_name) if vault
14
+ end
15
+
16
+ if (master_key = SessionCache.get(active_vault))
17
+ vault = build_vault(active_vault, master_key)
18
+ return Result.new(vault: vault, active_vault: active_vault, active_vault_source: source, session_vault: session_name) if vault
19
+ end
20
+
21
+ Result.new(vault: nil, active_vault: active_vault, active_vault_source: source, session_vault: session_name)
22
+ end
23
+
24
+ def self.status(name = nil)
25
+ result = resolve(name)
26
+ {
27
+ "localvault_home" => Config.root_path,
28
+ "active_vault" => result.active_vault,
29
+ "active_vault_source" => result.active_vault_source,
30
+ "session_vault" => result.session_vault,
31
+ "unlocked_vaults" => unlocked_vault_names,
32
+ "active_vault_unlocked" => !result.vault.nil?
33
+ }
34
+ end
35
+
36
+ def self.readiness_status(name = nil)
37
+ result = resolve(name)
38
+ {
39
+ "localvault_home" => Config.root_path,
40
+ "active_vault" => result.active_vault,
41
+ "active_vault_source" => result.active_vault_source,
42
+ "session_vault" => result.session_vault,
43
+ "active_vault_unlocked" => !result.vault.nil?
44
+ }
45
+ end
46
+
47
+ def self.active_vault_name(name = nil)
48
+ return [name, "argument"] if name && !name.empty?
49
+ return [ENV["LOCALVAULT_VAULT"], "env"] if ENV["LOCALVAULT_VAULT"] && !ENV["LOCALVAULT_VAULT"].empty?
50
+
51
+ [Config.default_vault, "config"]
52
+ end
53
+
54
+ def self.session_vault_name
55
+ session_credentials.first
56
+ end
57
+
58
+ def self.unlocked_vault_names
59
+ names = []
60
+ session_name, session_key = session_credentials
61
+ names << session_name if session_name && session_key && build_vault(session_name, session_key)
62
+
63
+ Store.list_vaults.each do |vault_name|
64
+ next if names.include?(vault_name)
65
+ names << vault_name if SessionCache.get(vault_name)
66
+ end
67
+
68
+ names.sort
69
+ end
70
+
71
+ def self.session_credentials
72
+ token = ENV["LOCALVAULT_SESSION"]
73
+ return [nil, nil] unless token
74
+
75
+ decoded = Base64.strict_decode64(token)
76
+ vault_name, key_b64 = decoded.split(":", 2)
77
+ return [nil, nil] unless vault_name && key_b64
78
+
79
+ [vault_name, Base64.strict_decode64(key_b64)]
80
+ rescue ArgumentError
81
+ [nil, nil]
82
+ end
83
+
84
+ def self.build_vault(name, master_key)
85
+ vault = Vault.new(name: name, master_key: master_key)
86
+ vault.all
87
+ vault
88
+ rescue Crypto::DecryptionError, Store::InvalidVaultName
89
+ nil
90
+ end
91
+ end
92
+ end
@@ -1,3 +1,3 @@
1
1
  module LocalVault
2
- VERSION = "1.6.2"
2
+ VERSION = "1.8.0"
3
3
  end
data/lib/localvault.rb CHANGED
@@ -2,6 +2,9 @@ require_relative "localvault/version"
2
2
  require_relative "localvault/crypto"
3
3
  require_relative "localvault/config"
4
4
  require_relative "localvault/store"
5
+ require_relative "localvault/env_projection"
6
+ require_relative "localvault/key_lookup"
7
+ require_relative "localvault/vault_resolver"
5
8
  require_relative "localvault/vault"
6
9
  require_relative "localvault/identity"
7
10
  require_relative "localvault/share_crypto"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localvault
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.2
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nauman Tariq
@@ -108,14 +108,20 @@ files:
108
108
  - lib/localvault.rb
109
109
  - lib/localvault/api_client.rb
110
110
  - lib/localvault/cli.rb
111
+ - lib/localvault/cli/error_presenter.rb
111
112
  - lib/localvault/cli/keys.rb
112
113
  - lib/localvault/cli/sync.rb
113
114
  - lib/localvault/cli/team.rb
114
115
  - lib/localvault/cli/team_helpers.rb
115
116
  - lib/localvault/config.rb
116
117
  - lib/localvault/crypto.rb
118
+ - lib/localvault/env_projection.rb
119
+ - lib/localvault/group_catalog.rb
117
120
  - lib/localvault/identity.rb
121
+ - lib/localvault/input_validation.rb
122
+ - lib/localvault/key_lookup.rb
118
123
  - lib/localvault/key_slot.rb
124
+ - lib/localvault/mcp/exec_command_builder.rb
119
125
  - lib/localvault/mcp/server.rb
120
126
  - lib/localvault/mcp/tools.rb
121
127
  - lib/localvault/session_cache.rb
@@ -124,6 +130,7 @@ files:
124
130
  - lib/localvault/sync_bundle.rb
125
131
  - lib/localvault/sync_state.rb
126
132
  - lib/localvault/vault.rb
133
+ - lib/localvault/vault_resolver.rb
127
134
  - lib/localvault/version.rb
128
135
  homepage: https://github.com/inventlist/localvault
129
136
  licenses: