diff --git a/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java b/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java deleted file mode 100644 index db839adc10..0000000000 --- a/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed 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.thingsboard.server.config; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.stereotype.Component; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.server.HandshakeInterceptor; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.auth.pat.ApiKeyAuthenticationProvider; -import org.thingsboard.server.service.security.model.SecurityUser; - -import java.util.Map; - -@Slf4j -@Component -@TbCoreComponent -@RequiredArgsConstructor -public class ApiKeyHandshakeInterceptor implements HandshakeInterceptor { - - public static final String API_KEY_SECURITY_CTX_ATTR = "apiKeySecurityCtx"; - - private final ApiKeyAuthenticationProvider apiKeyAuthenticationProvider; - - @Override - public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) { - String apiKey = extractApiKey(request); - if (apiKey != null) { - if (apiKey.isEmpty()) { - log.debug("Empty API key provided during WS handshake"); - response.setStatusCode(HttpStatus.UNAUTHORIZED); - return false; - } - try { - SecurityUser securityUser = apiKeyAuthenticationProvider.authenticate(apiKey); - attributes.put(API_KEY_SECURITY_CTX_ATTR, securityUser); - } catch (AuthenticationException e) { - log.warn("API key authentication failed during WS handshake: {}", e.getMessage()); - response.setStatusCode(HttpStatus.UNAUTHORIZED); - return false; - } - } - return true; - } - - private String extractApiKey(ServerHttpRequest request) { - String header = request.getHeaders().getFirst(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER); - if (header == null) { - header = request.getHeaders().getFirst(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER_V2); - } - if (header != null && header.startsWith(ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX)) { - return header.substring(ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX.length()); - } - return null; - } - - @Override - public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { - // no-op - } - -} diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index f5be3afa41..4ce7268232 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -40,7 +40,6 @@ public class WebSocketConfiguration implements WebSocketConfigurer { private static final String WS_API_MAPPING = "/api/ws/**"; private final WebSocketHandler wsHandler; - private final ApiKeyHandshakeInterceptor apiKeyHandshakeInterceptor; @Value("${server.ws.max_text_message_buffer_size:32768}") private int maxTextMessageBufferSize; @@ -62,7 +61,6 @@ public class WebSocketConfiguration implements WebSocketConfigurer { throw new RuntimeException("TbWebSocketHandler expected but " + wsHandler + " provided"); } registry.addHandler(wsHandler, WS_API_MAPPING) - .addInterceptors(apiKeyHandshakeInterceptor) .setAllowedOriginPatterns("*"); } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 5c24c1e6c1..48d3a907bc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.config.ApiKeyHandshakeInterceptor; import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -336,16 +335,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } SecurityUser securityCtx = null; - Object apiKeyCtx = session.getAttributes().get(ApiKeyHandshakeInterceptor.API_KEY_SECURITY_CTX_ATTR); - if (apiKeyCtx instanceof SecurityUser) { - securityCtx = (SecurityUser) apiKeyCtx; - } else { - String query = session.getUri().getQuery(); - if (query != null) { - String token = extractQueryParam(query, "token"); - if (StringUtils.isNotEmpty(token)) { - securityCtx = authenticationProvider.authenticate(token); - } + String query = session.getUri().getQuery(); + if (query != null) { + String token = extractQueryParam(query, "token"); + if (StringUtils.isNotEmpty(token)) { + securityCtx = authenticationProvider.authenticate(token); } } return WebSocketSessionRef.builder() diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index 7ddca44b36..996efb17be 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -32,7 +32,6 @@ import org.springframework.web.socket.config.annotation.EnableWebSocket; import java.net.URI; import java.net.URISyntaxException; -import java.util.Map; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @@ -121,10 +120,4 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { return wsClient; } - protected TbTestWebSocketClient buildAndConnectWebSocketClientWithApiKeyHeader(String apiKey) throws URISyntaxException, InterruptedException { - TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-Authorization", "ApiKey " + apiKey)); - assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); - return wsClient; - } - } diff --git a/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java index eead1d6a47..405eecec9b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java @@ -17,22 +17,14 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.pat.ApiKey; import org.thingsboard.server.common.data.pat.ApiKeyInfo; -import org.thingsboard.server.common.data.query.DeviceTypeFilter; -import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; import java.net.URI; import java.net.URISyntaxException; -import java.util.Collections; -import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @@ -64,49 +56,6 @@ public class ApiKeyWebSocketApiTest extends WebSocketApiTest { return buildAndConnectWebSocketClientWithApiKey(apiKey.getValue()); } - @Test - public void testApiKeyHeaderAuth() throws Exception { - TbTestWebSocketClient client = buildAndConnectWebSocketClientWithApiKeyHeader(apiKey.getValue()); - try { - assertThat(client.isOpen()).isTrue(); - - DeviceTypeFilter dtf = new DeviceTypeFilter(List.of("default"), "Device"); - EntityCountQuery ecq = new EntityCountQuery(dtf, Collections.emptyList()); - EntityCountCmd cmd = new EntityCountCmd(1, ecq); - client.send(cmd); - - EntityCountUpdate update = client.parseCountReply(client.waitForReply()); - Assert.assertEquals(1, update.getCmdId()); - Assert.assertTrue(update.getCount() >= 0); - } finally { - client.close(); - } - } - - @Test - public void testInvalidApiKeyHeader_connectionRejected() throws Exception { - TbTestWebSocketClient client = new TbTestWebSocketClient( - new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-Authorization", "ApiKey invalid-key")); - try { - boolean connected = client.connectBlocking(TIMEOUT, TimeUnit.SECONDS); - assertThat(connected).isFalse(); - } finally { - client.close(); - } - } - - @Test - public void testEmptyApiKeyHeader_connectionRejected() throws Exception { - TbTestWebSocketClient client = new TbTestWebSocketClient( - new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-Authorization", "ApiKey ")); - try { - boolean connected = client.connectBlocking(TIMEOUT, TimeUnit.SECONDS); - assertThat(connected).isFalse(); - } finally { - client.close(); - } - } - @Test public void testInvalidApiKeyAuthCmd_connectionClosed() throws Exception { TbTestWebSocketClient client = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws")); diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index d663b2c9ac..671c936585 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -44,7 +44,6 @@ import java.net.URI; import java.nio.channels.NotYetConnectedException; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -64,9 +63,6 @@ public class TbTestWebSocketClient extends WebSocketClient { super(serverUri); } - public TbTestWebSocketClient(URI serverUri, Map httpHeaders) { - super(serverUri, httpHeaders); - } @Override public void onOpen(ServerHandshake serverHandshake) {