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

chenzhida pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new 0c1216c48 [#4722] Fix bug in `ProviderAuthFilter` where `requestPath` 
not absolute problem (#4725)
0c1216c48 is described below

commit 0c1216c4872a3533df0d99f3942b3b5b87a18ee2
Author: Aithusa <[email protected]>
AuthorDate: Fri Feb 28 15:02:55 2025 +0800

    [#4722] Fix bug in `ProviderAuthFilter` where `requestPath` not absolute 
problem (#4725)
    
    * [#4722] fix provider auth filter requestPath not absolute problem
    
    1. Fix problem that ProviderAuthFilter not use absolute path.
    2. same logic has come up twice, move it to SwaggerUtils.
    
    * [#4722] add tests for `excludePathPatterns`/`includePathPatterns` feature 
in `handler-publickey-auth`
    
    * [#4722] fix test assertion formatting issue
    
    * [#4722] replaced hardcoded path strings in tests with predefined 
constants for better maintainability and readability.
---
 .../common/rest/definition/RestOperationMeta.java  |  17 +--
 .../servicecomb/core/governance/MatchType.java     |  17 +--
 .../provider/ProviderAuthFilter.java               |   5 +-
 .../provider/TestPathCheckUtils.java               | 161 +++++++++++++++++++++
 .../apache/servicecomb/swagger/SwaggerUtils.java   |  20 +++
 5 files changed, 187 insertions(+), 33 deletions(-)

diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
index 6b2e3b332..580175ec6 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
@@ -102,7 +102,7 @@ public class RestOperationMeta {
       }
     }
 
-    setAbsolutePath(concatPath(SwaggerUtils.getBasePath(swagger), 
operationMeta.getOperationPath()));
+    setAbsolutePath(SwaggerUtils.concatAbsolutePath(swagger, 
operationMeta.getOperationPath()));
   }
 
   private void addRestParamByName(OperationMeta operationMeta, String name, 
Operation operation) {
@@ -168,21 +168,6 @@ public class RestOperationMeta {
     this.operationMeta = operationMeta;
   }
 
-  /**
-   * Concat the two paths to an absolute path, end of '/' added.
-   *
-   * e.g. "/" + "/ope" = /ope/
-   * e.g. "/prefix" + "/ope" = /prefix/ope/
-   */
-  private String concatPath(String basePath, String operationPath) {
-    return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath))
-        .replaceAll("/{2,}", "/");
-  }
-
-  private String nonNullify(String path) {
-    return path == null ? "" : path;
-  }
-
   public String getAbsolutePath() {
     return this.absolutePath;
   }
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java 
b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java
index 2c1006706..15d58d666 100644
--- a/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java
+++ b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java
@@ -36,7 +36,7 @@ public final class MatchType {
     public String apiPath() {
       if 
(MatchType.REST.equalsIgnoreCase(invocation.getOperationMeta().getConfig().getGovernanceMatchType()))
 {
         if (!invocation.isProducer()) {
-          return 
concatAbsolutePath(SwaggerUtils.getBasePath(invocation.getSchemaMeta().getSwagger()),
+          return 
SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(),
               invocation.getOperationMeta().getOperationPath());
         }
         // not highway
@@ -114,19 +114,4 @@ public final class MatchType {
   public static GovernanceRequestExtractor createGovHttpRequest(Invocation 
invocation) {
     return new GovernanceRequestExtractorImpl(invocation);
   }
-
-  /**
-   * Concat the two paths to an absolute path, without end of '/'.
-   *
-   * e.g. "/" + "/ope" = /ope
-   * e.g. "/prefix" + "/ope" = /prefix/ope
-   */
-  private static String concatAbsolutePath(String basePath, String 
operationPath) {
-    return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath))
-        .replaceAll("/{2,}", "/");
-  }
-
-  private static String nonNullify(String path) {
-    return path == null ? "" : path;
-  }
 }
diff --git 
a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java
 
b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java
index 02b1108a5..f525ae88c 100644
--- 
a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java
+++ 
b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java
@@ -24,6 +24,7 @@ import org.apache.servicecomb.core.filter.AbstractFilter;
 import org.apache.servicecomb.core.filter.Filter;
 import org.apache.servicecomb.core.filter.FilterNode;
 import org.apache.servicecomb.core.filter.ProviderFilter;
+import org.apache.servicecomb.swagger.SwaggerUtils;
 import org.apache.servicecomb.swagger.invocation.Response;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -54,7 +55,9 @@ public class ProviderAuthFilter extends AbstractFilter 
implements ProviderFilter
 
   @Override
   public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
-    if 
(PathCheckUtils.isNotRequiredAuth(invocation.getOperationMeta().getOperationPath(),
 env)) {
+    String requestFullPath = 
SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(),
+            invocation.getOperationMeta().getOperationPath());
+    if (PathCheckUtils.isNotRequiredAuth(requestFullPath, env)) {
       return nextNode.onFilter(invocation);
     }
     String token = invocation.getContext(CoreConst.AUTH_TOKEN);
diff --git 
a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java
 
b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java
new file mode 100644
index 000000000..b8ff46230
--- /dev/null
+++ 
b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java
@@ -0,0 +1,161 @@
+/*
+ * 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.servicecomb.authentication.provider;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.mockito.Mockito.when;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.servers.Server;
+import org.apache.servicecomb.swagger.SwaggerUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.core.env.Environment;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TestPathCheckUtils {
+  private static final String KEY_INCLUDE_PATH = 
"servicecomb.publicKey.accessControl.includePathPatterns";
+
+  private static final String KEY_EXCLUDE_PATH = 
"servicecomb.publicKey.accessControl.excludePathPatterns";
+
+  private static final String BASE_PATH = "/api/v1";
+
+  private Environment environment;
+
+  private OpenAPI swagger;
+
+  @BeforeEach
+  public void setUp() {
+    environment = Mockito.mock(Environment.class);
+    swagger = new OpenAPI();
+    swagger.setServers(new ArrayList<>());
+    swagger.getServers().add(new Server().url(BASE_PATH));
+  }
+
+  @Test
+  public void testExcludePathWithBasePathAndExactMatch() {
+    String operationPath = "/public";
+    when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + 
operationPath);
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath);
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for excluded path with exact match");
+  }
+
+  @Test
+  public void testExcludePathWithBasePathAndWildcard() {
+    when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + 
"/public/*");
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/test");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for excluded path with wildcard");
+
+    fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/nested/path");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for excluded nested path with wildcard");
+  }
+
+  @Test
+  public void testIncludePathWithBasePath() {
+    when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn("");
+    when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + 
"/private/*");
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, 
"/private/resource");
+    assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should require auth for included path");
+
+    fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/resource");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for non-included path");
+  }
+
+  @Test
+  public void testExcludeOverrideIncludePath() {
+    when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + 
"/resource");
+    when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + 
"/*");
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/resource");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Exclude patterns should override include patterns");
+  }
+
+  @Test
+  public void testMultipleExcludePaths() {
+    List<String> operationPaths = List.of("/public", "/health", "/metrics/*");
+    when(environment.getProperty(KEY_EXCLUDE_PATH, 
"")).thenReturn(concatPath(BASE_PATH, operationPaths));
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for first exclude path");
+
+    fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/health");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for second exclude path");
+
+    fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/metrics/jvm");
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth for wildcard exclude path");
+  }
+
+  @Test
+  public void testDifferentBasePath() {
+    String basePath = "/different/base";
+    String publicOperationPath = "/public";
+    String privateOperationPath = "/private";
+    swagger.getServers().clear();
+    swagger.getServers().add(new Server().url(basePath));
+    when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(basePath + 
publicOperationPath);
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, 
publicOperationPath);
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth with different base path");
+
+    fullPath = SwaggerUtils.concatAbsolutePath(swagger, privateOperationPath);
+    assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should require auth for non-excluded path with different base path");
+  }
+
+  @Test
+  public void testNoBasePath() {
+    String operationPath = "/public";
+    swagger.setServers(null);
+    when(environment.getProperty(KEY_EXCLUDE_PATH, 
"")).thenReturn(operationPath);
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath);
+    assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should not require auth when no base path is set");
+  }
+
+  @Test
+  public void testEmptyConfiguration() {
+    when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn("");
+    when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn("");
+
+    String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/any/path");
+    assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment),
+        "Should require auth by default when no patterns are configured");
+  }
+
+  private String concatPath(String basePath, List<String> paths) {
+    return paths.stream().map(path -> basePath + 
path).collect(Collectors.joining(","));
+  }
+}
diff --git 
a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java
 
b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java
index 211a612a8..c1751b728 100644
--- 
a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java
+++ 
b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java
@@ -445,4 +445,24 @@ public final class SwaggerUtils {
       default -> false;
     };
   }
+
+  public static String concatAbsolutePath(OpenAPI swagger, String 
operationPath) {
+    String basePath = getBasePath(swagger);
+    return concatPath(basePath, operationPath);
+  }
+
+  /**
+   * Concat the two paths to an absolute path, without end of '/'.
+   * <p>
+   * e.g. "/" + "/ope" = /ope
+   * e.g. "/prefix" + "/ope" = /prefix/ope
+   */
+  public static String concatPath(String basePath, String operationPath) {
+    return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath))
+            .replaceAll("/{2,}", "/");
+  }
+
+  private static String nonNullify(String path) {
+    return path == null ? "" : path;
+  }
 }

Reply via email to