chef-vault 2.4.0 → 2.5.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/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +97 -0
- data/Changelog.md +34 -0
- data/DEMO.md +13 -6
- data/README.md +9 -4
- data/Rakefile +47 -10
- data/THEORY.md +361 -0
- data/bin/chef-vault +5 -5
- data/chef-vault.gemspec +11 -2
- data/features/clean_unknown_clients.feature +28 -3
- data/features/step_definitions/chef-databag.rb +5 -0
- data/features/step_definitions/chef-repo.rb +46 -8
- data/features/step_definitions/chef-vault.rb +69 -15
- data/features/support/env.rb +2 -2
- data/features/vault_create.feature +54 -0
- data/features/vault_list.feature +26 -0
- data/features/vault_show.feature +46 -0
- data/features/vault_update.feature +17 -0
- data/features/wrong_private_key.feature +14 -0
- data/lib/chef-vault.rb +0 -1
- data/lib/chef-vault/certificate.rb +1 -1
- data/lib/chef-vault/chef_patch/api_client.rb +1 -1
- data/lib/chef-vault/chef_patch/user.rb +1 -1
- data/lib/chef-vault/exceptions.rb +33 -12
- data/lib/chef-vault/item.rb +262 -209
- data/lib/chef-vault/item_keys.rb +90 -88
- data/lib/chef-vault/user.rb +1 -1
- data/lib/chef-vault/version.rb +2 -2
- data/lib/chef/knife/decrypt.rb +1 -2
- data/lib/chef/knife/encrypt_create.rb +1 -2
- data/lib/chef/knife/encrypt_delete.rb +1 -2
- data/lib/chef/knife/encrypt_remove.rb +1 -2
- data/lib/chef/knife/encrypt_rotate_keys.rb +1 -2
- data/lib/chef/knife/encrypt_update.rb +1 -2
- data/lib/chef/knife/mixin/compat.rb +3 -3
- data/lib/chef/knife/mixin/helper.rb +6 -8
- data/lib/chef/knife/vault_admins.rb +1 -2
- data/lib/chef/knife/vault_base.rb +2 -2
- data/lib/chef/knife/vault_create.rb +3 -4
- data/lib/chef/knife/vault_decrypt.rb +3 -4
- data/lib/chef/knife/vault_delete.rb +2 -4
- data/lib/chef/knife/vault_download.rb +1 -2
- data/lib/chef/knife/vault_edit.rb +3 -6
- data/lib/chef/knife/vault_list.rb +53 -0
- data/lib/chef/knife/vault_refresh.rb +1 -2
- data/lib/chef/knife/vault_remove.rb +3 -7
- data/lib/chef/knife/vault_rotate_all_keys.rb +2 -4
- data/lib/chef/knife/vault_rotate_keys.rb +2 -4
- data/lib/chef/knife/vault_show.rb +4 -5
- data/lib/chef/knife/vault_update.rb +7 -9
- data/spec/chef-vault/certificate_spec.rb +0 -2
- data/spec/chef-vault/item_spec.rb +77 -1
- data/spec/chef-vault/user_spec.rb +0 -2
- data/spec/chef-vault_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -3
- metadata +38 -14
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultDelete class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultDelete < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault delete VAULT ITEM (options)"
|
@@ -34,8 +33,7 @@ class Chef
|
|
34
33
|
begin
|
35
34
|
ChefVault::Item.load(vault, item).destroy
|
36
35
|
rescue ChefVault::Exceptions::KeysNotFound,
|
37
|
-
|
38
|
-
|
36
|
+
ChefVault::Exceptions::ItemNotFound
|
39
37
|
raise ChefVault::Exceptions::ItemNotFound,
|
40
38
|
"#{vault}/#{item} not found."
|
41
39
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultDownload class
|
2
|
-
# Copyright 2014, Nordstrom, Inc.
|
2
|
+
# Copyright 2014-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultDownload < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault download VAULT ITEM PATH (options)"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultEdit class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultEdit < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault edit VAULT ITEM (options)"
|
@@ -43,7 +42,7 @@ class Chef
|
|
43
42
|
updated_vault_json = edit_data(filtered_vault_data)
|
44
43
|
|
45
44
|
# Clean out contents of existing local vault_item
|
46
|
-
vault_item.raw_data.each do |key,
|
45
|
+
vault_item.raw_data.each do |key, _|
|
47
46
|
vault_item.remove(key) unless key == 'id'
|
48
47
|
end
|
49
48
|
|
@@ -54,8 +53,7 @@ class Chef
|
|
54
53
|
|
55
54
|
vault_item.save
|
56
55
|
rescue ChefVault::Exceptions::KeysNotFound,
|
57
|
-
|
58
|
-
|
56
|
+
ChefVault::Exceptions::ItemNotFound
|
59
57
|
raise ChefVault::Exceptions::ItemNotFound,
|
60
58
|
"#{vault}/#{item} does not exist, "\
|
61
59
|
"use 'knife vault create' to create."
|
@@ -67,4 +65,3 @@ class Chef
|
|
67
65
|
end
|
68
66
|
end
|
69
67
|
end
|
70
|
-
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Description: Chef-Vault VaultShow class
|
2
|
+
# Copyright 2013, Nordstrom, Inc.
|
3
|
+
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'chef/knife/vault_base'
|
17
|
+
|
18
|
+
class Chef
|
19
|
+
class Knife
|
20
|
+
class VaultList < Knife
|
21
|
+
include Chef::Knife::VaultBase
|
22
|
+
|
23
|
+
banner "knife vault list (options)"
|
24
|
+
|
25
|
+
def run
|
26
|
+
vaultbags = []
|
27
|
+
# iterate over all the data bags
|
28
|
+
bags = Chef::DataBag.list
|
29
|
+
bags.each_key do |bagname|
|
30
|
+
vaultbags.push(bagname) if bag_is_vault?(bagname)
|
31
|
+
end
|
32
|
+
output vaultbags.join("\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def bag_is_vault?(bagname)
|
38
|
+
bag = Chef::DataBag.load(bagname)
|
39
|
+
# vaults have at even number of keys >= 2
|
40
|
+
return false unless bag.keys.size >= 2 && 0 == bag.keys.size % 2
|
41
|
+
# partition into those that end in _keys
|
42
|
+
keylike, notkeylike = bag.keys.partition { |k| k =~ /_keys$/ }
|
43
|
+
# there must be an equal number of keyline and not-keylike items
|
44
|
+
return false unless keylike.size == notkeylike.size
|
45
|
+
# strip the _keys suffix and check if the sets match
|
46
|
+
keylike.map! { |k| k.gsub(/_keys$/, '') }
|
47
|
+
return false unless keylike.sort == notkeylike.sort
|
48
|
+
# it's (probably) a vault
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultReapply class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultRefresh < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault refresh VAULT ITEM"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultRemove class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultRemove < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault remove VAULT ITEM VALUES (options)"
|
@@ -56,13 +55,11 @@ class Chef
|
|
56
55
|
if values || json_file
|
57
56
|
begin
|
58
57
|
json = JSON.parse(values)
|
59
|
-
json.each do |key,
|
58
|
+
json.each do |key, _|
|
60
59
|
remove_items << key
|
61
60
|
end
|
62
61
|
rescue JSON::ParserError
|
63
62
|
remove_items = values.split(",")
|
64
|
-
rescue Exception => e
|
65
|
-
raise e
|
66
63
|
end
|
67
64
|
|
68
65
|
remove_items.each do |key|
|
@@ -76,8 +73,7 @@ class Chef
|
|
76
73
|
|
77
74
|
vault_item.rotate_keys!(clean_unknown_clients)
|
78
75
|
rescue ChefVault::Exceptions::KeysNotFound,
|
79
|
-
|
80
|
-
|
76
|
+
ChefVault::Exceptions::ItemNotFound
|
81
77
|
raise ChefVault::Exceptions::ItemNotFound,
|
82
78
|
"#{vault}/#{item} does not exist, "\
|
83
79
|
"use 'knife vault create' to create."
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultRotateAllKeys class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultRotateAllKeys < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault rotate all keys"
|
@@ -47,9 +46,8 @@ class Chef
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def vault_items(vault)
|
50
|
-
Chef::DataBag.load(vault).keys.
|
49
|
+
Chef::DataBag.load(vault).keys.each_with_object([]) do |key, array|
|
51
50
|
array << key.sub('_keys', '') if key.match(/.+_keys$/)
|
52
|
-
array
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultRotateKeys class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultRotateKeys < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault rotate keys VAULT ITEM (options)"
|
@@ -39,8 +38,7 @@ class Chef
|
|
39
38
|
item = ChefVault::Item.load(vault, item)
|
40
39
|
item.rotate_keys!(clean_unknown_clients)
|
41
40
|
rescue ChefVault::Exceptions::KeysNotFound,
|
42
|
-
|
43
|
-
|
41
|
+
ChefVault::Exceptions::ItemNotFound
|
44
42
|
raise ChefVault::Exceptions::ItemNotFound,
|
45
43
|
"#{vault}/#{item} does not exist, "\
|
46
44
|
"use 'knife vault create' to create."
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultShow class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
2
|
+
# Copyright 2013-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -18,7 +18,6 @@ require 'chef/knife/vault_base'
|
|
18
18
|
class Chef
|
19
19
|
class Knife
|
20
20
|
class VaultShow < Knife
|
21
|
-
|
22
21
|
include Chef::Knife::VaultBase
|
23
22
|
|
24
23
|
banner "knife vault show VAULT ITEM [VALUES] (options)"
|
@@ -67,14 +66,14 @@ class Chef
|
|
67
66
|
end
|
68
67
|
|
69
68
|
if values
|
70
|
-
included_values = %
|
69
|
+
included_values = %w(id)
|
71
70
|
|
72
71
|
values.split(",").each do |value|
|
73
72
|
value.strip! # remove white space
|
74
73
|
included_values << value
|
75
74
|
end
|
76
75
|
|
77
|
-
filtered_data = Hash[vault_item.raw_data.find_all{|k,
|
76
|
+
filtered_data = Hash[vault_item.raw_data.find_all{|k, _| included_values.include?(k)}]
|
78
77
|
|
79
78
|
output_data = filtered_data.merge(extra_data)
|
80
79
|
else
|
@@ -82,7 +81,7 @@ class Chef
|
|
82
81
|
|
83
82
|
output_data = all_data.merge(extra_data)
|
84
83
|
end
|
85
|
-
|
84
|
+
output(output_data)
|
86
85
|
end
|
87
86
|
end
|
88
87
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Description: Chef-Vault VaultUpdate class
|
2
|
-
# Copyright 2014, Nordstrom, Inc.
|
2
|
+
# Copyright 2014-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -19,7 +19,6 @@ require 'chef/knife/vault_admins'
|
|
19
19
|
class Chef
|
20
20
|
class Knife
|
21
21
|
class VaultUpdate < Knife
|
22
|
-
|
23
22
|
include Chef::Knife::VaultBase
|
24
23
|
include Chef::Knife::VaultAdmins
|
25
24
|
|
@@ -73,11 +72,11 @@ class Chef
|
|
73
72
|
end
|
74
73
|
|
75
74
|
if clean
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
clients = vault_item.clients().clone().sort()
|
76
|
+
clients.each do |client|
|
77
|
+
puts "Deleting #{client}"
|
78
|
+
vault_item.keys.delete(client, "clients")
|
79
|
+
end
|
81
80
|
end
|
82
81
|
vault_item.search(search) if search
|
83
82
|
vault_item.clients(search) if search
|
@@ -85,8 +84,7 @@ class Chef
|
|
85
84
|
|
86
85
|
vault_item.save
|
87
86
|
rescue ChefVault::Exceptions::KeysNotFound,
|
88
|
-
|
89
|
-
|
87
|
+
ChefVault::Exceptions::ItemNotFound
|
90
88
|
raise ChefVault::Exceptions::ItemNotFound,
|
91
89
|
"#{vault}/#{item} does not exist, "\
|
92
90
|
"use 'knife vault create' to create."
|
@@ -21,7 +21,6 @@ RSpec.describe ChefVault::Certificate do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
describe 'decrypt_contents' do
|
24
|
-
|
25
24
|
it 'echoes warning' do
|
26
25
|
expect(STDOUT).to receive(:puts).with("WARNING: This method is deprecated, please switch to item['value'] calls")
|
27
26
|
cert.decrypt_contents
|
@@ -33,5 +32,4 @@ RSpec.describe ChefVault::Certificate do
|
|
33
32
|
expect(cert.decrypt_contents).to eq 'baz'
|
34
33
|
end
|
35
34
|
end
|
36
|
-
|
37
35
|
end
|
@@ -1,8 +1,59 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'rspec/core/shared_context'
|
3
|
+
|
4
|
+
# it turns out that simulating a node that doesn't have a public
|
5
|
+
# key is not a simple thing
|
6
|
+
module BorkedNodeWithoutPublicKey
|
7
|
+
extend RSpec::Core::SharedContext
|
8
|
+
|
9
|
+
before do
|
10
|
+
# a node 'foo' with a public key
|
11
|
+
node_foo = double('chef node foo')
|
12
|
+
allow(node_foo).to receive(:name).and_return('foo')
|
13
|
+
client_foo = double('chef apiclient foo')
|
14
|
+
allow(client_foo).to receive(:name).and_return('foo')
|
15
|
+
privkey = OpenSSL::PKey::RSA.new(1024)
|
16
|
+
pubkey = privkey.public_key
|
17
|
+
allow(client_foo).to receive(:public_key).and_return(pubkey.to_pem)
|
18
|
+
# a node 'bar' without a public key
|
19
|
+
node_bar = double('chef node bar')
|
20
|
+
allow(node_bar).to receive(:name).and_return('bar')
|
21
|
+
# fake out searches to return both of our nodes
|
22
|
+
query_result = double('chef search results')
|
23
|
+
allow(query_result)
|
24
|
+
.to receive(:search)
|
25
|
+
.with(Symbol, String)
|
26
|
+
.and_return([[node_foo, node_bar]])
|
27
|
+
allow(Chef::Search::Query)
|
28
|
+
.to receive(:new)
|
29
|
+
.and_return(query_result)
|
30
|
+
# create a new vault item
|
31
|
+
@vaultitem = ChefVault::Item.new('foo', 'bar')
|
32
|
+
@vaultitem['foo'] = 'bar'
|
33
|
+
# make the vault item return the apiclient for foo but raise for bar
|
34
|
+
allow(@vaultitem).to receive(:load_client).with('foo')
|
35
|
+
.and_return(client_foo)
|
36
|
+
allow(@vaultitem).to receive(:load_client).with('bar')
|
37
|
+
.and_raise(ChefVault::Exceptions::ClientNotFound)
|
38
|
+
# make the vault item fall back to 'load-admin-as-client' behaviour
|
39
|
+
http_response = double('http not found')
|
40
|
+
allow(http_response).to receive(:code).and_return('404')
|
41
|
+
http_not_found = Net::HTTPServerException.new('not found', http_response)
|
42
|
+
allow(ChefVault::ChefPatch::User)
|
43
|
+
.to receive(:load)
|
44
|
+
.with('foo')
|
45
|
+
.and_return(client_foo)
|
46
|
+
allow(ChefVault::ChefPatch::User)
|
47
|
+
.to receive(:load)
|
48
|
+
.with('bar')
|
49
|
+
.and_raise(http_not_found)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
1
53
|
RSpec.describe ChefVault::Item do
|
2
54
|
subject(:item) { ChefVault::Item.new("foo", "bar") }
|
3
55
|
|
4
56
|
describe '#new' do
|
5
|
-
|
6
57
|
it { should be_an_instance_of ChefVault::Item }
|
7
58
|
|
8
59
|
its(:keys) { should be_an_instance_of ChefVault::ItemKeys }
|
@@ -23,4 +74,29 @@ RSpec.describe ChefVault::Item do
|
|
23
74
|
specify { expect { item.save }.to raise_error }
|
24
75
|
end
|
25
76
|
end
|
77
|
+
|
78
|
+
describe '#clients' do
|
79
|
+
include BorkedNodeWithoutPublicKey
|
80
|
+
|
81
|
+
it 'should not blow up when search returns a node without a public key' do
|
82
|
+
# try to set clients when we know a node is missing a public key
|
83
|
+
# this should not die as of v2.4.1
|
84
|
+
expect { @vaultitem.clients('*:*') }.not_to raise_error
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should emit a warning if search returns a node without a public key' do
|
88
|
+
# it should however emit a warning that you have a borked node
|
89
|
+
expect { @vaultitem.clients('*:*') }
|
90
|
+
.to output(/node 'bar' has no private key; skipping/).to_stderr
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#admins' do
|
95
|
+
include BorkedNodeWithoutPublicKey
|
96
|
+
|
97
|
+
it 'should blow up if you try to use a node without a public key as an admin' do
|
98
|
+
expect { @vaultitem.admins('foo,bar') }
|
99
|
+
.to raise_error(ChefVault::Exceptions::AdminNotFound)
|
100
|
+
end
|
101
|
+
end
|
26
102
|
end
|
@@ -21,7 +21,6 @@ RSpec.describe ChefVault::User do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
describe 'decrypt_password' do
|
24
|
-
|
25
24
|
it 'echoes warning' do
|
26
25
|
expect(STDOUT).to receive(:puts).with("WARNING: This method is deprecated, please switch to item['value'] calls")
|
27
26
|
user.decrypt_password
|
@@ -32,5 +31,4 @@ RSpec.describe ChefVault::User do
|
|
32
31
|
expect(user.decrypt_password).to eq "baz"
|
33
32
|
end
|
34
33
|
end
|
35
|
-
|
36
34
|
end
|
data/spec/chef-vault_spec.rb
CHANGED