secure_data_bag 1.1.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
-