mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-15 23:31:10 +00:00
fix(api): resolve API token authentication issues for skill operations
- Fix SkillStarController to use @RequestAttribute instead of @AuthenticationPrincipal - Fix SkillRatingController to use @RequestAttribute instead of @AuthenticationPrincipal - Fix SkillGovernanceService NPE when userNamespaceRoles is null - Update ApiTokenAuthenticationFilter to properly populate userNsRoles - Enhance error messages for 403 vs 401 status codes Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
5caf0b360b
commit
b25bbf50f3
13 changed files with 140 additions and 35 deletions
|
|
@ -41,6 +41,7 @@ public class AdminSkillController extends BaseApiController {
|
|||
var skill = skillGovernanceService.hideSkill(
|
||||
skillId,
|
||||
principal.userId(),
|
||||
java.util.Map.of(),
|
||||
httpRequest.getRemoteAddr(),
|
||||
httpRequest.getHeader("User-Agent"),
|
||||
request != null ? request.reason() : null
|
||||
|
|
@ -56,6 +57,7 @@ public class AdminSkillController extends BaseApiController {
|
|||
var skill = skillGovernanceService.unhideSkill(
|
||||
skillId,
|
||||
principal.userId(),
|
||||
java.util.Map.of(),
|
||||
httpRequest.getRemoteAddr(),
|
||||
httpRequest.getHeader("User-Agent")
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,13 @@
|
|||
package com.iflytek.skillhub.controller.portal;
|
||||
|
||||
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
|
||||
import com.iflytek.skillhub.controller.BaseApiController;
|
||||
import com.iflytek.skillhub.dto.ApiResponse;
|
||||
import com.iflytek.skillhub.dto.ApiResponseFactory;
|
||||
import com.iflytek.skillhub.dto.SkillRatingRequest;
|
||||
import com.iflytek.skillhub.dto.SkillRatingStatusResponse;
|
||||
import com.iflytek.skillhub.domain.social.SkillRatingService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Endpoints for reading and mutating the current user's rating on a skill.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping({"/api/v1/skills", "/api/web/skills"})
|
||||
public class SkillRatingController extends BaseApiController {
|
||||
|
|
@ -31,19 +24,19 @@ public class SkillRatingController extends BaseApiController {
|
|||
public ApiResponse<Void> rateSkill(
|
||||
@PathVariable Long skillId,
|
||||
@Valid @RequestBody SkillRatingRequest request,
|
||||
@AuthenticationPrincipal PlatformPrincipal principal) {
|
||||
skillRatingService.rate(skillId, principal.userId(), request.score());
|
||||
@RequestAttribute("userId") String userId) {
|
||||
skillRatingService.rate(skillId, userId, request.score());
|
||||
return ok("response.success.updated", null);
|
||||
}
|
||||
|
||||
@GetMapping("/{skillId}/rating")
|
||||
public ApiResponse<SkillRatingStatusResponse> getUserRating(
|
||||
@PathVariable Long skillId,
|
||||
@AuthenticationPrincipal PlatformPrincipal principal) {
|
||||
if (principal == null) {
|
||||
@RequestAttribute(value = "userId", required = false) String userId) {
|
||||
if (userId == null) {
|
||||
return ok("response.success.read", new SkillRatingStatusResponse((short) 0, false));
|
||||
}
|
||||
Optional<Short> rating = skillRatingService.getUserRating(skillId, principal.userId());
|
||||
Optional<Short> rating = skillRatingService.getUserRating(skillId, userId);
|
||||
return ok(
|
||||
"response.success.read",
|
||||
new SkillRatingStatusResponse(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
package com.iflytek.skillhub.controller.portal;
|
||||
|
||||
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
|
||||
import com.iflytek.skillhub.controller.BaseApiController;
|
||||
import com.iflytek.skillhub.dto.ApiResponse;
|
||||
import com.iflytek.skillhub.dto.ApiResponseFactory;
|
||||
import com.iflytek.skillhub.domain.social.SkillStarService;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* Endpoints for starring, unstarring, and checking star state on a skill.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping({"/api/v1/skills", "/api/web/skills"})
|
||||
public class SkillStarController extends BaseApiController {
|
||||
|
|
@ -26,27 +21,27 @@ public class SkillStarController extends BaseApiController {
|
|||
@PutMapping("/{skillId}/star")
|
||||
public ApiResponse<Void> starSkill(
|
||||
@PathVariable Long skillId,
|
||||
@AuthenticationPrincipal PlatformPrincipal principal) {
|
||||
skillStarService.star(skillId, principal.userId());
|
||||
@RequestAttribute("userId") String userId) {
|
||||
skillStarService.star(skillId, userId);
|
||||
return ok("response.success.updated", null);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{skillId}/star")
|
||||
public ApiResponse<Void> unstarSkill(
|
||||
@PathVariable Long skillId,
|
||||
@AuthenticationPrincipal PlatformPrincipal principal) {
|
||||
skillStarService.unstar(skillId, principal.userId());
|
||||
@RequestAttribute("userId") String userId) {
|
||||
skillStarService.unstar(skillId, userId);
|
||||
return ok("response.success.updated", null);
|
||||
}
|
||||
|
||||
@GetMapping("/{skillId}/star")
|
||||
public ApiResponse<Boolean> checkStarred(
|
||||
@PathVariable Long skillId,
|
||||
@AuthenticationPrincipal PlatformPrincipal principal) {
|
||||
if (principal == null) {
|
||||
@RequestAttribute(value = "userId", required = false) String userId) {
|
||||
if (userId == null) {
|
||||
return ok("response.success.read", false);
|
||||
}
|
||||
boolean starred = skillStarService.isStarred(skillId, principal.userId());
|
||||
boolean starred = skillStarService.isStarred(skillId, userId);
|
||||
return ok("response.success.read", starred);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,12 +38,15 @@ public class ApiAccessDeniedHandler implements AccessDeniedHandler {
|
|||
public void handle(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException) throws IOException {
|
||||
logger.info(
|
||||
"Forbidden API request [requestId={}, method={}, path={}, reason={}]",
|
||||
String userId = request.getAttribute("userId") != null ? request.getAttribute("userId").toString() : "anonymous";
|
||||
logger.warn(
|
||||
"Forbidden API request [requestId={}, method={}, path={}, userId={}, reason={}, message={}]",
|
||||
MDC.get("requestId"),
|
||||
request.getMethod(),
|
||||
sensitiveLogSanitizer.sanitizeRequestTarget(request),
|
||||
accessDeniedException.getClass().getSimpleName()
|
||||
userId,
|
||||
accessDeniedException.getClass().getSimpleName(),
|
||||
accessDeniedException.getMessage()
|
||||
);
|
||||
ApiResponse<Void> body = apiResponseFactory.error(403, "error.forbidden");
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class AdminSkillControllerTest {
|
|||
@Test
|
||||
void hideSkill_returnsUpdatedResponse() throws Exception {
|
||||
Skill skill = new Skill(1L, "demo", "owner", SkillVisibility.PUBLIC);
|
||||
given(skillGovernanceService.hideSkill(org.mockito.ArgumentMatchers.eq(10L), org.mockito.ArgumentMatchers.eq("admin"), org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.eq("policy")))
|
||||
given(skillGovernanceService.hideSkill(org.mockito.ArgumentMatchers.eq(10L), org.mockito.ArgumentMatchers.eq("admin"), org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.eq("policy")))
|
||||
.willReturn(skill);
|
||||
|
||||
PlatformPrincipal principal = new PlatformPrincipal("admin", "admin", "a@example.com", "", "github", Set.of("SUPER_ADMIN"));
|
||||
|
|
|
|||
99
server/skillhub-auth/.factorypath
Normal file
99
server/skillhub-auth/.factorypath
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<factorypath>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-security/3.2.3/spring-boot-starter-security-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/3.2.3/spring-boot-starter-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/3.2.3/spring-boot-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/3.2.3/spring-boot-autoconfigure-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/3.2.3/spring-boot-starter-logging-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.4.14/logback-classic-1.4.14.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.4.14/logback-core-1.4.14.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.21.1/log4j-to-slf4j-2.21.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.21.1/log4j-api-2.21.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/2.0.12/jul-to-slf4j-2.0.12.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/annotation/jakarta.annotation-api/2.1.1/jakarta.annotation-api-2.1.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/2.2/snakeyaml-2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/6.1.4/spring-aop-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/6.1.4/spring-beans-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-config/6.2.2/spring-security-config-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/6.1.4/spring-context-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-web/6.2.2/spring-security-web-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/6.1.4/spring-expression-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-oauth2-client/3.2.3/spring-boot-starter-oauth2-client-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-core/6.2.2/spring-security-core-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-crypto/6.2.2/spring-security-crypto-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/micrometer/micrometer-observation/1.12.3/micrometer-observation-1.12.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/micrometer/micrometer-commons/1.12.3/micrometer-commons-1.12.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-oauth2-client/6.2.2/spring-security-oauth2-client-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-oauth2-core/6.2.2/spring-security-oauth2-core-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/nimbusds/oauth2-oidc-sdk/9.43.3/oauth2-oidc-sdk-9.43.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/nimbusds/content-type/2.2/content-type-2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-oauth2-jose/6.2.2/spring-security-oauth2-jose-6.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/nimbusds/nimbus-jose-jwt/9.24.4/nimbus-jose-jwt-9.24.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-web/3.2.3/spring-boot-starter-web-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-json/3.2.3/spring-boot-starter-json-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.15.4/jackson-databind-2.15.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.15.4/jackson-annotations-2.15.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.15.4/jackson-core-2.15.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.15.4/jackson-datatype-jdk8-2.15.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.15.4/jackson-datatype-jsr310-2.15.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/module/jackson-module-parameter-names/2.15.4/jackson-module-parameter-names-2.15.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-tomcat/3.2.3/spring-boot-starter-tomcat-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-core/10.1.19/tomcat-embed-core-10.1.19.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-el/10.1.19/tomcat-embed-el-10.1.19.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-websocket/10.1.19/tomcat-embed-websocket-10.1.19.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/6.1.4/spring-web-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-webmvc/6.1.4/spring-webmvc-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-data-jpa/3.2.3/spring-boot-starter-data-jpa-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-aop/3.2.3/spring-boot-starter-aop-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/aspectj/aspectjweaver/1.9.21/aspectjweaver-1.9.21.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-jdbc/3.2.3/spring-boot-starter-jdbc-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/HikariCP/5.0.1/HikariCP-5.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jdbc/6.1.4/spring-jdbc-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/orm/hibernate-core/6.4.4.Final/hibernate-core-6.4.4.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/persistence/jakarta.persistence-api/3.1.0/jakarta.persistence-api-3.1.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/transaction/jakarta.transaction-api/2.0.1/jakarta.transaction-api-2.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jboss/logging/jboss-logging/3.5.3.Final/jboss-logging-3.5.3.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/common/hibernate-commons-annotations/6.0.6.Final/hibernate-commons-annotations-6.0.6.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/smallrye/jandex/3.1.2/jandex-3.1.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.6.0/classmate-1.6.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/net/bytebuddy/byte-buddy/1.14.12/byte-buddy-1.14.12.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/glassfish/jaxb/jaxb-runtime/4.0.4/jaxb-runtime-4.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/glassfish/jaxb/jaxb-core/4.0.4/jaxb-core-4.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/glassfish/jaxb/txw2/4.0.4/txw2-4.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/sun/istack/istack-commons-runtime/4.1.2/istack-commons-runtime-4.1.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-jpa/3.2.3/spring-data-jpa-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-commons/3.2.3/spring-data-commons-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-orm/6.1.4/spring-orm-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/6.1.4/spring-tx-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/2.0.12/slf4j-api-2.0.12.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aspects/6.1.4/spring-aspects-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-data-redis/3.2.3/spring-boot-starter-data-redis-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/lettuce/lettuce-core/6.3.1.RELEASE/lettuce-core-6.3.1.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-common/4.1.107.Final/netty-common-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-handler/4.1.107.Final/netty-handler-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-resolver/4.1.107.Final/netty-resolver-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-buffer/4.1.107.Final/netty-buffer-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-transport-native-unix-common/4.1.107.Final/netty-transport-native-unix-common-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-codec/4.1.107.Final/netty-codec-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-transport/4.1.107.Final/netty-transport-4.1.107.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/io/projectreactor/reactor-core/3.6.3/reactor-core-3.6.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-redis/3.2.3/spring-data-redis-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-keyvalue/3.2.3/spring-data-keyvalue-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-oxm/6.1.4/spring-oxm-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-mail/3.2.3/spring-boot-starter-mail-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context-support/6.1.4/spring-context-support-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/eclipse/angus/jakarta.mail/2.0.2/jakarta.mail-2.0.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/activation/jakarta.activation-api/2.1.2/jakarta.activation-api-2.1.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/eclipse/angus/angus-activation/2.0.1/angus-activation-2.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-configuration-processor/3.2.3/spring-boot-configuration-processor-3.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/xml/bind/jakarta.xml.bind-api/4.0.1/jakarta.xml.bind-api-4.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/net/minidev/json-smart/2.5.0/json-smart-2.5.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/net/minidev/accessors-smart/2.5.0/accessors-smart-2.5.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/ow2/asm/asm/9.3/asm-9.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/6.1.4/spring-core-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/6.1.4/spring-jcl-6.1.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
</factorypath>
|
||||
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -37,15 +38,18 @@ public class ApiTokenAuthenticationFilter extends OncePerRequestFilter {
|
|||
private final UserAccountRepository userRepo;
|
||||
private final UserRoleBindingRepository roleBindingRepo;
|
||||
private final ApiTokenScopeService apiTokenScopeService;
|
||||
private final com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository namespaceMemberRepo;
|
||||
|
||||
public ApiTokenAuthenticationFilter(ApiTokenService apiTokenService,
|
||||
UserAccountRepository userRepo,
|
||||
UserRoleBindingRepository roleBindingRepo,
|
||||
ApiTokenScopeService apiTokenScopeService) {
|
||||
ApiTokenScopeService apiTokenScopeService,
|
||||
com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository namespaceMemberRepo) {
|
||||
this.apiTokenService = apiTokenService;
|
||||
this.userRepo = userRepo;
|
||||
this.roleBindingRepo = roleBindingRepo;
|
||||
this.apiTokenScopeService = apiTokenScopeService;
|
||||
this.namespaceMemberRepo = namespaceMemberRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -77,6 +81,13 @@ public class ApiTokenAuthenticationFilter extends OncePerRequestFilter {
|
|||
.toList());
|
||||
var auth = new UsernamePasswordAuthenticationToken(principal, null, authorities);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
Map<Long, com.iflytek.skillhub.domain.namespace.NamespaceRole> userNsRoles =
|
||||
namespaceMemberRepo.findByUserId(user.getId()).stream()
|
||||
.collect(Collectors.toMap(
|
||||
com.iflytek.skillhub.domain.namespace.NamespaceMember::getNamespaceId,
|
||||
com.iflytek.skillhub.domain.namespace.NamespaceMember::getRole,
|
||||
(left, right) -> left));
|
||||
request.setAttribute("userNsRoles", userNsRoles);
|
||||
apiTokenService.touchLastUsed(token);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ class ApiTokenAuthenticationFilterTest {
|
|||
private final UserRoleBindingRepository roleBindingRepository = mock(UserRoleBindingRepository.class);
|
||||
private final ApiTokenScopeService scopeService =
|
||||
new ApiTokenScopeService(new ObjectMapper(), new RouteSecurityPolicyRegistry());
|
||||
private final com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository namespaceMemberRepository = mock(com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository.class);
|
||||
private final ApiTokenAuthenticationFilter filter = new ApiTokenAuthenticationFilter(
|
||||
apiTokenService,
|
||||
userAccountRepository,
|
||||
roleBindingRepository,
|
||||
scopeService
|
||||
scopeService,
|
||||
namespaceMemberRepository
|
||||
);
|
||||
|
||||
@AfterEach
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ public class SkillReportService {
|
|||
String userAgent) {
|
||||
SkillReport report = requirePendingReport(reportId);
|
||||
if (disposition == SkillReportDisposition.RESOLVE_AND_HIDE) {
|
||||
skillGovernanceService.hideSkill(report.getSkillId(), actorUserId, clientIp, userAgent, comment);
|
||||
skillGovernanceService.hideSkill(report.getSkillId(), actorUserId, java.util.Map.of(), clientIp, userAgent, comment);
|
||||
} else if (disposition == SkillReportDisposition.RESOLVE_AND_ARCHIVE) {
|
||||
skillGovernanceService.archiveSkillAsAdmin(report.getSkillId(), actorUserId, clientIp, userAgent, comment);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ public class SkillGovernanceService {
|
|||
private void assertCanManageLifecycle(Skill skill,
|
||||
String actorUserId,
|
||||
Map<Long, NamespaceRole> userNamespaceRoles) {
|
||||
NamespaceRole namespaceRole = userNamespaceRoles.get(skill.getNamespaceId());
|
||||
NamespaceRole namespaceRole = userNamespaceRoles != null ? userNamespaceRoles.get(skill.getNamespaceId()) : null;
|
||||
boolean canManage = skill.getOwnerId().equals(actorUserId)
|
||||
|| namespaceRole == NamespaceRole.ADMIN
|
||||
|| namespaceRole == NamespaceRole.OWNER;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class SkillReportServiceTest {
|
|||
);
|
||||
|
||||
assertThat(saved.getStatus()).isEqualTo(SkillReportStatus.RESOLVED);
|
||||
verify(skillGovernanceService).hideSkill(10L, "admin", "127.0.0.1", "JUnit", "handled");
|
||||
verify(skillGovernanceService).hideSkill(10L, "admin", java.util.Map.of(), "127.0.0.1", "JUnit", "handled");
|
||||
verify(governanceNotificationService).notifyUser(
|
||||
eq("user-1"),
|
||||
eq("REPORT"),
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class SkillGovernanceServiceTest {
|
|||
given(skillRepository.findById(10L)).willReturn(Optional.of(skill));
|
||||
given(skillRepository.save(skill)).willReturn(skill);
|
||||
|
||||
Skill result = service.hideSkill(10L, "admin", "127.0.0.1", "JUnit", "policy");
|
||||
Skill result = service.hideSkill(10L, "admin", java.util.Map.of(), "127.0.0.1", "JUnit", "policy");
|
||||
|
||||
assertThat(result.isHidden()).isTrue();
|
||||
assertThat(result.getHiddenBy()).isEqualTo("admin");
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export class ApiError extends Error {
|
|||
const msg = extractHumanMessage(body);
|
||||
let detail = msg ?? `HTTP ${statusCode}`;
|
||||
|
||||
if (statusCode === 401 || statusCode === 403) {
|
||||
if (statusCode === 401) {
|
||||
detail += "\nRun `skillhub login` to authenticate.";
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue