package com.mastercard.developer.interceptors; import com.mastercard.developer.encryption.EncryptionException; import com.mastercard.developer.encryption.FieldLevelEncryptionConfig; import com.mastercard.developer.test.TestUtils; import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; import java.lang.reflect.Type; import static com.mastercard.developer.test.TestUtils.getTestFieldLevelEncryptionConfigBuilder; import static org.hamcrest.core.Is.isA; import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class OpenFeignFieldLevelEncryptionEncoderTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testEncode_ShouldEncryptRequestPayloadAndUpdateContentLengthHeader() throws Exception { // GIVEN FieldLevelEncryptionConfig config = getTestFieldLevelEncryptionConfigBuilder() .withEncryptionPath("$.foo", "$.encryptedFoo") .build(); Type type = mock(Type.class); Encoder delegate = mock(Encoder.class); Object object = mock(Object.class); RequestTemplate request = mock(RequestTemplate.class); when(request.body()).thenReturn("{\"foo\":\"bar\"}".getBytes()); // WHEN OpenFeignFieldLevelEncryptionEncoder instanceUnderTest = new OpenFeignFieldLevelEncryptionEncoder(config, delegate); instanceUnderTest.encode(object, type, request); // THEN verify(delegate).encode(object, type, request); verify(request).body(); ArgumentCaptor<String> encryptedPayloadCaptor = ArgumentCaptor.forClass(String.class); verify(request).body(encryptedPayloadCaptor.capture()); verify(request).header(eq("Content-Length"), anyString()); String encryptedPayload = encryptedPayloadCaptor.getValue(); assertFalse(encryptedPayload.contains("foo")); assertTrue(encryptedPayload.contains("encryptedFoo")); } @Test public void testEncode_ShouldDoNothing_WhenNoPayload() throws Exception { // GIVEN FieldLevelEncryptionConfig config = getTestFieldLevelEncryptionConfigBuilder() .withEncryptionPath("$.foo", "$.encryptedFoo") .build(); Type type = mock(Type.class); Encoder delegate = mock(Encoder.class); Object object = mock(Object.class); RequestTemplate request = mock(RequestTemplate.class); when(request.body()).thenReturn(null); // WHEN OpenFeignFieldLevelEncryptionEncoder instanceUnderTest = new OpenFeignFieldLevelEncryptionEncoder(config, delegate); instanceUnderTest.encode(object, type, request); // THEN verify(request).body(); verifyNoMoreInteractions(request); } @Test public void testEncode_ShouldDoNothing_WhenEmptyPayload() throws Exception { // GIVEN FieldLevelEncryptionConfig config = getTestFieldLevelEncryptionConfigBuilder() .withEncryptionPath("$.foo", "$.encryptedFoo") .build(); Type type = mock(Type.class); Encoder delegate = mock(Encoder.class); Object object = mock(Object.class); RequestTemplate request = mock(RequestTemplate.class); when(request.body()).thenReturn("".getBytes()); // WHEN OpenFeignFieldLevelEncryptionEncoder instanceUnderTest = new OpenFeignFieldLevelEncryptionEncoder(config, delegate); instanceUnderTest.encode(object, type, request); // THEN verify(request).body(); verifyNoMoreInteractions(request); } @Test public void testEncode_ShouldThrowEncodeException_WhenEncryptionFails() throws Exception { // GIVEN FieldLevelEncryptionConfig config = getTestFieldLevelEncryptionConfigBuilder() .withEncryptionPath("$.foo", "$.encryptedFoo") .withEncryptionCertificate(TestUtils.getTestInvalidEncryptionCertificate()) // Invalid certificate .build(); Type type = mock(Type.class); Encoder delegate = mock(Encoder.class); Object object = mock(Object.class); RequestTemplate request = mock(RequestTemplate.class); when(request.body()).thenReturn("{\"foo\":\"bar\"}".getBytes()); // THEN expectedException.expect(EncodeException.class); expectedException.expectMessage("Failed to intercept and encrypt request!"); expectedException.expectCause(isA(EncryptionException.class)); // WHEN OpenFeignFieldLevelEncryptionEncoder instanceUnderTest = new OpenFeignFieldLevelEncryptionEncoder(config, delegate); instanceUnderTest.encode(object, type, request); } @Test public void testEncode_ShouldEncryptRequestPayloadAndAddEncryptionHttpHeaders_WhenRequestedInConfig() throws Exception { // GIVEN FieldLevelEncryptionConfig config = getTestFieldLevelEncryptionConfigBuilder() .withEncryptionPath("$.foo", "$.encryptedFoo") .withIvHeaderName("x-iv") .withEncryptedKeyHeaderName("x-encrypted-key") .withOaepPaddingDigestAlgorithmHeaderName("x-oaep-padding-digest-algorithm") .withEncryptionCertificateFingerprintHeaderName("x-encryption-certificate-fingerprint") .withEncryptionKeyFingerprintHeaderName("x-encryption-key-fingerprint") .build(); Type type = mock(Type.class); Encoder delegate = mock(Encoder.class); Object object = mock(Object.class); RequestTemplate request = mock(RequestTemplate.class); when(request.body()).thenReturn("{\"foo\":\"bar\"}".getBytes()); // WHEN OpenFeignFieldLevelEncryptionEncoder instanceUnderTest = new OpenFeignFieldLevelEncryptionEncoder(config, delegate); instanceUnderTest.encode(object, type, request); // THEN verify(delegate).encode(object, type, request); verify(request).body(); ArgumentCaptor<String> encryptedPayloadCaptor = ArgumentCaptor.forClass(String.class); verify(request).body(encryptedPayloadCaptor.capture()); verify(request).header(eq("Content-Length"), anyString()); String encryptedPayload = encryptedPayloadCaptor.getValue(); assertFalse(encryptedPayload.contains("foo")); assertTrue(encryptedPayload.contains("encryptedFoo")); verify(request).header(eq("x-iv"), anyString()); verify(request).header(eq("x-encrypted-key"), anyString()); verify(request).header("x-oaep-padding-digest-algorithm", "SHA256"); verify(request).header("x-encryption-certificate-fingerprint", "80810fc13a8319fcf0e2ec322c82a4c304b782cc3ce671176343cfe8160c2279"); verify(request).header("x-encryption-key-fingerprint", "761b003c1eade3a5490e5000d37887baa5e6ec0e226c07706e599451fc032a79"); } }