lib/kms_encrypted/model.rb in kms_encrypted-1.1.1 vs lib/kms_encrypted/model.rb in kms_encrypted-1.2.0

- old
+ new

@@ -105,30 +105,71 @@ model_name: model_name.to_s, model_id: id } end + # automatically detects attributes and files where the encryption key is: + # 1. a symbol that matches kms key method exactly + # does not detect attributes and files where the encryption key is: + # 1. callable (warns) + # 2. a symbol that internally calls kms key method + # it could try to get the exact key and compare + # (there's a very small chance this could have false positives) + # but bias towards simplicity for now + # TODO possibly raise error for callable keys in 2.0 + # with option to override/specify attributes define_method("rotate_#{key_method}!") do # decrypt plaintext_attributes = {} # attr_encrypted if self.class.respond_to?(:encrypted_attributes) - self.class.encrypted_attributes.select { |_, v| v[:key] == key_method.to_sym }.keys.each do |key| - plaintext_attributes[key] = send(key) + self.class.encrypted_attributes.each do |key, v| + if v[:key] == key_method.to_sym + plaintext_attributes[key] = send(key) + elsif v[:key].respond_to?(:call) + warn "[kms_encrypted] Can't detect if encrypted attribute uses this key" + end end end # lockbox attributes + # only checks key, not previous versions if self.class.respond_to?(:lockbox_attributes) - self.class.lockbox_attributes.select { |_, v| v[:key] == key_method.to_sym }.keys.each do |key| - plaintext_attributes[key] = send(key) + self.class.lockbox_attributes.each do |key, v| + if v[:key] == key_method.to_sym + plaintext_attributes[key] = send(key) + elsif v[:key].respond_to?(:call) + warn "[kms_encrypted] Can't detect if encrypted attribute uses this key" + end end end - # TODO lockbox attachments - # if self.class.respond_to?(:lockbox_attachments) - # end + # lockbox attachments + # only checks key, not previous versions + if self.class.respond_to?(:lockbox_attachments) + self.class.lockbox_attachments.each do |key, v| + if v[:key] == key_method.to_sym + # can likely add support at some point, but may be complicated + # ideally use rotate_encryption! from Lockbox + # but needs access to both old and new keys + # also need to update database atomically + raise KmsEncrypted::Error, "Can't rotate key used for encrypted files" + elsif v[:key].respond_to?(:call) + warn "[kms_encrypted] Can't detect if encrypted attachment uses this key" + end + end + end + + # CarrierWave uploaders + if self.class.respond_to?(:uploaders) + self.class.uploaders.each do |_, uploader| + # for simplicity, only checks if key is callable + if uploader.respond_to?(:lockbox_options) && uploader.lockbox_options[:key].respond_to?(:call) + warn "[kms_encrypted] Can't detect if encrypted uploader uses this key" + end + end + end # reset key instance_variable_set("@#{key_method}", nil) send("encrypted_#{key_method}=", nil)