Skip to content

Commit 1b328b0

Browse files
Copilotnobodyiam
andcommitted
Fix Base64 encoding mismatch for complex keys in OpenAPI
Co-authored-by: nobodyiam <[email protected]>
1 parent e0caf85 commit 1b328b0

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

apollo-adminservice/src/test/java/com/ctrip/framework/apollo/adminservice/controller/ItemControllerTest.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.ctrip.framework.apollo.biz.service.ItemService;
2525
import com.ctrip.framework.apollo.common.dto.*;
2626

27+
import java.nio.charset.StandardCharsets;
28+
import java.util.Base64;
2729
import java.util.List;
2830
import java.util.Objects;
2931
import org.junit.Assert;
@@ -175,4 +177,74 @@ public void testSearch() {
175177
assertThat(itemInfoDTOS.getContent().toString())
176178
.isEqualTo(response.getBody().getContent().toString());
177179
}
180+
181+
@Test
182+
@Sql(scripts = "/controller/test-itemset.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
183+
@Sql(scripts = "/controller/cleanup.sql", executionPhase = ExecutionPhase.AFTER_TEST_METHOD)
184+
public void testGetByEncodedKey() {
185+
this.testCreate();
186+
187+
String appId = "someAppId";
188+
AppDTO app = restTemplate.getForObject(appBaseUrl(), AppDTO.class, appId);
189+
assert app != null;
190+
ClusterDTO cluster =
191+
restTemplate.getForObject(clusterBaseUrl(), ClusterDTO.class, app.getAppId(), "default");
192+
assert cluster != null;
193+
NamespaceDTO namespace = restTemplate.getForObject(namespaceBaseUrl(), NamespaceDTO.class,
194+
app.getAppId(), cluster.getName(), "application");
195+
196+
String itemKey = "test-key";
197+
198+
// Test with URL-safe Base64 encoding without padding
199+
String encodedKey = Base64.getUrlEncoder().withoutPadding()
200+
.encodeToString(itemKey.getBytes(StandardCharsets.UTF_8));
201+
202+
String getByEncodedKeyUrl = url(
203+
"/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key}");
204+
assert namespace != null;
205+
ResponseEntity<ItemDTO> response = restTemplate.getForEntity(getByEncodedKeyUrl, ItemDTO.class,
206+
app.getAppId(), cluster.getName(), namespace.getNamespaceName(), encodedKey);
207+
208+
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
209+
Assert.assertEquals(itemKey, Objects.requireNonNull(response.getBody()).getKey());
210+
}
211+
212+
@Test
213+
@Sql(scripts = "/controller/test-itemset.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
214+
@Sql(scripts = "/controller/cleanup.sql", executionPhase = ExecutionPhase.AFTER_TEST_METHOD)
215+
public void testGetByEncodedKeyWithComplexKey() {
216+
String appId = "someAppId";
217+
AppDTO app = restTemplate.getForObject(appBaseUrl(), AppDTO.class, appId);
218+
assert app != null;
219+
ClusterDTO cluster =
220+
restTemplate.getForObject(clusterBaseUrl(), ClusterDTO.class, app.getAppId(), "default");
221+
assert cluster != null;
222+
NamespaceDTO namespace = restTemplate.getForObject(namespaceBaseUrl(), NamespaceDTO.class,
223+
app.getAppId(), cluster.getName(), "application");
224+
225+
// Create an item with a complex key containing special characters
226+
String complexKey = "wonfu.soa.circuit-breaker.enable.gitea-svc@/api/v1/fetchWorkflows";
227+
String itemValue = "test-value";
228+
ItemDTO item = new ItemDTO(complexKey, itemValue, "", 1);
229+
assert namespace != null;
230+
item.setNamespaceId(namespace.getId());
231+
item.setDataChangeLastModifiedBy("apollo");
232+
233+
ResponseEntity<ItemDTO> createResponse = restTemplate.postForEntity(itemBaseUrl(), item,
234+
ItemDTO.class, app.getAppId(), cluster.getName(), namespace.getNamespaceName());
235+
Assert.assertEquals(HttpStatus.OK, createResponse.getStatusCode());
236+
237+
// Now retrieve it using the encoded key
238+
String encodedKey = Base64.getUrlEncoder().withoutPadding()
239+
.encodeToString(complexKey.getBytes(StandardCharsets.UTF_8));
240+
241+
String getByEncodedKeyUrl = url(
242+
"/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key}");
243+
ResponseEntity<ItemDTO> response = restTemplate.getForEntity(getByEncodedKeyUrl, ItemDTO.class,
244+
app.getAppId(), cluster.getName(), namespace.getNamespaceName(), encodedKey);
245+
246+
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
247+
Assert.assertEquals(complexKey, Objects.requireNonNull(response.getBody()).getKey());
248+
Assert.assertEquals(itemValue, response.getBody().getValue());
249+
}
178250
}

apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public OpenItemDTO getItemByEncodedKey(@PathVariable String appId, @PathVariable
7878
@PathVariable String clusterName, @PathVariable String namespaceName,
7979
@PathVariable String key) {
8080
return this.getItem(appId, env, clusterName, namespaceName,
81-
new String(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8))));
81+
new String(Base64.getUrlDecoder().decode(key.getBytes(StandardCharsets.UTF_8))));
8282
}
8383

8484
@PreAuthorize(
@@ -158,7 +158,7 @@ public void updateItemByEncodedKey(@PathVariable String appId, @PathVariable Str
158158
@PathVariable String key, @RequestBody OpenItemDTO item,
159159
@RequestParam(defaultValue = "false") boolean createIfNotExists) {
160160
this.updateItem(appId, env, clusterName, namespaceName,
161-
new String(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8))), item,
161+
new String(Base64.getUrlDecoder().decode(key.getBytes(StandardCharsets.UTF_8))), item,
162162
createIfNotExists);
163163
}
164164

@@ -191,7 +191,7 @@ public void deleteItemByEncodedKey(@PathVariable String appId, @PathVariable Str
191191
@PathVariable String clusterName, @PathVariable String namespaceName,
192192
@PathVariable String key, @RequestParam String operator) {
193193
this.deleteItem(appId, env, clusterName, namespaceName,
194-
new String(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8))), operator);
194+
new String(Base64.getUrlDecoder().decode(key.getBytes(StandardCharsets.UTF_8))), operator);
195195
}
196196

197197
@GetMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items")

apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public ItemDTO loadItemByEncodeKey(Env env, String appId, String clusterName,
218218
return restTemplate.get(env,
219219
"apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key}",
220220
ItemDTO.class, appId, clusterName, namespaceName,
221-
new String(Base64.getEncoder().encode(key.getBytes(StandardCharsets.UTF_8))));
221+
new String(Base64.getUrlEncoder().withoutPadding().encode(key.getBytes(StandardCharsets.UTF_8))));
222222
}
223223

224224
public ItemDTO loadItemById(Env env, long itemId) {

0 commit comments

Comments
 (0)