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)