Skip to content

Commit a38ba5d

Browse files
committed
Only calculate encrypted values if value has changed
1 parent bb69f81 commit a38ba5d

File tree

3 files changed

+40
-16
lines changed

3 files changed

+40
-16
lines changed

lib/diffcrypt/encryptor.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# rubocop:disable Layout/LineLength
12
# frozen_string_literal: true
23

34
require 'pathname'
@@ -41,9 +42,11 @@ def decrypt_hash(data)
4142
end
4243

4344
# @param [String] contents The raw YAML string to be encrypted
44-
def encrypt(contents)
45+
# @param [String, nil] original_encrypted_contents The original (encrypted) content to determine which keys have changed
46+
def encrypt(contents, original_encrypted_contents = nil)
4547
yaml = YAML.safe_load contents
46-
encrypted = encrypt_values yaml
48+
original_yaml = original_encrypted_contents ? YAML.safe_load(original_encrypted_contents) : nil
49+
encrypted = encrypt_values yaml, original_yaml
4750
YAML.dump encrypted
4851
end
4952

@@ -53,18 +56,24 @@ def encrypt_string(value)
5356
@encryptor.encrypt_and_sign value
5457
end
5558

59+
# TODO: Fix the complexity of this method
60+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
5661
# @param [Hash] keys
5762
# @return [Hash]
58-
def encrypt_values(data)
63+
def encrypt_values(data, original_data = nil)
5964
data.each do |key, value|
65+
original_encrypted_value = original_data ? original_data[key] : nil
6066
data[key] = if value.is_a?(Hash) || value.is_a?(Array)
61-
encrypt_values(value)
67+
encrypt_values(value, original_encrypted_value)
6268
else
63-
encrypt_string value
69+
original_decrypted_value = original_data ? decrypt_string(original_encrypted_value) : nil
70+
key_changed = original_decrypted_value.nil? || original_decrypted_value != value
71+
key_changed ? encrypt_string(value) : original_encrypted_value
6472
end
6573
end
6674
data
6775
end
76+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
6877

6978
# @param [String] value The encrypted value that needs decrypting
7079
# @return [String]
@@ -73,3 +82,5 @@ def decrypt_string(value)
7382
end
7483
end
7584
end
85+
86+
# rubocop:enable Layout/LineLength

lib/diffcrypt/rails/encrypted_configuration.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ def read
3939
''
4040
end
4141

42-
def write(contents)
42+
def write(contents, original_encrypted_contents = nil)
4343
deserialize(contents)
4444

45-
IO.binwrite "#{content_path}.tmp", encrypt(contents)
45+
IO.binwrite "#{content_path}.tmp", encrypt(contents, original_encrypted_contents)
4646
FileUtils.mv "#{content_path}.tmp", content_path
4747
end
4848

@@ -62,6 +62,7 @@ def change(&block)
6262

6363
protected
6464

65+
# rubocop:disable Metrics/AbcSize
6566
def writing(contents)
6667
tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
6768
tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
@@ -71,15 +72,17 @@ def writing(contents)
7172

7273
updated_contents = tmp_path.binread
7374

74-
write(updated_contents) if updated_contents != contents
75+
write(updated_contents, content_path.binread)
7576
ensure
7677
FileUtils.rm(tmp_path) if tmp_path&.exist?
7778
end
79+
# rubocop:enable Metrics/AbcSize
7880

79-
# @param [String] contents
80-
# @return [String]
81-
def encrypt(contents)
82-
encryptor.encrypt contents
81+
# @param [String] contents The new content to be encrypted
82+
# @param [String] diff_against The original (encrypted) content to determine which keys have changed
83+
# @return [String] Encrypted content to commit
84+
def encrypt(contents, original_encrypted_contents = nil)
85+
encryptor.encrypt contents, original_encrypted_contents
8386
end
8487

8588
# @param [String] contents

test/diffcrypt/encryptor_test.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ def test_it_decrypts_root_values
2121
end
2222

2323
def test_it_encrypts_root_values
24-
encrypted_content = <<~CONTENT
24+
content = <<~CONTENT
2525
---
2626
secret_key_base: secret_key_base_test
2727
CONTENT
2828
expected_pattern = /---\nsecret_key_base: #{ENCRYPTED_VALUE_PATTERN}\n/
2929

30-
assert_match expected_pattern, Diffcrypt::Encryptor.new(TEST_KEY).encrypt(encrypted_content)
30+
assert_match expected_pattern, Diffcrypt::Encryptor.new(TEST_KEY).encrypt(content)
3131
end
3232

3333
def test_it_decrypts_nested_structures
@@ -47,14 +47,24 @@ def test_it_decrypts_nested_structures
4747
end
4848

4949
def test_it_encrypts_nested_structures
50-
encrypted_content = <<~CONTENT
50+
content = <<~CONTENT
5151
---
5252
secret_key_base: secret_key_base_test
5353
aws:
5454
access_key_id: AKIAXXX
5555
CONTENT
5656
expected_pattern = /---\nsecret_key_base: #{ENCRYPTED_VALUE_PATTERN}\naws:\n access_key_id: #{ENCRYPTED_VALUE_PATTERN}\n/
5757

58-
assert_match expected_pattern, Diffcrypt::Encryptor.new(TEST_KEY).encrypt(encrypted_content)
58+
assert_match expected_pattern, Diffcrypt::Encryptor.new(TEST_KEY).encrypt(content)
59+
end
60+
61+
# Verifies that a change to one key does not cause the encrypted values for other keys to be recomputed
62+
# Mainly used in conjunction with rails credentials editor
63+
def test_it_only_updates_changed_values
64+
original_encrypted_content = "---\nsecret_key_base_1: 88Ry6HESUoXBr6QUFXmni9zzfCIYt9qGNFvIWFcN--4xoecI5mqbNRBibI--62qPJbkzzh5h8lhFEFOSaQ==\naws:\n secret_access_key: 88Ry6HESUoXBr6QUFXmni9zzfCIYt9qGNFvIWFcN--4xoecI5mqbNRBibI--62qPJbkzzh5h8lhFEFOSaQ==\n"
65+
updated_content = "---\nsecret_key_base_1: secret_key_base_test\naws:\n secret_access_key: secret_access_key_2"
66+
expected_pattern = /---\nsecret_key_base_1: 88Ry6HESUoXBr6QUFXmni9zzfCIYt9qGNFvIWFcN--4xoecI5mqbNRBibI--62qPJbkzzh5h8lhFEFOSaQ==\naws:\n secret_access_key: #{ENCRYPTED_VALUE_PATTERN}\n/
67+
68+
assert_match expected_pattern, Diffcrypt::Encryptor.new(TEST_KEY).encrypt(updated_content, original_encrypted_content)
5969
end
6070
end

0 commit comments

Comments
 (0)