chef-encrypted-attributes 0.3.0 → 0.4.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +40 -4
- data/CONTRIBUTING.md +7 -6
- data/KNIFE.md +151 -0
- data/README.md +70 -192
- data/Rakefile +27 -14
- data/TESTING.md +18 -7
- data/TODO.md +2 -5
- data/lib/chef-encrypted-attributes.rb +7 -1
- data/lib/chef/encrypted_attribute.rb +282 -121
- data/lib/chef/encrypted_attribute/api.rb +521 -0
- data/lib/chef/encrypted_attribute/assertions.rb +16 -6
- data/lib/chef/encrypted_attribute/cache_lru.rb +54 -13
- data/lib/chef/encrypted_attribute/config.rb +198 -89
- data/lib/chef/encrypted_attribute/encrypted_mash.rb +127 -33
- data/lib/chef/encrypted_attribute/encrypted_mash/version0.rb +236 -48
- data/lib/chef/encrypted_attribute/encrypted_mash/version1.rb +249 -36
- data/lib/chef/encrypted_attribute/encrypted_mash/version2.rb +133 -19
- data/lib/chef/encrypted_attribute/exceptions.rb +19 -3
- data/lib/chef/encrypted_attribute/local_node.rb +15 -4
- data/lib/chef/encrypted_attribute/remote_clients.rb +33 -17
- data/lib/chef/encrypted_attribute/remote_node.rb +84 -29
- data/lib/chef/encrypted_attribute/remote_nodes.rb +62 -11
- data/lib/chef/encrypted_attribute/remote_users.rb +58 -19
- data/lib/chef/encrypted_attribute/search_helper.rb +214 -74
- data/lib/chef/encrypted_attribute/version.rb +3 -1
- data/lib/chef/encrypted_attributes.rb +20 -0
- data/lib/chef/knife/core/config.rb +4 -1
- data/lib/chef/knife/core/encrypted_attribute_base.rb +179 -0
- data/lib/chef/knife/core/encrypted_attribute_depends.rb +43 -0
- data/lib/chef/knife/core/encrypted_attribute_editor_options.rb +125 -61
- data/lib/chef/knife/encrypted_attribute_create.rb +51 -31
- data/lib/chef/knife/encrypted_attribute_delete.rb +32 -40
- data/lib/chef/knife/encrypted_attribute_edit.rb +51 -32
- data/lib/chef/knife/encrypted_attribute_show.rb +30 -55
- data/lib/chef/knife/encrypted_attribute_update.rb +43 -28
- data/spec/benchmark_helper.rb +2 -1
- data/spec/integration_helper.rb +1 -0
- data/spec/spec_helper.rb +21 -7
- metadata +75 -36
- metadata.gz.sig +1 -1
- data/API.md +0 -174
- data/INTERNAL.md +0 -166
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -18,23 +19,38 @@
|
|
18
19
|
|
19
20
|
class Chef
|
20
21
|
class EncryptedAttribute
|
21
|
-
|
22
|
+
# Exception raised when some requirements to use the encrypted attributes
|
23
|
+
# are not met.
|
22
24
|
class RequirementsFailure < StandardError; end
|
25
|
+
# Exception raised when the encrypted attribute format is unknown.
|
23
26
|
class UnsupportedEncryptedAttributeFormat < StandardError; end
|
27
|
+
# Exception raised when the encrypted attribute format is wrong.
|
24
28
|
class UnacceptableEncryptedAttributeFormat < StandardError; end
|
29
|
+
# Exception raised when there are decryption errors.
|
25
30
|
class DecryptionFailure < StandardError; end
|
31
|
+
# Exception raised when there are encryption errors.
|
26
32
|
class EncryptionFailure < StandardError; end
|
33
|
+
# Exception raised when there errors generating the HMAC.
|
27
34
|
class MessageAuthenticationFailure < StandardError; end
|
35
|
+
# Exception raised when the public key is wrong.
|
28
36
|
class InvalidPublicKey < StandardError; end
|
29
|
-
|
37
|
+
# Exception raised when the key is wrong.
|
38
|
+
class InvalidKey < StandardError; end
|
30
39
|
|
40
|
+
# Exception raised when you don't have enough privileges in the Chef Server
|
41
|
+
# to do what you intend. Usually happens when you try to read Client or Node
|
42
|
+
# keys without being admin.
|
31
43
|
class InsufficientPrivileges < StandardError; end
|
44
|
+
# Exception raised when the user does not exist.
|
32
45
|
class UserNotFound < StandardError; end
|
46
|
+
# Exception raised when the client does not exist.
|
33
47
|
class ClientNotFound < StandardError; end
|
34
48
|
|
49
|
+
# Exception raised for search errors.
|
35
50
|
class SearchFailure < StandardError; end
|
51
|
+
# Exception raised for search fatal errors.
|
36
52
|
class SearchFatalError < StandardError; end
|
53
|
+
# Exception raised when search keys are wrong.
|
37
54
|
class InvalidSearchKeys < StandardError; end
|
38
|
-
|
39
55
|
end
|
40
56
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -18,21 +19,31 @@
|
|
18
19
|
|
19
20
|
class Chef
|
20
21
|
class EncryptedAttribute
|
22
|
+
# Get name and keys from local Chef Node.
|
21
23
|
class LocalNode
|
22
|
-
|
23
|
-
#
|
24
|
+
# Gets the local node name.
|
25
|
+
#
|
26
|
+
# @return [String] local node name.
|
27
|
+
# @note currently not used
|
24
28
|
def name
|
25
29
|
Chef::Config[:node_name]
|
26
30
|
end
|
27
31
|
|
32
|
+
# Gets the local node key.
|
33
|
+
#
|
34
|
+
# The key has the private key and the public key embedded.
|
35
|
+
#
|
36
|
+
# @return [OpenSSL::PKey::RSA] the local node private and the public key.
|
28
37
|
def key
|
29
|
-
OpenSSL::PKey::RSA.new(open(Chef::Config[:client_key]).read
|
38
|
+
OpenSSL::PKey::RSA.new(open(Chef::Config[:client_key]).read)
|
30
39
|
end
|
31
40
|
|
41
|
+
# Gets the local node public key.
|
42
|
+
#
|
43
|
+
# @return [OpenSSL::PKey::RSA] the local node public key.
|
32
44
|
def public_key
|
33
45
|
key.public_key
|
34
46
|
end
|
35
|
-
|
36
47
|
end
|
37
48
|
end
|
38
49
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -24,37 +25,52 @@ require 'chef/encrypted_attribute/cache_lru'
|
|
24
25
|
|
25
26
|
class Chef
|
26
27
|
class EncryptedAttribute
|
28
|
+
# Search remote Chef Clients public keys.
|
27
29
|
class RemoteClients
|
28
30
|
extend ::Chef::EncryptedAttribute::SearchHelper
|
29
31
|
|
32
|
+
# Remote clients search results cache.
|
33
|
+
#
|
34
|
+
# You can disable it setting it's size to zero:
|
35
|
+
#
|
36
|
+
# ```ruby
|
37
|
+
# Chef::EncryptedAttribute::RemoteClients.cache.max_size(0)
|
38
|
+
# ```
|
39
|
+
#
|
40
|
+
# @return [CacheLru] Remote clients LRU cache.
|
30
41
|
def self.cache
|
31
42
|
@@cache ||= Chef::EncryptedAttribute::CacheLru.new
|
32
43
|
end
|
33
44
|
|
45
|
+
# Gets remote client public key.
|
46
|
+
#
|
47
|
+
# @param name [String] Chef client name.
|
48
|
+
# @return [String] Chef client public key as string.
|
49
|
+
# @raise [ClientNotFound] if client does not exist.
|
50
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
34
51
|
def self.get_public_key(name)
|
35
52
|
Chef::ApiClient.load(name).public_key
|
36
53
|
rescue Net::HTTPServerException => e
|
37
|
-
|
38
|
-
|
39
|
-
raise ClientNotFound, "Chef Client not found: \"#{name}\"."
|
40
|
-
else
|
41
|
-
raise e
|
42
|
-
end
|
54
|
+
raise e unless e.response.code == '404'
|
55
|
+
raise ClientNotFound, "Chef Client not found: #{name.inspect}."
|
43
56
|
end
|
44
57
|
|
45
|
-
|
58
|
+
# Searches for chef client public keys.
|
59
|
+
#
|
60
|
+
# @param search [Array<String>, String] search queries to perform, the
|
61
|
+
# query result will be *OR*-ed.
|
62
|
+
# @return [Array<String>] list of public keys.
|
63
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
64
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
65
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
66
|
+
def self.search_public_keys(search = '*:*', partial_search = true)
|
46
67
|
escaped_query = escape_query(search)
|
47
|
-
if cache.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
}, 1000, partial_search).map do |client|
|
53
|
-
client['public_key']
|
54
|
-
end.compact
|
55
|
-
end
|
68
|
+
return cache[escaped_query] if cache.key?(escaped_query)
|
69
|
+
cache[escaped_query] = search(
|
70
|
+
:client, search,
|
71
|
+
{ 'public_key' => %w(public_key) }, 1000, partial_search
|
72
|
+
).map { |client| client['public_key'] }.compact
|
56
73
|
end
|
57
|
-
|
58
74
|
end
|
59
75
|
end
|
60
76
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -22,55 +23,77 @@ require 'chef/encrypted_attribute/cache_lru'
|
|
22
23
|
|
23
24
|
class Chef
|
24
25
|
class EncryptedAttribute
|
26
|
+
# Remote Node object to read and save node attributes remotely.
|
25
27
|
class RemoteNode
|
26
28
|
include ::Chef::Mixin::ParamsValidate
|
27
29
|
include ::Chef::EncryptedAttribute::SearchHelper
|
28
30
|
|
31
|
+
# Remote Node object constructor.
|
32
|
+
#
|
33
|
+
# @param name [String] node name.
|
29
34
|
def initialize(name)
|
30
35
|
name(name)
|
31
36
|
end
|
32
37
|
|
38
|
+
# Remote node attribute values cache.
|
39
|
+
#
|
40
|
+
# It is disabled by default. You can enable it changing it's size:
|
41
|
+
#
|
42
|
+
# ```ruby
|
43
|
+
# Chef::EncryptedAttribute::RemoteNode.cache.max_size(1024)
|
44
|
+
# ```
|
45
|
+
#
|
46
|
+
# @return [CacheLru] node attributes LRU cache.
|
33
47
|
def self.cache
|
34
|
-
|
48
|
+
# disabled by default
|
49
|
+
@@cache ||= Chef::EncryptedAttribute::CacheLru.new(0)
|
35
50
|
end
|
36
51
|
|
37
|
-
|
52
|
+
# Read or set node name.
|
53
|
+
#
|
54
|
+
# @param arg [String] node name.
|
55
|
+
# @return [String] node name.
|
56
|
+
def name(arg = nil)
|
57
|
+
# TODO: clean the cache when changed?
|
38
58
|
set_or_return(
|
39
59
|
:name,
|
40
60
|
arg,
|
41
|
-
:
|
61
|
+
kind_of: String
|
42
62
|
)
|
43
63
|
end
|
44
64
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
65
|
+
# Loads a remote node attribute.
|
66
|
+
#
|
67
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
68
|
+
# @param partial_search [Boolean] whether to use partial search.
|
69
|
+
# @return [Mixed] node attribute value.
|
70
|
+
# @raise [ArgumentError] if the attribute path format is wrong.
|
71
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
72
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
73
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
74
|
+
def load_attribute(attr_ary, partial_search = true)
|
75
|
+
assert_attribute_array(attr_ary)
|
49
76
|
cache_key = cache_key(name, attr_ary)
|
50
|
-
if self.class.cache.
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
res = search(:node, "name:#{@name}", keys, 1, partial_search)
|
55
|
-
self.class.cache[cache_key] = if res.kind_of?(Array) and
|
56
|
-
res[0].kind_of?(Hash) and res[0].has_key?('value')
|
57
|
-
res[0]['value']
|
58
|
-
else
|
59
|
-
nil
|
60
|
-
end
|
61
|
-
end
|
77
|
+
return self.class.cache[cache_key] if self.class.cache.key?(cache_key)
|
78
|
+
keys = { 'value' => attr_ary }
|
79
|
+
res = search(:node, "name:#{@name}", keys, 1, partial_search)
|
80
|
+
self.class.cache[cache_key] = parse_search_result(res)
|
62
81
|
end
|
63
82
|
|
83
|
+
# Saves a remote node attribute.
|
84
|
+
#
|
85
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
86
|
+
# @param value [Mixed] node attribute value to set.
|
87
|
+
# @return [Mixed] node attribute value.
|
88
|
+
# @raise [ArgumentError] if the attribute path format is wrong.
|
64
89
|
def save_attribute(attr_ary, value)
|
65
|
-
|
66
|
-
raise ArgumentError, "#{self.class.to_s}##{__method__} attr_ary argument must be an array of strings. You passed #{attr_ary.inspect}."
|
67
|
-
end
|
90
|
+
assert_attribute_array(attr_ary)
|
68
91
|
cache_key = cache_key(name, attr_ary)
|
69
92
|
|
70
93
|
node = Chef::Node.load(name)
|
71
94
|
last = attr_ary.pop
|
72
95
|
node_attr = attr_ary.reduce(node.normal) do |a, k|
|
73
|
-
a[k] = Mash.new unless a.
|
96
|
+
a[k] = Mash.new unless a.key?(k)
|
74
97
|
a[k]
|
75
98
|
end
|
76
99
|
node_attr[last] = value
|
@@ -79,18 +102,22 @@ class Chef
|
|
79
102
|
self.class.cache[cache_key] = value
|
80
103
|
end
|
81
104
|
|
105
|
+
# Deletes a remote node attribute.
|
106
|
+
#
|
107
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
108
|
+
# @return [Boolean] whether the node attribute has been found and
|
109
|
+
# successfully deleted.
|
110
|
+
# @raise [ArgumentError] if the attribute path format is wrong.
|
82
111
|
def delete_attribute(attr_ary)
|
83
|
-
|
84
|
-
raise ArgumentError, "#{self.class.to_s}##{__method__} attr_ary argument must be an array of strings. You passed #{attr_ary.inspect}."
|
85
|
-
end
|
112
|
+
assert_attribute_array(attr_ary)
|
86
113
|
cache_key = cache_key(name, attr_ary)
|
87
114
|
|
88
115
|
node = Chef::Node.load(name)
|
89
116
|
last = attr_ary.pop
|
90
117
|
node_attr = attr_ary.reduce(node.normal) do |a, k|
|
91
|
-
a.respond_to?(:
|
118
|
+
a.respond_to?(:key?) && a.key?(k) ? a[k] : nil
|
92
119
|
end
|
93
|
-
if node_attr.respond_to?(:
|
120
|
+
if node_attr.respond_to?(:key?) && node_attr.key?(last)
|
94
121
|
node_attr.delete(last)
|
95
122
|
node.save
|
96
123
|
self.class.cache.delete(cache_key)
|
@@ -102,10 +129,38 @@ class Chef
|
|
102
129
|
|
103
130
|
protected
|
104
131
|
|
132
|
+
# Parses {SearchHelper#search} result.
|
133
|
+
#
|
134
|
+
# @param res [Array<Hash>] {SearchHelper#search} result.
|
135
|
+
# @return [Mixed] final search result value.
|
136
|
+
def parse_search_result(res)
|
137
|
+
if res.is_a?(Array) && res[0].is_a?(Hash) && res[0].key?('value')
|
138
|
+
res[0]['value']
|
139
|
+
else
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Generates the cache key.
|
145
|
+
#
|
146
|
+
# @param name [String] node name.
|
147
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
148
|
+
# @return [String] cache key.
|
105
149
|
def cache_key(name, attr_ary)
|
106
|
-
"#{name}:#{attr_ary.inspect}" # TODO
|
150
|
+
"#{name}:#{attr_ary.inspect}" # TODO: OK, this can be improved
|
107
151
|
end
|
108
152
|
|
153
|
+
# Asserts that the attribute path is in the correct format.
|
154
|
+
#
|
155
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
156
|
+
# @return void
|
157
|
+
# @raise [ArgumentError] if the attribute path format is wrong.
|
158
|
+
def assert_attribute_array(attr_ary)
|
159
|
+
return if attr_ary.is_a?(Array)
|
160
|
+
fail ArgumentError,
|
161
|
+
"#{self.class}##{__method__} attr_ary argument must be an array "\
|
162
|
+
"of strings. You passed #{attr_ary.inspect}."
|
163
|
+
end
|
109
164
|
end
|
110
165
|
end
|
111
166
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -23,26 +24,76 @@ require 'chef/encrypted_attribute/remote_clients'
|
|
23
24
|
|
24
25
|
class Chef
|
25
26
|
class EncryptedAttribute
|
27
|
+
# Helpers to search nodes remotely and get it's public keys.
|
26
28
|
class RemoteNodes
|
27
29
|
extend ::Chef::EncryptedAttribute::SearchHelper
|
28
30
|
|
31
|
+
# Remote nodes search results cache.
|
32
|
+
#
|
33
|
+
# You can disable it setting it's size to zero:
|
34
|
+
#
|
35
|
+
# ```ruby
|
36
|
+
# Chef::EncryptedAttribute::RemoteNodes.cache.max_size(0)
|
37
|
+
# ```
|
38
|
+
#
|
39
|
+
# @return [CacheLru] Remote nodes LRU cache.
|
29
40
|
def self.cache
|
30
41
|
@@cache ||= Chef::EncryptedAttribute::CacheLru.new
|
31
42
|
end
|
32
43
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
+
# Gets remote node public key.
|
45
|
+
#
|
46
|
+
# It first tries to read the key from the `node['public_key']` attribute.
|
47
|
+
#
|
48
|
+
# If the `"public_key"` attribute does not exist, it tries to read the
|
49
|
+
# node client key directly using the Chef API (this require **admin**
|
50
|
+
# privileges).
|
51
|
+
#
|
52
|
+
# @param node [Chef::Node] Chef node object.
|
53
|
+
# @return [String] Chef client public key as string.
|
54
|
+
# @raise [InsufficientPrivileges] if you lack enoght privileges to read
|
55
|
+
# the keys from the Chef Server.
|
56
|
+
# @raise [ClientNotFound] if client does not exist.
|
57
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
58
|
+
def self.get_public_key(node)
|
59
|
+
return node['public_key'] unless node['public_key'].nil?
|
60
|
+
RemoteClients.get_public_key(node['name'])
|
61
|
+
rescue Net::HTTPServerException => e
|
62
|
+
raise e unless e.response.code == '403'
|
63
|
+
raise InsufficientPrivileges,
|
64
|
+
"You cannot read #{node['name']} client key. Consider including "\
|
65
|
+
'the encrypted_attributes::expose_key recipe in the '\
|
66
|
+
"#{node['name']} node run list."
|
44
67
|
end
|
45
68
|
|
69
|
+
# Searches for node client public keys.
|
70
|
+
#
|
71
|
+
# It first tries to read the key from the `node['public_key']` attribute.
|
72
|
+
#
|
73
|
+
# If the `"public_key"` attribute does not exist, it tries to read the
|
74
|
+
# node client key directly using the Chef API (this require **admin**
|
75
|
+
# privileges).
|
76
|
+
#
|
77
|
+
# @param search [Array<String>, String] search queries to perform, the
|
78
|
+
# query result will be *OR*-ed.
|
79
|
+
# @return [Array<String>] list of public keys.
|
80
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
81
|
+
# the keys from the Chef Server.
|
82
|
+
# @raise [ClientNotFound] if client does not exist.
|
83
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
84
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
85
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
86
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
87
|
+
def self.search_public_keys(search = '*:*', partial_search = true)
|
88
|
+
escaped_query = escape_query(search)
|
89
|
+
return cache[escaped_query] if cache.key?(escaped_query)
|
90
|
+
cache[escaped_query] =
|
91
|
+
search(
|
92
|
+
:node, search,
|
93
|
+
{ 'name' => %w(name), 'public_key' => %w(public_key) },
|
94
|
+
1000, partial_search
|
95
|
+
).map { |node| get_public_key(node) }.compact
|
96
|
+
end
|
46
97
|
end
|
47
98
|
end
|
48
99
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -22,52 +23,90 @@ require 'chef/encrypted_attribute/cache_lru'
|
|
22
23
|
|
23
24
|
class Chef
|
24
25
|
class EncryptedAttribute
|
26
|
+
# Helpers to get remote Chef Users keys.
|
27
|
+
#
|
28
|
+
# @note This class methods require **admin** privileges.
|
25
29
|
class RemoteUsers
|
26
|
-
|
30
|
+
# Remote users public keys cache.
|
31
|
+
#
|
32
|
+
# You can disable it setting it's size to zero:
|
33
|
+
#
|
34
|
+
# ```ruby
|
35
|
+
# Chef::EncryptedAttribute::RemoteUsers.cache.max_size(0)
|
36
|
+
# ```
|
37
|
+
#
|
38
|
+
# @return [CacheLru] Remote users LRU cache.
|
27
39
|
def self.cache
|
28
40
|
@@cache ||= Chef::EncryptedAttribute::CacheLru.new
|
29
41
|
end
|
30
42
|
|
31
|
-
|
43
|
+
# Gets some Chef users public keys.
|
44
|
+
#
|
45
|
+
# @note This method requires **admin** privileges.
|
46
|
+
#
|
47
|
+
# @param users [Array<String>, '*'] user list. Use `'*'` to get all users
|
48
|
+
# public keys.
|
49
|
+
# @return [Array<String>] public key list.
|
50
|
+
# @raise [ArgumentError] if user list is wrong.
|
51
|
+
def self.get_public_keys(users = [])
|
32
52
|
if users == '*' # users are [a-z0-9\-_]+, cannot be *
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
cache['*'] = get_all_public_keys
|
37
|
-
end
|
38
|
-
elsif users.kind_of?(Array)
|
53
|
+
cache.key?('*') ? cache['*'] : cache['*'] = all_public_keys
|
54
|
+
elsif users.is_a?(Array)
|
39
55
|
get_users_public_keys(users)
|
40
|
-
elsif
|
41
|
-
|
56
|
+
elsif !users.nil?
|
57
|
+
fail ArgumentError,
|
58
|
+
"#{self.class}##{__method__} users argument must be an array "\
|
59
|
+
'or "*".'
|
42
60
|
end
|
43
61
|
end
|
44
62
|
|
45
|
-
|
46
|
-
|
63
|
+
# Reads a Chef user public key.
|
64
|
+
#
|
65
|
+
# @note This method requires **admin** privileges.
|
66
|
+
#
|
67
|
+
# @param name [String] user name.
|
68
|
+
# @return [String] user public key as string.
|
69
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
70
|
+
# the keys from the Chef Server.
|
71
|
+
# @api private
|
47
72
|
def self.get_user_public_key(name)
|
48
|
-
return cache[name] if cache.
|
73
|
+
return cache[name] if cache.key?(name)
|
49
74
|
user = Chef::User.load(name)
|
50
75
|
cache[name] = user.public_key
|
51
76
|
rescue Net::HTTPServerException => e
|
52
77
|
case e.response.code
|
53
78
|
when '403'
|
54
|
-
raise InsufficientPrivileges,
|
55
|
-
|
56
|
-
|
79
|
+
raise InsufficientPrivileges,
|
80
|
+
'Your node needs admin privileges to be able to work with '\
|
81
|
+
'Chef Users.'
|
82
|
+
when '404' then raise UserNotFound, "Chef User not found: \"#{name}\"."
|
57
83
|
else
|
58
84
|
raise e
|
59
85
|
end
|
60
86
|
end
|
61
87
|
|
88
|
+
# Gets some Chef users public keys.
|
89
|
+
#
|
90
|
+
# @note This method requires **admin** privileges.
|
91
|
+
#
|
92
|
+
# @param users [Array<String>] user list.
|
93
|
+
# @return [Array<String>] public key list.
|
94
|
+
# @api private
|
62
95
|
def self.get_users_public_keys(users)
|
63
96
|
users.map { |n| get_user_public_key(n) }
|
64
97
|
end
|
65
98
|
|
66
|
-
|
67
|
-
|
99
|
+
# Gets all Chef users public keys.
|
100
|
+
#
|
101
|
+
# @note This method requires **admin** privileges.
|
102
|
+
#
|
103
|
+
# @return [Array<String>] public key list.
|
104
|
+
# @api private
|
105
|
+
def self.all_public_keys
|
106
|
+
# Chef::User.list(inflate=true) has a bug (fixed in 11.14.0)
|
107
|
+
# https://tickets.opscode.com/browse/CHEF-5328
|
68
108
|
get_users_public_keys(Chef::User.list.keys)
|
69
109
|
end
|
70
|
-
|
71
110
|
end
|
72
111
|
end
|
73
112
|
end
|