You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by ka...@apache.org on 2024/09/03 08:20:34 UTC

(druid) 03/04: Improve exception handling in extension druid-pac4j (#16979)

This is an automated email from the ASF dual-hosted git repository.

karan pushed a commit to branch 30.0.1
in repository https://gitbox.apache.org/repos/asf/druid.git

commit 74cab7a76c99da457c3a883939cc0b03301b8771
Author: Kashif Faraz <ka...@gmail.com>
AuthorDate: Fri Aug 30 00:02:49 2024 -0700

    Improve exception handling in extension druid-pac4j (#16979)
    
    Changes:
    - Simplify exception handling in `CryptoService` by just catching a `Exception`
    - Throw a `DruidException` as the exception is user facing
    - Log the exception for easier debugging
    - Add a test to verify thrown exception
    
    (cherry picked from commit d5b64ba2e3a4848f4d62157d9d50fec4953563fc)
---
 .../security/pac4j/Pac4jSessionStoreTest.java      | 42 ++++++++++++++++++++--
 .../org/apache/druid/crypto/CryptoService.java     | 20 +++++------
 2 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/extensions-core/druid-pac4j/src/test/java/org/apache/druid/security/pac4j/Pac4jSessionStoreTest.java b/extensions-core/druid-pac4j/src/test/java/org/apache/druid/security/pac4j/Pac4jSessionStoreTest.java
index 772bef7ef6c..e2f46a4062e 100644
--- a/extensions-core/druid-pac4j/src/test/java/org/apache/druid/security/pac4j/Pac4jSessionStoreTest.java
+++ b/extensions-core/druid-pac4j/src/test/java/org/apache/druid/security/pac4j/Pac4jSessionStoreTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.security.pac4j;
 
+import org.apache.druid.error.DruidException;
 import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.junit.Assert;
@@ -41,7 +42,7 @@ public class Pac4jSessionStoreTest
   @Test
   public void testSetAndGet()
   {
-    Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore(COOKIE_PASSPHRASE);
+    Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore<>(COOKIE_PASSPHRASE);
 
     WebContext webContext1 = EasyMock.mock(WebContext.class);
     EasyMock.expect(webContext1.getScheme()).andReturn("https");
@@ -68,7 +69,7 @@ public class Pac4jSessionStoreTest
   @Test
   public void testSetAndGetClearUserProfile()
   {
-    Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore(COOKIE_PASSPHRASE);
+    Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore<>(COOKIE_PASSPHRASE);
 
     WebContext webContext1 = EasyMock.mock(WebContext.class);
     EasyMock.expect(webContext1.getScheme()).andReturn("https");
@@ -78,6 +79,7 @@ public class Pac4jSessionStoreTest
     EasyMock.replay(webContext1);
 
     CommonProfile profile = new CommonProfile();
+    profile.setId("profile1");
     profile.addAttribute(CommonProfileDefinition.DISPLAY_NAME, "name");
     sessionStore.set(webContext1, "pac4jUserProfiles", profile);
 
@@ -99,7 +101,7 @@ public class Pac4jSessionStoreTest
   @Test
   public void testSetAndGetClearUserMultipleProfile()
   {
-    Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore(COOKIE_PASSPHRASE);
+    Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore<>(COOKIE_PASSPHRASE);
 
     WebContext webContext1 = EasyMock.mock(WebContext.class);
     EasyMock.expect(webContext1.getScheme()).andReturn("https");
@@ -109,8 +111,10 @@ public class Pac4jSessionStoreTest
     EasyMock.replay(webContext1);
 
     CommonProfile profile1 = new CommonProfile();
+    profile1.setId("profile1");
     profile1.addAttribute(CommonProfileDefinition.DISPLAY_NAME, "name1");
     CommonProfile profile2 = new CommonProfile();
+    profile2.setId("profile2");
     profile2.addAttribute(CommonProfileDefinition.DISPLAY_NAME, "name2");
     Map<String, CommonProfile> profiles = new HashMap<>();
     profiles.put("profile1", profile1);
@@ -131,4 +135,36 @@ public class Pac4jSessionStoreTest
     Assert.assertTrue(Objects.requireNonNull(value).isPresent());
     Assert.assertEquals(2, ((Map<String, CommonProfile>) value.get()).size());
   }
+
+  @Test
+  public void testGetWithWrongPassphraseThrowsDruidException()
+  {
+    final WebContext webContext = EasyMock.mock(WebContext.class);
+    EasyMock.expect(webContext.getScheme()).andReturn("https");
+
+    final Capture<Cookie> cookieCapture = EasyMock.newCapture();
+    EasyMock.expect(webContext.getRequestCookies())
+            .andAnswer(() -> Collections.singleton(cookieCapture.getValue()));
+
+    webContext.addResponseCookie(EasyMock.capture(cookieCapture));
+    EasyMock.expectLastCall().once();
+    EasyMock.replay(webContext);
+
+    // Create a cookie with an invalid passphrase
+    new Pac4jSessionStore<>("invalid-passphrase").set(webContext, "key", "value");
+
+    // Verify that trying to decrypt the invalid cookie throws an exception
+    final Pac4jSessionStore<WebContext> sessionStore = new Pac4jSessionStore<>(COOKIE_PASSPHRASE);
+    DruidException exception = Assert.assertThrows(
+        DruidException.class,
+        () -> sessionStore.get(webContext, "key")
+    );
+    Assert.assertEquals(
+        "Decryption failed. Check service logs.",
+        exception.getMessage()
+    );
+    Assert.assertNull(exception.getCause());
+
+    EasyMock.verify(webContext);
+  }
 }
diff --git a/processing/src/main/java/org/apache/druid/crypto/CryptoService.java b/processing/src/main/java/org/apache/druid/crypto/CryptoService.java
index cd4b3279636..92d4f6602c4 100644
--- a/processing/src/main/java/org/apache/druid/crypto/CryptoService.java
+++ b/processing/src/main/java/org/apache/druid/crypto/CryptoService.java
@@ -20,25 +20,21 @@
 package org.apache.druid.crypto;
 
 import com.google.common.base.Preconditions;
+import org.apache.druid.error.InternalServerError;
 import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.java.util.common.logger.Logger;
 
 import javax.annotation.Nullable;
-import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 import java.nio.ByteBuffer;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.security.spec.InvalidKeySpecException;
-import java.security.spec.InvalidParameterSpecException;
 import java.security.spec.KeySpec;
 
 /**
@@ -50,6 +46,8 @@ import java.security.spec.KeySpec;
  */
 public class CryptoService
 {
+  private static final Logger log = new Logger(CryptoService.class);
+
   // Based on Javadocs on SecureRandom, It is threadsafe as well.
   private static final SecureRandom SECURE_RANDOM_INSTANCE = new SecureRandom();
 
@@ -125,8 +123,9 @@ public class CryptoService
           ecipher.doFinal(plain)
       ).toByteAray();
     }
-    catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException ex) {
-      throw new RuntimeException(ex);
+    catch (Exception ex) {
+      log.noStackTrace().warn(ex, "Encryption failed");
+      throw InternalServerError.exception("Encryption failed. Check service logs.");
     }
   }
 
@@ -145,8 +144,9 @@ public class CryptoService
       dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(encryptedData.getIv()));
       return dcipher.doFinal(encryptedData.getCipher());
     }
-    catch (InvalidKeySpecException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
-      throw new RuntimeException(ex);
+    catch (Exception ex) {
+      log.noStackTrace().warn(ex, "Decryption failed");
+      throw InternalServerError.exception("Decryption failed. Check service logs.");
     }
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org