Skip to content

Commit

Permalink
Merge pull request #99 from FundingCircle/CF-471-transit-json-codec-b…
Browse files Browse the repository at this point in the history
…atch-encryption

Add batch methods to TransitJsonCodec
  • Loading branch information
ellispritchard authored Jul 1, 2024
2 parents 24fc43c + 209e794 commit 0dadfef
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Vault Rails Changelog

## 2.2.0 (June 28, 2024)
* Adds `TransitJsonCodec.batch_*` functions which use the Vault batch API to process
an array of values with one API call.
* like `Vault::Rails.batch_decrypt` but with `transit` and FC's standard JSON pre-encoding of values.

## 2.1.2 (November 17, 2023)

IMPROVEMENTS
Expand Down
2 changes: 1 addition & 1 deletion lib/vault/rails/version.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Vault
module Rails
VERSION = '2.1.2'
VERSION = '2.2.0'

def self.latest?
ActiveRecord.version >= Gem::Version.new('5.0.0')
Expand Down
22 changes: 22 additions & 0 deletions lib/vault/transit_json_codec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ def encrypt(plaintext)
secret.data[:ciphertext]
end

def batch_encrypt(plaintexts)
return [] if plaintexts.blank?

secrets = Vault.logical.write(
"transit/encrypt/#{key}",
batch_input: plaintexts.map { |plaintext| { plaintext: Base64.strict_encode64(Oj.dump(plaintext)) } }
)

secrets.data[:batch_results].map { |result| result[:ciphertext] }
end

def decrypt(ciphertext)
return if ciphertext.blank?

Expand All @@ -29,6 +40,17 @@ def decrypt(ciphertext)
Oj.load(Base64.strict_decode64(secret.data[:plaintext]))
end

def batch_decrypt(ciphertexts)
return [] if ciphertexts.blank?

secret = Vault.logical.write(
"transit/decrypt/#{key}",
batch_input: ciphertexts.map { |ciphertext| { ciphertext: ciphertext } }
)

secret.data[:batch_results].map { |result| Oj.load(Base64.strict_decode64(result[:plaintext])) }
end

private

attr_reader :key
Expand Down
63 changes: 63 additions & 0 deletions spec/unit/transit_json_codec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,67 @@
end
end
end

describe '#batch_encrypt' do
context 'when plaintexts array is empty' do
it 'returns empty array' do
expect(codec.batch_encrypt([])).to eq([])
expect(codec.batch_encrypt(nil)).to eq([])
end
end

context 'when plaintexts are present' do
let(:plaintexts) { ['some text', 'other text'] }

it 'returns array of encrypted values' do
ciphertexts = codec.batch_encrypt(plaintexts)
expect(ciphertexts).not_to be_blank
expect(ciphertexts.length).to eq(plaintexts.length)
ciphertexts.each { |ciphertext| expect(ciphertext).to start_with('vault:v1:') }
ciphertexts.each_with_index { |ciphertext, i| expect(codec.decrypt(ciphertext)).to eq(plaintexts[i]) }
end

context 'when encryption fails' do
before do
allow(Vault).to receive(:logical).and_raise(StandardError, 'Oh no!')
end

it 're-raises error' do
expect { codec.batch_encrypt(plaintexts) }.to raise_error(StandardError)
end
end
end
end

describe '#batch_decrypt' do
context 'when ciphertexts array is empty' do
it 'returns empty array' do
expect(codec.batch_decrypt([])).to eq([])
expect(codec.batch_decrypt(nil)).to eq([])
end
end

context 'when ciphertexts are present' do
let(:plaintexts) { ['some text', 'other text'] }
let(:ciphertexts) { codec.batch_encrypt(plaintexts) }

it 'returns array of decrypted values' do
decrypted = codec.batch_decrypt(ciphertexts)
expect(decrypted).not_to be_blank
expect(decrypted.length).to eq(plaintexts.length)
expect(decrypted).to eq(plaintexts)
end

context 'when decryption fails' do
before do
allow(Vault).to receive(:logical).and_raise(StandardError, 'Oh no!')
end

it 're-raises error' do
expect { codec.batch_decrypt(plaintexts) }.to raise_error(StandardError)
end
end
end
end

end

0 comments on commit 0dadfef

Please sign in to comment.