From 74f0caab5ddc051cdd2257be4a18ebb034f8731f Mon Sep 17 00:00:00 2001 From: Logic Date: Thu, 30 Jul 2026 16:59:37 +0800 Subject: [PATCH] maintenance: scope Grafana request authentication --- .../service/ServiceAccountService.java | 4 - .../service/ServiceAccountServiceTest.java | 90 +++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/ServiceAccountServiceTest.java diff --git a/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/ServiceAccountService.java b/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/ServiceAccountService.java index 7e1e251d7d1..2e87bd3339c 100644 --- a/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/ServiceAccountService.java +++ b/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/ServiceAccountService.java @@ -44,7 +44,6 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.support.BasicAuthenticationInterceptor; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -98,7 +97,6 @@ public Long createServiceAccount() { String endpoint = String.format(prefix + CREATE_SERVICE_ACCOUNT_API, url); HttpHeaders headers = createHeaders(); String body = String.format("{\"name\":\"%s\",\"role\":\"%s\",\"isDisabled\":false}", ACCOUNT_NAME, ACCOUNT_ROLE); - restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(username, password)); HttpEntity request = new HttpEntity<>(body, headers); try { ResponseEntity response = restTemplate.postForEntity(endpoint, request, String.class); @@ -128,7 +126,6 @@ public String applyForToken() { String endpoint = String.format(prefix + CREATE_SERVICE_TOKEN_API, url, accountId); HttpHeaders headers = createHeaders(); String body = String.format("{\"name\":\"%s\"}", CommonUtil.generateRandomWord(6)); - restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(username, password)); HttpEntity request = new HttpEntity<>(body, headers); try { ResponseEntity response = restTemplate.postForEntity(endpoint, request, String.class); @@ -178,7 +175,6 @@ public String getToken() { public ResponseEntity getAccounts() { String endpoint = String.format(prefix + GET_SERVICE_ACCOUNTS_API, url); HttpHeaders headers = createHeaders(); - restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(username, password)); HttpEntity request = new HttpEntity<>(headers); try { ResponseEntity response = restTemplate.exchange(endpoint, HttpMethod.GET, request, String.class); diff --git a/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/ServiceAccountServiceTest.java b/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/ServiceAccountServiceTest.java new file mode 100644 index 00000000000..ad253f9c39d --- /dev/null +++ b/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/ServiceAccountServiceTest.java @@ -0,0 +1,90 @@ +/* + * 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.hertzbeat.grafana.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.hertzbeat.common.constants.NetworkConstants; +import org.apache.hertzbeat.grafana.config.GrafanaProperties; +import org.apache.hertzbeat.grafana.dao.GrafanaConfigDao; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +/** + * Test case for {@link ServiceAccountService}. + */ +@ExtendWith(MockitoExtension.class) +class ServiceAccountServiceTest { + + @Mock + private GrafanaProperties grafanaProperties; + + @Mock + private GrafanaConfigDao grafanaConfigDao; + + @Mock + private RestTemplate restTemplate; + + private ServiceAccountService serviceAccountService; + + @BeforeEach + void setUp() { + when(grafanaProperties.getPrefix()).thenReturn("https://"); + when(grafanaProperties.getUrl()).thenReturn("grafana.example"); + when(grafanaProperties.username()).thenReturn("admin"); + when(grafanaProperties.password()).thenReturn("password"); + serviceAccountService = new ServiceAccountService(grafanaProperties, grafanaConfigDao, restTemplate); + serviceAccountService.init(); + } + + @Test + void keepsGrafanaAuthenticationScopedToTheRequest() { + when(restTemplate.exchange( + eq("https://grafana.example/api/serviceaccounts/search"), + eq(HttpMethod.GET), + any(HttpEntity.class), + eq(String.class))) + .thenReturn(ResponseEntity.ok("{\"serviceAccounts\":[]}")); + + serviceAccountService.getAccounts(); + + verify(restTemplate, never()).getInterceptors(); + ArgumentCaptor> requestCaptor = ArgumentCaptor.forClass(HttpEntity.class); + verify(restTemplate).exchange( + eq("https://grafana.example/api/serviceaccounts/search"), + eq(HttpMethod.GET), + requestCaptor.capture(), + eq(String.class)); + assertEquals( + "Basic YWRtaW46cGFzc3dvcmQ=", + requestCaptor.getValue().getHeaders().getFirst(NetworkConstants.AUTHORIZATION)); + } +}