secure_data_bag 1.1.4 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b6fcbd13a41e6f976656fdbe7bb6e43f02031ae
4
- data.tar.gz: a433da64df0447378ea05e5cecda6aa8b24cf1d9
3
+ metadata.gz: fe94b528cef47838c539b57ce62634072750b5af
4
+ data.tar.gz: d6c2ca03370f5c26c6888ac411741972b0e631a7
5
5
  SHA512:
6
- metadata.gz: 6203e56251f529905b373645c7af57f1fe4834a0ff4bf8cb8836613783995224e64a1f259bdbbce2e51e17c06112677800e831cf0d121acb04ffa3343e96c771
7
- data.tar.gz: 15134db5f30add0a8bdd30bef9e63ff8b343dec7b60f1a9eb85e45507c23a8b4d202e0ff81aa851629aaf75849cd5ebcee5ba55537d27b875583c64381cc8467
6
+ metadata.gz: 41ea852190213e90f432c4ade570d6b8280dc1778c2d16ec29316032b81cb47bd78c64ed9679f488ab0e292d28090bfed1c2f00b025f8a14ac725365521c7c9f
7
+ data.tar.gz: 026d10232c68d57bf8af33a9f6f75af0fda05576c3d28cb7ed6bde634ef751064d7ba674ecc9941044ff83e6673b566eefff8cc354828102104bb19c942c914d
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/README.md CHANGED
@@ -22,6 +22,8 @@ Or install it yourself as:
22
22
 
23
23
  For the most part, this behaves exactly like a standard DataBagItem would. Encryption and Decryption of attributes ought to be completely transparent.
24
24
 
25
+ SecureDataBagItem is also able to read either standard DataBagItem objects or EncryptedDataBagItem since the encryption mechanism is on a per-key basis and is entirely compatible with EncryptedDataBagItem's format. One caveat, however, is that SecureDataBagItem can not be read by EncryptedDataBagItem.
26
+
25
27
  SecureDataBagItem is also built on Mash rather than Hash so you'll find it more compatible with symbol keys.
26
28
 
27
29
  ```
@@ -30,17 +32,17 @@ SecureDataBagItem is also built on Mash rather than Hash so you'll find it more
30
32
 
31
33
  > data = { id:"databag", "encoded":"my string", "unencoded":"other string" }
32
34
 
33
- > item = SecureDataBagItem.from_hash(data, secret_key)
34
- > item.raw_data # Unencoded hash with data[:encryption] added
35
+ > item = SecureDataBagItem.from_hash(data, key: secret_key)
36
+ > item.raw_data # Unencoded hash
35
37
  {
36
38
  id: "databag",
37
- encoded: "my string",
38
- unencoded: "other string",
39
- encryption:{
40
- cipher:"aes-256-cbc",
41
- iv:nil,
42
- encoded_fields:[]
39
+ encoded: {
40
+ encrypted_data: "encoded",
41
+ cipher: aes-256-cbc,
42
+ iv: 13453453dkgfefg==
43
+ version: 1
43
44
  }
45
+ unencoded: "other string",
44
46
  }
45
47
 
46
48
  > item.to_hash
@@ -49,54 +51,35 @@ SecureDataBagItem is also built on Mash rather than Hash so you'll find it more
49
51
  chef_type: "data_bag_item",
50
52
  data_bag: "",
51
53
  encoded: "my string",
52
- unencoded: "other string",
53
- encryption:{
54
- cipher:"aes-256-cbc",
55
- iv:nil,
56
- encoded_fields:[]
57
- }
54
+ unencoded: "other string"
58
55
  }
59
-
60
- > item.encode_fields ["encoded"]
61
- > item.to_hash # Encoded hash compatible with DataBagItem
62
- {
63
- id: "databag",
64
- chef_type: "data_bag_item",
65
- data_bag: "",
66
- encoded: "[encoded]",
67
- unencoded: "other string",
68
- encryption:{
69
- cipher:"aes-256-cbc",
70
- iv:nil,
71
- encoded_fields:["encoded"]
72
- }
73
- }
74
-
75
- > item.raw_data
76
- {
77
- id: "databag",
78
- chef_type: "data_bag_item",
79
- data_bag: "",
80
- encoded: "my string",
81
- unencoded: "other string",
82
- encryption:{
83
- cipher:"aes-256-cbc",
84
- iv:nil,
85
- encoded_fields:[]
86
- }
87
- }
88
-
89
56
  ```
90
57
 
91
58
  A few knife commands are also provided which allow you to create / edit / show / from file any DataBagItem or EncryptedDataBagItem and convert them to SecureDataBag::Item format.
92
59
 
93
60
  ```
94
- knife secure bag create data_bag item
61
+ knife secure bag --help
62
+ ** SECURE BAG COMMANDS **
63
+ knife secure bag create BAG [ITEM] (options)
64
+ knife secure bag edit BAG [ITEM] (options)
65
+ knife secure bag from file BAG FILE|FLDR [FILE|FLDR] (options)
66
+ knife secure bag show BAG [ITEM] (options)
67
+ ```
68
+
69
+ Additionally it accepts the following command-line options :
95
70
 
96
- knife secure bag edit data_bag item
71
+ ```
72
+ --secret-file /path/to/secret.pem
73
+ --secret passcode
74
+ --encoded-fields field_one,field_two,field_three
75
+ ```
97
76
 
98
- knife secure bag show data_bag item
77
+ Which may also be configured in knife.rb:
99
78
 
100
- knife secure bag from file data_bag /path/to/item.json
79
+ ```
80
+ knife[:secure_data_bag] = {
81
+ fields: ["password","ssh_keys"],
82
+ secret_file: "/path/to/secret.pem"
83
+ }
101
84
  ```
102
85
 
@@ -0,0 +1,14 @@
1
+
2
+ require 'chef/config'
3
+
4
+ class Chef
5
+ class Config
6
+ config_context :knife do
7
+ config_context :secure_data_bag do
8
+ default :secret_file, nil
9
+ default :fields, nil
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -19,51 +19,65 @@ class Chef
19
19
  option :secret_file,
20
20
  long: "--secret-file SECRET_FILE",
21
21
  description: "A file containing a secret key to use to encrypt data bag item values",
22
- proc: Proc.new { |sf| Chef::Config[:knife][:secret_file] = sf }
22
+ proc: Proc.new { |sf|
23
+ Chef::Config[:encrypted_data_bag_secret] = sf
24
+ }
23
25
 
24
- option :encode_fields,
25
- long: "--encode-fields FIELD1,FIELD2,FIELD3",
26
+ option :secure_data_bag_fields,
27
+ long: "--encoded-fields FIELD1,FIELD2,FIELD3",
26
28
  description: "List of attribute keys for which to encode values",
27
- default: ""
29
+ proc: Proc.new { |o|
30
+ Chef::Config[:knife][:secure_data_bag][:fields] = o.split(",")
31
+ }
28
32
  end
29
33
  end
30
-
31
- def encode_fields_to_array
32
- unless config[:encode_fields].is_a?(Array)
33
- config[:encode_fields] = config[:encode_fields].split(",")
34
- end
34
+
35
+ def encoded_fields(arg=nil)
36
+ config[:secure_data_bag_fields] = arg unless arg.nil?
37
+ config[:secure_data_bag_fields] ||
38
+ Chef::Config[:knife][:secure_data_bag][:fields]
35
39
  end
36
40
 
37
- def use_encryption
38
- if use_secure_databag then false
39
- else
40
- if @raw_data["encrypted_data"] or
41
- @raw_data.reject { |k,v| k == "id" }.
42
- all? { |k,v| v.is_a?(Hash) and v.key? "encrypted_data" }
43
- then super
44
- else false
45
- end
46
- end
41
+ def secret_file
42
+ config[:secret] ||
43
+ Chef::Config[:knife][:secure_data_bag][:secret_file] ||
44
+ Chef::Config[:encrypted_data_bag_secret]
47
45
  end
48
46
 
49
- def use_secure_databag
50
- @raw_data["encryption"]
47
+ def use_encryption
48
+ true
51
49
  end
52
50
 
53
- def encoded_fields_for(item)
54
- [].concat(config[:encode_fields].split(",")).
55
- concat(item.encode_fields).
56
- uniq
51
+ def read_secret
52
+ if config[:secret] then config[:secret]
53
+ else SecureDataBag::Item.load_secret(secret_file)
54
+ end
57
55
  end
58
56
 
59
57
  def require_secret
60
- if not config[:secret] and not config[:secret_file]
58
+ if not config[:secret] and not secret_file
61
59
  show_usage
62
60
  ui.fatal("A secret or secret_file must be specified")
63
61
  exit 1
64
62
  end
65
63
  end
66
64
 
65
+ def data_for_create(hash={})
66
+ hash[:id] = @data_bag_item_name
67
+ hash = data_for_edit(hash)
68
+ hash
69
+ end
70
+
71
+ def data_for_edit(hash)
72
+ hash[:_encoded_fields] = encoded_fields
73
+ hash
74
+ end
75
+
76
+ def data_for_save(hash)
77
+ @encoded_fields = hash.delete(:_encoded_fields)
78
+ hash
79
+ end
80
+
67
81
  end
68
82
  end
69
83
  end
@@ -21,20 +21,13 @@ class Chef
21
21
  end
22
22
 
23
23
  def create_databag_item
24
- create_object({ id: @data_bag_item_name },
24
+ create_object(initial_data,
25
25
  "data_bag_item[#{@data_bag_item_name}]") do |output|
26
26
 
27
- @raw_data = output
27
+ @raw_data = data_for_save(output)
28
28
 
29
- item = if use_encryption
30
- item = Chef::EncryptedDataBagItem.
31
- encrypt_data_bag_item(output,read_secret)
32
- else
33
- output
34
- end
35
-
36
- item = SecureDataBag::Item.from_hash(item, read_secret)
37
- item.encode_fields encoded_fields_for(item)
29
+ item = SecureDataBag::Item.from_hash(@raw_data, read_secret)
30
+ item.encoded_fields(encoded_fields)
38
31
  item.data_bag(@data_bag_name)
39
32
 
40
33
  rest.post_rest("data/#{@data_bag_name}", item.to_hash)
@@ -14,32 +14,18 @@ class Chef
14
14
  item = Chef::DataBagItem.load(bag, item_name)
15
15
  @raw_data = item.to_hash
16
16
 
17
- if use_encryption
18
- item = Chef::EncryptedDataBagItem.load(item, read_secret)
19
- end
20
-
21
- item = SecureDataBag::Item.from_item(item, read_secret)
22
- hash = item.to_hash(false)
23
- #
24
- # Ensure that we display encoded_fields
25
- # - generally this would be blank given that all fields are decrypted
26
- #
27
- hash[:encryption][:encoded_fields] = encoded_fields_for(item)
17
+ item = SecureDataBag::Item.from_item(item, key:read_secret)
18
+ hash = item.to_hash(encoded: false)
19
+ hash = data_for_edit(hash)
28
20
  hash
29
21
  end
30
22
 
31
23
  def edit_item(item)
32
24
  output = super
25
+ output = data_for_save(output)
33
26
 
34
- #
35
- # Store the desired fields to encode and set to hash to unencrypted
36
- # until we have created the object
37
- #
38
- encode_fields = output["encryption"]["encoded_fields"]
39
- output["encryption"]["encoded_fields"] = []
40
-
41
- item = SecureDataBag::Item.from_hash(output, read_secret)
42
- item.encode_fields encode_fields
27
+ item = SecureDataBag::Item.from_hash(output, key:read_secret)
28
+ item.encoded_fields encoded_fields
43
29
  item.to_hash
44
30
  end
45
31
  end
@@ -12,7 +12,6 @@ class Chef
12
12
  require 'chef/data_bag_item'
13
13
  require 'chef/knife/core/object_loader'
14
14
  require 'chef/json_compat'
15
- require 'chef/encrypted_data_bag_item'
16
15
  require 'secure_data_bag'
17
16
  end
18
17
 
@@ -27,13 +26,7 @@ class Chef
27
26
  def load_data_bag_hash(hash)
28
27
  @raw_data = hash
29
28
 
30
- if use_encryption
31
- item = Chef::EncryptedDataBagItem.
32
- encrypt_data_bag_item(output, read_secret)
33
- end
34
-
35
- item = SecureDataBag::Item.from_hash(hash, read_secret)
36
- item.encode_fields encoded_fields_for(item)
29
+ item = SecureDataBag::Item.from_hash(hash, secret:read_secret)
37
30
  item.to_hash
38
31
  end
39
32
 
@@ -43,9 +36,10 @@ class Chef
43
36
  item_paths.each do |item_path|
44
37
  item = loader.load_from("#{data_bags_path}", data_bag, item_path)
45
38
  item = load_data_bag_hash(item)
46
- dbag = Chef::DataBagItem.new
47
- dbag.data_bag(data_bag)
39
+ dbag = SecureDataBag::Item.new(secret:read_secret)
40
+ dbag.encoded_fields encoded_fields
48
41
  dbag.raw_data = item
42
+ dbag.data_bag(data_bag)
49
43
  dbag.save
50
44
  ui.info("Updated data_bag_item[#{dbag.data_bag}::#{dbag.id}]")
51
45
  end
@@ -11,16 +11,15 @@ class Chef
11
11
  category "secure bag"
12
12
 
13
13
  def load_item(bag, item_name)
14
- item = Chef::DataBagItem.load(bag, item_name)
15
- @raw_data = item.raw_data
16
-
17
- if use_encryption
18
- item = Chef::EncryptedDataBagItem.new(@raw_data, read_secret)
19
- end
20
-
21
- item = SecureDataBag::Item.from_item(item, read_secret)
22
- data = item.to_hash(false)
23
- data[:encryption][:encoded_fields] = item.encode_fields
14
+ item = SecureDataBag::Item.load(
15
+ bag, item_name,
16
+ key: read_secret,
17
+ fields: encoded_fields
18
+ )
19
+ item.encoded_fields(encoded_fields)
20
+
21
+ data = item.to_hash(encoded:false)
22
+ data = data_for_edit(data)
24
23
  data
25
24
  end
26
25
 
@@ -1,6 +1,5 @@
1
1
 
2
+ require_relative "chef/config"
2
3
  require "secure_data_bag/version"
3
4
  require "secure_data_bag/secure_data_bag_item"
4
- require "secure_data_bag/decryptor"
5
- require "secure_data_bag/encryptor"
6
5
 
@@ -1,6 +1,8 @@
1
1
 
2
2
  require 'open-uri'
3
3
  require 'chef/data_bag_item'
4
+ require 'chef/encrypted_data_bag_item/encryptor'
5
+ require 'chef/encrypted_data_bag_item/decryptor'
4
6
 
5
7
  module SecureDataBag
6
8
  #
@@ -12,13 +14,21 @@ module SecureDataBag
12
14
  #
13
15
 
14
16
  class Item < Chef::DataBagItem
15
- def initialize(key=nil)
17
+ def initialize(key:nil, fields:nil, secret:nil, data:nil)
16
18
  super()
17
19
 
18
20
  @secret = Chef::Config[:encrypted_data_bag_secret]
19
21
  @key = key
20
- @raw_data = {}
21
- @encode_fields = ["password"]
22
+
23
+ unless data.nil?
24
+ self.raw_data = data
25
+ end
26
+
27
+ encoded_fields(
28
+ fields ||
29
+ Chef::Config[:knife][:secure_data_bag][:fields] ||
30
+ ["password"]
31
+ )
22
32
  end
23
33
 
24
34
  #
@@ -39,7 +49,9 @@ module SecureDataBag
39
49
  end
40
50
 
41
51
  def self.load_secret(path=nil)
42
- path ||= Chef::Config[:encrypted_data_bag_secret]
52
+ path ||=
53
+ Chef::Config[:knife][:secure_data_bag][:secret_file] ||
54
+ Chef::Config[:encrypted_data_bag_secret]
43
55
 
44
56
  unless path
45
57
  raise ArgumentError, "No secret specified and no secret found."
@@ -68,20 +80,9 @@ module SecureDataBag
68
80
  key
69
81
  end
70
82
 
71
- #
72
- # Wrapper for raw_data encryption settings
73
- # - always ensure that encryption hash is present
74
- # - always ensure encryption settings have defaults
75
- #
76
-
77
- def encryption(arg=nil)
78
- @raw_data[:encryption] ||= {}
79
- @raw_data[:encryption] = arg unless arg.nil?
80
- encryption = @raw_data[:encryption]
81
- encryption[:iv] = nil if encryption[:iv].nil?
82
- encryption[:cipher] = "aes-256-cbc" if encryption[:cipher].nil?
83
- encryption[:encoded_fields] = [] if encryption[:encoded_fields].nil?
84
- encryption
83
+ def self.load(data_bag, name, opts={})
84
+ data = super(data_bag, name)
85
+ new(data:data.to_hash, **opts)
85
86
  end
86
87
 
87
88
  #
@@ -94,89 +95,98 @@ module SecureDataBag
94
95
 
95
96
  def raw_data=(data)
96
97
  data = Mash.new(data)
97
- super data
98
- encryption
98
+ super(data)
99
99
  decode_data!
100
100
  end
101
101
 
102
102
  #
103
- # Determine whether the data is encoded or not
104
- # - yeah, it's pretty naive
103
+ # Fields we wish to encode
105
104
  #
106
105
 
107
- def encoded?
108
- not encryption[:encoded_fields].empty?
106
+ def encoded_fields(arg=nil)
107
+ arg = arg.uniq if arg.is_a?(Array)
108
+ set_or_return(:encoded_fields, arg, kind_of: Array, default:[]).uniq
109
109
  end
110
110
 
111
111
  #
112
- # Fields we wish to encode
113
- # - this differs from encryption[:encoded_fields] and will get merged
114
- # into the latter upon an encode
112
+ # Raw Data decoder methods
115
113
  #
116
114
 
117
- def encode_fields(arg=nil)
118
- arg = Array(arg).uniq if arg
119
- set_or_return(:encode_fields, arg, kind_of: Array).uniq
115
+ def decode_data!
116
+ @raw_data = decoded_data
117
+ @raw_data
120
118
  end
121
119
 
122
- def encoded_fields
123
- encryption[:encoded_fields]
120
+ def decoded_data
121
+ if encoded_value?(@raw_data) then decode_value(@raw_data)
122
+ else decode_hash(@raw_data)
123
+ end
124
124
  end
125
125
 
126
- #
127
- # Encoder / Decoder
128
- #
126
+ def decode_hash(hash)
127
+ hash.each do |k,v|
128
+ v = if encoded_value?(v) then decode_value(v)
129
+ elsif v.is_a?(Hash) then decode_hash(v)
130
+ else v
131
+ end
132
+ hash[k] = v
133
+ end
134
+ hash
135
+ end
129
136
 
130
- def decode_data!
131
- #
132
- # Ensure that we save previously encoded fields into our list of fields
133
- # we wish to encode next time
134
- #
135
- encode_fields.concat(encryption[:encoded_fields]).uniq!
136
- @raw_data = decoded_data if encoded?
137
- @raw_data
137
+ def decode_value(value)
138
+ Chef::EncryptedDataBagItem::Decryptor.for(value, key).for_decrypted_item
138
139
  end
139
140
 
140
- def decoded_data
141
- data = Decryptor.new(raw_data, encryption, key).for_decrypted_item
142
- data[:encryption][:encoded_fields] = []
143
- data
141
+ def encoded_value?(value)
142
+ value.is_a?(Hash) and value.key?(:encrypted_data)
144
143
  end
145
144
 
145
+ #
146
+ # Raw Data encoded methods
147
+ #
148
+
146
149
  def encode_data!
147
150
  @raw_data = encoded_data
151
+ @raw_data
148
152
  end
149
153
 
150
154
  def encoded_data
151
- #
152
- # When encoding data we'll merge those fields already encoded during
153
- # the previous state, found in encryption[:encoded_fields] with those
154
- # which we wish to encode
155
- #
156
- encryption = self.encryption.dup
157
- encryption[:encoded_fields] = @encode_fields.
158
- concat(encryption[:encoded_fields]).uniq
159
- Encryptor.new(raw_data, encryption, key).for_encrypted_item
155
+ encode_hash(@raw_data.dup)
156
+ end
157
+
158
+ def encode_hash(hash)
159
+ hash.each do |k,v|
160
+ v = if encoded_fields.include?(k) then encode_value(v)
161
+ elsif v.is_a?(Hash) then encode_hash(v)
162
+ else v
163
+ end
164
+ hash[k] = v
165
+ end
166
+ hash
167
+ end
168
+
169
+ def encode_value(value)
170
+ Chef::EncryptedDataBagItem::Encryptor.new(value, key).for_encrypted_item
160
171
  end
161
172
 
162
173
  #
163
174
  # Transitions
164
175
  #
165
176
 
166
- def self.from_hash(h, key=nil)
167
- item = new(key)
168
- item.raw_data = h
177
+ def self.from_hash(h, opts={})
178
+ item = new(data:h, **opts)
169
179
  item
170
180
  end
171
181
 
172
- def self.from_item(h, key=nil)
173
- item = self.from_hash(h.to_hash, key)
182
+ def self.from_item(h, opts={})
183
+ item = self.from_hash(h.to_hash, **opts)
174
184
  item.data_bag h.data_bag
175
185
  item
176
186
  end
177
187
 
178
- def to_hash(encoded = true)
179
- result = encoded ? encoded_data : decoded_data
188
+ def to_hash(encoded: true)
189
+ result = encoded ? encoded_data : @raw_data
180
190
  result["chef_type"] = "data_bag_item"
181
191
  result["data_bag"] = self.data_bag
182
192
  result
@@ -187,7 +197,7 @@ module SecureDataBag
187
197
  name: self.object_name,
188
198
  json_class: "Chef::DataBagItem",
189
199
  chef_type: "data_bag_item",
190
- data_bag: self.data_bag,
200
+ data_bag: data_bag,
191
201
  raw_data: encoded_data
192
202
  }
193
203
  result.to_json(*a)
@@ -1,5 +1,5 @@
1
1
 
2
2
  module SecureDataBag
3
- VERSION = "1.1.4"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
 
@@ -18,9 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency 'chef'
22
- spec.add_dependency 'yajl-ruby'
21
+ spec.add_dependency "chef"
23
22
 
24
23
  spec.add_development_dependency "bundler", "~> 1.6"
25
24
  spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "chef"
26
27
  end
data/spec/item_spec.rb ADDED
@@ -0,0 +1,59 @@
1
+
2
+ require 'spec_helper'
3
+ require 'chef/data_bag_item'
4
+ require 'chef/encrypted_data_bag_item'
5
+
6
+ describe SecureDataBag::Item do
7
+ let(:key) { "password" }
8
+ let(:secret) { "data/secret.pem" }
9
+ let(:fields) { ["encoded"] }
10
+
11
+ let(:simple_data) { { "id"=>"test", decoded:"decoded", encoded:"encoded" } }
12
+ let(:nested_data) { simple_data.merge({ nested:simple_data }) }
13
+
14
+ let(:item) { SecureDataBag::Item.new(key:key, fields:fields) }
15
+
16
+ let(:dbag_item) do
17
+ dbag_item = Chef::DataBagItem.new
18
+ dbag_item.raw_data = simple_data
19
+ dbag_item
20
+ end
21
+
22
+ let(:simple_item) do
23
+ simple_item = item
24
+ simple_item.raw_data = simple_data
25
+ simple_item
26
+ end
27
+
28
+ let(:nested_item) do
29
+ nested_item = item
30
+ nested_item.raw_data = nested_data
31
+ nested_item
32
+ end
33
+
34
+ it "encodes simple data" do
35
+ dbag = simple_item
36
+ data = dbag.encoded_data
37
+ expect(data[:decoded]).to eq("decoded")
38
+ expect(data[:encoded]).not_to eq("encoded")
39
+ expect(data[:encoded][:encrypted_data]).not_to eq(nil)
40
+ end
41
+
42
+ it "encodes nested data" do
43
+ dbag = nested_item
44
+ data = dbag.encoded_data
45
+ expect(data[:nested][:decoded]).to eq("decoded")
46
+ expect(data[:nested][:encoded]).not_to eq("encoded")
47
+ expect(data[:nested][:encoded][:encrypted_data]).not_to eq(nil)
48
+ end
49
+
50
+ it "decodes encrypted_data_bag_item" do
51
+ edbag = Chef::EncryptedDataBagItem.encrypt_data_bag_item(dbag_item, key)
52
+ dbag = item
53
+ dbag.raw_data = edbag
54
+ data = dbag.raw_data
55
+ expect(data[:decoded]).to eq("decoded")
56
+ expect(data[:encoded]).to eq("encoded")
57
+ end
58
+ end
59
+
@@ -0,0 +1,17 @@
1
+
2
+ require 'bundler/setup'
3
+ Bundler.setup
4
+
5
+ require 'secure_data_bag'
6
+ require 'chef/config'
7
+
8
+ RSpec.configure do |config|
9
+ config.expect_with :rspec do |expectations|
10
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
+ end
12
+
13
+ config.mock_with :rspec do |mocks|
14
+ mocks.verify_partial_doubles = true
15
+ end
16
+ end
17
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_data_bag
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Serafini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-18 00:00:00.000000000 Z
11
+ date: 2014-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -25,13 +25,27 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: yajl-ruby
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - '>='
32
46
  - !ruby/object:Gem::Version
33
47
  version: '0'
34
- type: :runtime
48
+ type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
@@ -39,21 +53,21 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: bundler
56
+ name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - ~>
59
+ - - '>='
46
60
  - !ruby/object:Gem::Version
47
- version: '1.6'
61
+ version: '0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - ~>
66
+ - - '>='
53
67
  - !ruby/object:Gem::Version
54
- version: '1.6'
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: rake
70
+ name: chef
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - '>='
@@ -75,10 +89,12 @@ extensions: []
75
89
  extra_rdoc_files: []
76
90
  files:
77
91
  - .gitignore
92
+ - .rspec
78
93
  - Gemfile
79
94
  - LICENSE.txt
80
95
  - README.md
81
96
  - Rakefile
97
+ - lib/chef/config.rb
82
98
  - lib/chef/dsl/data_query.rb
83
99
  - lib/chef/knife/secure_bag_base.rb
84
100
  - lib/chef/knife/secure_bag_create.rb
@@ -86,11 +102,11 @@ files:
86
102
  - lib/chef/knife/secure_bag_from_file.rb
87
103
  - lib/chef/knife/secure_bag_show.rb
88
104
  - lib/secure_data_bag.rb
89
- - lib/secure_data_bag/decryptor.rb
90
- - lib/secure_data_bag/encryptor.rb
91
105
  - lib/secure_data_bag/secure_data_bag_item.rb
92
106
  - lib/secure_data_bag/version.rb
93
107
  - secure_data_bag.gemspec
108
+ - spec/item_spec.rb
109
+ - spec/spec_helper.rb
94
110
  homepage: https://github.com/JonathanSerafini/chef-secure_data_bag
95
111
  licenses:
96
112
  - MIT
@@ -115,4 +131,6 @@ rubygems_version: 2.1.11
115
131
  signing_key:
116
132
  specification_version: 4
117
133
  summary: Per-field data bag item encryption
118
- test_files: []
134
+ test_files:
135
+ - spec/item_spec.rb
136
+ - spec/spec_helper.rb
@@ -1,88 +0,0 @@
1
-
2
- require 'yaml'
3
- require 'yajl'
4
- require 'openssl'
5
- require 'base64'
6
- require 'digest/sha2'
7
-
8
- module SecureDataBag
9
- class Item
10
- class Decryptor
11
- class DecryptionFailure < StandardError
12
- end
13
-
14
- def initialize(encrypted_hash, encryption, key)
15
- @encryption = encryption
16
- @encrypted_hash = encrypted_hash
17
- @key = key
18
- @iv = nil
19
- end
20
-
21
- def for_decrypted_item
22
- decrypted_hash
23
- end
24
-
25
- attr_reader :encrypted_hash
26
- attr_reader :encryption
27
- attr_reader :key
28
-
29
- def iv
30
- @iv ||= begin
31
- iv_string = encryption[:iv]
32
- Base64.decode64(iv_string)
33
- end
34
- end
35
-
36
- def decrypted_hash
37
- @decrypted_hash ||= begin
38
- decrypt_hash(encrypted_hash.dup)
39
- end
40
- end
41
-
42
- def decrypt_hash(hash)
43
- hash.each do |k,v|
44
- if encryption[:encoded_fields].include?(k)
45
- begin
46
- v = decrypt_value(v)
47
- rescue Yajl::ParseError
48
- raise DecryptionFailure,
49
- "Error decrypting data bag value for #{k}."
50
- rescue OpenSSL::Cipher::CipherError => e
51
- raise DecryptionFailure,
52
- "Error decrypting data bag value for #{k}: #{e.message}"
53
- end
54
- elsif v.is_a? Hash
55
- v = decrypt_hash(v)
56
- end
57
- hash[k] = v
58
- end
59
- hash
60
- end
61
-
62
- def decrypt_value(value)
63
- if value.is_a? String and not value.empty?
64
- value = Base64.decode64(value)
65
- value = openssl_decryptor.update(value)
66
- value << openssl_decryptor.final
67
-
68
- if value.include? "json_wrapper"
69
- value = Yajl::Parser.parse(value)["json_wrapper"]
70
- end
71
- @openssl_decryptor = nil
72
- end
73
- value
74
- end
75
-
76
- def openssl_decryptor
77
- @openssl_decryptor ||= begin
78
- d = OpenSSL::Cipher::Cipher.new(encryption[:cipher])
79
- d.decrypt
80
- d.key = Digest::SHA256.digest(key)
81
- d.iv = iv
82
- d
83
- end
84
- end
85
- end
86
- end
87
- end
88
-
@@ -1,88 +0,0 @@
1
-
2
- require 'yaml'
3
- require 'yajl'
4
- require 'openssl'
5
- require 'base64'
6
- require 'digest/sha2'
7
-
8
- module SecureDataBag
9
- class Item
10
- class Encryptor
11
- def initialize(unencrypted_hash, encryption, key)
12
- @encryption = encryption
13
- @unencrypted_hash = unencrypted_hash
14
- @encoded_fields = []
15
- @key = key
16
- end
17
-
18
- def for_encrypted_item
19
- data = encrypted_hash
20
- encryption_hash = encryption.dup
21
- encryption_hash[:iv] = Base64.encode64(encryption_hash[:iv] || "")
22
- encryption_hash[:encoded_fields] = encoded_fields.uniq
23
- data.merge({encryption:encryption_hash})
24
- end
25
-
26
- attr_reader :unencrypted_hash
27
- attr_reader :encoded_fields
28
- attr_reader :encryption
29
- attr_reader :key
30
-
31
- def encrypted_hash
32
- @encrypted_data ||= begin
33
- encrypt_hash(unencrypted_hash.dup)
34
- end
35
- end
36
-
37
- def encrypt_hash(hash)
38
- hash.each do |k,v|
39
- if encryption[:encoded_fields].include?(k)
40
- v = encrypt_value(v)
41
- encoded_fields << k
42
- elsif v.is_a? Hash
43
- v = encrypt_hash(v)
44
- end
45
- hash[k] = v
46
- end
47
- hash
48
- end
49
-
50
- def encrypt_value(value)
51
- value = normalize_value(value)
52
-
53
- if not value.nil? and not value.empty?
54
- value = openssl_encryptor.update(value)
55
- value << openssl_encryptor.final
56
- @openssl_encryptor = nil
57
- value = Base64.encode64(value)
58
- end
59
-
60
- value
61
- end
62
-
63
- def normalize_value(value)
64
- if [Hash,Array].any? {|c| value.is_a? c}
65
- serialize_value(value)
66
- else
67
- value.to_s
68
- end
69
- end
70
-
71
- def serialize_value(value)
72
- Yajl::Encoder.encode(:json_wrapper => value)
73
- end
74
-
75
- def openssl_encryptor
76
- @openssl_encryptor ||= begin
77
- encryptor = OpenSSL::Cipher::Cipher.new(encryption[:cipher])
78
- encryptor.encrypt
79
- encryption[:iv] ||= encryptor.random_iv
80
- encryptor.iv = encryption[:iv]
81
- encryptor.key = Digest::SHA256.digest(key)
82
- encryptor
83
- end
84
- end
85
- end
86
- end
87
- end
88
-