lib/mongoid/kms.rb in mongoid-kms-0.0.14 vs lib/mongoid/kms.rb in mongoid-kms-0.0.15
- old
+ new
@@ -4,77 +4,148 @@
module Mongoid
module Kms
extend ActiveSupport::Concern
+ included do
+ @kms_field_map ||= {}
+
+ unless self.ancestors.include?(ActiveModel::Dirty)
+ include ActiveModel::Dirty
+ end
+ end
+
@configuration = {}
@kms = nil
- def self.configure(args)
- @configuration = args
- end
+ # Module methods
+ class << self
+ def configure(args)
+ @configuration = args
+ end
- def self.configuration
- @configuration || {}
- end
+ def configuration
+ @configuration || {}
+ end
- def self.kms
- @kms ||= Aws::KMS::Client.new(region: self.region)
- end
+ def kms
+ @kms ||= Aws::KMS::Client.new(region: self.region)
+ end
- def self.region
- configuration[:region]
- end
+ def region
+ configuration[:region]
+ end
- def self.key
- configuration[:key]
+ def key
+ configuration[:key]
+ end
+
+ def bson_class
+ if defined? Moped::BSON
+ Moped::BSON
+ elsif defined? BSON
+ BSON
+ end
+ end
end
- def self.bson_class
- if defined? Moped::BSON
- Moped::BSON
- elsif defined? BSON
- BSON
+ # Instance methods
+ def set_kms_values
+ self.class.kms_field_map.each do |field_name, settings|
+ if self.send("#{field_name}_changed?") || kms_context_value_changed?(field_name)
+ encrypted_field_name = self.class.get_encrypted_field_name(field_name)
+
+ if instance_variable_get("@#{field_name}").nil? && kms_context_value_changed?(field_name)
+ value = self.class.decrypt_field(self, field_name, self.send(encrypted_field_name), self.class.kms_context_was(self, field_name))
+ else
+ value = send("#{field_name}")
+ end
+
+ if value.nil?
+ self.send("#{encrypted_field_name}=", nil)
+ else
+ self.send("#{encrypted_field_name}=", self.class.encrypt_field(self, field_name, value))
+ end
+ end
end
end
+ def kms_context_value_changed?(field_name)
+ self.class.kms_context_array(self, field_name).find { |f| self.respond_to?(f) && self.send("#{f}_changed?") }
+ end
+
+ # Class methods
module ClassMethods
+ def kms_field_map
+ @kms_field_map
+ end
+
def encrypt_field(object, field_name, value)
Mongoid::Kms.kms.encrypt({
key_id: Mongoid::Kms.key,
plaintext: value,
encryption_context: kms_context(object, field_name)
})[:ciphertext_blob].force_encoding('UTF-8')
- rescue ArgumentError
- raise "Error using KMS context. If you use an object's field for context, set your encrypted fields explicitly: myobject.#{field_name} = #{value.inspect}"
end
- def decrypt_field(object, field_name, data)
+ def decrypt_field(object, field_name, data, encryption_context = nil)
+ encryption_context ||= kms_context(object, field_name)
+
Mongoid::Kms.kms.decrypt({
ciphertext_blob: data,
- encryption_context: kms_context(object, field_name)
+ encryption_context: encryption_context
})[:plaintext]
end
def kms_context(object, field_name)
- c = @kms_field_map[field_name.to_s][:context]
- c = c.call(object) if c.is_a?(Proc)
- c
+ kms_context_array(object, field_name).inject({}) do |hash, key|
+ if object.respond_to?(key)
+ hash[key] = object.send(key)
+ else
+ hash[key] = key
+ end
+
+ hash
+ end
end
+ def kms_context_was(object, field_name)
+ kms_context_array(object, field_name).inject({}) do |hash, key|
+ if object.respond_to?("#{key}_was") && object.send("#{key}_changed?")
+ hash[key] = object.send("#{key}_was")
+ elsif object.respond_to?(key)
+ hash[key] = object.send(key)
+ else
+ hash[key] = key
+ end
+
+ hash
+ end
+ end
+
+ def kms_context_array(object, field_name)
+ @kms_field_map[field_name.to_s][:context] || []
+ end
+
def kms_type(field_name)
@kms_field_map[field_name.to_s][:type]
end
+ def get_encrypted_field_name(field_name)
+ "kms_secure_#{field_name}"
+ end
+
def secure_field(field_name, args)
- encrypted_field_name = "kms_secure_#{field_name}"
+ encrypted_field_name = get_encrypted_field_name(field_name)
- @kms_field_map ||= {}
@kms_field_map[field_name.to_s] = {context: args.delete(:context), type: args.delete(:type)}
field encrypted_field_name, args.merge(type: Mongoid::Kms.bson_class::Binary)
+ define_attribute_methods field_name.to_sym
+ before_save :set_kms_values
+
define_method(field_name) do
instance_variable_get("@#{field_name}") || begin
raw = send("kms_secure_#{field_name}")
if raw.nil?
@@ -86,16 +157,11 @@
end
end
end
define_method("#{field_name}=") do |value|
+ self.send("#{field_name}_will_change!")
instance_variable_set("@#{field_name}", value)
-
- if value.nil?
- self.send("#{encrypted_field_name}=", nil)
- else
- self.send("#{encrypted_field_name}=", self.class.encrypt_field(self, field_name, value))
- end
end
end
end
end
end