diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/AuthMethodCatalog.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/AuthMethodCatalog.java index 84324f18..8c63cb9d 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/AuthMethodCatalog.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/AuthMethodCatalog.java @@ -12,6 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Locale; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.stereotype.Service; @@ -43,6 +44,7 @@ public class AuthMethodCatalog { public List listOAuthProviders(String returnTo) { String sanitizedReturnTo = OAuthLoginRedirectSupport.sanitizeReturnTo(returnTo); return new ArrayList<>(oAuth2ClientProperties.getRegistration().entrySet().stream() + .filter(entry -> isValidOAuthProvider(entry.getValue())) .sorted(Comparator.comparing(entry -> entry.getKey())) .map(entry -> new AuthProviderResponse( entry.getKey(), @@ -54,6 +56,17 @@ public class AuthMethodCatalog { .toList()); } + /** + * Checks whether an OAuth provider has a non-empty, non-placeholder client ID. + */ + private boolean isValidOAuthProvider(OAuth2ClientProperties.Registration registration) { + String clientId = registration.getClientId(); + if (clientId == null || clientId.isBlank()) { + return false; + } + return !clientId.toLowerCase(Locale.ROOT).contains("placeholder"); + } + public List listMethods(String returnTo) { String sanitizedReturnTo = OAuthLoginRedirectSupport.sanitizeReturnTo(returnTo); List methods = new ArrayList<>(); @@ -67,6 +80,7 @@ public class AuthMethodCatalog { )); oAuth2ClientProperties.getRegistration().entrySet().stream() + .filter(entry -> isValidOAuthProvider(entry.getValue())) .sorted(Comparator.comparing(entry -> entry.getKey())) .forEach(entry -> methods.add(new AuthMethodResponse( "oauth-" + entry.getKey(), diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/AuthControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/AuthControllerTest.java index d79d368a..8e4118c6 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/AuthControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/AuthControllerTest.java @@ -150,13 +150,9 @@ class AuthControllerTest { mockMvc.perform(get("/api/v1/auth/providers")) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)) - .andExpect(jsonPath("$.data.length()").value(3)) - .andExpect(jsonPath("$.data[*].id", hasItems("github", "gitee", "gitlab"))) - .andExpect(jsonPath("$.data[*].authorizationUrl", hasItems( - "/oauth2/authorization/github", - "/oauth2/authorization/gitee", - "/oauth2/authorization/gitlab" - ))) + .andExpect(jsonPath("$.data.length()").value(1)) + .andExpect(jsonPath("$.data[*].id", hasItems("github"))) + .andExpect(jsonPath("$.data[*].authorizationUrl", hasItems("/oauth2/authorization/github"))) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andExpect(jsonPath("$.requestId").isNotEmpty()); } @@ -167,8 +163,7 @@ class AuthControllerTest { .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)) .andExpect(jsonPath("$.data[*].authorizationUrl", hasItems( - "/oauth2/authorization/github?returnTo=%2Fdashboard%2Fpublish", - "/oauth2/authorization/gitee?returnTo=%2Fdashboard%2Fpublish" + "/oauth2/authorization/github?returnTo=%2Fdashboard%2Fpublish" ))); } @@ -177,7 +172,8 @@ class AuthControllerTest { mockMvc.perform(get("/api/v1/auth/methods").param("returnTo", "/dashboard/publish")) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)) - .andExpect(jsonPath("$.data[*].id", hasItems("local-password", "oauth-github", "oauth-gitee"))) + .andExpect(jsonPath("$.data.length()").value(2)) + .andExpect(jsonPath("$.data[*].id", hasItems("local-password", "oauth-github"))) .andExpect(jsonPath("$.data[?(@.id=='local-password')].methodType").value(hasItems("PASSWORD"))) .andExpect(jsonPath("$.data[?(@.id=='oauth-github')].actionUrl") .value(hasItems("/oauth2/authorization/github?returnTo=%2Fdashboard%2Fpublish"))); diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/AuthMethodCatalogTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/AuthMethodCatalogTest.java index 35ca8d75..e9ef372f 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/AuthMethodCatalogTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/AuthMethodCatalogTest.java @@ -16,6 +16,31 @@ import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2Clien class AuthMethodCatalogTest { + @Test + void catalogsShouldHideEmptyAndPlaceholderOAuthProviders() { + OAuth2ClientProperties oauthProperties = new OAuth2ClientProperties(); + oauthProperties.getRegistration().put("valid", registration("production-client", "Valid")); + oauthProperties.getRegistration().put("missing", registration(null, "Missing")); + oauthProperties.getRegistration().put("blank", registration(" ", "Blank")); + oauthProperties.getRegistration().put("placeholder", registration("PLACEHOLDER", "Placeholder")); + oauthProperties.getRegistration().put("local", registration("local-placeholder", "Local")); + + AuthMethodCatalog catalog = new AuthMethodCatalog( + oauthProperties, + new DirectAuthProperties(), + new AuthSessionBootstrapProperties(), + List.of(), + List.of() + ); + + assertThat(catalog.listOAuthProviders(null)) + .extracting(provider -> provider.id()) + .containsExactly("valid"); + assertThat(catalog.listMethods(null)) + .extracting(method -> method.id()) + .containsExactly("local-password", "oauth-valid"); + } + @Test void listMethodsShouldUseProviderDisplayNamesForCompatibleAuthMethods() { OAuth2ClientProperties oauthProperties = new OAuth2ClientProperties(); @@ -122,4 +147,11 @@ class AuthMethodCatalogTest { "bootstrap-private-sso:private-sso" ); } + + private static OAuth2ClientProperties.Registration registration(String clientId, String clientName) { + OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration(); + registration.setClientId(clientId); + registration.setClientName(clientName); + return registration; + } }