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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.yardopts +8 -0
  5. data/CHANGELOG.md +40 -4
  6. data/CONTRIBUTING.md +7 -6
  7. data/KNIFE.md +151 -0
  8. data/README.md +70 -192
  9. data/Rakefile +27 -14
  10. data/TESTING.md +18 -7
  11. data/TODO.md +2 -5
  12. data/lib/chef-encrypted-attributes.rb +7 -1
  13. data/lib/chef/encrypted_attribute.rb +282 -121
  14. data/lib/chef/encrypted_attribute/api.rb +521 -0
  15. data/lib/chef/encrypted_attribute/assertions.rb +16 -6
  16. data/lib/chef/encrypted_attribute/cache_lru.rb +54 -13
  17. data/lib/chef/encrypted_attribute/config.rb +198 -89
  18. data/lib/chef/encrypted_attribute/encrypted_mash.rb +127 -33
  19. data/lib/chef/encrypted_attribute/encrypted_mash/version0.rb +236 -48
  20. data/lib/chef/encrypted_attribute/encrypted_mash/version1.rb +249 -36
  21. data/lib/chef/encrypted_attribute/encrypted_mash/version2.rb +133 -19
  22. data/lib/chef/encrypted_attribute/exceptions.rb +19 -3
  23. data/lib/chef/encrypted_attribute/local_node.rb +15 -4
  24. data/lib/chef/encrypted_attribute/remote_clients.rb +33 -17
  25. data/lib/chef/encrypted_attribute/remote_node.rb +84 -29
  26. data/lib/chef/encrypted_attribute/remote_nodes.rb +62 -11
  27. data/lib/chef/encrypted_attribute/remote_users.rb +58 -19
  28. data/lib/chef/encrypted_attribute/search_helper.rb +214 -74
  29. data/lib/chef/encrypted_attribute/version.rb +3 -1
  30. data/lib/chef/encrypted_attributes.rb +20 -0
  31. data/lib/chef/knife/core/config.rb +4 -1
  32. data/lib/chef/knife/core/encrypted_attribute_base.rb +179 -0
  33. data/lib/chef/knife/core/encrypted_attribute_depends.rb +43 -0
  34. data/lib/chef/knife/core/encrypted_attribute_editor_options.rb +125 -61
  35. data/lib/chef/knife/encrypted_attribute_create.rb +51 -31
  36. data/lib/chef/knife/encrypted_attribute_delete.rb +32 -40
  37. data/lib/chef/knife/encrypted_attribute_edit.rb +51 -32
  38. data/lib/chef/knife/encrypted_attribute_show.rb +30 -55
  39. data/lib/chef/knife/encrypted_attribute_update.rb +43 -28
  40. data/spec/benchmark_helper.rb +2 -1
  41. data/spec/integration_helper.rb +1 -0
  42. data/spec/spec_helper.rb +21 -7
  43. metadata +75 -36
  44. metadata.gz.sig +1 -1
  45. data/API.md +0 -174
  46. data/INTERNAL.md +0 -166
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@onddo.com>)
4
+ # Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef/knife'
21
+
22
+ class Chef
23
+ class Knife
24
+ module Core
25
+ # Loads knife encrypted attribute dependencies.
26
+ module EncryptedAttributeDepends
27
+ # Reopens EncryptedAttributeDepends class to define knife dependencies.
28
+ #
29
+ # Includes the required gems to work with encrypted attributes.
30
+ #
31
+ # @param includer [Class] includer class.
32
+ def self.included(includer)
33
+ includer.class_eval do
34
+ deps do
35
+ require 'chef/encrypted_attribute'
36
+ require 'chef/json_compat'
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ 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)
@@ -21,88 +22,151 @@ require 'chef/knife/core/config'
21
22
  class Chef
22
23
  class Knife
23
24
  module Core
24
-
25
+ # Reads knife encrypted attribute edit commands arguments
25
26
  module EncryptedAttributeEditorOptions
27
+ # Reopens EncryptedAttributeEditorOptions class to define knife
28
+ # argument options.
29
+ #
30
+ # @param includer [Class] includer class.
26
31
  def self.included(includer)
27
32
  includer.class_eval do
28
33
 
29
- deps do
30
- require 'chef/encrypted_attribute'
31
- require 'chef/json_compat'
34
+ # Helper method to set the encrypted attributes configuration.
35
+ def self.encrypted_attributes_option_set(key, value)
36
+ Chef::Config[:knife][:encrypted_attributes][key] = value
37
+ end
38
+
39
+ # Helper method to add a value to an encrypted configuration array
40
+ # option.
41
+ def self.encrypted_attributes_option_push(key, value)
42
+ unless Chef::Config[:knife][:encrypted_attributes][key]
43
+ .is_a?(Array)
44
+ Chef::Config[:knife][:encrypted_attributes][key] = []
45
+ end
46
+ Chef::Config[:knife][:encrypted_attributes][key] << value
32
47
  end
33
48
 
34
49
  option :encrypted_attribute_version,
35
- :long => '--encrypted-attribute-version VERSION',
36
- :description => 'Encrypted Attribute protocol version to use',
37
- :proc => lambda { |i| Chef::Config[:knife][:encrypted_attributes][:version] = i }
50
+ long: '--encrypted-attribute-version VERSION',
51
+ description: 'Encrypted Attribute protocol version to use',
52
+ proc: ->(i) { encrypted_attributes_option_set(:version, i) }
38
53
 
39
54
  option :encrypted_attribute_partial_search,
40
- :short => '-P',
41
- :long => '--disable-partial-search',
42
- :description => 'Disable partial search',
43
- :boolean => true,
44
- :proc => lambda { |i| Chef::Config[:knife][:encrypted_attributes][:partial_search] = false }
55
+ short: '-P',
56
+ long: '--disable-partial-search',
57
+ description: 'Disable partial search',
58
+ boolean: true,
59
+ proc:
60
+ (lambda do |_i|
61
+ encrypted_attributes_option_set(:partial_search, false)
62
+ end)
45
63
 
46
64
  option :encrypted_attribute_client_search,
47
- :short => '-C CLIENT_SEARCH_QUERY',
48
- :long => '--client-search CLIENT_SEARCH_QUERY',
49
- :description => 'Client search query. Can be specified multiple times',
50
- :proc => lambda { |i|
51
- Chef::Config[:knife][:encrypted_attributes][:client_search] = [] unless Chef::Config[:knife][:encrypted_attributes][:client_search].kind_of?(Array)
52
- Chef::Config[:knife][:encrypted_attributes][:client_search] << i
53
- }
65
+ short: '-C CLIENT_SEARCH_QUERY',
66
+ long: '--client-search CLIENT_SEARCH_QUERY',
67
+ description:
68
+ 'Client search query. Can be specified multiple times',
69
+ proc:
70
+ (lambda do |i|
71
+ encrypted_attributes_option_push(:client_search, i)
72
+ end)
54
73
 
55
74
  option :encrypted_attribute_node_search,
56
- :short => '-N NODE_SEARCH_QUERY',
57
- :long => '--node-search NODE_SEARCH_QUERY',
58
- :description => 'Node search query. Can be specified multiple times',
59
- :proc => lambda { |i|
60
- Chef::Config[:knife][:encrypted_attributes][:node_search] = [] unless Chef::Config[:knife][:encrypted_attributes][:node_search].kind_of?(Array)
61
- Chef::Config[:knife][:encrypted_attributes][:node_search] << i
62
- }
75
+ short: '-N NODE_SEARCH_QUERY',
76
+ long: '--node-search NODE_SEARCH_QUERY',
77
+ description:
78
+ 'Node search query. Can be specified multiple times',
79
+ proc:
80
+ ->(i) { encrypted_attributes_option_push(:node_search, i) }
63
81
 
64
82
  option :encrypted_attribute_users,
65
- :short => '-U USER',
66
- :long => '--encrypted-attribute-user USER',
67
- :description => 'User name to allow access to. Can be specified multiple times',
68
- :proc => lambda { |i|
69
- Chef::Config[:knife][:encrypted_attributes][:users] = [] unless Chef::Config[:knife][:encrypted_attributes][:users].kind_of?(Array)
70
- Chef::Config[:knife][:encrypted_attributes][:users] << i
71
- }
83
+ short: '-U USER',
84
+ long: '--encrypted-attribute-user USER',
85
+ description:
86
+ 'User name to allow access to. Can be specified multiple '\
87
+ 'times',
88
+ proc: ->(i) { encrypted_attributes_option_push(:users, i) }
72
89
 
73
- # TODO option :keys
90
+ # TODO: option :keys
91
+ end # includer.class_eval
92
+ end # self.included(includer)
74
93
 
75
- # Modified Chef::Knife::UI#edit_data method with plain text format support
76
- def edit_data(data=nil, format='plain')
77
- output = case format
78
- when 'JSON', 'json'
79
- data.nil? ? {} : Chef::JSONCompat.to_json_pretty(data, {:quirks_mode => true})
80
- else
81
- data.nil? ? '' : data
82
- end
94
+ # Converts a string data to a Ruby value.
95
+ #
96
+ # @param data [String] data to convert.
97
+ # @param format [String] `'plain'` or `'json'`.
98
+ # @return [String, Mixed] Ruby value.
99
+ def edit_data_string_to_obj(data, format)
100
+ case format
101
+ when 'JSON', 'json'
102
+ if data.nil?
103
+ {}
104
+ else
105
+ Chef::JSONCompat.to_json_pretty(data, quirks_mode: true)
106
+ end
107
+ else
108
+ data.nil? ? '' : data
109
+ end
110
+ end
83
111
 
84
- if !config[:disable_editing]
85
- Tempfile.open([ 'knife-edit-', '.json' ]) do |tf|
86
- tf.sync = true
87
- tf.puts output
88
- tf.close
89
- raise 'Please set EDITOR environment variable' unless system("#{config[:editor]} #{tf.path}")
112
+ # Converts Ruby values to string.
113
+ #
114
+ # @param data [Mixed] Ruby value to convert.
115
+ # @param format [String] `'plain'` or `'json'`.
116
+ # @return [String] value encoded as string.
117
+ def edit_data_obj_to_string(data, format)
118
+ case format
119
+ when 'JSON', 'json'
120
+ FFI_Yajl::Parser.parse(data)
121
+ else
122
+ data
123
+ end
124
+ end
90
125
 
91
- output = IO.read(tf.path)
92
- tf.unlink # not needed, but recommended
93
- end
94
- end
126
+ # Runs system editor.
127
+ #
128
+ # @param path [String] path file to edit.
129
+ # @return void
130
+ # @raise [RuntimeError] if the editing command fails.
131
+ def edit_data_run_editor_command(path)
132
+ return if system("#{config[:editor]} #{path}")
133
+ fail 'Please set EDITOR environment variable'
134
+ end
95
135
 
96
- case format
97
- when 'JSON', 'json'
98
- Yajl::Parser.parse(output)
99
- else
100
- output
101
- end
102
- end # def edit_data
136
+ # Edits a data using the system editor.
137
+ #
138
+ # Creates a temporal file to edit the data value.
139
+ #
140
+ # @param data [String] data to edit.
141
+ # @return [String] the data after the editing.
142
+ # @raise [RuntimeError] if the editing command fails.
143
+ def edit_data_run_editor(data)
144
+ return if config[:disable_editing]
145
+ result = nil
146
+ Tempfile.open(%w(knife-edit- .json)) do |tf|
147
+ tf.sync = true
148
+ tf.puts(data)
149
+ tf.close
150
+ edit_data_run_editor_command(tf.path)
151
+ result = IO.read(tf.path)
152
+ end
153
+ result
154
+ end
103
155
 
104
- end # includer.class_eval
105
- end # self.included(includer)
156
+ # Edits data using an editor.
157
+ #
158
+ # Modified `Chef::Knife::UI#edit_data` method with plain text format
159
+ # support
160
+ #
161
+ # @param data [String] default data value to edit.
162
+ # @param format [String] `'plain'` or `'json'`.
163
+ # @return [String] resulting data value after edition.
164
+ # @raise [RuntimeError] if the editing command fails.
165
+ def edit_data(data = nil, format = 'plain')
166
+ output = edit_data_string_to_obj(data, format)
167
+ output = edit_data_run_editor(output)
168
+ edit_data_obj_to_string(output, format)
169
+ end # def edit_data
106
170
  end # EncryptedAttributeEditorOptions
107
171
  end # Core
108
172
  end # Knife
@@ -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)
@@ -16,52 +17,71 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'chef/knife/encrypted_attribute_show'
20
+ require 'chef/knife/core/encrypted_attribute_base'
21
+ require 'chef/knife/core/encrypted_attribute_depends'
20
22
  require 'chef/knife/core/encrypted_attribute_editor_options'
21
23
 
22
24
  class Chef
23
25
  class Knife
24
- class EncryptedAttributeCreate < EncryptedAttributeShow
25
-
26
+ # knife encrypted attribute create command.
27
+ #
28
+ # ```
29
+ # $ knife encrypted attribute create NODE ATTRIBUTE (options)
30
+ # ```
31
+ class EncryptedAttributeCreate < Core::EncryptedAttributeBase
32
+ include Knife::Core::EncryptedAttributeDepends
26
33
  include Knife::Core::EncryptedAttributeEditorOptions
27
34
 
28
35
  option :input_format,
29
- :short => '-i FORMAT',
30
- :long => '--input-format FORMAT',
31
- :description => 'Input (EDITOR) format, supported formats are "plain" (default) and "json"'
36
+ short: '-i FORMAT',
37
+ long: '--input-format FORMAT',
38
+ description:
39
+ 'Input (EDITOR) format, supported formats are "plain" '\
40
+ '(default) and "json"'
32
41
 
33
42
  banner 'knife encrypted attribute create NODE ATTRIBUTE (options)'
34
43
 
35
- def run
36
- node_name = @name_args[0]
37
- attr_path = @name_args[1]
38
-
39
- if node_name.nil?
40
- show_usage
41
- ui.fatal('You must specify a node name')
42
- exit 1
43
- end
44
-
45
- if attr_path.nil?
46
- show_usage
47
- ui.fatal('You must specify an encrypted attribute name')
48
- exit 1
49
- end
50
-
51
- attr_ary = attribute_path_to_ary(attr_path)
52
-
44
+ # (see EncryptedAttributeBase#assert_valid_args)
45
+ # @raise [ArgumentError] if the attribute path format is wrong.
46
+ def assert_valid_args
53
47
  # check if the encrypted attribute already exists
54
- if Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
55
- ui.fatal('Encrypted attribute already exists')
56
- exit 1
57
- end
48
+ assert_attribute_does_not_exist(@node_name, @attr_ary)
49
+ end
50
+
51
+ # Runs knife command.
52
+ #
53
+ # @raise [RuntimeError] if the editing command fails.
54
+ # @raise [ArgumentError] if the attribute path format or the user list is
55
+ # wrong.
56
+ # @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
57
+ # format is wrong or does not exist.
58
+ # @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
59
+ # format is not supported or unknown.
60
+ # @raise [EncryptionFailure] if there are encryption errors.
61
+ # @raise [MessageAuthenticationFailure] if HMAC calculation error.
62
+ # @raise [InvalidPublicKey] if it is not a valid RSA public key.
63
+ # @raise [InvalidKey] if the RSA key format is wrong.
64
+ # @raise [InsufficientPrivileges] if you lack enough privileges to read
65
+ # the keys from the Chef Server.
66
+ # @raise [ClientNotFound] if client does not exist.
67
+ # @raise [Net::HTTPServerException] for Chef Server HTTP errors.
68
+ # @raise [RequirementsFailure] if the specified encrypted attribute
69
+ # version cannot be used.
70
+ # @raise [SearchFailure] if there is a Chef search error.
71
+ # @raise [SearchFatalError] if the Chef search response is wrong.
72
+ # @raise [InvalidSearchKeys] if search keys structure is wrong.
73
+ # @return void
74
+ def run
75
+ parse_args
58
76
 
59
77
  # create encrypted attribute
60
78
  output = edit_data(nil, config[:input_format])
61
- enc_attr = Chef::EncryptedAttribute.new(Chef::Config[:knife][:encrypted_attributes])
62
- enc_attr.create_on_node(node_name, attr_ary, output)
79
+ enc_attr =
80
+ Chef::EncryptedAttribute.new(
81
+ Chef::Config[:knife][:encrypted_attributes]
82
+ )
83
+ enc_attr.create_on_node(@node_name, @attr_ary, output)
63
84
  end
64
-
65
85
  end
66
86
  end
67
87
  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)
@@ -16,56 +17,47 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'chef/knife'
20
- require 'chef/knife/encrypted_attribute_show'
20
+ require 'chef/knife/core/encrypted_attribute_base'
21
+ require 'chef/knife/core/encrypted_attribute_depends'
21
22
  require 'chef/encrypted_attribute/remote_node'
22
23
 
23
24
  class Chef
24
25
  class Knife
25
- class EncryptedAttributeDelete < EncryptedAttributeShow
26
-
27
- deps do
28
- require 'chef/encrypted_attribute'
29
- require 'chef/json_compat'
30
- end
26
+ # knife encrypted attribute delete command.
27
+ #
28
+ # ```
29
+ # $ knife encrypted attribute delete NODE ATTRIBUTE (options)
30
+ # ```
31
+ class EncryptedAttributeDelete < Core::EncryptedAttributeBase
32
+ include Knife::Core::EncryptedAttributeDepends
31
33
 
32
34
  banner 'knife encrypted attribute delete NODE ATTRIBUTE (options)'
33
35
 
34
36
  option :force,
35
- :short => '-f',
36
- :long => '--force',
37
- :description => 'Force the attribute deletion even if you cannot read it',
38
- :boolean => true
39
-
37
+ short: '-f',
38
+ long: '--force',
39
+ description:
40
+ 'Force the attribute deletion even if you cannot read it',
41
+ boolean: true
42
+
43
+ # Runs knife command.
44
+ #
45
+ # @return void
46
+ # @raise [ArgumentError] if the attribute path format is wrong.
47
+ # @raise [SearchFailure] if there is a Chef search error.
48
+ # @raise [SearchFatalError] if the Chef search response is wrong.
49
+ # @raise [InvalidSearchKeys] if search keys structure is wrong.
40
50
  def run
41
- node_name = @name_args[0]
42
- attr_path = @name_args[1]
43
-
44
- if node_name.nil?
45
- show_usage
46
- ui.fatal('You must specify a node name')
47
- exit 1
48
- end
49
-
50
- if attr_path.nil?
51
- show_usage
52
- ui.fatal('You must specify an encrypted attribute name')
53
- exit 1
54
- end
55
-
56
- attr_ary = attribute_path_to_ary(attr_path)
57
- if Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
58
- # TODO move this to lib/EncryptedAttribute
59
- unless config[:force] # try to read the attribute
60
- Chef::EncryptedAttribute.load_from_node(node_name, attr_ary)
61
- end
62
- remote_node = Chef::EncryptedAttribute::RemoteNode.new(node_name)
63
- if remote_node.delete_attribute(attr_ary)
64
- ui.info('Encrypted attribute deleted.')
65
- end
66
- end
51
+ parse_args
52
+
53
+ return unless
54
+ Chef::EncryptedAttribute.exist_on_node?(@node_name, @attr_ary)
55
+ # TODO: move this to lib/EncryptedAttribute
56
+ assert_attribute_readable(@node_name, @attr_ary) unless config[:force]
57
+ remote_node = Chef::EncryptedAttribute::RemoteNode.new(@node_name)
58
+ return unless remote_node.delete_attribute(@attr_ary)
59
+ ui.info('Encrypted attribute deleted.')
67
60
  end
68
-
69
61
  end
70
62
  end
71
63
  end