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 09:34:38 UTC

[tomcat] branch 10.0.x updated (e0e1e6d45e -> 02ca95e7ec)

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

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


    from e0e1e6d45e Clean-up / improve naming. No functional change.
     new 40b38dc3cd Rename / refactor in preparation for fixing BZ 62312
     new 39233d1a71 Refactor and use i18n for exception messages
     new 90b2a3de90 Add support for optionally validating the authentication realm
     new be9f3532e6 Refactor in preparation for fixinf BZ 62312
     new e0351b4474 More refactoring in preparation for the BZ 62312 fix
     new 02ca95e7ec 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] 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 10.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit e0351b4474b25e1d3b2230c72e59cb12ed3f9ed8
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 859f26a9f2..87f66cb17e 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -96,13 +96,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 41d14a9fb9..85a128532b 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -141,7 +141,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.
@@ -149,8 +149,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 6e26a08cce..92dbe3d3b0 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] 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 10.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 90b2a3de90d1885c6932185c33ec7d52a2ea5ed4
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 682fff4b9b..859f26a9f2 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -127,6 +127,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";
 
     /* Configuration for extensions
      * Note: These options are primarily present to enable this implementation
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 45162204cc..41d14a9fb9 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 709a2c35e2..97600ef127 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -145,7 +145,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] 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 10.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 02ca95e7ec1f745fb0ef798e7ac17d86f882f704
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 87f66cb17e..8dffa30399 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -90,6 +90,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";
@@ -104,6 +106,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 =
@@ -130,6 +133,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";
+
     /* Configuration for extensions
      * Note: These options are primarily present to enable this implementation
      *       to pass compliance tests. They are expected to be removed once
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index 92dbe3d3b0..509fabe979 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 d13f69ee36..c14c4168cb 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -277,6 +277,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 97600ef127..34bd6c5eb2 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -147,7 +147,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>
@@ -164,6 +164,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] 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 10.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 40b38dc3cdb973bc18381046666734d951ea95ca
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] 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 10.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit be9f3532e67c734698a31fbf191b156e102f3abd
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 7792122008..6e26a08cce 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


[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 10.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 39233d1a715735ee2df262fa7ea8eeafa5154c7e
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 382cc04039..45162204cc 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -29,6 +29,9 @@ asyncChannelWrapperSecure.tooBig=The result [{0}] is too big to be expressed as
 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