chef-vault 3.0.0.rc1 → 3.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +1 -0
- data/lib/chef-vault/item.rb +43 -46
- data/lib/chef-vault/item_keys.rb +97 -6
- data/lib/chef-vault/mixins.rb +11 -1
- data/lib/chef-vault/version.rb +1 -1
- data/lib/chef/knife/mixin/helper.rb +2 -2
- data/lib/chef/knife/vault_create.rb +1 -1
- data/lib/chef/knife/vault_update.rb +1 -1
- data/spec/chef-vault/item_keys_spec.rb +73 -6
- data/spec/chef-vault/item_spec.rb +5 -7
- data/spec/chef-vault_spec.rb +30 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6dd267f10f94be45c16f2a088e476226b4b0cf5
|
4
|
+
data.tar.gz: 27fc5a1ee95c7be2ad1af5f967d4bbda96427b6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9397b282b2497e73b1fef2cb86f09f84df98bc9333513cd4648767436e15138f9c9cee187c56854ee3e2197a61ee992e6cdd4f502145b89d194eb428c0ccb07c
|
7
|
+
data.tar.gz: 6a978b6a18e4a1a4b4eb8e3ddd3ce48318126e3f152dd5080ee20e19993afece13540df9b33b9a64b6873b483876c9041653ba91739a64ca3d0bc94b632a4dfd
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/lib/chef-vault/item.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Author:: Kevin Moser <kevin.moser@nordstrom.com>
|
2
2
|
# Copyright:: Copyright 2013-15, Nordstrom, Inc.
|
3
|
+
# Copyright:: Copyright 2015-16, Chef Software, Inc.
|
3
4
|
# License:: Apache License, Version 2.0
|
4
5
|
|
5
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -67,6 +68,7 @@ class ChefVault
|
|
67
68
|
}.merge(opts)
|
68
69
|
@node_name = opts[:node_name]
|
69
70
|
@client_key_path = opts[:client_key_path]
|
71
|
+
@current_query = search
|
70
72
|
end
|
71
73
|
|
72
74
|
# private
|
@@ -75,39 +77,42 @@ class ChefVault
|
|
75
77
|
@secret = secret
|
76
78
|
end
|
77
79
|
|
78
|
-
def clients(search_or_client, action = :add)
|
79
|
-
if
|
80
|
+
def clients(search_or_client = search_results, action = :add)
|
81
|
+
# for backwards compatibility, if we're handed a string
|
82
|
+
# do a search using that string and recurse
|
83
|
+
if search_or_client.is_a?(String)
|
84
|
+
clients(search_results(search_or_client), action)
|
85
|
+
elsif search_or_client.is_a?(Chef::ApiClient)
|
80
86
|
handle_client_action(search_or_client, action)
|
81
|
-
|
87
|
+
else
|
82
88
|
search_or_client.each do |name|
|
83
|
-
|
84
|
-
|
89
|
+
begin
|
90
|
+
client = load_actor(name, "clients")
|
91
|
+
handle_client_action(client, action)
|
92
|
+
rescue ChefVault::Exceptions::ClientNotFound
|
93
|
+
ChefVault::Log.warn "node '#{name}' has no private key; skipping"
|
94
|
+
end
|
85
95
|
end
|
86
|
-
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def search_results(statement = search)
|
100
|
+
@search_results = nil if statement != @current_query
|
101
|
+
@current_query = statement
|
102
|
+
@search_results ||= begin
|
87
103
|
results_returned = false
|
104
|
+
results = []
|
88
105
|
query = Chef::Search::Query.new
|
89
|
-
query.search(:node,
|
106
|
+
query.search(:node, statement, filter_result: { name: ["name"] }, rows: 100000) do |node|
|
90
107
|
results_returned = true
|
91
|
-
|
92
|
-
when :add
|
93
|
-
begin
|
94
|
-
client_key = load_actor(node.name, "clients")
|
95
|
-
add_client(client_key)
|
96
|
-
rescue ChefVault::Exceptions::ClientNotFound
|
97
|
-
ChefVault::Log.warn "node '#{node.name}' has no private key; skipping"
|
98
|
-
end
|
99
|
-
when :delete
|
100
|
-
delete_client_or_node(node)
|
101
|
-
else
|
102
|
-
raise ChefVault::Exceptions::KeysActionNotValid,
|
103
|
-
"#{action} is not a valid action"
|
104
|
-
end
|
108
|
+
results << node["name"]
|
105
109
|
end
|
106
110
|
|
107
111
|
unless results_returned
|
108
112
|
ChefVault::Log.warn "No clients were returned from search, you may not have "\
|
109
|
-
|
113
|
+
"got what you expected!!"
|
110
114
|
end
|
115
|
+
results
|
111
116
|
end
|
112
117
|
end
|
113
118
|
|
@@ -172,9 +177,7 @@ class ChefVault
|
|
172
177
|
# admins, just clients which are nodes
|
173
178
|
remove_unknown_nodes if clean_unknown_clients
|
174
179
|
# re-encrypt the new shared secret for all remaining clients
|
175
|
-
get_clients
|
176
|
-
clients("name:#{client}")
|
177
|
-
end
|
180
|
+
clients(get_clients)
|
178
181
|
end
|
179
182
|
|
180
183
|
unless get_admins.empty?
|
@@ -211,7 +214,7 @@ class ChefVault
|
|
211
214
|
encrypt! unless @encrypted
|
212
215
|
|
213
216
|
# Now save the encrypted data
|
214
|
-
if Chef::Config[:
|
217
|
+
if Chef::Config[:solo_legacy_mode]
|
215
218
|
save_solo(item_id)
|
216
219
|
else
|
217
220
|
begin
|
@@ -257,7 +260,7 @@ class ChefVault
|
|
257
260
|
def destroy
|
258
261
|
keys.destroy
|
259
262
|
|
260
|
-
if Chef::Config[:
|
263
|
+
if Chef::Config[:solo_legacy_mode]
|
261
264
|
data_bag_path = File.join(Chef::Config[:data_bag_path],
|
262
265
|
data_bag)
|
263
266
|
data_bag_item_path = File.join(data_bag_path, @raw_data["id"])
|
@@ -362,7 +365,7 @@ class ChefVault
|
|
362
365
|
remove_unknown_nodes if clean_unknown_clients
|
363
366
|
|
364
367
|
# re-process the search query to add new clients
|
365
|
-
clients
|
368
|
+
clients
|
366
369
|
|
367
370
|
# save the updated keys only
|
368
371
|
save_keys(@raw_data["id"])
|
@@ -413,14 +416,10 @@ class ChefVault
|
|
413
416
|
# @param nodename [String] the name of the node
|
414
417
|
# @return [Boolean] whether the node exists or not
|
415
418
|
def node_exists?(nodename)
|
416
|
-
#
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
return false unless numresults > 0
|
421
|
-
# if the node search does return results, predicate node
|
422
|
-
# existence on the existence of a like-named client
|
423
|
-
client_exists?(nodename)
|
419
|
+
# if we don't have a client it really doesn't matter if we have a node.
|
420
|
+
if client_exists?(nodename)
|
421
|
+
search_results.include?(nodename)
|
422
|
+
end
|
424
423
|
end
|
425
424
|
|
426
425
|
# checks if a client exists on the Chef server. If we get back
|
@@ -429,13 +428,11 @@ class ChefVault
|
|
429
428
|
# @param clientname [String] the name of the client
|
430
429
|
# @return [Boolean] whether the client exists or not
|
431
430
|
def client_exists?(clientname)
|
432
|
-
|
433
|
-
Chef::ApiClient.load(clientname)
|
434
|
-
rescue Net::HTTPServerException => http_error
|
435
|
-
return false if http_error.response.code == "404"
|
436
|
-
raise http_error
|
437
|
-
end
|
431
|
+
Chef::ApiClient.load(clientname)
|
438
432
|
true
|
433
|
+
rescue Net::HTTPServerException => http_error
|
434
|
+
return false if http_error.response.code == "404"
|
435
|
+
raise http_error
|
439
436
|
end
|
440
437
|
|
441
438
|
# adds or deletes an API client from the vault item keys
|
@@ -448,7 +445,7 @@ class ChefVault
|
|
448
445
|
client = load_actor(api_client.name, "clients")
|
449
446
|
add_client(client)
|
450
447
|
when :delete
|
451
|
-
delete_client_or_node(api_client)
|
448
|
+
delete_client_or_node(api_client.name)
|
452
449
|
end
|
453
450
|
end
|
454
451
|
|
@@ -460,10 +457,10 @@ class ChefVault
|
|
460
457
|
end
|
461
458
|
|
462
459
|
# removes a client to the vault item keys
|
463
|
-
# @param client_or_node [
|
460
|
+
# @param client_or_node [String] the name of the API client or node to remove
|
464
461
|
# @return [void]
|
465
|
-
def delete_client_or_node(
|
466
|
-
client = load_actor(
|
462
|
+
def delete_client_or_node(name)
|
463
|
+
client = load_actor(name, "clients")
|
467
464
|
keys.delete(client)
|
468
465
|
end
|
469
466
|
end
|
data/lib/chef-vault/item_keys.rb
CHANGED
@@ -28,9 +28,33 @@ class ChefVault
|
|
28
28
|
@raw_data["admins"] = []
|
29
29
|
@raw_data["clients"] = []
|
30
30
|
@raw_data["search_query"] = []
|
31
|
+
@raw_data["mode"] = "default"
|
32
|
+
@cache = {} # write-back cache for keys
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](key)
|
36
|
+
# return options immediately
|
37
|
+
return @raw_data[key] if %w{id admins clients search_query mode}.include?(key)
|
38
|
+
# check if the key is in the write-back cache
|
39
|
+
ckey = @cache[key]
|
40
|
+
return ckey unless ckey.nil?
|
41
|
+
# check if the key is saved in sparse mode
|
42
|
+
skey = sparse_key(sparse_id(key))
|
43
|
+
if skey
|
44
|
+
skey[key]
|
45
|
+
else
|
46
|
+
# fallback to raw data
|
47
|
+
@raw_data[key]
|
48
|
+
end
|
31
49
|
end
|
32
50
|
|
33
51
|
def include?(key)
|
52
|
+
# check if the key is in the write-back cache
|
53
|
+
ckey = @cache[key]
|
54
|
+
return (ckey ? true : false) unless ckey.nil?
|
55
|
+
# check if the key is saved in sparse mode
|
56
|
+
return true unless sparse_key(sparse_id(key)).nil?
|
57
|
+
# fallback to non-sparse mode if sparse key is not found
|
34
58
|
@raw_data.keys.include?(key)
|
35
59
|
end
|
36
60
|
|
@@ -40,16 +64,24 @@ class ChefVault
|
|
40
64
|
raise ChefVault::Exceptions::V1Format,
|
41
65
|
"cannot manage a v1 vault. See UPGRADE.md for help"
|
42
66
|
end
|
43
|
-
|
67
|
+
@cache[chef_key.name] = ChefVault::ItemKeys.encode_key(chef_key.key, data_bag_shared_secret)
|
44
68
|
@raw_data[type] << chef_key.name unless @raw_data[type].include?(chef_key.name)
|
45
69
|
@raw_data[type]
|
46
70
|
end
|
47
71
|
|
48
72
|
def delete(chef_key)
|
49
|
-
|
73
|
+
@cache[chef_key.name] = false
|
50
74
|
raw_data[chef_key.type].delete(chef_key.name)
|
51
75
|
end
|
52
76
|
|
77
|
+
def mode(mode = nil)
|
78
|
+
if mode
|
79
|
+
@raw_data["mode"] = mode
|
80
|
+
else
|
81
|
+
@raw_data["mode"]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
53
85
|
def search_query(search_query = nil)
|
54
86
|
if search_query
|
55
87
|
@raw_data["search_query"] = search_query
|
@@ -67,9 +99,8 @@ class ChefVault
|
|
67
99
|
end
|
68
100
|
|
69
101
|
def save(item_id = @raw_data["id"])
|
70
|
-
if
|
71
|
-
|
72
|
-
else
|
102
|
+
# create data bag if not running in solo mode
|
103
|
+
unless Chef::Config[:solo_legacy_mode]
|
73
104
|
begin
|
74
105
|
Chef::DataBag.load(data_bag)
|
75
106
|
rescue Net::HTTPServerException => http_error
|
@@ -79,13 +110,57 @@ class ChefVault
|
|
79
110
|
chef_data_bag.create
|
80
111
|
end
|
81
112
|
end
|
113
|
+
end
|
82
114
|
|
115
|
+
# write cached keys to data
|
116
|
+
@cache.each do |key, val|
|
117
|
+
# delete across all modes on key deletion
|
118
|
+
if val == false
|
119
|
+
# sparse mode key deletion
|
120
|
+
if Chef::Config[:solo_legacy_mode]
|
121
|
+
delete_solo(sparse_id(key))
|
122
|
+
else
|
123
|
+
begin
|
124
|
+
Chef::DataBagItem.from_hash("data_bag" => data_bag,
|
125
|
+
"id" => sparse_id(key))
|
126
|
+
.destroy(data_bag, sparse_id(key))
|
127
|
+
rescue Net::HTTPServerException => http_error
|
128
|
+
raise http_error unless http_error.response.code == "404"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
# default mode key deletion
|
132
|
+
@raw_data.delete(key)
|
133
|
+
else
|
134
|
+
if @raw_data["mode"] == "sparse"
|
135
|
+
# sparse mode key creation
|
136
|
+
skey = Chef::DataBagItem.from_hash(
|
137
|
+
"data_bag" => data_bag,
|
138
|
+
"id" => sparse_id(key),
|
139
|
+
key => val
|
140
|
+
)
|
141
|
+
if Chef::Config[:solo_legacy_mode]
|
142
|
+
save_solo(skey.id, skey.raw_data)
|
143
|
+
else
|
144
|
+
skey.save
|
145
|
+
end
|
146
|
+
else
|
147
|
+
# default mode key creation
|
148
|
+
@raw_data[key] = val
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
# save raw data
|
153
|
+
if Chef::Config[:solo_legacy_mode]
|
154
|
+
save_solo(item_id)
|
155
|
+
else
|
83
156
|
super
|
84
157
|
end
|
158
|
+
# clear write-back cache
|
159
|
+
@cache = {}
|
85
160
|
end
|
86
161
|
|
87
162
|
def destroy
|
88
|
-
if Chef::Config[:
|
163
|
+
if Chef::Config[:solo_legacy_mode]
|
89
164
|
data_bag_path = File.join(Chef::Config[:data_bag_path],
|
90
165
|
data_bag)
|
91
166
|
data_bag_item_path = File.join(data_bag_path, @raw_data["id"])
|
@@ -129,6 +204,22 @@ class ChefVault
|
|
129
204
|
|
130
205
|
# @private
|
131
206
|
|
207
|
+
def sparse_id(key, item_id = @raw_data["id"])
|
208
|
+
"#{item_id}_key_#{key}"
|
209
|
+
end
|
210
|
+
|
211
|
+
def sparse_key(sid)
|
212
|
+
if Chef::Config[:solo_legacy_mode]
|
213
|
+
load_solo(sid)
|
214
|
+
else
|
215
|
+
begin
|
216
|
+
Chef::DataBagItem.load(@data_bag, sid)
|
217
|
+
rescue Net::HTTPServerException => http_error
|
218
|
+
nil if http_error.response.code == "404"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
132
223
|
def self.encode_key(key_string, data_bag_shared_secret)
|
133
224
|
public_key = OpenSSL::PKey::RSA.new(key_string)
|
134
225
|
Base64.encode64(public_key.public_encrypt(data_bag_shared_secret))
|
data/lib/chef-vault/mixins.rb
CHANGED
@@ -22,7 +22,7 @@ class ChefVault
|
|
22
22
|
[data_bag_path, data_bag_item_path]
|
23
23
|
end
|
24
24
|
|
25
|
-
def save_solo(item_id = @raw_data["id"])
|
25
|
+
def save_solo(item_id = @raw_data["id"], raw_data = @raw_data)
|
26
26
|
data_bag_path, data_bag_item_path = find_solo_path(item_id)
|
27
27
|
|
28
28
|
FileUtils.mkdir(data_bag_path) unless File.exist?(data_bag_path)
|
@@ -32,5 +32,15 @@ class ChefVault
|
|
32
32
|
|
33
33
|
raw_data
|
34
34
|
end
|
35
|
+
|
36
|
+
def delete_solo(item_id = @raw_data["id"])
|
37
|
+
_data_bag_path, data_bag_item_path = find_solo_path(item_id)
|
38
|
+
FileUtils.rm(data_bag_item_path) if File.exist?(data_bag_item_path)
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_solo(item_id = @raw_data["id"])
|
42
|
+
_data_bag_path, data_bag_item_path = find_solo_path(item_id)
|
43
|
+
JSON.parse(File.read(data_bag_item_path)) if File.exist?(data_bag_item_path)
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
data/lib/chef-vault/version.rb
CHANGED
@@ -2,6 +2,9 @@ RSpec.describe ChefVault::ItemKeys do
|
|
2
2
|
describe "#new" do
|
3
3
|
let(:keys) { ChefVault::ItemKeys.new("foo", "bar") }
|
4
4
|
let(:shared_secret) { "super_secret" }
|
5
|
+
let(:public_key_string) do
|
6
|
+
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyMXT9IOV9pkQsxsnhSx8\n8RX6GW3caxkjcXFfHg6E7zUVBFAsfw4B1D+eHAks3qrDB7UrUxsmCBXwU4dQHaQy\ngAn5Sv0Jc4CejDNL2EeCBLZ4TF05odHmuzyDdPkSZP6utpR7+uF7SgVQedFGySIB\nih86aM+HynhkJqgJYhoxkrdo/JcWjpk7YEmWb6p4esnvPWOpbcjIoFs4OjavWBOF\niTfpkS0SkygpLi/iQu9RQfd4hDMWCc6yh3Th/1nVMUd+xQCdUK5wxluAWSv8U0zu\nhiIlZNazpCGHp+3QdP3f6rebmQA8pRM8qT5SlOvCYPk79j+IMUVSYrR4/DTZ+VM+\naQIDAQAB\n-----END PUBLIC KEY-----\n"
|
7
|
+
end
|
5
8
|
|
6
9
|
it "'foo' is assigned to @data_bag" do
|
7
10
|
expect(keys.data_bag).to eq "foo"
|
@@ -19,10 +22,7 @@ RSpec.describe ChefVault::ItemKeys do
|
|
19
22
|
expect(keys["admins"]).to eq []
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
|
-
let(:public_key_string) do
|
24
|
-
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyMXT9IOV9pkQsxsnhSx8\n8RX6GW3caxkjcXFfHg6E7zUVBFAsfw4B1D+eHAks3qrDB7UrUxsmCBXwU4dQHaQy\ngAn5Sv0Jc4CejDNL2EeCBLZ4TF05odHmuzyDdPkSZP6utpR7+uF7SgVQedFGySIB\nih86aM+HynhkJqgJYhoxkrdo/JcWjpk7YEmWb6p4esnvPWOpbcjIoFs4OjavWBOF\niTfpkS0SkygpLi/iQu9RQfd4hDMWCc6yh3Th/1nVMUd+xQCdUK5wxluAWSv8U0zu\nhiIlZNazpCGHp+3QdP3f6rebmQA8pRM8qT5SlOvCYPk79j+IMUVSYrR4/DTZ+VM+\naQIDAQAB\n-----END PUBLIC KEY-----\n"
|
25
|
-
end
|
25
|
+
shared_context "key mgmt operations" do
|
26
26
|
|
27
27
|
shared_examples_for "proper key management" do
|
28
28
|
let(:chef_key) { ChefVault::Actor.new(type, name) }
|
@@ -41,6 +41,7 @@ RSpec.describe ChefVault::ItemKeys do
|
|
41
41
|
keys.add(chef_key, shared_secret)
|
42
42
|
expect(keys[name]).to eq("encrypted_result")
|
43
43
|
expect(keys[type].include?(name)).to eq(true)
|
44
|
+
expect(keys.include?(name)).to eq(true)
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
@@ -53,6 +54,7 @@ RSpec.describe ChefVault::ItemKeys do
|
|
53
54
|
keys.delete(chef_key)
|
54
55
|
expect(keys.has_key?(chef_key.name)).to eq(false)
|
55
56
|
expect(keys[type].include?(name)).to eq(false)
|
57
|
+
expect(keys.include?(name)).to eq(false)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -70,7 +72,52 @@ RSpec.describe ChefVault::ItemKeys do
|
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
75
|
+
context "when running with chef-zero" do
|
76
|
+
let(:server) { chef_zero }
|
77
|
+
before { server.start_background }
|
78
|
+
after { server.stop }
|
79
|
+
|
80
|
+
include_context "key mgmt operations"
|
81
|
+
|
82
|
+
describe "#save" do
|
83
|
+
let(:client_name) { "client_name" }
|
84
|
+
let(:chef_key) { ChefVault::Actor.new("clients", client_name) }
|
85
|
+
|
86
|
+
before do
|
87
|
+
allow(chef_key).to receive(:key) { public_key_string }
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should save the key data" do
|
91
|
+
keys.add(chef_key, shared_secret)
|
92
|
+
keys.save("bar")
|
93
|
+
expect(Chef::DataBagItem.load("foo", "bar").to_hash).to include("id" => "bar")
|
94
|
+
expect(keys[client_name]).not_to be_empty
|
95
|
+
keys.delete(chef_key)
|
96
|
+
keys.save("bar")
|
97
|
+
expect(keys[client_name]).to be_nil
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should save the key data in sparse mode" do
|
101
|
+
keys.add(chef_key, shared_secret)
|
102
|
+
keys.mode("sparse")
|
103
|
+
keys.save("bar")
|
104
|
+
expect(Chef::DataBagItem.load("foo", "bar").to_hash).to include("id" => "bar")
|
105
|
+
expect(Chef::DataBagItem.load("foo", "bar_key_client_name").to_hash).to include("id" => "bar_key_client_name")
|
106
|
+
expect(keys[client_name]).not_to be_empty
|
107
|
+
keys.delete(chef_key)
|
108
|
+
keys.save("bar")
|
109
|
+
expect(keys[client_name]).to be_nil
|
110
|
+
keys.mode("default")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
73
115
|
context "when running with chef-solo" do
|
116
|
+
before { Chef::Config[:solo_legacy_mode] = true }
|
117
|
+
after { Chef::Config[:solo_legacy_mode] = false }
|
118
|
+
|
119
|
+
include_context "key mgmt operations"
|
120
|
+
|
74
121
|
describe "#find_solo_path" do
|
75
122
|
context "when data_bag_path is an array" do
|
76
123
|
before do
|
@@ -103,16 +150,36 @@ RSpec.describe ChefVault::ItemKeys do
|
|
103
150
|
end
|
104
151
|
|
105
152
|
describe "#save" do
|
153
|
+
let(:client_name) { "client_name" }
|
154
|
+
let(:chef_key) { ChefVault::Actor.new("clients", client_name) }
|
106
155
|
let(:data_bag_path) { Dir.mktmpdir("vault_item_keys") }
|
156
|
+
|
107
157
|
before do
|
108
|
-
Chef::Config[:solo] = true
|
109
158
|
Chef::Config[:data_bag_path] = data_bag_path
|
159
|
+
allow(chef_key).to receive(:key) { public_key_string }
|
110
160
|
end
|
111
161
|
|
112
162
|
it "should save the key data" do
|
113
|
-
|
163
|
+
keys.add(chef_key, shared_secret)
|
164
|
+
keys.save("bar")
|
165
|
+
expect(File.read(File.join(data_bag_path, "foo", "bar.json"))).to match(/"id":.*"bar"/)
|
166
|
+
expect(keys[client_name]).not_to be_empty
|
167
|
+
keys.delete(chef_key)
|
168
|
+
keys.save("bar")
|
169
|
+
expect(keys[client_name]).to be_nil
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should save the key data in sparse mode" do
|
173
|
+
keys.add(chef_key, shared_secret)
|
174
|
+
keys.mode("sparse")
|
114
175
|
keys.save("bar")
|
115
176
|
expect(File.read(File.join(data_bag_path, "foo", "bar.json"))).to match(/"id":.*"bar"/)
|
177
|
+
expect(File.read(File.join(data_bag_path, "foo", "bar_key_client_name.json"))).to match(/"id":.*"bar_key_client_name"/)
|
178
|
+
expect(keys[client_name]).not_to be_empty
|
179
|
+
keys.delete(chef_key)
|
180
|
+
keys.save("bar")
|
181
|
+
expect(keys[client_name]).to be_nil
|
182
|
+
keys.mode("default")
|
116
183
|
end
|
117
184
|
end
|
118
185
|
end
|
@@ -172,6 +172,7 @@ RSpec.describe ChefVault::Item do
|
|
172
172
|
end
|
173
173
|
|
174
174
|
describe "#refresh" do
|
175
|
+
let(:node) { { "name" => "testnode" } }
|
175
176
|
|
176
177
|
it "saves only the keys" do
|
177
178
|
keys = double("keys",
|
@@ -184,7 +185,6 @@ RSpec.describe ChefVault::Item do
|
|
184
185
|
|
185
186
|
item = ChefVault::Item.new("foo", "bar")
|
186
187
|
|
187
|
-
node = double("node", name: "testnode")
|
188
188
|
query = double("query")
|
189
189
|
allow(Chef::Search::Query).to receive(:new).and_return(query)
|
190
190
|
allow(query).to receive(:search).and_yield(node)
|
@@ -202,14 +202,13 @@ RSpec.describe ChefVault::Item do
|
|
202
202
|
|
203
203
|
describe "#clients" do
|
204
204
|
context "when search returns a node with a valid client backing it and one without a valid client" do
|
205
|
-
let(:node_with_valid_client) {
|
206
|
-
let(:node_without_valid_client) {
|
205
|
+
let(:node_with_valid_client) { { "name" => "foo" } }
|
206
|
+
let(:node_without_valid_client) { { "name" => "bar" } }
|
207
207
|
let(:query_result) { double("chef search results") }
|
208
208
|
let(:client_key) { double("chef key") }
|
209
209
|
|
210
210
|
before do
|
211
211
|
# node with valid client proper loads client key
|
212
|
-
allow(node_with_valid_client).to receive(:name).and_return("foo")
|
213
212
|
allow(item).to receive(:load_actor).with("foo", "clients").and_return(client_key)
|
214
213
|
privkey = OpenSSL::PKey::RSA.new(1024)
|
215
214
|
pubkey = privkey.public_key
|
@@ -218,12 +217,11 @@ RSpec.describe ChefVault::Item do
|
|
218
217
|
allow(client_key).to receive(:type).and_return("clients")
|
219
218
|
|
220
219
|
# node without client throws relevant error on key load
|
221
|
-
allow(node_without_valid_client).to receive(:name).and_return("bar")
|
222
220
|
allow(item).to receive(:load_actor).with("bar", "clients").and_raise(ChefVault::Exceptions::ClientNotFound)
|
223
221
|
|
224
222
|
allow(query_result)
|
225
223
|
.to receive(:search)
|
226
|
-
.with(Symbol, String)
|
224
|
+
.with(Symbol, String, Hash)
|
227
225
|
.and_yield(node_with_valid_client).and_yield(node_without_valid_client)
|
228
226
|
allow(Chef::Search::Query)
|
229
227
|
.to receive(:new)
|
@@ -306,7 +304,7 @@ RSpec.describe ChefVault::Item do
|
|
306
304
|
end
|
307
305
|
|
308
306
|
context "when no action is passed" do
|
309
|
-
it "
|
307
|
+
it "defaults to add and properly adds the client" do
|
310
308
|
item.clients(clients)
|
311
309
|
expect(item.get_clients).to include(client_name)
|
312
310
|
end
|
data/spec/chef-vault_spec.rb
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
#
|
2
|
+
# Helper for configuring the Chef Zero server
|
3
|
+
# (inspired by ChefSpec)
|
4
|
+
#
|
5
|
+
def chef_zero
|
6
|
+
require "socket"
|
7
|
+
require "tmpdir"
|
8
|
+
require "fileutils"
|
9
|
+
require "chef_zero/server"
|
10
|
+
# Find a free TCP port
|
11
|
+
server = TCPServer.new("127.0.0.1", 0)
|
12
|
+
port = server.addr[1].to_i
|
13
|
+
server.close
|
14
|
+
# Define a Chef Zero Server
|
15
|
+
server = ChefZero::Server.new(port: port)
|
16
|
+
# Write the private key
|
17
|
+
tmp = Dir.mktmpdir
|
18
|
+
key = File.join(tmp, "client.pem")
|
19
|
+
File.write(key, ChefZero::PRIVATE_KEY)
|
20
|
+
# Configure the server
|
21
|
+
Chef::Config[:client_key] = key
|
22
|
+
Chef::Config[:client_name] = "chefvault"
|
23
|
+
Chef::Config[:node_name] = "chefvault"
|
24
|
+
Chef::Config[:chef_server_url] = server.url
|
25
|
+
# Exit handlers
|
26
|
+
at_exit { FileUtils.rm_rf(tmp) }
|
27
|
+
at_exit { server.stop if server.running? }
|
28
|
+
server
|
29
|
+
end
|
30
|
+
|
1
31
|
RSpec.describe ChefVault do
|
2
32
|
let(:vault) { ChefVault.new("foo") }
|
3
33
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chef-vault
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thom May
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|