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-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org