secure_data_bag 1.0.5 → 1.1.1

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: 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: []