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

coheigea pushed a commit to branch 3.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 926ebafc410de012210123e679b83bf5cfca7b13
Author: eberntsen <ei...@fimbulvinter.com>
AuthorDate: Thu May 27 13:34:36 2021 +0200

    [CXF-8535] Query missing from signature request-target (#795)
    
    * [CXF-8535] Include query in request-target
    
    * Fix codestyle issues
    
    (cherry picked from commit 504cb84e0bf6043d3b787def4070c0649dd51c32)
---
 parent/pom.xml                                     |   1 +
 rt/rs/security/http-signature/pom.xml              |  20 ++
 .../filters/CreateSignatureInterceptor.java        |  14 +-
 .../filters/VerifySignatureFilter.java             |   6 +-
 .../httpsignature/utils/SignatureHeaderUtils.java  |  12 +-
 .../security/httpsignature/SpecExamplesTest.java   | 214 +++++++++++++++++++--
 6 files changed, 242 insertions(+), 25 deletions(-)

diff --git a/parent/pom.xml b/parent/pom.xml
index df207f8..4e20851 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -181,6 +181,7 @@
         <cxf.persistence-api.version>2.2.3</cxf.persistence-api.version>
         <cxf.plexus-archiver.version>4.2.0</cxf.plexus-archiver.version>
         <cxf.plexus-utils.version>3.2.0</cxf.plexus-utils.version>
+        <cxf.powermock.version>2.0.4</cxf.powermock.version>
         <cxf.reactivestreams.version>1.0.3</cxf.reactivestreams.version>
         <cxf.reactor.version>3.3.17.RELEASE</cxf.reactor.version>
         <cxf.rhino.version>1.7R2</cxf.rhino.version>
diff --git a/rt/rs/security/http-signature/pom.xml 
b/rt/rs/security/http-signature/pom.xml
index 2c43e28..cb26c5c 100644
--- a/rt/rs/security/http-signature/pom.xml
+++ b/rt/rs/security/http-signature/pom.xml
@@ -70,6 +70,26 @@
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-frontend-jaxrs</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <version>${cxf.powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <version>${cxf.powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${cxf.mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git 
a/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/CreateSignatureInterceptor.java
 
b/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/CreateSignatureInterceptor.java
index ca128f4..103cd72 100644
--- 
a/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/CreateSignatureInterceptor.java
+++ 
b/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/CreateSignatureInterceptor.java
@@ -18,10 +18,8 @@
  */
 package org.apache.cxf.rs.security.httpsignature.filters;
 
-import java.io.IOException;
-import java.io.OutputStream;
+import java.io.*;
 import java.nio.charset.StandardCharsets;
-
 import javax.annotation.Priority;
 import javax.ws.rs.Priorities;
 import javax.ws.rs.client.ClientRequestContext;
@@ -35,7 +33,6 @@ import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Provider;
 import javax.ws.rs.ext.WriterInterceptor;
 import javax.ws.rs.ext.WriterInterceptorContext;
-
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.io.CachedOutputStream;
 import org.apache.cxf.jaxrs.utils.HttpUtils;
@@ -85,9 +82,8 @@ public class CreateSignatureInterceptor extends 
AbstractSignatureOutFilter
         // Only sign the request if we have no Body.
         if (requestContext.getEntity() == null) {
             String method = requestContext.getMethod();
-            String path = requestContext.getUri().getPath();
-
-            performSignature(requestContext.getHeaders(), path, method);
+            performSignature(requestContext.getHeaders(),
+                
SignatureHeaderUtils.createRequestTarget(requestContext.getUri()), method);
         }
     }
 
@@ -108,8 +104,8 @@ public class CreateSignatureInterceptor extends 
AbstractSignatureOutFilter
         // We don't pass the HTTP method + URI for the response case
         if (MessageUtils.isRequestor(m)) {
             method = 
HttpUtils.getProtocolHeader(JAXRSUtils.getCurrentMessage(),
-                                                 Message.HTTP_REQUEST_METHOD, 
"");
-            path = uriInfo.getRequestUri().getPath();
+                Message.HTTP_REQUEST_METHOD, "");
+            path = 
SignatureHeaderUtils.createRequestTarget(uriInfo.getRequestUri());
         }
 
         performSignature(writerInterceptorContext.getHeaders(), path, method);
diff --git 
a/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/VerifySignatureFilter.java
 
b/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/VerifySignatureFilter.java
index 5e77e1c..444d019 100644
--- 
a/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/VerifySignatureFilter.java
+++ 
b/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/filters/VerifySignatureFilter.java
@@ -18,14 +18,14 @@
  */
 package org.apache.cxf.rs.security.httpsignature.filters;
 
-import java.io.ByteArrayInputStream;
-
+import java.io.*;
 import javax.annotation.Priority;
 import javax.ws.rs.BadRequestException;
 import javax.ws.rs.Priorities;
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.container.ContainerRequestFilter;
 import javax.ws.rs.ext.Provider;
+import org.apache.cxf.rs.security.httpsignature.utils.SignatureHeaderUtils;
 
 /**
  * RS CXF container Filter which verifies the Digest header, and then extracts 
signature data from the context
@@ -43,7 +43,7 @@ public class VerifySignatureFilter extends 
AbstractSignatureInFilter implements
         }
 
         verifySignature(requestCtx.getHeaders(),
-                        requestCtx.getUriInfo().getAbsolutePath().getPath(),
+                
SignatureHeaderUtils.createRequestTarget(requestCtx.getUriInfo().getAbsolutePath()),
                         requestCtx.getMethod());
     }
 
diff --git 
a/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/utils/SignatureHeaderUtils.java
 
b/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/utils/SignatureHeaderUtils.java
index 7c14cd8..4bcc440 100644
--- 
a/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/utils/SignatureHeaderUtils.java
+++ 
b/rt/rs/security/http-signature/src/main/java/org/apache/cxf/rs/security/httpsignature/utils/SignatureHeaderUtils.java
@@ -18,6 +18,7 @@
  */
 package org.apache.cxf.rs.security.httpsignature.utils;
 
+import java.net.*;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.time.Clock;
@@ -26,7 +27,6 @@ import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Stream;
-
 import 
org.apache.cxf.rs.security.httpsignature.exception.DigestFailureException;
 
 public final class SignatureHeaderUtils {
@@ -101,4 +101,14 @@ public final class SignatureHeaderUtils {
         });
     }
 
+    public static String createRequestTarget(URI uri) {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(uri.getPath());
+
+        if (uri.getRawQuery() != null) {
+            stringBuilder.append("?");
+            stringBuilder.append(uri.getRawQuery());
+        }
+        return stringBuilder.toString();
+    }
 }
diff --git 
a/rt/rs/security/http-signature/src/test/java/org/apache/cxf/rs/security/httpsignature/SpecExamplesTest.java
 
b/rt/rs/security/http-signature/src/test/java/org/apache/cxf/rs/security/httpsignature/SpecExamplesTest.java
index 6d164c2..fc5a445 100644
--- 
a/rt/rs/security/http-signature/src/test/java/org/apache/cxf/rs/security/httpsignature/SpecExamplesTest.java
+++ 
b/rt/rs/security/http-signature/src/test/java/org/apache/cxf/rs/security/httpsignature/SpecExamplesTest.java
@@ -18,8 +18,9 @@
  */
 package org.apache.cxf.rs.security.httpsignature;
 
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
+import java.net.*;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -30,25 +31,35 @@ import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
+import java.util.*;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import 
org.apache.cxf.rs.security.httpsignature.filters.CreateSignatureInterceptor;
+import org.apache.cxf.rs.security.httpsignature.filters.VerifySignatureFilter;
 import org.apache.cxf.rs.security.httpsignature.provider.KeyProvider;
 import org.apache.cxf.rs.security.httpsignature.provider.MockAlgorithmProvider;
 import org.apache.cxf.rs.security.httpsignature.provider.MockSecurityProvider;
-
 import org.junit.BeforeClass;
 import org.junit.Test;
-
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
 import static org.junit.Assert.assertEquals;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
 
 /**
  * Some examples from the Appendix C of the spec.
  */
+@PrepareForTest(PhaseInterceptorChain.class)
+@RunWith(PowerMockRunner.class)
 public class SpecExamplesTest {
 
     private static KeyProvider keyProvider;
@@ -56,7 +67,6 @@ public class SpecExamplesTest {
 
     @BeforeClass
     public static void setUp() throws IOException, InvalidKeySpecException {
-
         try {
             // Load keys
             String basedir = System.getProperty("basedir");
@@ -108,11 +118,61 @@ public class SpecExamplesTest {
     }
 
     @Test
+    public void interceptorDefaultTest() {
+        URI uri = 
URI.create("https://www.example.com/foo?param=value&pet=dog";);
+        String method = "POST";
+        MessageSigner messageSigner = new MessageSigner(keyProvider, "Test", 
Collections.singletonList("Date"));
+
+        CreateSignatureInterceptor interceptor = new 
CreateSignatureInterceptor();
+        interceptor.setMessageSigner(messageSigner);
+
+        Map<String, List<String>> headers = createMockHeaders();
+        MultivaluedMap<String, Object> requestHeaders = new 
MultivaluedHashMap<>();
+        MultivaluedMap<String, String> requestStringHeaders = new 
MultivaluedHashMap<>();
+        headers.forEach((header, value) -> {
+            requestHeaders.add(header, value.get(0));
+            requestStringHeaders.add(header, value.get(0));
+        });
+
+        mockStatic(PhaseInterceptorChain.class);
+        Message message = mock(Message.class);
+        when(PhaseInterceptorChain.getCurrentMessage()).thenReturn(message);
+
+        ClientRequestContext requestContext = getClientRequestContextMock(uri, 
method, requestHeaders);
+
+        interceptor.filter(requestContext);
+
+        String signatureHeader = (String) 
requestHeaders.get("Signature").get(0);
+        requestStringHeaders.add("Signature", signatureHeader);
+
+        String expectedHeader = "keyId=\"Test\",algorithm=\"rsa-sha256\","
+            + 
"signature=\"SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz"
+            + "6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB"
+            + "6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM=\"";
+
+        assertEquals(signatureHeader.replaceAll("headers=\"date\",", ""), 
expectedHeader);
+
+        // Verify that the request signature can be verified by the filter
+        MessageVerifier messageVerifier = new MessageVerifier(keyId -> 
publicKey);
+        messageVerifier.setAddDefaultRequiredHeaders(false);
+        messageVerifier.setSecurityProvider(new MockSecurityProvider());
+        messageVerifier.setAlgorithmProvider(new MockAlgorithmProvider());
+
+        VerifySignatureFilter verifySignatureFilter = new 
VerifySignatureFilter();
+        verifySignatureFilter.setMessageVerifier(messageVerifier);
+
+        ContainerRequestContext containerRequestContext =
+            getContainerRequestContextMock(uri, method, requestStringHeaders);
+
+        verifySignatureFilter.filter(containerRequestContext);
+    }
+
+    @Test
     public void basicTest() throws IOException {
         Map<String, List<String>> headers = createMockHeaders();
 
         MessageSigner messageSigner = new MessageSigner(keyProvider, "Test",
-                                                        
Arrays.asList("(request-target)", "host", "Date"));
+            Arrays.asList("(request-target)", "host", "Date"));
         messageSigner.sign(headers, "/foo?param=value&pet=dog", "POST");
         String signatureHeader = headers.get("Signature").get(0);
 
@@ -132,6 +192,58 @@ public class SpecExamplesTest {
     }
 
     @Test
+    public void interceptorBasicTest() {
+        URI uri = 
URI.create("https://www.example.com/foo?param=value&pet=dog";);
+        String method = "POST";
+        MessageSigner messageSigner = new MessageSigner(keyProvider, "Test",
+            Arrays.asList("(request-target)", "host", "Date"));
+
+        CreateSignatureInterceptor interceptor = new 
CreateSignatureInterceptor();
+        interceptor.setMessageSigner(messageSigner);
+
+        Map<String, List<String>> headers = createMockHeaders();
+        MultivaluedMap<String, Object> requestHeaders = new 
MultivaluedHashMap<>();
+        MultivaluedMap<String, String> requestStringHeaders = new 
MultivaluedHashMap<>();
+        headers.forEach((header, value) -> {
+            requestHeaders.add(header, value.get(0));
+            requestStringHeaders.add(header, value.get(0));
+        });
+
+        mockStatic(PhaseInterceptorChain.class);
+        Message message = mock(Message.class);
+        when(PhaseInterceptorChain.getCurrentMessage()).thenReturn(message);
+
+        ClientRequestContext requestContext = getClientRequestContextMock(uri, 
method, requestHeaders);
+
+        interceptor.filter(requestContext);
+
+        String signatureHeader = (String) 
requestHeaders.get("Signature").get(0);
+        requestStringHeaders.add("Signature", signatureHeader);
+
+        String expectedHeader = "keyId=\"Test\",algorithm=\"rsa-sha256\","
+            + "headers=\"(request-target) host 
date\",signature=\"qdx+H7PHHDZgy4"
+            + "y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn"
+            + "7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBs"
+            + "kLu6kd9Fswtemr3lgdDEmn04swr2Os0=\"";
+
+        assertEquals(signatureHeader, expectedHeader);
+
+        // Verify that the request signature can be verified by the filter
+        MessageVerifier messageVerifier = new MessageVerifier(keyId -> 
publicKey);
+        messageVerifier.setAddDefaultRequiredHeaders(false);
+        messageVerifier.setSecurityProvider(new MockSecurityProvider());
+        messageVerifier.setAlgorithmProvider(new MockAlgorithmProvider());
+
+        VerifySignatureFilter verifySignatureFilter = new 
VerifySignatureFilter();
+        verifySignatureFilter.setMessageVerifier(messageVerifier);
+
+        ContainerRequestContext containerRequestContext =
+            getContainerRequestContextMock(uri, method, requestStringHeaders);
+
+        verifySignatureFilter.filter(containerRequestContext);
+    }
+
+    @Test
     public void allHeadersTest() throws IOException {
         Map<String, List<String>> headers = createMockHeaders();
 
@@ -155,6 +267,84 @@ public class SpecExamplesTest {
         messageVerifier.verifyMessage(headers, "POST", 
"/foo?param=value&pet=dog", new MessageImpl());
     }
 
+    @Test
+    public void interceptorAllHeadersTest() {
+        URI uri = 
URI.create("https://www.example.com/foo?param=value&pet=dog";);
+        String method = "POST";
+        MessageSigner messageSigner = new MessageSigner(keyProvider, "Test",
+            Arrays.asList("(request-target)", "host", "date",
+                "content-type", "digest", "content-length"));
+
+        CreateSignatureInterceptor interceptor = new 
CreateSignatureInterceptor();
+
+        interceptor.setMessageSigner(messageSigner);
+
+        Map<String, List<String>> headers = createMockHeaders();
+        MultivaluedMap<String, Object> requestHeaders = new 
MultivaluedHashMap<>();
+        MultivaluedMap<String, String> requestStringHeaders = new 
MultivaluedHashMap<>();
+        headers.forEach((header, value) -> {
+            requestHeaders.add(header, value.get(0));
+            requestStringHeaders.add(header, value.get(0));
+        });
+
+        ClientRequestContext requestContext = getClientRequestContextMock(uri, 
method, requestHeaders);
+
+        mockStatic(PhaseInterceptorChain.class);
+        Message message = mock(Message.class);
+        when(PhaseInterceptorChain.getCurrentMessage()).thenReturn(message);
+
+        interceptor.filter(requestContext);
+
+        String signatureHeader = (String) 
requestHeaders.get("Signature").get(0);
+        requestStringHeaders.add("Signature", signatureHeader);
+
+        String expectedHeader = "keyId=\"Test\",algorithm=\"rsa-sha256\","
+            + "headers=\"(request-target) host date content-type digest 
content-length\","
+            + 
"signature=\"vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs"
+            + "8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZF"
+            + "ukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE=\"";
+
+        assertEquals(signatureHeader, expectedHeader);
+
+        // Verify that the request signature can be verified by the filter
+        MessageVerifier messageVerifier = new MessageVerifier(keyId -> 
publicKey);
+        messageVerifier.setAddDefaultRequiredHeaders(false);
+        messageVerifier.setSecurityProvider(new MockSecurityProvider());
+        messageVerifier.setAlgorithmProvider(new MockAlgorithmProvider());
+
+        VerifySignatureFilter verifySignatureFilter = new 
VerifySignatureFilter();
+        verifySignatureFilter.setMessageVerifier(messageVerifier);
+
+        ContainerRequestContext containerRequestContext =
+            getContainerRequestContextMock(uri, method, requestStringHeaders);
+        InputStream stream = new ByteArrayInputStream("{\"hello\": 
\"world\"}".getBytes(StandardCharsets.UTF_8));
+        when(containerRequestContext.getEntityStream()).thenReturn(stream);
+
+        verifySignatureFilter.filter(containerRequestContext);
+    }
+
+    private ClientRequestContext getClientRequestContextMock(URI uri, String 
method,
+                                                             
MultivaluedMap<String, Object> requestHeaders) {
+        ClientRequestContext requestContext = mock(ClientRequestContext.class);
+        when(requestContext.getEntity()).thenReturn(null);
+        when(requestContext.getMethod()).thenReturn(method);
+        when(requestContext.getHeaders()).thenReturn(requestHeaders);
+        when(requestContext.getUri()).thenReturn(uri);
+        return requestContext;
+    }
+
+    private ContainerRequestContext getContainerRequestContextMock(URI uri, 
String method,
+                                                                   
MultivaluedMap<String, String>
+                                                                       
requestStringHeaders) {
+        ContainerRequestContext containerRequestContext = 
mock(ContainerRequestContext.class);
+        UriInfo uriInfo = mock(UriInfo.class);
+        when(uriInfo.getAbsolutePath()).thenReturn(uri);
+        when(containerRequestContext.getUriInfo()).thenReturn(uriInfo);
+        when(containerRequestContext.getMethod()).thenReturn(method);
+        
when(containerRequestContext.getHeaders()).thenReturn(requestStringHeaders);
+        return containerRequestContext;
+    }
+
     private static Map<String, List<String>> createMockHeaders() {
         Map<String, List<String>> headers = new HashMap<>();
         headers.put("Host", Collections.singletonList("example.com"));

Reply via email to