From 8c3d4f2b24b82c124577228e62904876c816bf23 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Fri, 19 Jan 2024 14:53:33 +0000 Subject: [PATCH] Allow encryption without compression Add a `compress` option to ActiveRecord::Encryption::Encryptor, which defaults to `true`. When set to `false`, the encryptor will never compress the data. This is useful for cases where the data is already compressed. This can be used with the `encryptor` option in the model: ```ruby class Record < ApplicationRecord encrypts :field, encryptor: ActiveRecord::Encryption::Encryptor.new(compress: false) end ``` --- activerecord/CHANGELOG.md | 12 ++++++++++++ .../lib/active_record/encryption/encryptor.rb | 14 +++++++++++++- .../test/cases/encryption/encryptor_test.rb | 9 +++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index df4614ccb0..1c385682b6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,15 @@ +* Add an option to `ActiveRecord::Encryption::Encryptor` to disable compression + + Allow compression to be disabled by setting `compress: false` + + ```ruby + class User + encrypts :name, encryptor: ActiveRecord::Encryption::Encryptor.new(compress: false) + end + ``` + + *Donal McBreen* + * Deprecate passing strings to `ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename`. A `ActiveRecord::DatabaseConfigurations::DatabaseConfig` object should be passed instead. diff --git a/activerecord/lib/active_record/encryption/encryptor.rb b/activerecord/lib/active_record/encryption/encryptor.rb index 1c19d9e545..1ecc096436 100644 --- a/activerecord/lib/active_record/encryption/encryptor.rb +++ b/activerecord/lib/active_record/encryption/encryptor.rb @@ -12,6 +12,14 @@ module Encryption # It interacts with a KeyProvider for getting the keys, and delegate to # ActiveRecord::Encryption::Cipher the actual encryption algorithm. class Encryptor + # === Options + # + # * :compress - Boolean indicating whether records should be compressed before encryption. + # Defaults to +true+. + def initialize(compress: true) + @compress = compress + end + # Encrypts +clean_text+ and returns the encrypted result # # Internally, it will: @@ -111,13 +119,17 @@ def serializer # Under certain threshold, ZIP compression is actually worse that not compressing def compress_if_worth_it(string) - if string.bytesize > THRESHOLD_TO_JUSTIFY_COMPRESSION + if compress? && string.bytesize > THRESHOLD_TO_JUSTIFY_COMPRESSION [compress(string), true] else [string, false] end end + def compress? + @compress + end + def compress(data) Zlib::Deflate.deflate(data).tap do |compressed_data| compressed_data.force_encoding(data.encoding) diff --git a/activerecord/test/cases/encryption/encryptor_test.rb b/activerecord/test/cases/encryption/encryptor_test.rb index 1195a6cdfa..2d0e683635 100644 --- a/activerecord/test/cases/encryption/encryptor_test.rb +++ b/activerecord/test/cases/encryption/encryptor_test.rb @@ -48,6 +48,15 @@ class ActiveRecord::Encryption::EncryptorTest < ActiveRecord::EncryptionTestCase assert cipher_text.bytesize < content.bytesize end + test "content is not compressed, when disabled" do + @encryptor = ActiveRecord::Encryption::Encryptor.new(compress: false) + content = SecureRandom.hex(5.kilobytes) + cipher_text = @encryptor.encrypt(content) + + assert_encrypt_text content + assert cipher_text.bytesize > content.bytesize + end + test "trying to encrypt custom classes raises a ForbiddenClass exception" do assert_raises ActiveRecord::Encryption::Errors::ForbiddenClass do @encryptor.encrypt(Struct.new(:name).new("Jorge"))