You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2022/09/09 08:46:12 UTC

[tomcat] branch main updated (116d9babd8 -> 69e1c84ea1)

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

markt pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


    from 116d9babd8 More clean-up
     new 14832c677a Rename / refactor in preparation for fixing BZ 62312
     new dcf48fb742 Refactor and use i18n for exception messages
     new d67968c4f5 Add support for optionally validating the authentication realm
     new a747a41f50 Refactor in preparation for fixinf BZ 62312
     new ed7bc91102 More refactoring in preparation for the BZ 62312 fix
     new 69e1c84ea1 Fix BZ 62312 - add support for forward proxy authentication to WebSocket

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../tomcat/websocket/AuthenticationType.java       |  67 +++++++
 .../org/apache/tomcat/websocket/Authenticator.java |  95 +++++++++-
 .../tomcat/websocket/BasicAuthenticator.java       |  16 +-
 java/org/apache/tomcat/websocket/Constants.java    |  14 +-
 .../tomcat/websocket/DigestAuthenticator.java      |  17 +-
 .../tomcat/websocket/LocalStrings.properties       |   8 +-
 .../tomcat/websocket/WsWebSocketContainer.java     | 107 +++++++-----
 .../websocket/TesterWebSocketClientProxy.java      | 192 +++++++++++++++++++++
 webapps/docs/changelog.xml                         |  10 ++
 webapps/docs/web-socket-howto.xml                  |  37 +++-
 10 files changed, 496 insertions(+), 67 deletions(-)
 create mode 100644 java/org/apache/tomcat/websocket/AuthenticationType.java
 create mode 100644 test/org/apache/tomcat/websocket/TesterWebSocketClientProxy.java


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/06: Rename / refactor in preparation for fixing BZ 62312

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 14832c677a1efb4ced9e011c1c256669bc48eb0c
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 9 08:50:37 2022 +0100

    Rename / refactor in preparation for fixing BZ 62312
---
 java/org/apache/tomcat/websocket/Authenticator.java   | 19 +++++++++++++++++++
 .../apache/tomcat/websocket/BasicAuthenticator.java   |  2 +-
 .../apache/tomcat/websocket/DigestAuthenticator.java  |  2 +-
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/Authenticator.java b/java/org/apache/tomcat/websocket/Authenticator.java
index aacbdb47f0..4ff78281d1 100644
--- a/java/org/apache/tomcat/websocket/Authenticator.java
+++ b/java/org/apache/tomcat/websocket/Authenticator.java
@@ -28,6 +28,7 @@ public abstract class Authenticator {
 
     private static final Pattern pattern = Pattern.compile("(\\w+)\\s*=\\s*(\"([^\"]+)\"|([^,=\"]+))\\s*,?");
 
+
     /**
      * Generate the authorization header value that will be sent to the server.
      *
@@ -42,6 +43,7 @@ public abstract class Authenticator {
     public abstract String getAuthorization(String requestUri, String authenticateHeader,
             Map<String, Object> userProperties) throws AuthenticationException;
 
+
     /**
      * Get the authentication method.
      *
@@ -49,14 +51,31 @@ public abstract class Authenticator {
      */
     public abstract String getSchemeName();
 
+
     /**
      * Utility method to parse the authentication header.
      *
      * @param authenticateHeader The server authenticate header received
      *
      * @return a map of authentication parameter names and values
+     *
+     * @deprecated Use {@link Authenticator#parseAuthenticateHeader(String)}.
+     *             Will be removed in Tomcat 10.1.x onwards
      */
+    @Deprecated
     public Map<String, String> parseWWWAuthenticateHeader(String authenticateHeader) {
+        return parseAuthenticateHeader(authenticateHeader);
+    }
+
+
+    /**
+     * Utility method to parse the authentication header.
+     *
+     * @param authenticateHeader The server authenticate header received
+     *
+     * @return a map of authentication parameter names and values
+     */
+    public Map<String, String> parseAuthenticateHeader(String authenticateHeader) {
 
         Matcher m = pattern.matcher(authenticateHeader);
         Map<String, String> parameterMap = new HashMap<>();
diff --git a/java/org/apache/tomcat/websocket/BasicAuthenticator.java b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
index 02f34ebee2..68dcccdd5f 100644
--- a/java/org/apache/tomcat/websocket/BasicAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
@@ -41,7 +41,7 @@ public class BasicAuthenticator extends Authenticator {
                     "Failed to perform Basic authentication due to  missing user/password");
         }
 
-        Map<String, String> parameterMap = parseWWWAuthenticateHeader(authenticateHeader);
+        Map<String, String> parameterMap = parseAuthenticateHeader(authenticateHeader);
 
         String userPass = userName + ":" + userPassword;
         Charset charset;
diff --git a/java/org/apache/tomcat/websocket/DigestAuthenticator.java b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
index 53c3f45a28..fef0106013 100644
--- a/java/org/apache/tomcat/websocket/DigestAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
@@ -47,7 +47,7 @@ public class DigestAuthenticator extends Authenticator {
                     "Failed to perform Digest authentication due to  missing user/password");
         }
 
-        Map<String, String> parameterMap = parseWWWAuthenticateHeader(authenticateHeader);
+        Map<String, String> parameterMap = parseAuthenticateHeader(authenticateHeader);
 
         String realm = parameterMap.get("realm");
         String nonce = parameterMap.get("nonce");


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/06: Refactor and use i18n for exception messages

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit dcf48fb74276bde9828faa310be4d4ec3fce916f
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 9 08:56:40 2022 +0100

    Refactor and use i18n for exception messages
---
 java/org/apache/tomcat/websocket/Authenticator.java    | 18 ++++++++++++++++++
 .../apache/tomcat/websocket/BasicAuthenticator.java    |  6 ++----
 .../apache/tomcat/websocket/DigestAuthenticator.java   |  6 ++----
 .../apache/tomcat/websocket/LocalStrings.properties    |  3 +++
 4 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/Authenticator.java b/java/org/apache/tomcat/websocket/Authenticator.java
index 4ff78281d1..30787205cb 100644
--- a/java/org/apache/tomcat/websocket/Authenticator.java
+++ b/java/org/apache/tomcat/websocket/Authenticator.java
@@ -21,11 +21,15 @@ import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.tomcat.util.res.StringManager;
+
 /**
  * Base class for the authentication methods used by the WebSocket client.
  */
 public abstract class Authenticator {
 
+    private static final StringManager sm = StringManager.getManager(Authenticator.class);
+
     private static final Pattern pattern = Pattern.compile("(\\w+)\\s*=\\s*(\"([^\"]+)\"|([^,=\"]+))\\s*,?");
 
 
@@ -91,4 +95,18 @@ public abstract class Authenticator {
 
         return parameterMap;
     }
+
+
+    protected void validateUsername(String userName) throws AuthenticationException {
+        if (userName == null) {
+            throw new AuthenticationException(sm.getString("authenticator.nullUserName"));
+        }
+    }
+
+
+    protected void validatePassword(String password) throws AuthenticationException {
+        if (password == null) {
+            throw new AuthenticationException(sm.getString("authenticator.nullPassword"));
+        }
+    }
 }
diff --git a/java/org/apache/tomcat/websocket/BasicAuthenticator.java b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
index 68dcccdd5f..63b0a3d0e5 100644
--- a/java/org/apache/tomcat/websocket/BasicAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
@@ -36,10 +36,8 @@ public class BasicAuthenticator extends Authenticator {
         String userName = (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME);
         String userPassword = (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD);
 
-        if (userName == null || userPassword == null) {
-            throw new AuthenticationException(
-                    "Failed to perform Basic authentication due to  missing user/password");
-        }
+        validateUsername(userName);
+        validatePassword(userPassword);
 
         Map<String, String> parameterMap = parseAuthenticateHeader(authenticateHeader);
 
diff --git a/java/org/apache/tomcat/websocket/DigestAuthenticator.java b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
index fef0106013..6552001c1c 100644
--- a/java/org/apache/tomcat/websocket/DigestAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
@@ -42,10 +42,8 @@ public class DigestAuthenticator extends Authenticator {
         String userName = (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME);
         String userPassword = (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD);
 
-        if (userName == null || userPassword == null) {
-            throw new AuthenticationException(
-                    "Failed to perform Digest authentication due to  missing user/password");
-        }
+        validateUsername(userName);
+        validatePassword(userPassword);
 
         Map<String, String> parameterMap = parseAuthenticateHeader(authenticateHeader);
 
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties
index c755121bb3..c86a75efab 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -29,6 +29,9 @@ asyncChannelWrapperSecure.unexpectedHandshakeState=Unexpected state [{0}] during
 asyncChannelWrapperSecure.wrongStateRead=Flag that indicates a read is in progress was found to be false (it should have been true) when trying to complete a read operation
 asyncChannelWrapperSecure.wrongStateWrite=Flag that indicates a write is in progress was found to be false (it should have been true) when trying to complete a write operation
 
+authenticator.nullPassword=No password was provided to use for authentication
+authenticator.nullUserName=No user name was provided to use for authentication
+
 backgroundProcessManager.processFailed=A background process failed
 
 caseInsensitiveKeyMap.nullKey=Null keys are not permitted


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 06/06: Fix BZ 62312 - add support for forward proxy authentication to WebSocket

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 69e1c84ea172d0229acc1bacb355c9fc0102f47f
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 9 09:39:24 2022 +0100

    Fix BZ 62312 - add support for forward proxy authentication to WebSocket
    
    https://bz.apache.org/bugzilla/show_bug.cgi?id=62312
    Based on a patch by Joe Mokos
---
 .../tomcat/websocket/AuthenticationType.java       |   8 +-
 java/org/apache/tomcat/websocket/Constants.java    |  10 ++
 .../tomcat/websocket/WsWebSocketContainer.java     |  21 ++-
 .../websocket/TesterWebSocketClientProxy.java      | 192 +++++++++++++++++++++
 webapps/docs/changelog.xml                         |  10 ++
 webapps/docs/web-socket-howto.xml                  |  20 ++-
 6 files changed, 254 insertions(+), 7 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/AuthenticationType.java b/java/org/apache/tomcat/websocket/AuthenticationType.java
index c3a9fa5736..a88ea94358 100644
--- a/java/org/apache/tomcat/websocket/AuthenticationType.java
+++ b/java/org/apache/tomcat/websocket/AuthenticationType.java
@@ -22,7 +22,13 @@ public enum AuthenticationType {
             Constants.WWW_AUTHENTICATE_HEADER_NAME,
             Constants.WS_AUTHENTICATION_USER_NAME,
             Constants.WS_AUTHENTICATION_PASSWORD,
-            Constants.WS_AUTHENTICATION_REALM);
+            Constants.WS_AUTHENTICATION_REALM),
+
+    PROXY(Constants.PROXY_AUTHORIZATION_HEADER_NAME,
+            Constants.PROXY_AUTHENTICATE_HEADER_NAME,
+            Constants.WS_AUTHENTICATION_PROXY_USER_NAME,
+            Constants.WS_AUTHENTICATION_PROXY_PASSWORD,
+            Constants.WS_AUTHENTICATION_PROXY_REALM);
 
     private final String authorizationHeaderName;
     private final String authenticateHeaderName;
diff --git a/java/org/apache/tomcat/websocket/Constants.java b/java/org/apache/tomcat/websocket/Constants.java
index b2a843eba5..c83ab4c431 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -102,6 +102,8 @@ public class Constants {
     public static final String LOCATION_HEADER_NAME = "Location";
     public static final String AUTHORIZATION_HEADER_NAME = "Authorization";
     public static final String WWW_AUTHENTICATE_HEADER_NAME = "WWW-Authenticate";
+    public static final String PROXY_AUTHORIZATION_HEADER_NAME = "Proxy-Authorization";
+    public static final String PROXY_AUTHENTICATE_HEADER_NAME = "Proxy-Authenticate";
     public static final String WS_VERSION_HEADER_NAME = "Sec-WebSocket-Version";
     public static final String WS_VERSION_HEADER_VALUE = "13";
     public static final String WS_KEY_HEADER_NAME = "Sec-WebSocket-Key";
@@ -116,6 +118,7 @@ public class Constants {
     public static final int USE_PROXY = 305;
     public static final int TEMPORARY_REDIRECT = 307;
     public static final int UNAUTHORIZED = 401;
+    public static final int PROXY_AUTHENTICATION_REQUIRED = 407;
 
     // Configuration for Origin header in client
     static final String DEFAULT_ORIGIN_HEADER_VALUE =
@@ -142,6 +145,13 @@ public class Constants {
     public static final String WS_AUTHENTICATION_PASSWORD = "org.apache.tomcat.websocket.WS_AUTHENTICATION_PASSWORD";
     public static final String WS_AUTHENTICATION_REALM = "org.apache.tomcat.websocket.WS_AUTHENTICATION_REALM";
 
+    public static final String WS_AUTHENTICATION_PROXY_USER_NAME =
+            "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_USER_NAME";
+    public static final String WS_AUTHENTICATION_PROXY_PASSWORD =
+            "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_PASSWORD";
+    public static final String WS_AUTHENTICATION_PROXY_REALM =
+            "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_REALM";
+
     public static final List<Extension> INSTALLED_EXTENSIONS;
 
     static {
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index 571fe611c9..05bf453eaa 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -250,11 +250,14 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
             }
         }
 
+        Map<String,Object> userProperties = clientEndpointConfiguration.getUserProperties();
+
         // If sa is null, no proxy is configured so need to create sa
         if (sa == null) {
             sa = new InetSocketAddress(host, port);
         } else {
-            proxyConnect = createProxyRequest(host, port);
+            proxyConnect = createProxyRequest(
+                    host, port, (String) userProperties.get(Constants.PROXY_AUTHORIZATION_HEADER_NAME));
         }
 
         // Create the initial HTTP request to open the WebSocket connection
@@ -277,8 +280,6 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
                     "wsWebSocketContainer.asynchronousSocketChannelFail"), ioe);
         }
 
-        Map<String,Object> userProperties = clientEndpointConfiguration.getUserProperties();
-
         // Get the connection timeout
         long timeout = Constants.IO_TIMEOUT_MS_DEFAULT;
         String timeoutValue = (String) userProperties.get(Constants.IO_TIMEOUT_MS_PROPERTY);
@@ -305,7 +306,10 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
                 channel = new AsyncChannelWrapperNonSecure(socketChannel);
                 writeRequest(channel, proxyConnect, timeout);
                 HttpResponse httpResponse = processResponse(response, channel, timeout);
-                if (httpResponse.getStatus() != 200) {
+                if (httpResponse.status == Constants.PROXY_AUTHENTICATION_REQUIRED) {
+                    return processAuthenticationChallenge(clientEndpointHolder, clientEndpointConfiguration, path,
+                        redirectSet, userProperties, request, httpResponse, AuthenticationType.PROXY);
+                } else if (httpResponse.getStatus() != 200) {
                     throw new DeploymentException(sm.getString(
                             "wsWebSocketContainer.proxyConnectFail", selectedProxy,
                             Integer.toString(httpResponse.getStatus())));
@@ -573,7 +577,7 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
     }
 
 
-    private static ByteBuffer createProxyRequest(String host, int port) {
+    private static ByteBuffer createProxyRequest(String host, int port, String authorizationHeader) {
         StringBuilder request = new StringBuilder();
         request.append("CONNECT ");
         request.append(host);
@@ -585,6 +589,13 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
         request.append(':');
         request.append(port);
 
+        if (authorizationHeader != null) {
+            request.append("\r\n");
+            request.append(Constants.PROXY_AUTHORIZATION_HEADER_NAME);
+            request.append(':');
+            request.append(authorizationHeader);
+        }
+
         request.append("\r\n\r\n");
 
         byte[] bytes = request.toString().getBytes(StandardCharsets.ISO_8859_1);
diff --git a/test/org/apache/tomcat/websocket/TesterWebSocketClientProxy.java b/test/org/apache/tomcat/websocket/TesterWebSocketClientProxy.java
new file mode 100644
index 0000000000..89919c8d8a
--- /dev/null
+++ b/test/org/apache/tomcat/websocket/TesterWebSocketClientProxy.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+import java.net.URI;
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import jakarta.websocket.ClientEndpointConfig;
+import jakarta.websocket.ContainerProvider;
+import jakarta.websocket.Session;
+import jakarta.websocket.WebSocketContainer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+import org.apache.tomcat.websocket.TesterMessageCountClient.BasicText;
+import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEndpoint;
+
+/*
+ * Tests WebSocket connections via a forward proxy.
+ *
+ * These tests have been successfully used with Apache Web Server (httpd)
+ * configured with the following:
+ *
+ * Listen 8888
+ * <VirtualHost *:8888>
+ *     ProxyRequests On
+ *     ProxyVia On
+ *     AllowCONNECT 0-65535
+ * </VirtualHost>
+ *
+ * Listen 8889
+ * <VirtualHost *:8889>
+ *     ProxyRequests On
+ *     ProxyVia On
+ *     AllowCONNECT 0-65535
+ *     <Proxy *>
+ *         Order deny,allow
+ *         Allow from all
+ *         AuthType Basic
+ *         AuthName "Proxy Password Required"
+ *         AuthUserFile password.file
+ *         Require valid-user
+ *     </Proxy>
+ * </VirtualHost>
+ *
+ * and
+ * # htpasswd -c password.file proxy
+ * New Password: proxy-pass
+ *
+ */
+public class TesterWebSocketClientProxy extends WebSocketBaseTest {
+
+    private static final String MESSAGE_STRING = "proxy-test-message";
+
+    private static final String PROXY_ADDRESS = "192.168.0.200";
+    private static final String PROXY_PORT_NO_AUTH = "8888";
+    private static final String PROXY_PORT_AUTH = "8889";
+    // The IP address of the test instance that is reachable from the proxy
+    private static final String TOMCAT_ADDRESS = "192.168.0.100";
+
+    private static final String TOMCAT_USER = "tomcat";
+    private static final String TOMCAT_PASSWORD = "tomcat-pass";
+    private static final String TOMCAT_ROLE = "tomcat-role";
+
+    private static final String PROXY_USER = "proxy";
+    private static final String PROXY_PASSWORD = "proxy-pass";
+
+    @Test
+    public void testConnectToServerViaProxyWithNoAuthentication() throws Exception {
+        doTestConnectToServerViaProxy(false, false);
+    }
+
+
+    @Test
+    public void testConnectToServerViaProxyWithServerAuthentication() throws Exception {
+        doTestConnectToServerViaProxy(true, false);
+    }
+
+
+    @Test
+    public void testConnectToServerViaProxyWithProxyAuthentication() throws Exception {
+        doTestConnectToServerViaProxy(false, true);
+    }
+
+
+    @Test
+    public void testConnectToServerViaProxyWithServerAndProxyAuthentication() throws Exception {
+        doTestConnectToServerViaProxy(true, true);
+    }
+
+
+    private void doTestConnectToServerViaProxy(boolean serverAuthentication, boolean proxyAuthentication)
+            throws Exception {
+
+        // Configure the proxy
+        System.setProperty("http.proxyHost", PROXY_ADDRESS);
+        if (proxyAuthentication) {
+            System.setProperty("http.proxyPort", PROXY_PORT_AUTH);
+        } else {
+            System.setProperty("http.proxyPort", PROXY_PORT_NO_AUTH);
+        }
+
+        Tomcat tomcat = getTomcatInstance();
+
+        // Need to listen on all addresses, not just loop-back
+        tomcat.getConnector().setProperty("address", "0.0.0.0");
+
+        // No file system docBase required
+        Context ctx = tomcat.addContext("", null);
+        ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMappingDecoded("/", "default");
+
+        if (serverAuthentication) {
+            // Configure Realm
+            tomcat.addUser(TOMCAT_USER, TOMCAT_PASSWORD);
+            tomcat.addRole(TOMCAT_USER, TOMCAT_ROLE);
+
+            // Configure security constraints
+            SecurityCollection securityCollection = new SecurityCollection();
+            securityCollection.addPatternDecoded("/*");
+            SecurityConstraint securityConstraint = new SecurityConstraint();
+            securityConstraint.addAuthRole(TOMCAT_ROLE);
+            securityConstraint.addCollection(securityCollection);
+            ctx.addConstraint(securityConstraint);
+
+            // Configure authenticator
+            LoginConfig loginConfig = new LoginConfig();
+            loginConfig.setAuthMethod(BasicAuthenticator.schemeName);
+            ctx.setLoginConfig(loginConfig);
+            AuthenticatorBase basicAuthenticator = new org.apache.catalina.authenticator.BasicAuthenticator();
+            ctx.getPipeline().addValve(basicAuthenticator);
+        }
+
+        tomcat.start();
+
+        WebSocketContainer wsContainer = ContainerProvider.getWebSocketContainer();
+
+        ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create().build();
+        // Configure the client
+        if (serverAuthentication) {
+            clientEndpointConfig.getUserProperties().put(Constants.WS_AUTHENTICATION_USER_NAME, TOMCAT_USER);
+            clientEndpointConfig.getUserProperties().put(Constants.WS_AUTHENTICATION_PASSWORD, TOMCAT_PASSWORD);
+        }
+        if (proxyAuthentication) {
+            clientEndpointConfig.getUserProperties().put(Constants.WS_AUTHENTICATION_PROXY_USER_NAME, PROXY_USER);
+            clientEndpointConfig.getUserProperties().put(Constants.WS_AUTHENTICATION_PROXY_PASSWORD, PROXY_PASSWORD);
+        }
+
+        Session wsSession = wsContainer.connectToServer(
+                TesterProgrammaticEndpoint.class,
+                clientEndpointConfig,
+                new URI("ws://" + TOMCAT_ADDRESS + ":" + getPort() +
+                        TesterEchoServer.Config.PATH_ASYNC));
+        CountDownLatch latch = new CountDownLatch(1);
+        BasicText handler = new BasicText(latch);
+        wsSession.addMessageHandler(handler);
+        wsSession.getBasicRemote().sendText(MESSAGE_STRING);
+
+        boolean latchResult = handler.getLatch().await(10, TimeUnit.SECONDS);
+
+        Assert.assertTrue(latchResult);
+
+        Queue<String> messages = handler.getMessages();
+        Assert.assertEquals(1, messages.size());
+        Assert.assertEquals(MESSAGE_STRING, messages.peek());
+    }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 09aab642b7..5bb482242b 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -282,6 +282,16 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="WebSocket">
+    <changelog>
+      <add>
+        <bug>62312</bug>: Add support for authenticating WebSocket clients with
+        an HTTP forward proxy when establishing a connection to a WebSocket
+        endpoint via a foward proxy that requires authentication. Based on a
+        patch provided by Joe Mokos. (markt)
+      </add>
+    </changelog>
+  </subsection>
   <subsection name="Other">
     <changelog>
       <fix>
diff --git a/webapps/docs/web-socket-howto.xml b/webapps/docs/web-socket-howto.xml
index 8e7751d921..20cf2caf38 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -141,7 +141,7 @@
    <ocde>org.apache.tomcat.websocket.MAX_REDIRECTIONS</ocde>. The default value
    is 20. Redirection support can be disabled by configuring a value of zero.
    </p>
- 
+
 <p>When using the WebSocket client to connect to a server endpoint that requires
    BASIC or DIGEST authentication, the following user properties must be set:
    </p>
@@ -158,6 +158,24 @@
      <li><code>org.apache.tomcat.websocket.WS_AUTHENTICATION_REALM</code></li>
    </ul>
 
+<p>When using the WebSocket client to connect to a server endpoint via a forward
+   proxy (also known as a gateway) that requires BASIC or DIGEST authentication,
+   the following user properties must be set:
+   </p>
+   <ul>
+     <li><code>org.apache.tomcat.websocket.WS_PROXY_AUTHENTICATION_USER_NAME
+     </code></li>
+     <li><code>org.apache.tomcat.websocket.WS_PROXY_AUTHENTICATION_PASSWORD
+     </code></li>
+   </ul>
+   <p>Optionally, the WebSocket client can be configured only to send
+   credentials if the server authentication challenge includes a specific realm
+   by defining that realm in the optional user property:</p>
+   <ul>
+     <li><code>org.apache.tomcat.websocket.WS_PROXY_AUTHENTICATION_REALM</code>
+     </li>
+   </ul>
+
 </section>
 
 </body>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 03/06: Add support for optionally validating the authentication realm

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit d67968c4f5e221f9e32ced8aaff2bb6722ec09f7
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 9 09:03:01 2022 +0100

    Add support for optionally validating the authentication realm
    
    Behaviour is unchanged unless the new realm property is set in the user
    properties.
    Document the authentication properties.
---
 .../org/apache/tomcat/websocket/Authenticator.java | 26 ++++++++++++++++++++++
 .../tomcat/websocket/BasicAuthenticator.java       |  4 ++++
 java/org/apache/tomcat/websocket/Constants.java    |  1 +
 .../tomcat/websocket/DigestAuthenticator.java      |  5 ++++-
 .../tomcat/websocket/LocalStrings.properties       |  1 +
 webapps/docs/web-socket-howto.xml                  | 19 +++++++++++++++-
 6 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/Authenticator.java b/java/org/apache/tomcat/websocket/Authenticator.java
index 30787205cb..a49e18cb51 100644
--- a/java/org/apache/tomcat/websocket/Authenticator.java
+++ b/java/org/apache/tomcat/websocket/Authenticator.java
@@ -109,4 +109,30 @@ public abstract class Authenticator {
             throw new AuthenticationException(sm.getString("authenticator.nullPassword"));
         }
     }
+
+
+    protected void validateRealm(String userRealm, String serverRealm) throws AuthenticationException {
+        if (userRealm == null) {
+            return;
+        }
+
+        userRealm = userRealm.trim();
+        if (userRealm.length() == 0) {
+            return;
+        }
+
+        /*
+         * User has configured a realm. Only allow authentication to proceed if
+         * the realm in the authentication challenge matches (both BASIC and
+         * DIGEST are required to include a realm).
+         */
+        if (serverRealm != null) {
+            serverRealm = serverRealm.trim();
+            if (userRealm.equals(serverRealm)) {
+                return;
+            }
+        }
+
+        throw new AuthenticationException(sm.getString("authenticator.realmMismatch", userRealm, serverRealm));
+    }
 }
diff --git a/java/org/apache/tomcat/websocket/BasicAuthenticator.java b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
index 63b0a3d0e5..e7731cbdb5 100644
--- a/java/org/apache/tomcat/websocket/BasicAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
@@ -35,11 +35,15 @@ public class BasicAuthenticator extends Authenticator {
 
         String userName = (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME);
         String userPassword = (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD);
+        String userRealm = (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM);
 
         validateUsername(userName);
         validatePassword(userPassword);
 
         Map<String, String> parameterMap = parseAuthenticateHeader(authenticateHeader);
+        String realm = parameterMap.get("realm");
+
+        validateRealm(userRealm, realm);
 
         String userPass = userName + ":" + userPassword;
         Charset charset;
diff --git a/java/org/apache/tomcat/websocket/Constants.java b/java/org/apache/tomcat/websocket/Constants.java
index e8b0ffaf1b..b506694fe0 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -139,6 +139,7 @@ public class Constants {
 
     public static final String WS_AUTHENTICATION_USER_NAME = "org.apache.tomcat.websocket.WS_AUTHENTICATION_USER_NAME";
     public static final String WS_AUTHENTICATION_PASSWORD = "org.apache.tomcat.websocket.WS_AUTHENTICATION_PASSWORD";
+    public static final String WS_AUTHENTICATION_REALM = "org.apache.tomcat.websocket.WS_AUTHENTICATION_REALM";
 
     public static final List<Extension> INSTALLED_EXTENSIONS;
 
diff --git a/java/org/apache/tomcat/websocket/DigestAuthenticator.java b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
index 6552001c1c..cb6c9318de 100644
--- a/java/org/apache/tomcat/websocket/DigestAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
@@ -41,13 +41,16 @@ public class DigestAuthenticator extends Authenticator {
 
         String userName = (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME);
         String userPassword = (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD);
+        String userRealm = (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM);
 
         validateUsername(userName);
         validatePassword(userPassword);
 
         Map<String, String> parameterMap = parseAuthenticateHeader(authenticateHeader);
-
         String realm = parameterMap.get("realm");
+
+        validateRealm(userRealm, realm);
+
         String nonce = parameterMap.get("nonce");
         String messageQop = parameterMap.get("qop");
         String algorithm = parameterMap.get("algorithm") == null ? "MD5" : parameterMap.get("algorithm");
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties
index c86a75efab..030d43b7fb 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -31,6 +31,7 @@ asyncChannelWrapperSecure.wrongStateWrite=Flag that indicates a write is in prog
 
 authenticator.nullPassword=No password was provided to use for authentication
 authenticator.nullUserName=No user name was provided to use for authentication
+authenticator.realmMismatch=The user provided authentication realm [{0}] did not match the realm in the authentication challenge received from the server [{1}]
 
 backgroundProcessManager.processFailed=A background process failed
 
diff --git a/webapps/docs/web-socket-howto.xml b/webapps/docs/web-socket-howto.xml
index ac36b827de..8e7751d921 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -139,7 +139,24 @@
    <code>userProperties</code> of the provided
    <code>jakarta.websocket.ClientEndpointConfig</code>. The property is
    <ocde>org.apache.tomcat.websocket.MAX_REDIRECTIONS</ocde>. The default value
-   is 20. Redirection support can be disabled by configuring a value of zero.</p>
+   is 20. Redirection support can be disabled by configuring a value of zero.
+   </p>
+ 
+<p>When using the WebSocket client to connect to a server endpoint that requires
+   BASIC or DIGEST authentication, the following user properties must be set:
+   </p>
+   <ul>
+     <li><code>org.apache.tomcat.websocket.WS_AUTHENTICATION_USER_NAME</code>
+     </li>
+     <li><code>org.apache.tomcat.websocket.WS_AUTHENTICATION_PASSWORD</code>
+     </li>
+   </ul>
+   <p>Optionally, the WebSocket client can be configured only to send
+   credentials if the server authentication challenge includes a specific realm
+   by defining that realm in the optional user property:</p>
+   <ul>
+     <li><code>org.apache.tomcat.websocket.WS_AUTHENTICATION_REALM</code></li>
+   </ul>
 
 </section>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 05/06: More refactoring in preparation for the BZ 62312 fix

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ed7bc9110220bb620da4a16c599d4e6b5fb6bc97
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 9 09:27:33 2022 +0100

    More refactoring in preparation for the BZ 62312 fix
---
 .../tomcat/websocket/AuthenticationType.java       | 61 +++++++++++++++
 java/org/apache/tomcat/websocket/Constants.java    |  3 +-
 .../tomcat/websocket/LocalStrings.properties       |  4 +-
 .../tomcat/websocket/WsWebSocketContainer.java     | 89 ++++++++++++----------
 4 files changed, 114 insertions(+), 43 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/AuthenticationType.java b/java/org/apache/tomcat/websocket/AuthenticationType.java
new file mode 100644
index 0000000000..c3a9fa5736
--- /dev/null
+++ b/java/org/apache/tomcat/websocket/AuthenticationType.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+public enum AuthenticationType {
+
+    WWW(Constants.AUTHORIZATION_HEADER_NAME,
+            Constants.WWW_AUTHENTICATE_HEADER_NAME,
+            Constants.WS_AUTHENTICATION_USER_NAME,
+            Constants.WS_AUTHENTICATION_PASSWORD,
+            Constants.WS_AUTHENTICATION_REALM);
+
+    private final String authorizationHeaderName;
+    private final String authenticateHeaderName;
+    private final String userNameProperty;
+    private final String userPasswordProperty;
+    private final String userRealmProperty;
+
+    private AuthenticationType(String authorizationHeaderName, String authenticateHeaderName, String userNameProperty,
+            String userPasswordProperty, String userRealmProperty) {
+        this.authorizationHeaderName = authorizationHeaderName;
+        this.authenticateHeaderName = authenticateHeaderName;
+        this.userNameProperty = userNameProperty;
+        this.userPasswordProperty = userPasswordProperty;
+        this.userRealmProperty = userRealmProperty;
+    }
+
+    public String getAuthorizationHeaderName() {
+        return authorizationHeaderName;
+    }
+
+    public String getAuthenticateHeaderName() {
+        return authenticateHeaderName;
+    }
+
+    public String getUserNameProperty() {
+        return userNameProperty;
+    }
+
+    public String getUserPasswordProperty() {
+        return userPasswordProperty;
+    }
+
+    public String getUserRealmProperty() {
+        return userRealmProperty;
+    }
+}
diff --git a/java/org/apache/tomcat/websocket/Constants.java b/java/org/apache/tomcat/websocket/Constants.java
index b506694fe0..b2a843eba5 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -108,13 +108,14 @@ public class Constants {
     public static final String WS_PROTOCOL_HEADER_NAME = "Sec-WebSocket-Protocol";
     public static final String WS_EXTENSIONS_HEADER_NAME = "Sec-WebSocket-Extensions";
 
-    /// HTTP redirection status codes
+    // HTTP status codes
     public static final int MULTIPLE_CHOICES = 300;
     public static final int MOVED_PERMANENTLY = 301;
     public static final int FOUND = 302;
     public static final int SEE_OTHER = 303;
     public static final int USE_PROXY = 305;
     public static final int TEMPORARY_REDIRECT = 307;
+    public static final int UNAUTHORIZED = 401;
 
     // Configuration for Origin header in client
     static final String DEFAULT_ORIGIN_HEADER_VALUE =
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties
index 030d43b7fb..41b1e1efa2 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -139,7 +139,7 @@ wsWebSocketContainer.asynchronousSocketChannelFail=Unable to open a connection t
 wsWebSocketContainer.connect.entry=Connecting endpoint instance of type [{0}] to [{1}]
 wsWebSocketContainer.connect.write=Writing the HTTP upgrade request from buffer starting at [{0}] with a limit of [{1}] from local address [{2}]
 wsWebSocketContainer.defaultConfiguratorFail=Failed to create the default configurator
-wsWebSocketContainer.failedAuthentication=Failed to handle HTTP response code [{0}]. Authentication header was not accepted by server.
+wsWebSocketContainer.failedAuthentication=Failed to handle HTTP response code [{0}]. [{1}] header was not accepted by server.
 wsWebSocketContainer.httpRequestFailed=The HTTP request to initiate the WebSocket connection to [{0}] failed
 wsWebSocketContainer.invalidExtensionParameters=The server responded with extension parameters the client is unable to support
 wsWebSocketContainer.invalidHeader=Unable to parse HTTP header as no colon is present to delimit header name and header value in [{0}]. The header has been skipped.
@@ -147,8 +147,8 @@ wsWebSocketContainer.invalidStatus=The HTTP response from the server [{0}] did n
 wsWebSocketContainer.invalidSubProtocol=The WebSocket server returned multiple values for the Sec-WebSocket-Protocol header
 wsWebSocketContainer.maxBuffer=This implementation limits the maximum size of a buffer to Integer.MAX_VALUE
 wsWebSocketContainer.missingAnnotation=Cannot use POJO class [{0}] as it is not annotated with @ClientEndpoint
+wsWebSocketContainer.missingAuthenticateHeader=Failed to handle HTTP response code [{0}]. Missing [{1}] header in response
 wsWebSocketContainer.missingLocationHeader=Failed to handle HTTP response code [{0}]. Missing Location header in response
-wsWebSocketContainer.missingWWWAuthenticateHeader=Failed to handle HTTP response code [{0}]. Missing WWW-Authenticate header in response
 wsWebSocketContainer.pathNoHost=No host was specified in URI
 wsWebSocketContainer.pathWrongScheme=The scheme [{0}] is not supported. The supported schemes are ws and wss
 wsWebSocketContainer.proxyConnectFail=Failed to connect to the configured Proxy [{0}]. The HTTP response code was [{1}]
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index 6b301aa086..571fe611c9 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -389,46 +389,9 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
                     return connectToServerRecursive(
                             clientEndpointHolder, clientEndpointConfiguration, redirectLocation, redirectSet);
 
-                }
-
-                else if (httpResponse.status == 401) {
-
-                    if (userProperties.get(Constants.AUTHORIZATION_HEADER_NAME) != null) {
-                        throw new DeploymentException(sm.getString(
-                                "wsWebSocketContainer.failedAuthentication",
-                                Integer.valueOf(httpResponse.status)));
-                    }
-
-                    List<String> wwwAuthenticateHeaders = httpResponse.getHandshakeResponse()
-                            .getHeaders().get(Constants.WWW_AUTHENTICATE_HEADER_NAME);
-
-                    if (wwwAuthenticateHeaders == null || wwwAuthenticateHeaders.isEmpty() ||
-                            wwwAuthenticateHeaders.get(0) == null || wwwAuthenticateHeaders.get(0).isEmpty()) {
-                        throw new DeploymentException(sm.getString(
-                                "wsWebSocketContainer.missingWWWAuthenticateHeader",
-                                Integer.toString(httpResponse.status)));
-                    }
-
-                    String authScheme = wwwAuthenticateHeaders.get(0).split("\\s+", 2)[0];
-                    String requestUri = new String(request.array(), StandardCharsets.ISO_8859_1)
-                            .split("\\s", 3)[1];
-
-                    Authenticator auth = AuthenticatorFactory.getAuthenticator(authScheme);
-
-                    if (auth == null) {
-                        throw new DeploymentException(
-                                sm.getString("wsWebSocketContainer.unsupportedAuthScheme",
-                                        Integer.valueOf(httpResponse.status), authScheme));
-                    }
-
-                    userProperties.put(Constants.AUTHORIZATION_HEADER_NAME, auth.getAuthorization(
-                            requestUri, wwwAuthenticateHeaders.get(0),
-                            (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME),
-                            (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD),
-                            (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM)));
-
-                    return connectToServerRecursive(
-                            clientEndpointHolder, clientEndpointConfiguration, path, redirectSet);
+                } else if (httpResponse.status == Constants.UNAUTHORIZED) {
+                    return processAuthenticationChallenge(clientEndpointHolder, clientEndpointConfiguration, path,
+                            redirectSet, userProperties, request, httpResponse, AuthenticationType.WWW);
 
                 } else {
                     throw new DeploymentException(sm.getString("wsWebSocketContainer.invalidStatus",
@@ -527,6 +490,52 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
     }
 
 
+    private Session processAuthenticationChallenge(ClientEndpointHolder clientEndpointHolder,
+            ClientEndpointConfig clientEndpointConfiguration, URI path, Set<URI> redirectSet,
+            Map<String,Object> userProperties, ByteBuffer request, HttpResponse httpResponse,
+            AuthenticationType authenticationType)
+            throws DeploymentException, AuthenticationException {
+
+        if (userProperties.get(authenticationType.getAuthorizationHeaderName()) != null) {
+            throw new DeploymentException(sm.getString(
+                    "wsWebSocketContainer.failedAuthentication",
+                    Integer.valueOf(httpResponse.status), authenticationType.getAuthorizationHeaderName()));
+        }
+
+        List<String> authenticateHeaders = httpResponse.getHandshakeResponse().getHeaders().get(
+                authenticationType.getAuthenticateHeaderName());
+
+        if (authenticateHeaders == null || authenticateHeaders.isEmpty() ||
+                authenticateHeaders.get(0) == null || authenticateHeaders.get(0).isEmpty()) {
+            throw new DeploymentException(sm.getString(
+                    "wsWebSocketContainer.missingAuthenticateHeader",
+                    Integer.toString(httpResponse.status), authenticationType.getAuthenticateHeaderName()));
+        }
+
+        String authScheme = authenticateHeaders.get(0).split("\\s+", 2)[0];
+
+        Authenticator auth = AuthenticatorFactory.getAuthenticator(authScheme);
+
+        if (auth == null) {
+            throw new DeploymentException(
+                    sm.getString("wsWebSocketContainer.unsupportedAuthScheme",
+                            Integer.valueOf(httpResponse.status), authScheme));
+        }
+
+        String requestUri = new String(request.array(), StandardCharsets.ISO_8859_1).split("\\s", 3)[1];
+
+        userProperties.put(authenticationType.getAuthorizationHeaderName(), auth.getAuthorization(
+                requestUri, authenticateHeaders.get(0),
+                (String) userProperties.get(authenticationType.getUserNameProperty()),
+                (String) userProperties.get(authenticationType.getUserPasswordProperty()),
+                (String) userProperties.get(authenticationType.getUserRealmProperty())));
+
+        return connectToServerRecursive(
+                clientEndpointHolder, clientEndpointConfiguration, path, redirectSet);
+    }
+
+
+
     private static void writeRequest(AsyncChannelWrapper channel, ByteBuffer request,
             long timeout) throws TimeoutException, InterruptedException, ExecutionException {
         int toWrite = request.limit();


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 04/06: Refactor in preparation for fixinf BZ 62312

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit a747a41f509df1568c5467ece9d9db65f31f0adf
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 9 09:16:04 2022 +0100

    Refactor in preparation for fixinf BZ 62312
    
    https://bz.apache.org/bugzilla/show_bug.cgi?id=62312
---
 .../org/apache/tomcat/websocket/Authenticator.java | 32 ++++++++++++++++++++--
 .../tomcat/websocket/BasicAuthenticator.java       |  8 ++----
 .../tomcat/websocket/DigestAuthenticator.java      |  8 ++----
 .../tomcat/websocket/WsWebSocketContainer.java     |  5 +++-
 4 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/Authenticator.java b/java/org/apache/tomcat/websocket/Authenticator.java
index a49e18cb51..0c82ea7fb0 100644
--- a/java/org/apache/tomcat/websocket/Authenticator.java
+++ b/java/org/apache/tomcat/websocket/Authenticator.java
@@ -43,9 +43,37 @@ public abstract class Authenticator {
      * @return The generated authorization header value
      *
      * @throws AuthenticationException When an error occurs
+     *
+     * @deprecated Use {@link
+     *             #getAuthorization(String, String, String, String, String)}.
+     *             Will be removed in Tomcat 10.1.x
+     */
+    @Deprecated
+    public String getAuthorization(String requestUri, String authenticateHeader, Map<String, Object> userProperties)
+            throws AuthenticationException {
+        return getAuthorization(requestUri, authenticateHeader,
+                (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME),
+                (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD),
+                (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM));
+    }
+
+    /**
+     * Generate the authorization header value that will be sent to the server.
+     *
+     * @param requestUri            The request URI
+     * @param authenticateHeader    The server authentication header received
+     * @param userName              The user name
+     * @param userPassword          The user password
+     * @param userRealm             The realm for which the provided user name
+     *                                  and password are valid. {@code null} to
+     *                                  indicate all realms.
+     *
+     * @return The generated authorization header value
+     *
+     * @throws AuthenticationException When an error occurs
      */
-    public abstract String getAuthorization(String requestUri, String authenticateHeader,
-            Map<String, Object> userProperties) throws AuthenticationException;
+    public abstract String getAuthorization(String requestUri, String authenticateHeader, String userName,
+            String userPassword, String userRealm) throws AuthenticationException;
 
 
     /**
diff --git a/java/org/apache/tomcat/websocket/BasicAuthenticator.java b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
index e7731cbdb5..d993ccf40d 100644
--- a/java/org/apache/tomcat/websocket/BasicAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/BasicAuthenticator.java
@@ -30,12 +30,8 @@ public class BasicAuthenticator extends Authenticator {
     public static final String charsetparam = "charset";
 
     @Override
-    public String getAuthorization(String requestUri, String authenticateHeader,
-            Map<String, Object> userProperties) throws AuthenticationException {
-
-        String userName = (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME);
-        String userPassword = (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD);
-        String userRealm = (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM);
+    public String getAuthorization(String requestUri, String authenticateHeader, String userName, String userPassword,
+            String userRealm) throws AuthenticationException {
 
         validateUsername(userName);
         validatePassword(userPassword);
diff --git a/java/org/apache/tomcat/websocket/DigestAuthenticator.java b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
index cb6c9318de..f9511537be 100644
--- a/java/org/apache/tomcat/websocket/DigestAuthenticator.java
+++ b/java/org/apache/tomcat/websocket/DigestAuthenticator.java
@@ -36,12 +36,8 @@ public class DigestAuthenticator extends Authenticator {
     private long cNonce;
 
     @Override
-    public String getAuthorization(String requestUri, String authenticateHeader,
-            Map<String, Object> userProperties) throws AuthenticationException {
-
-        String userName = (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME);
-        String userPassword = (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD);
-        String userRealm = (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM);
+    public String getAuthorization(String requestUri, String authenticateHeader, String userName, String userPassword,
+            String userRealm) throws AuthenticationException {
 
         validateUsername(userName);
         validatePassword(userPassword);
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index 608e854bf9..6b301aa086 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -422,7 +422,10 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
                     }
 
                     userProperties.put(Constants.AUTHORIZATION_HEADER_NAME, auth.getAuthorization(
-                            requestUri, wwwAuthenticateHeaders.get(0), userProperties));
+                            requestUri, wwwAuthenticateHeaders.get(0),
+                            (String) userProperties.get(Constants.WS_AUTHENTICATION_USER_NAME),
+                            (String) userProperties.get(Constants.WS_AUTHENTICATION_PASSWORD),
+                            (String) userProperties.get(Constants.WS_AUTHENTICATION_REALM)));
 
                     return connectToServerRecursive(
                             clientEndpointHolder, clientEndpointConfiguration, path, redirectSet);


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org