secure_data_bag 1.0.5 → 1.1.1

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: 6e857860657969144ad84d433c71517d5076133d
4
- data.tar.gz: b4d2d9ebd99805b360c1fa2582b4b0b44816cd85
3
+ metadata.gz: 00d1848993d374b1b9cc72055cb378eda434cabe
4
+ data.tar.gz: 1e13eb1b536318100059af425078fda53427c2b6
5
5
  SHA512:
6
- metadata.gz: ea08041ad71ebfed7d3c7278efedf4e93cfac58640d085ee93124fa0f6d2b78560c2bbf60e2a7a1e61195a40c1f3bbc7754c4897356c98c62b1ed141456c0c6b
7
- data.tar.gz: 4af1de749137f618776d3d43298d9cc57cd35e25fc88f49c8531d46910cd512a232f2814e28c05843c9aabd32f9b9e89f418ef97919196a5884f13f896aa2424
6
+ metadata.gz: f1945a42718d9f1c9c3a72ca4e19a729b0109a4471e2b6881287a4b24108ddf435e8f6778d379e106be6f1e5754f3a34cdf57d9108ab5bfa3f692bc09d7c1e02
7
+ data.tar.gz: b5208299a8b4fc1ea9fe10b7bb4b008ee4c9b2d2e68ff097155a7bceb42d062f6c34bcef3aa81142ec5946ce141c61d9308fb0252da64f25d308ce06e33fd4b0
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Provides a mechanism to partially encrypt data bag items on a per-key basis which gives us the opportunity to still search for every other field.
4
4
 
5
+ When specifying keys to encrypt, the library will recursively walk through the data bag content searching for encryption candidates.
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
@@ -23,18 +25,78 @@ For the most part, this behaves exactly like a standard DataBagItem would. Encry
23
25
  SecureDataBagItem is also built on Mash rather than Hash so you'll find it more compatible with symbol keys.
24
26
 
25
27
  ```
26
- data = { id:"databag", "value":"my string" }
27
- item = SecureDataBagItem.from_hash(data)
28
- item.raw_data
29
- item.encoded_fields ["value"]
30
- item.to_hash
28
+ > secret_key = SecureDataBagItem.load_key("/path/to/secret")
29
+ > secret_key = nil # Load default secret
30
+
31
+ > data = { id:"databag", "encoded":"my string", "unencoded":"other string" }
32
+
33
+ > item = SecureDataBagItem.from_hash(data, secret_key)
34
+ > item.raw_data # Unencoded hash with data[:encryption] added
35
+ {
36
+ id: "databag",
37
+ encoded: "my string",
38
+ unencoded: "other string",
39
+ encryption:{
40
+ cipher:"aes-256-cbc",
41
+ iv:nil,
42
+ encoded_fields:[]
43
+ }
44
+ }
45
+
46
+ > item.to_hash
47
+ {
48
+ id: "databag",
49
+ chef_type: "data_bag_item",
50
+ data_bag: "",
51
+ encoded: "my string",
52
+ unencoded: "other string",
53
+ encryption:{
54
+ cipher:"aes-256-cbc",
55
+ iv:nil,
56
+ encoded_fields:[]
57
+ }
58
+ }
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
+
31
89
  ```
32
90
 
33
- By default, this will use the system wide secret file. But this can be changed.
91
+ 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.
34
92
 
35
93
  ```
36
- item.secret "/path/to/file.pem"
37
- item.secret "uri://path/to/file.pem"
38
- item.cipher "aes-256-cbc"
94
+ knife secure bag create data_bag item
95
+
96
+ knife secure bag edit data_bag item
97
+
98
+ knife secure bag show data_bag item
99
+
100
+ knife secure bag from file data_bag /path/to/item.json
39
101
  ```
40
102
 
@@ -0,0 +1,23 @@
1
+
2
+ class Chef
3
+ module DSL
4
+ module DataQuery
5
+ def secure_data_bag_item(bag, item)
6
+ DataBag.validate_name!(bag.to_s)
7
+ SecureDataBag::Item.validate_id!(item)
8
+ SecureDataBag::Item.load(bag, item)
9
+ rescue Exception
10
+ Log.error("Failed to load secure data bag item: #{bag.inspect} #{item.inspect}")
11
+ raise
12
+ end
13
+
14
+ def secure_data_bag_item!(item, fields=[])
15
+ secure = SecureDataBag::Item.from_item item
16
+ secure.encoded_fields.concat(Array(fields))
17
+ secure
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+
@@ -0,0 +1,64 @@
1
+
2
+ require 'chef/knife'
3
+
4
+ class Chef
5
+ class Knife
6
+ module SecureBagBase
7
+ def self.included(includer)
8
+ includer.class_eval do
9
+ deps do
10
+ require 'secure_data_bag'
11
+ end
12
+
13
+ option :secret,
14
+ short: "-s SECRET",
15
+ long: "--secret",
16
+ description: "The secret key to use to encrypt data bag item values",
17
+ proc: Proc.new { |s| Chef::Config[:knife][:secret] = s }
18
+
19
+ option :secret_file,
20
+ long: "--secret-file SECRET_FILE",
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 }
23
+
24
+ option :encode_fields,
25
+ long: "--encode-fields FIELD1,FIELD2,FIELD3",
26
+ description: "List of attribute keys for which to encode values",
27
+ default: Array.new
28
+ end
29
+ end
30
+
31
+ def use_encryption
32
+ if use_secure_databag then false
33
+ else
34
+ if @raw_data["encrypted_data"] or
35
+ @raw_data.reject { |k,v| k == "id" }.
36
+ all? { |k,v| v.key? "encrypted_data" }
37
+ then super
38
+ else false
39
+ end
40
+ end
41
+ end
42
+
43
+ def use_secure_databag
44
+ @raw_data["encryption"]
45
+ end
46
+
47
+ def encoded_fields_for(item)
48
+ [].concat(config[:encode_fields]).
49
+ concat(item.encode_fields).
50
+ uniq
51
+ end
52
+
53
+ def require_secret
54
+ if not config[:secret] and not config[:secret_file]
55
+ show_usage
56
+ ui.fatal("A secret or secret_file must be specified")
57
+ exit 1
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,72 @@
1
+
2
+ require 'chef/knife/secure_bag_base'
3
+ require 'chef/knife/data_bag_create'
4
+
5
+ class Chef
6
+ class Knife
7
+ class SecureBagCreate < Knife::DataBagCreate
8
+ include Knife::SecureBagBase
9
+
10
+ banner "knife secure bag create BAG [ITEM] (options)"
11
+ category "secure bag"
12
+
13
+ def create_databag
14
+ begin
15
+ rest.post_rest("data", { name: @data_bag_name })
16
+ ui.info("Created data_bag[#{@data_bag_name}]")
17
+ rescue Net::HTTPServerException => e
18
+ raise unless e.to_s =~ /^409/
19
+ ui.info("Data bag #{@data_bag_name} already exists")
20
+ end
21
+ end
22
+
23
+ def create_databag_item
24
+ create_object({ id: @data_bag_item_name },
25
+ "data_bag_item[#{@data_bag_item_name}]") do |output|
26
+
27
+ @raw_data = output
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)
38
+ item.data_bag(@data_bag_name)
39
+
40
+ rest.post_rest("data/#{@data_bag_name}", item.to_hash)
41
+ end
42
+ end
43
+
44
+ def run
45
+ @data_bag_name, @data_bag_item_name = @name_args
46
+
47
+ if @data_bag_name.nil?
48
+ show_usage
49
+ ui.fatal("You must specify a data bag name")
50
+ exit 1
51
+ end
52
+
53
+ require_secret
54
+
55
+ begin
56
+ Chef::DataBag.validate_name!(@data_bag_name)
57
+ rescue Chef::Exceptions::InvalidDataBagName => e
58
+ ui.fatal(e.message)
59
+ exit(1)
60
+ end
61
+
62
+ # create the data bag
63
+ create_databag
64
+
65
+ if @data_bag_item_name
66
+ create_databag_item
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,48 @@
1
+
2
+ require 'chef/knife/secure_bag_base'
3
+ require 'chef/knife/data_bag_edit'
4
+
5
+ class Chef
6
+ class Knife
7
+ class SecureBagEdit < Knife::DataBagEdit
8
+ include Knife::SecureBagBase
9
+
10
+ banner "knife secure bag edit BAG [ITEM] (options)"
11
+ category "secure bag"
12
+
13
+ def load_item(bag, item_name)
14
+ item = Chef::DataBagItem.load(bag, item_name)
15
+ @raw_data = item.to_hash
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)
28
+ hash
29
+ end
30
+
31
+ def edit_item(item)
32
+ output = super
33
+
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
43
+ item.to_hash
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,56 @@
1
+
2
+ require 'chef/knife/secure_bag_base'
3
+ require 'chef/knife/data_bag_from_file'
4
+
5
+ class Chef
6
+ class Knife
7
+ class SecureBagFromFile < Knife::DataBagFromFile
8
+ include Knife::SecureBagBase
9
+
10
+ deps do
11
+ require 'chef/data_bag'
12
+ require 'chef/data_bag_item'
13
+ require 'chef/knife/core/object_loader'
14
+ require 'chef/json_compat'
15
+ require 'chef/encrypted_data_bag_item'
16
+ require 'secure_data_bag'
17
+ end
18
+
19
+ banner "knife secure bag from file BAG FILE|FLDR [FILE|FLDR] (options)"
20
+ category "secure bag"
21
+
22
+ option :all,
23
+ short: "-a",
24
+ long: "--all",
25
+ description: "Upload all data bags or all items for specified databag"
26
+
27
+ def load_data_bag_hash(hash)
28
+ @raw_data = hash
29
+
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)
37
+ item.to_hash
38
+ end
39
+
40
+ def load_data_bag_items(data_bag, items=nil)
41
+ items ||= find_all_data_bag_items(data_bag)
42
+ item_paths = normalize_item_paths(items)
43
+ item_paths.each do |item_path|
44
+ item = loader.load_from("#{data_bags_path}", data_bag, item_path)
45
+ item = load_data_bag_hash(item)
46
+ dbag = Chef::DataBagItem.new
47
+ dbag.data_bag(data_bag)
48
+ dbag.raw_data = item
49
+ dbag.save
50
+ ui.info("Updated data_bag_item[#{dbag.data_bag}::#{dbag.id}]")
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,41 @@
1
+
2
+ require 'chef/knife/secure_bag_base'
3
+ require 'chef/knife/data_bag_show'
4
+
5
+ class Chef
6
+ class Knife
7
+ class SecureBagShow < Knife::DataBagShow
8
+ include Knife::SecureBagBase
9
+
10
+ banner "knife secure bag show BAG [ITEM] (options)"
11
+ category "secure bag"
12
+
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
+ item.to_hash
23
+ end
24
+
25
+ def run
26
+ display = case @name_args.length
27
+ when 2
28
+ item = load_item(@name_args[0], @name_args[1])
29
+ display = format_for_display(item)
30
+ when 1
31
+ format_list_for_display(Chef::DataBag.load(@name_args[0]))
32
+ else
33
+ stdout.puts opt_parser
34
+ exit(1)
35
+ end
36
+ output(display)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -1,30 +1,6 @@
1
1
 
2
- module SecureDataBag
3
- end
4
-
5
2
  require "secure_data_bag/version"
6
- require "secure_data_bag/secure_data_bag_item.rb"
7
- require "secure_data_bag/decryptor.rb"
8
- require "secure_data_bag/encryptor.rb"
9
-
10
- class Chef
11
- module DSL
12
- module DataQuery
13
- def secure_data_bag_item(bag, item)
14
- DataBag.validate_name!(bag.to_s)
15
- SecureDataBagItem.validate_id!(item)
16
- SecureDataBagItem.load(bag, item)
17
- rescue Exception
18
- Log.error("Failed to load secure data bag item: #{bag.inspect} #{item.inspect}")
19
- raise
20
- end
21
-
22
- def secure_data_bag_item!(item, fields=[])
23
- secure = SecureDataBag::SecureDataBagItem.from_item item
24
- secure.encoded_fields secure.encoded_fields + fields
25
- secure
26
- end
27
- end
28
- end
29
- end
3
+ require "secure_data_bag/secure_data_bag_item"
4
+ require "secure_data_bag/decryptor"
5
+ require "secure_data_bag/encryptor"
30
6
 
@@ -6,29 +6,22 @@ require 'base64'
6
6
  require 'digest/sha2'
7
7
 
8
8
  module SecureDataBag
9
- class SecureDataBagItem
9
+ class Item
10
10
  class Decryptor
11
- attr_reader :key
12
- attr_reader :encryption
13
- attr_reader :encrypted_hash
14
-
15
11
  def initialize(encrypted_hash, encryption, key)
16
12
  @encryption = encryption
17
13
  @encrypted_hash = encrypted_hash
18
14
  @key = key
15
+ @iv = nil
19
16
  end
20
17
 
21
18
  def for_decrypted_item
22
- pp "decrypted_hash"
23
19
  decrypted_hash
24
20
  end
25
21
 
26
- def decryption_error(e=nil)
27
- msg = "Error decrypting data bag value"
28
- msg << ": '#{e.message}'" if e
29
- msg << ". Most likely the provided key is incorrect"
30
- msg
31
- end
22
+ attr_reader :encrypted_hash
23
+ attr_reader :encryption
24
+ attr_reader :key
32
25
 
33
26
  def iv
34
27
  @iv ||= begin
@@ -46,7 +39,15 @@ module SecureDataBag
46
39
  def decrypt_hash(hash)
47
40
  hash.each do |k,v|
48
41
  if encryption[:encoded_fields].include?(k)
49
- v = decrypt_value(v)
42
+ begin
43
+ v = decrypt_value(v)
44
+ rescue Yajl::ParseError
45
+ raise DecryptionFailure,
46
+ "Error decrypting data bag value for #{k}."
47
+ rescue OpenSSL::Cipher::CipherError => e
48
+ raise DecryptionFailure,
49
+ "Error decrypting data bag value for #{k}: #{e.message}"
50
+ end
50
51
  elsif v.is_a? Hash
51
52
  v = decrypt_hash(v)
52
53
  end
@@ -6,13 +6,8 @@ require 'base64'
6
6
  require 'digest/sha2'
7
7
 
8
8
  module SecureDataBag
9
- class SecureDataBagItem
9
+ class Item
10
10
  class Encryptor
11
- attr_reader :encryption
12
- attr_reader :unencrypted_hash
13
- attr_reader :encoded_fields
14
- attr_reader :key
15
-
16
11
  def initialize(unencrypted_hash, encryption, key)
17
12
  @encryption = encryption
18
13
  @unencrypted_hash = unencrypted_hash
@@ -28,8 +23,12 @@ module SecureDataBag
28
23
  data.merge({encryption:encryption_hash})
29
24
  end
30
25
 
26
+ attr_reader :unencrypted_hash
27
+ attr_reader :encoded_fields
28
+ attr_reader :encryption
29
+ attr_reader :key
30
+
31
31
  def encrypted_hash
32
- pp "encrypted_hash"
33
32
  @encrypted_data ||= begin
34
33
  encrypt_hash(unencrypted_hash.dup)
35
34
  end
@@ -11,27 +11,21 @@ module SecureDataBag
11
11
  # crypto functions, it should be used the same way.
12
12
  #
13
13
 
14
- class SecureDataBagItem < Chef::DataBagItem
15
- def initialize
16
- super
17
-
18
- @secret = nil
19
- @key = nil
20
- @cipher = nil
21
- @iv = nil
22
- @algorithm = nil
23
- @encoded_fields = []
24
- @default_encoded_fields = ["password"]
14
+ class Item < Chef::DataBagItem
15
+ def initialize(key=nil)
16
+ super()
17
+
18
+ @secret = Chef::Config[:encrypted_data_bag_secret]
19
+ @key = key
20
+ @raw_data = {}
21
+ @encode_fields = ["password"]
25
22
  end
26
23
 
27
24
  #
28
- # Define attributes for encryption related tasks
25
+ # Methods for encryption key
29
26
  #
30
27
 
31
- attr_reader :default_encoded_fields
32
-
33
28
  def secret(arg=nil)
34
- arg ||= Chef::Config[:encrypted_data_bag_secret] if @secret.nil?
35
29
  set_or_return(:secret, arg, kind_of: String)
36
30
  end
37
31
 
@@ -41,86 +35,123 @@ module SecureDataBag
41
35
  end
42
36
 
43
37
  def load_key
44
- unless secret
45
- raise ArgumentError, "No secret specified and no secret found."
46
- end
38
+ @key = self.class.load_secret(secret)
39
+ end
40
+
41
+ def self.load_secret(path=nil)
42
+ path ||= Chef::Config[:encrypted_data_bag_secret]
47
43
 
48
- key = case secret
49
- when /^\w+:\/\// # Remove key
50
- begin
51
- Kernel.open(path).read.strip
52
- rescue Errno::ECONNREFUSED
53
- raise ArgumentError, "Remove key not available from '#{secret}'"
54
- rescue OpenURI::HTTPError
55
- raise ArgumentError, "Remove key not found at '#{secret}'"
56
- end
57
- else
58
- unless File.exist?(secret)
59
- raise Errno::ENOENT, "file not found '#{secret}'"
60
- end
61
- IO.read(secret).strip
44
+ unless path
45
+ raise ArgumentError, "No secret specified and no secret found."
62
46
  end
63
47
 
48
+ key = case path
49
+ when /^\w+:\/\// # Remove key
50
+ begin
51
+ Kernel.open(path).read.strip
52
+ rescue Errno::ECONNREFUSED
53
+ raise ArgumentError, "Remove key not available from '#{path}'"
54
+ rescue OpenURI::HTTPError
55
+ raise ArgumentError, "Remove key not found at '#{path}'"
56
+ end
57
+ else
58
+ unless File.exist?(path)
59
+ raise Errno::ENOENT, "file not found '#{path}'"
60
+ end
61
+ IO.read(path).strip
62
+ end
63
+
64
64
  if key.size < 1
65
- raise ArgumentError, "invalid zero length secret in '#{secret}'"
65
+ raise ArgumentError, "invalid zero length path in '#{path}'"
66
66
  end
67
+
67
68
  key
68
69
  end
69
70
 
70
- def cipher(arg=nil)
71
- arg ||= "aes-256-cbc" if @cipher.nil?
72
- set_or_return(:cipher, arg, kind_of: String)
73
- end
71
+ #
72
+ # Wrapper for raw_data encryption settings
73
+ # - always ensure that encryption hash is present
74
+ # - always ensure encryption settings have defaults
75
+ #
74
76
 
75
- def iv(arg=nil)
76
- set_or_return(:iv, arg, kind_of: String)
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
77
85
  end
78
86
 
79
87
  #
80
- # These are either the fields which are currently encoded or those that
81
- # we wish to encode
88
+ # Setter for @raw_data
89
+ # - ensure the data we receive is a Mash to support symbols
90
+ # - pass it to DataBagItem for additional validation
91
+ # - ensure the data has the encryption hash
92
+ # - decode the data
82
93
  #
83
94
 
84
- def encoded_fields(arg=nil)
85
- arg = arg.uniq if arg
86
- set_or_return(:encoded_fields, arg, kind_of: Array)
95
+ def raw_data=(data)
96
+ data = Mash.new(data)
97
+ super data
98
+ encryption
99
+ decode_data!
87
100
  end
88
101
 
89
102
  #
90
- # The encryption definition
103
+ # Determine whether the data is encoded or not
104
+ # - yeah, it's pretty naive
91
105
  #
92
106
 
93
- def encryption
94
- {
95
- iv: iv,
96
- cipher: cipher,
97
- encoded_fields: encoded_fields + default_encoded_fields
98
- }
107
+ def encoded?
108
+ not encryption[:encoded_fields].empty?
99
109
  end
100
110
 
101
- def raw_data=(enc_data)
102
- super enc_data
103
- decode_data
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
115
+ #
116
+
117
+ def encode_fields(arg=nil)
118
+ arg = Array(arg).uniq if arg
119
+ set_or_return(:encode_fields, arg, kind_of: Array).uniq
104
120
  end
105
121
 
106
122
  #
107
123
  # Encoder / Decoder
108
124
  #
109
125
 
110
- def decode_data
111
- if @raw_data.key? :encryption
112
- encryption = @raw_data.delete(:encryption) || {}
126
+ def decode_data!
127
+ #
128
+ # Ensure that we save previously encoded fields into our list of fields
129
+ # we wish to encode next time
130
+ #
131
+ encode_fields.concat(encryption[:encoded_fields]).uniq!
132
+ @raw_data = decoded_data if encoded?
133
+ @raw_data
134
+ end
113
135
 
114
- cipher encryption[:cipher]
115
- iv encryption[:iv]
116
- encoded_fields encryption[:encoded_fields]
136
+ def decoded_data
137
+ data = Decryptor.new(raw_data, encryption, key).for_decrypted_item
138
+ data[:encryption][:encoded_fields] = []
139
+ data
140
+ end
117
141
 
118
- @raw_data = decode_data
119
- end
120
- @raw_data
142
+ def encode_data!
143
+ @raw_data = encoded_data
121
144
  end
122
145
 
123
- def encode_data
146
+ def encoded_data
147
+ #
148
+ # When encoding data we'll merge those fields already encoded during
149
+ # the previous state, found in encryption[:encoded_fields] with those
150
+ # which we wish to encode
151
+ #
152
+ encryption = self.encryption.dup
153
+ encryption[:encoded_fields] = @encode_fields.
154
+ concat(encryption[:encoded_fields]).uniq
124
155
  Encryptor.new(raw_data, encryption, key).for_encrypted_item
125
156
  end
126
157
 
@@ -128,21 +159,20 @@ module SecureDataBag
128
159
  # Transitions
129
160
  #
130
161
 
131
- def self.from_hash(h)
132
- m = Mash.new(h)
133
- item = new
134
- item.raw_data = m
162
+ def self.from_hash(h, key=nil)
163
+ item = new(key)
164
+ item.raw_data = h
135
165
  item
136
166
  end
137
167
 
138
- def self.from_item(h)
139
- item = self.from_hash(h.to_hash)
168
+ def self.from_item(h, key=nil)
169
+ item = self.from_hash(h.to_hash, key)
140
170
  item.data_bag h.data_bag
141
171
  item
142
172
  end
143
173
 
144
- def to_hash
145
- result = encode_data
174
+ def to_hash(encoded = true)
175
+ result = encoded ? encoded_data : decoded_data
146
176
  result["chef_type"] = "data_bag_item"
147
177
  result["data_bag"] = self.data_bag
148
178
  result
@@ -154,7 +184,7 @@ module SecureDataBag
154
184
  json_class: "Chef::DataBagItem",
155
185
  chef_type: "data_bag_item",
156
186
  data_bag: self.data_bag,
157
- raw_data: encode_data
187
+ raw_data: encoded_data
158
188
  }
159
189
  result.to_json(*a)
160
190
  end
@@ -1,5 +1,5 @@
1
1
 
2
2
  module SecureDataBag
3
- VERSION = "1.0.5"
3
+ VERSION = "1.1.1"
4
4
  end
5
5
 
@@ -8,16 +8,19 @@ Gem::Specification.new do |spec|
8
8
  spec.version = SecureDataBag::VERSION
9
9
  spec.authors = ["Jonathan Serafini"]
10
10
  spec.email = ["jonathan@lightspeedretail.com"]
11
- spec.summary = 'Partially encrypted chef data bag items'
12
- spec.description = 'Provides a mechanism to partially encrypt data bag items and therefore ensure that they are searchable'
13
- spec.homepage = ""
11
+ spec.summary = "Per-field data bag item encryption"
12
+ spec.description = "Provides a mechanism to partially encrypt data bag items and therefore ensure that they are searchable"
14
13
  spec.license = "MIT"
15
- spec.homepage = 'https://github.com/lightspeedretail'
14
+ spec.homepage =
15
+ "https://github.com/JonathanSerafini/chef-secure_data_bag"
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- #spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_dependency 'chef'
22
+ spec.add_dependency 'yajl-ruby'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.6"
22
25
  spec.add_development_dependency "rake"
23
26
  end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_data_bag
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.1
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-14 00:00:00.000000000 Z
11
+ date: 2014-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yajl-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
13
55
  - !ruby/object:Gem::Dependency
14
56
  name: rake
15
57
  requirement: !ruby/object:Gem::Requirement
@@ -37,13 +79,19 @@ files:
37
79
  - LICENSE.txt
38
80
  - README.md
39
81
  - Rakefile
82
+ - lib/chef/dsl/data_query.rb
83
+ - lib/chef/knife/secure_bag_base.rb
84
+ - lib/chef/knife/secure_bag_create.rb
85
+ - lib/chef/knife/secure_bag_edit.rb
86
+ - lib/chef/knife/secure_bag_from_file.rb
87
+ - lib/chef/knife/secure_bag_show.rb
40
88
  - lib/secure_data_bag.rb
41
89
  - lib/secure_data_bag/decryptor.rb
42
90
  - lib/secure_data_bag/encryptor.rb
43
91
  - lib/secure_data_bag/secure_data_bag_item.rb
44
92
  - lib/secure_data_bag/version.rb
45
93
  - secure_data_bag.gemspec
46
- homepage: https://github.com/lightspeedretail
94
+ homepage: https://github.com/JonathanSerafini/chef-secure_data_bag
47
95
  licenses:
48
96
  - MIT
49
97
  metadata: {}
@@ -66,5 +114,5 @@ rubyforge_project:
66
114
  rubygems_version: 2.1.11
67
115
  signing_key:
68
116
  specification_version: 4
69
- summary: Partially encrypted chef data bag items
117
+ summary: Per-field data bag item encryption
70
118
  test_files: []