chef-encrypted-attributes 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|