chef-vault 2.9.2 → 3.0.0.rc1

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -11
  3. data/Changelog.md +1 -6
  4. data/Gemfile +4 -5
  5. data/KNIFE_EXAMPLES.md +66 -14
  6. data/LICENSE +201 -177
  7. data/README.md +74 -4
  8. data/Rakefile +1 -1
  9. data/bin/chef-vault +3 -2
  10. data/chef-vault.gemspec +13 -15
  11. data/features/clean.feature +0 -1
  12. data/features/clean_on_refresh.feature +0 -1
  13. data/features/clean_unknown_clients.feature +0 -1
  14. data/features/detect_and_warn_v1_vault.feature +0 -1
  15. data/features/isvault.feature +0 -1
  16. data/features/itemtype.feature +0 -1
  17. data/features/vault_create.feature +1 -2
  18. data/features/vault_list.feature +0 -1
  19. data/features/vault_show.feature +0 -1
  20. data/features/vault_show_vaultname.feature +0 -1
  21. data/features/vault_update.feature +0 -1
  22. data/features/verify_id_matches.feature +0 -1
  23. data/features/wrong_private_key.feature +0 -1
  24. data/hooks/pre-commit +43 -0
  25. data/lib/chef-vault.rb +10 -2
  26. data/lib/chef-vault/actor.rb +149 -0
  27. data/lib/chef-vault/certificate.rb +1 -1
  28. data/lib/chef-vault/chef_api.rb +39 -0
  29. data/lib/chef-vault/item.rb +57 -71
  30. data/lib/chef-vault/item_keys.rb +14 -9
  31. data/lib/chef-vault/user.rb +1 -1
  32. data/lib/chef-vault/version.rb +1 -1
  33. data/lib/chef/knife/vault_base.rb +5 -2
  34. data/lib/chef/knife/{encrypt_delete.rb → vault_clients.rb} +6 -12
  35. data/lib/chef/knife/vault_create.rb +9 -1
  36. data/lib/chef/knife/vault_remove.rb +9 -1
  37. data/lib/chef/knife/vault_rotate_all_keys.rb +1 -1
  38. data/lib/chef/knife/vault_show.rb +4 -4
  39. data/lib/chef/knife/vault_update.rb +13 -5
  40. data/spec/chef-vault/actor_spec.rb +247 -0
  41. data/spec/chef-vault/certificate_spec.rb +2 -9
  42. data/spec/chef-vault/chef_api_spec.rb +39 -0
  43. data/spec/chef-vault/item_keys_spec.rb +52 -0
  44. data/spec/chef-vault/item_spec.rb +139 -85
  45. data/spec/chef-vault/user_spec.rb +2 -9
  46. data/spec/spec_helper.rb +1 -0
  47. metadata +36 -42
  48. data/CONTRIBUTING.md +0 -118
  49. data/lib/chef-vault/chef_patch/api_client.rb +0 -45
  50. data/lib/chef-vault/chef_patch/user.rb +0 -33
  51. data/lib/chef/knife/decrypt.rb +0 -32
  52. data/lib/chef/knife/encrypt_create.rb +0 -51
  53. data/lib/chef/knife/encrypt_remove.rb +0 -42
  54. data/lib/chef/knife/encrypt_rotate_keys.rb +0 -32
  55. data/lib/chef/knife/encrypt_update.rb +0 -51
  56. data/lib/chef/knife/mixin/compat.rb +0 -33
  57. data/lib/chef/knife/vault_decrypt.rb +0 -58
@@ -14,11 +14,13 @@
14
14
  # limitations under the License.
15
15
 
16
16
  require "chef/knife/vault_base"
17
+ require "chef/knife/vault_clients"
17
18
 
18
19
  class Chef
19
20
  class Knife
20
21
  class VaultRemove < Knife
21
22
  include Chef::Knife::VaultBase
23
+ include Chef::Knife::VaultClients
22
24
 
23
25
  banner "knife vault remove VAULT ITEM VALUES (options)"
24
26
 
@@ -27,6 +29,11 @@ class Chef
27
29
  :long => "--search SEARCH",
28
30
  :description => "Chef SOLR search for clients"
29
31
 
32
+ option :clients,
33
+ :short => "-C CLIENTS",
34
+ :long => "--clients CLIENTS",
35
+ :description => "Chef clients to be added as clients"
36
+
30
37
  option :admins,
31
38
  :short => "-A ADMINS",
32
39
  :long => "--admins ADMINS",
@@ -47,7 +54,7 @@ class Chef
47
54
 
48
55
  set_mode(config[:vault_mode])
49
56
 
50
- if vault && item && ((values || json_file) || (search || admins))
57
+ if vault && item && ((values || json_file) || (search || clients || admins))
51
58
  begin
52
59
  vault_item = ChefVault::Item.load(vault, item)
53
60
  remove_items = []
@@ -69,6 +76,7 @@ class Chef
69
76
  end
70
77
 
71
78
  vault_item.clients(search, :delete) if search
79
+ vault_item.clients(clients, :delete) if clients
72
80
  vault_item.admins(admins, :delete) if admins
73
81
 
74
82
  vault_item.rotate_keys!(clean_unknown_clients)
@@ -52,7 +52,7 @@ class Chef
52
52
  end
53
53
 
54
54
  def rotate_vault_item_keys(vault, item, clean_unknown_clients)
55
- $stdout.puts "Rotating keys for: #{vault} #{item}"
55
+ ui.info "Rotating keys for: #{vault} #{item}"
56
56
  ChefVault::Item.load(vault, item).rotate_keys!(clean_unknown_clients)
57
57
  end
58
58
  end
@@ -58,13 +58,13 @@ class Chef
58
58
  when "search"
59
59
  extra_data["search_query"] = vault_item.search
60
60
  when "admins"
61
- extra_data["admins"] = vault_item.admins
61
+ extra_data["admins"] = vault_item.get_admins
62
62
  when "clients"
63
- extra_data["clients"] = vault_item.clients
63
+ extra_data["clients"] = vault_item.get_clients
64
64
  when "all"
65
65
  extra_data["search_query"] = vault_item.search
66
- extra_data["admins"] = vault_item.admins
67
- extra_data["clients"] = vault_item.clients
66
+ extra_data["admins"] = vault_item.get_admins
67
+ extra_data["clients"] = vault_item.get_clients
68
68
  end
69
69
  end
70
70
 
@@ -15,12 +15,14 @@
15
15
 
16
16
  require "chef/knife/vault_base"
17
17
  require "chef/knife/vault_admins"
18
+ require "chef/knife/vault_clients"
18
19
 
19
20
  class Chef
20
21
  class Knife
21
22
  class VaultUpdate < Knife
22
23
  include Chef::Knife::VaultBase
23
24
  include Chef::Knife::VaultAdmins
25
+ include Chef::Knife::VaultClients
24
26
 
25
27
  banner "knife vault update VAULT ITEM VALUES (options)"
26
28
 
@@ -29,6 +31,11 @@ class Chef
29
31
  :long => "--search SEARCH",
30
32
  :description => "Chef SOLR search for clients"
31
33
 
34
+ option :clients,
35
+ :short => "-C CLIENTS",
36
+ :long => "--clients CLIENTS",
37
+ :description => "Chef clients to be added as clients"
38
+
32
39
  option :admins,
33
40
  :short => "-A ADMINS",
34
41
  :long => "--admins ADMINS",
@@ -58,21 +65,22 @@ class Chef
58
65
 
59
66
  set_mode(config[:vault_mode])
60
67
 
61
- if vault && item && ((values || json_file || file) || (search || admins))
68
+ if vault && item && ((values || json_file || file) || (search || clients || admins))
62
69
  begin
63
70
  vault_item = ChefVault::Item.load(vault, item)
64
71
 
65
72
  # Keys management first
66
73
  if clean
67
- clients = vault_item.clients().clone().sort()
68
- clients.each do |client|
69
- $stdout.puts "Deleting #{client}"
70
- vault_item.keys.delete(client, "clients")
74
+ vault_clients = vault_item.get_clients.clone().sort()
75
+ vault_clients.each do |client|
76
+ ui.info "Deleting #{client}"
77
+ vault_item.delete_client(client)
71
78
  end
72
79
  end
73
80
 
74
81
  vault_item.search(search) if search
75
82
  vault_item.clients(search) if search
83
+ vault_item.clients(clients) if clients
76
84
  vault_item.admins(admins) if admins
77
85
 
78
86
  # Save only the keys if no value is provided, otherwise save the item
@@ -0,0 +1,247 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe ChefVault::Actor do
4
+ let(:actor_name) { "actor" }
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
8
+
9
+ let(:key_response) do
10
+ {
11
+ "name" => "default",
12
+ "public_key" => "-----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",
13
+ "expiration_date" => "infinity",
14
+ }
15
+ end
16
+
17
+ let(:http_response_code) do
18
+ "404"
19
+ end
20
+
21
+ let(:http_error) do
22
+ http_response = double("http error")
23
+ allow(http_response).to receive(:code).and_return(http_response_code)
24
+ Net::HTTPServerException.new("http error message", http_response)
25
+ end
26
+
27
+ let(:api) { double("api") }
28
+
29
+ subject(:chef_key) { described_class.new(actor_type, actor_name) }
30
+
31
+ describe "#new" do
32
+ context "when something besides 'clients' or 'users' is passed" do
33
+ let(:actor_type) { "charmander" }
34
+ it "throws an error" do
35
+ expect { described_class.new("charmander", actor_name) }.to raise_error(RuntimeError)
36
+ end
37
+ end
38
+
39
+ context "when 'clients' is passed" do
40
+ it "requests a client key" do
41
+ expect_any_instance_of(described_class).to receive(:get_client_key)
42
+ described_class.new("clients", actor_name).key
43
+ end
44
+ end
45
+
46
+ context "when 'admins' is passed" do
47
+ it "requests a admin key" do
48
+ expect_any_instance_of(described_class).to receive(:get_admin_key)
49
+ described_class.new("admins", actor_name).key
50
+ end
51
+ end
52
+ end
53
+
54
+ shared_examples_for "get_key_handling" do
55
+ context "when the default key exists for the requested client" do
56
+ it "sets up a valid key" do
57
+ expect(chef_key).to receive(:get_key).with(request_actor_type).and_return(public_key_string)
58
+ expect(chef_key.send(method)).to eq(public_key_string)
59
+ end
60
+ end
61
+
62
+ context "when get_key returns an http error" do
63
+ before do
64
+ allow(chef_key).to receive(:get_key).with(request_actor_type).and_raise(http_error)
65
+ end
66
+
67
+ context "when the error code is not 404 or 403" do
68
+ let(:http_response_code) { "500" }
69
+
70
+ it "raises the original error" do
71
+ expect { chef_key.send(method) }.to raise_error(http_error)
72
+ end
73
+ end
74
+
75
+ context "when the error code is 403" do
76
+ let(:http_response_code) { "403" }
77
+
78
+ it "prints information for the user to resolve the issue and raises the original error" do
79
+ expect(chef_key).to receive(:print_forbidden_error)
80
+ expect { chef_key.send(method) }.to raise_error(http_error)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#get_client_key" do
87
+ let(:request_actor_type) { "clients" }
88
+ let(:actor_type) { "clients" }
89
+ let(:method) { :get_client_key }
90
+
91
+ it_should_behave_like "get_key_handling"
92
+
93
+ context "when get_key returns an http error" do
94
+ before do
95
+ allow(chef_key).to receive(:get_key).with(actor_type).and_raise(http_error)
96
+ end
97
+
98
+ context "when the error code is 404" do
99
+ let(:http_response_code) { "404" }
100
+
101
+ it "raises ChefVault::Exceptions::ClientNotFound" do
102
+ expect { chef_key.get_client_key }.to raise_error(ChefVault::Exceptions::ClientNotFound)
103
+ end
104
+ end
105
+ end
106
+ end # get_client_key
107
+
108
+ describe "#get_admin_key" do
109
+ let(:request_actor_type) { "users" }
110
+ let(:actor_type) { "admins" }
111
+ let(:method) { :get_admin_key }
112
+
113
+ it_should_behave_like "get_key_handling"
114
+
115
+ context "when the first get_key for users returns an http error" do
116
+ before do
117
+ allow(chef_key).to receive(:get_key).with(request_actor_type).and_raise(http_error)
118
+ end
119
+
120
+ context "when the error code from the users get is a 404" do
121
+ let(:http_response_code) { "404" }
122
+
123
+ context "when the second get_key for clients returns an http error" do
124
+
125
+ let(:http_error_2) do
126
+ http_response = double("http error")
127
+ allow(http_response).to receive(:code).and_return(http_response_code_2)
128
+ Net::HTTPServerException.new("http error message", http_response)
129
+ end
130
+
131
+ before do
132
+ allow(chef_key).to receive(:get_key).with("clients").and_raise(http_error_2)
133
+ end
134
+
135
+ context "when it is a 404" do
136
+ let(:http_response_code_2) { "404" }
137
+
138
+ it "rasies ChefVault::Exceptions::AdminNotFound" do
139
+ expect { chef_key.get_admin_key }.to raise_error(ChefVault::Exceptions::AdminNotFound)
140
+ end
141
+ end
142
+
143
+ context "when it is a 403" do
144
+ let(:http_response_code_2) { "403" }
145
+
146
+ it "raises the original error" do
147
+ expect { chef_key.get_admin_key }.to raise_error(http_error_2)
148
+ end
149
+ end
150
+
151
+ context "when it is not a 404" do
152
+ let(:http_response_code_2) { "500" }
153
+
154
+ it "raises the original error" do
155
+ expect { chef_key.get_admin_key }.to raise_error(http_error_2)
156
+ end
157
+ end
158
+ end # when the second get_key for clients returns an http error
159
+
160
+ context "when the second get_key for clients exists with the same name as the admin requested" do
161
+ it "strangely returns the client key as an admin key" do
162
+ expect(chef_key).to receive(:get_key).with(request_actor_type).and_return(public_key_string)
163
+ expect(chef_key.send(method)).to eq(public_key_string)
164
+ end
165
+ end
166
+ end # when the first get_key for users returns an http erro
167
+ end
168
+ end # get_admin_key
169
+
170
+ describe "#get_key" do
171
+
172
+ shared_examples_for "a properly retrieved and error handled key fetch" do
173
+ # mock out the API
174
+ before do
175
+ allow(chef_key).to receive(:api).and_return(api)
176
+ [:rest_v0, :rest_v1, :org_scoped_rest_v0, :org_scoped_rest_v1].each do |method|
177
+ allow(api).to receive(method)
178
+ end
179
+ end
180
+
181
+ context "when keys/default returns 200 for org scoped endpoint" do
182
+ before do
183
+ allow(api.org_scoped_rest_v1).to receive(:get).with("#{request_actor_type}/#{actor_name}/keys/default").and_return(key_response)
184
+ end
185
+
186
+ it "returns the public_key" do
187
+ expect(chef_key.get_key(request_actor_type)).to eql(public_key_string)
188
+ end
189
+
190
+ it "hits the proper endpoint" do
191
+ expect(api.org_scoped_rest_v1).to receive(:get).with("#{request_actor_type}/#{actor_name}/keys/default")
192
+ chef_key.get_key(request_actor_type)
193
+ end
194
+ end
195
+
196
+ context "when a 500 is returned" do
197
+ let(:http_response_code) { "500" }
198
+ before do
199
+ allow(api.org_scoped_rest_v1).to receive(:get).with("#{request_actor_type}/#{actor_name}/keys/default").and_raise(http_error)
200
+ end
201
+
202
+ it "raises the http error" do
203
+ expect { chef_key.get_key(request_actor_type) }.to raise_error(http_error)
204
+ end
205
+ end
206
+
207
+ context "when keys/default returns 404" do
208
+ let(:http_response_code) { "404" }
209
+ let(:chef_object) { double("chef object") }
210
+
211
+ before do
212
+ allow(api.org_scoped_rest_v1).to receive(:get).with("#{request_actor_type}/#{actor_name}/keys/default").and_raise(http_error)
213
+ allow(chef_object_type).to receive(:load).with(actor_name).and_return(chef_object)
214
+ allow(chef_object).to receive(:public_key).and_return(public_key_string)
215
+ end
216
+
217
+ it "tries to load the object via Chef::<object>_v1" do
218
+ expect(chef_object_type).to receive(:load).with(actor_name)
219
+ chef_key.get_key(request_actor_type)
220
+ end
221
+
222
+ context "when the Chef::<object>_v1 object loads properly" do
223
+ it "returns the public key" do
224
+ expect(chef_key.get_key(request_actor_type)).to eql(public_key_string)
225
+ end
226
+ end
227
+ end
228
+ end # shared_examples_for
229
+
230
+ context "when a client is passed" do
231
+ let(:request_actor_type) { "clients" }
232
+ let(:actor_type) { "clients" }
233
+ let(:chef_object_type) { Chef::ApiClient }
234
+
235
+ it_behaves_like "a properly retrieved and error handled key fetch"
236
+ end
237
+
238
+ context "when an admin is passed" do
239
+ let(:request_actor_type) { "users" }
240
+ let(:actor_type) { "admins" }
241
+ let(:chef_object_type) { Chef::User }
242
+
243
+ it_behaves_like "a properly retrieved and error handled key fetch"
244
+ end
245
+
246
+ end
247
+ end
@@ -6,12 +6,6 @@ RSpec.describe ChefVault::Certificate do
6
6
  allow(ChefVault::Item).to receive(:load).with("foo", "bar") { item }
7
7
  allow(item).to receive(:[]).with("id") { "bar" }
8
8
  allow(item).to receive(:[]).with("contents") { "baz" }
9
- @orig_stdout = $stdout
10
- $stdout = File.open(File::NULL, "w")
11
- end
12
-
13
- after do
14
- $stdout = @orig_stdout
15
9
  end
16
10
 
17
11
  describe "#new" do
@@ -30,9 +24,8 @@ RSpec.describe ChefVault::Certificate do
30
24
 
31
25
  describe "decrypt_contents" do
32
26
  it "echoes warning" do
33
- expect { cert.decrypt_contents }
34
- .to output("WARNING: This method is deprecated, please switch to item['value'] calls\n")
35
- .to_stdout
27
+ expect(ChefVault::Log).to receive(:warn).with("This method is deprecated, please switch to item['value'] calls")
28
+ cert.decrypt_contents
36
29
  end
37
30
 
38
31
  it "returns items contents" do
@@ -0,0 +1,39 @@
1
+ RSpec.describe ChefVault::ChefApi do
2
+ let(:root_url) { "https://localhost" }
3
+ let(:scoped_url) { "https://localhost/organizations/fakeorg" }
4
+ let(:api_v0_hash) { { :api_version => "0" } }
5
+ let(:api_v1_hash) { { :api_version => "1" } }
6
+
7
+ before do
8
+ Chef::Config[:chef_server_root] = root_url
9
+ Chef::Config[:chef_server_url] = scoped_url
10
+ end
11
+
12
+ describe "#rest_v0" do
13
+ it "returns an instance of Chef::ServerAPI set to use API version 0 scoped to root" do
14
+ expect(Chef::ServerAPI).to receive(:new).with(root_url, api_v0_hash)
15
+ subject.rest_v0
16
+ end
17
+ end
18
+
19
+ describe "#rest_v1" do
20
+ it "returns an instance of Chef::ServerAPI set to use API version 0 scoped to root" do
21
+ expect(Chef::ServerAPI).to receive(:new).with(root_url, api_v1_hash)
22
+ subject.rest_v1
23
+ end
24
+ end
25
+
26
+ describe "#org_scoped_rest_v0" do
27
+ it "returns an instance of Chef::ServerAPI set to use API version 0 scoped to root" do
28
+ expect(Chef::ServerAPI).to receive(:new).with(scoped_url, api_v0_hash)
29
+ subject.org_scoped_rest_v0
30
+ end
31
+ end
32
+
33
+ describe "#org_scoped_rest_v1" do
34
+ it "returns an instance of Chef::ServerAPI set to use API version 0 scoped to root" do
35
+ expect(Chef::ServerAPI).to receive(:new).with(scoped_url, api_v1_hash)
36
+ subject.org_scoped_rest_v1
37
+ end
38
+ end
39
+ end
@@ -1,6 +1,7 @@
1
1
  RSpec.describe ChefVault::ItemKeys do
2
2
  describe "#new" do
3
3
  let(:keys) { ChefVault::ItemKeys.new("foo", "bar") }
4
+ let(:shared_secret) { "super_secret" }
4
5
 
5
6
  it "'foo' is assigned to @data_bag" do
6
7
  expect(keys.data_bag).to eq "foo"
@@ -18,6 +19,57 @@ RSpec.describe ChefVault::ItemKeys do
18
19
  expect(keys["admins"]).to eq []
19
20
  end
20
21
 
22
+ describe "key mgmt operations" do
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
26
+
27
+ shared_examples_for "proper key management" do
28
+ let(:chef_key) { ChefVault::Actor.new(type, name) }
29
+ before do
30
+ allow(chef_key).to receive(:key) { public_key_string }
31
+ keys.add(chef_key, shared_secret)
32
+ end
33
+
34
+ describe "#add" do
35
+ after do
36
+ keys.delete(chef_key)
37
+ end
38
+
39
+ it "stores the encoded key in the data bag item under the actor's name and the name in the raw data" do
40
+ expect(described_class).to receive(:encode_key).with(public_key_string, shared_secret).and_return("encrypted_result")
41
+ keys.add(chef_key, shared_secret)
42
+ expect(keys[name]).to eq("encrypted_result")
43
+ expect(keys[type].include?(name)).to eq(true)
44
+ end
45
+ end
46
+
47
+ describe "#delete" do
48
+ before do
49
+ keys.add(chef_key, shared_secret)
50
+ end
51
+
52
+ it "removes the actor's name from the data bag and from the array for the actor's type" do
53
+ keys.delete(chef_key)
54
+ expect(keys.has_key?(chef_key.name)).to eq(false)
55
+ expect(keys[type].include?(name)).to eq(false)
56
+ end
57
+ end
58
+ end
59
+
60
+ context "when a client is added" do
61
+ let(:name) { "client_name" }
62
+ let(:type) { "clients" }
63
+ it_should_behave_like "proper key management"
64
+ end
65
+
66
+ context "when a admin is added" do
67
+ let(:name) { "admin_name" }
68
+ let(:type) { "admins" }
69
+ it_should_behave_like "proper key management"
70
+ end
71
+ end
72
+
21
73
  context "when running with chef-solo" do
22
74
  describe "#find_solo_path" do
23
75
  context "when data_bag_path is an array" do