class SecureDataBag::Item

Attributes

decryption_format[RW]

Format to enforce when decrypting this Item. This item will automatically be updated when importing decrypted data. @since 3.0.0

encrypted_keys[RW]

Array of hash keys which should be encrypted when encrypting this item. For previously decrypted items, this will contain the keys which has previously been encrypted. @since 3.0.0

encryption_format[RW]

Format to enforce when encrypting this Item. This item will automatically be updated when importing encrypted data. @since 3.0.0

Public Class Methods

from_hash(hash, opts = {}) click to toggle source

Create a new SecureDataBag::Item from a hash and optional options. @param hash [Hash] the data @param opts [Hash] the optional options to pass to Item.new @return [SecureDataBag::Item] @since 3.0.0

# File lib/secure_data_bag/item.rb, line 44
def from_hash(hash, opts = {})
  data = hash.dup
  data.delete('chef_type')
  data.delete('json_class')

  metadata = Mash.new(data.delete(SecureDataBag::METADATA_KEY) || {})
  metadata = metadata.merge(opts)

  item = new(metadata)
  item.data_bag(data.delete('data_bag')) if data.key?('data_bag')
  item.raw_data = data.key?('raw_data') ? data['raw_data'] : data
  item
end
from_item(data_bag_item, opts = {}) click to toggle source

Create a new SecureDataBag::Item from a DataBagItem. @param data_bag_item [Chef::DataBagItem] the item to create from @param opts [Hash] the optional options ot pass to Item.new @return [SecureDataBag::Item] @since 3.0.0

# File lib/secure_data_bag/item.rb, line 63
def from_item(data_bag_item, opts = {})
  data = data_bag_item.to_hash
  from_hash(data, opts)
end
load(data_bag, name, opts = {}) click to toggle source

Load a data_bag_item and convert into the a SecureDataBag::Item. @param data_bag [String] the data_bag to load the item from @param name [String] the data_bag_item id @param opts [Hash] optional options to pass to SecureDataBag::Item.new @return [SecureDataBag::Item] @since 3.0.0

# File lib/secure_data_bag/item.rb, line 28
def load(data_bag, name, opts = {})
  data = {
    'data_bag' => data_bag,
    'id' => name
  }.merge(
    Chef::DataBagItem.load(data_bag, name).to_hash
  )
  item = from_hash(data, opts)
  item
end
load_secret(path = nil) click to toggle source

Class method used to load the secret key from path @param path [String] the optional path to the file @return [String] the secret @since 3.0.0

# File lib/secure_data_bag/item.rb, line 14
def load_secret(path = nil)
  path ||= (
    Chef::Config[:knife][:secure_data_bag][:secret_file] ||
    Chef::Config[:encrypted_data_bag_secret]
  )
  Chef::EncryptedDataBagItem.load_secret(path)
end
new(opts = {}) click to toggle source

Initializer @param opts [Hash] optional options to configure the SecureDataBag::Item

opts[:data] the initial data to set
opts[:secret] the secret key to use when encrypting/decrypting
opts[:secret_path] the path to the secret key
opts[:encrypted_keys] an array of keys to encrypt
opts[:format] the SecureDataBag::Item format to enforce

@since 3.0.0

Calls superclass method
# File lib/secure_data_bag/item.rb, line 77
def initialize(opts = {})
  opts = Mash.new(opts)

  # Initiate the APIClient in Chef 12.3+
  begin super(chef_server_rest: opts.delete(:chef_server_rest))
  rescue ArgumentError; super()
  end

  # Optionally define the Item vesion
  @version = opts[:version] || SecureDataBag::VERSION

  # Optionally define the Item formats
  @encryption_format = opts[:encryption_format]
  @decryption_format = opts[:decryption_format]

  # Optionally provide the shared secret
  @secret = opts[:secret] if opts[:secret]

  # Optionally provide a path to the shared secret. If not provided, the
  # secret loader will automatically attempt to select one.
  @secret_path = opts[:secret_path]

  # Optionally provide a list of keys that should be encrypted or attempt
  # to determine it based on configuration options.
  @encrypted_keys = (
    opts[:encrypted_keys] ||
    Chef::Config[:knife][:secure_data_bag][:encrypted_keys] ||
    []
  ).uniq

  self.raw_data = opts[:data] if opts[:data]
  self
end

Public Instance Methods

metadata() click to toggle source

Hash representing the metadata associated to this Item @return [Hash] the metadata @since 3.0.0

# File lib/secure_data_bag/item.rb, line 139
def metadata
  Mash.new(
    encryption_format: @encryption_format,
    decryption_format: @decryption_format,
    encrypted_keys: @encrypted_keys,
    version: @version
  )
end
raw_data=(new_data) click to toggle source

Override the default setter to first ensure that the data is a Mash and then to automatically decrypt the data. @param new_data [Hash] the potentially encrypted data @since 3.0.0

Calls superclass method
# File lib/secure_data_bag/item.rb, line 152
def raw_data=(new_data)
  new_data = Mash.new(new_data)
  new_data.delete(SecureDataBag::METADATA_KEY)
  super(decrypt_data!(new_data))
end
secret(arg = nil) click to toggle source

Fetch, Set or optionally Load the shared secret @param arg [String] optionally set the shared set @return [String] the shared secret @since 3.0.0

# File lib/secure_data_bag/item.rb, line 131
def secret(arg = nil)
  @secret = arg unless arg.nil?
  @secret ||= load_secret
end
to_data(opts = {}) click to toggle source

Export this SecureDataBag::Item to it's raw_data @param opts [Hash] the optional options @return [Hash] @since 3.0.0

# File lib/secure_data_bag/item.rb, line 162
def to_data(opts = {})
  opts = Mash.new(opts)
  result = opts[:encrypt] ? encrypt_data(raw_data) : raw_data
  result[SecureDataBag::METADATA_KEY] = metadata if opts[:metadata]
  result
end
to_hash(opts = {}) click to toggle source

Export this SecureDataBag::Item to a Chef::DataBagItem compatible hash @param opts [Hash] the optional options @return [Hash] @since 3.0.0

# File lib/secure_data_bag/item.rb, line 173
def to_hash(opts = {})
  opts = Mash.new(opts)
  result = to_data(opts)
  result['chef_type'] = 'data_bag_item'
  result['data_bag'] = data_bag.to_s
  result
end
to_json(*a) click to toggle source

Export this SecureDataBag::Item to a Chef::DataBagItem compatible json @return [String] @since 3.0.0

# File lib/secure_data_bag/item.rb, line 184
def to_json(*a)
  result = {
    'name' => object_name,
    'json_class' => 'Chef::DataBagItem',
    'chef_type' => 'data_bag_item',
    'data_bag' => data_bag.to_s,
    'raw_data' => encrypt_data(raw_data)
  }
  result.to_json(*a)
end

Private Instance Methods

decrypt_data(data, save: false) click to toggle source

Decrypt the data, save the both the decrypted_keys and format for possible re-encryption, and return the descrypted hash. @param data [Hash] the potentially encrypted hash @param save [Boolean] whether to save the encrypted keys and format @return [Hash] the decrypted hash @since 3.0.0

# File lib/secure_data_bag/item.rb, line 211
def decrypt_data(data, save: false)
  decryptor = SecureDataBag::Decryptor.for(data, secret, metadata)
  decryptor.decrypt!
  @encrypted_keys.concat(decryptor.decrypted_keys).uniq! if save
  @decryption_format = decryptor.format if save
  decryptor.decrypted_hash
end
decrypt_data!(data) click to toggle source
# File lib/secure_data_bag/item.rb, line 219
def decrypt_data!(data)
  decrypt_data(data, save: true)
end
encrypt_data(data, _save: false) click to toggle source

Encrypt the data and save the encrypted_keys. @param data [Hash] the hash to encrypt @param save [Boolean] whether to save the encrypted keys @return [Hash] the encrypted hash @since 3.0.0

# File lib/secure_data_bag/item.rb, line 228
def encrypt_data(data, _save: false)
  encryptor = SecureDataBag::Encryptor.new(data, secret, metadata)
  encryptor.encrypt!
  encrypted_hash = encryptor.encrypted_hash
  # Ensure that protected fields are never encrypted
  %w(data_bag id).each do |k|
    encrypted_hash[k] = raw_data[k] if encrypted_hash.key?(k)
  end
  encrypted_hash
end
encrypt_data!(data) click to toggle source
# File lib/secure_data_bag/item.rb, line 239
def encrypt_data!(data)
  encrypt_data(data, save: true)
end
load_secret() click to toggle source

Load the shared secret from the configured secret_path (or auto-detect the path if undefined). @return [String] the shared secret @since 3.0.0

# File lib/secure_data_bag/item.rb, line 201
def load_secret
  @secret = self.class.load_secret(@secret_path)
end