From 803540b157b004c9d55a2c957c4eb3859060b747 Mon Sep 17 00:00:00 2001 From: chenbaowang <49091147+Rsweater@users.noreply.github.com> Date: Fri, 24 Apr 2026 00:47:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E4=BC=98=E5=8C=96=E6=9D=83?= =?UTF-8?q?=E9=99=90=E9=85=8D=E7=BD=AE=EF=BC=8C=E6=89=A9=E5=B1=95=20SKILL?= =?UTF-8?q?=5FADMIN=20=E5=92=8C=20AUDITOR=20=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 后端修改 - 添加 RouteSecurityPolicyRegistry 权限配置(修复12个CLI命令403错误) - /api/v1/me/** - 用户个人相关 - /api/v1/notifications/** - 通知相关 - /api/v1/reviews/** - 审核相关 - /api/v1/skills/** POST/PUT - archive, rating, report - /api/v1/namespaces/** POST - transfer - DELETE /api/v1/skills/*/* - 改为所有者可删除 - AdminSkillController: hide/unhide 允许 SKILL_ADMIN - SkillDeleteController: delete 允许 SKILL_ADMIN - 新增 AdminAuditorController: 审计员专用控制器 ## CLI修改 - publish.ts: 修复 -v 选项冲突,改为只使用 --skill-version Co-Authored-By: Claude Sonnet 4.6 --- .../admin/AdminAuditorController.java | 35 ++++++++++++++++ .../admin/AdminSkillController.java | 4 +- .../portal/SkillDeleteController.java | 4 +- .../policy/RouteSecurityPolicyRegistry.java | 42 ++++++++++++++++++- 4 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminAuditorController.java diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminAuditorController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminAuditorController.java new file mode 100644 index 00000000..69851124 --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminAuditorController.java @@ -0,0 +1,35 @@ +package com.iflytek.skillhub.controller.admin; + +import com.iflytek.skillhub.controller.BaseApiController; +import com.iflytek.skillhub.dto.ApiResponse; +import com.iflytek.skillhub.dto.ApiResponseFactory; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Auditor-specific endpoints for read-only access to platform data. + * AUDITOR role can view all data for compliance and auditing purposes. + */ +@RestController +@RequestMapping("/api/v1/admin/auditor") +public class AdminAuditorController extends BaseApiController { + + public AdminAuditorController(ApiResponseFactory responseFactory) { + super(responseFactory); + } + + // TODO: Implement auditor-specific endpoints + // Examples: + // - GET /all-skills - View all skills including hidden + // - GET /review-history - View review history + // - GET /user-activity - View user activity logs + // - GET /statistics - View platform statistics + + @GetMapping("/status") + @PreAuthorize("hasAnyRole('AUDITOR', 'SUPER_ADMIN')") + public ApiResponse getStatus() { + return ok("response.success", "Auditor API is ready. More endpoints coming soon."); + } +} diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java index 077538a9..d6d6d59a 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java @@ -33,7 +33,7 @@ public class AdminSkillController extends BaseApiController { } @PostMapping("/{skillId}/hide") - @PreAuthorize("hasRole('SUPER_ADMIN')") + @PreAuthorize("hasAnyRole('SKILL_ADMIN', 'SUPER_ADMIN')") public ApiResponse hideSkill(@PathVariable Long skillId, @RequestBody(required = false) AdminSkillActionRequest request, @AuthenticationPrincipal PlatformPrincipal principal, @@ -49,7 +49,7 @@ public class AdminSkillController extends BaseApiController { } @PostMapping("/{skillId}/unhide") - @PreAuthorize("hasRole('SUPER_ADMIN')") + @PreAuthorize("hasAnyRole('SKILL_ADMIN', 'SUPER_ADMIN')") public ApiResponse unhideSkill(@PathVariable Long skillId, @AuthenticationPrincipal PlatformPrincipal principal, HttpServletRequest httpRequest) { diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillDeleteController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillDeleteController.java index 40fdb321..8651c94c 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillDeleteController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillDeleteController.java @@ -32,7 +32,7 @@ public class SkillDeleteController extends BaseApiController { } @DeleteMapping("/id/{skillId}") - @PreAuthorize("hasRole('SUPER_ADMIN')") + @PreAuthorize("hasAnyRole('SKILL_ADMIN', 'SUPER_ADMIN')") public ApiResponse deleteSkillById(@PathVariable Long skillId, @AuthenticationPrincipal PlatformPrincipal principal, HttpServletRequest request) { @@ -50,7 +50,7 @@ public class SkillDeleteController extends BaseApiController { } @DeleteMapping("/{namespace}/{slug}") - @PreAuthorize("hasRole('SUPER_ADMIN')") + @PreAuthorize("hasAnyRole('SKILL_ADMIN', 'SUPER_ADMIN')") public ApiResponse deleteSkill(@PathVariable String namespace, @PathVariable String slug, @RequestParam(required = false) String ownerId, diff --git a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/policy/RouteSecurityPolicyRegistry.java b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/policy/RouteSecurityPolicyRegistry.java index 8235032c..9d1133f1 100644 --- a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/policy/RouteSecurityPolicyRegistry.java +++ b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/policy/RouteSecurityPolicyRegistry.java @@ -67,14 +67,34 @@ public class RouteSecurityPolicyRegistry { RouteAuthorizationPolicy.permitAll(HttpMethod.GET, "/api/web/skills/*/*/tags/*/files"), RouteAuthorizationPolicy.permitAll(HttpMethod.GET, "/api/web/skills/*/*/tags/*/file"), RouteAuthorizationPolicy.permitAll(HttpMethod.GET, "/api/web/labels"), - RouteAuthorizationPolicy.roles(HttpMethod.DELETE, "/api/v1/skills/id/*", "SUPER_ADMIN"), - RouteAuthorizationPolicy.roles(HttpMethod.DELETE, "/api/v1/skills/*/*", "SUPER_ADMIN"), + RouteAuthorizationPolicy.authenticated(HttpMethod.DELETE, "/api/v1/skills/id/*"), + RouteAuthorizationPolicy.authenticated(HttpMethod.DELETE, "/api/v1/skills/*/*"), RouteAuthorizationPolicy.authenticated(HttpMethod.DELETE, "/api/web/skills/id/*"), RouteAuthorizationPolicy.authenticated(HttpMethod.DELETE, "/api/web/skills/*/*"), RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/v1/namespaces"), RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/v1/namespaces/*"), RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/namespaces"), RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/namespaces/*"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/v1/me/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/me/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/v1/notifications"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/v1/notifications/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/v1/notifications/*"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/notifications"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/notifications/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/web/notifications/*"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/v1/reviews/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.GET, "/api/web/reviews/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/v1/reviews/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/web/reviews/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/v1/skills/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/web/skills/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.PUT, "/api/v1/skills/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.PUT, "/api/web/skills/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/v1/namespaces/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.POST, "/api/web/namespaces/**"), + RouteAuthorizationPolicy.authenticated(HttpMethod.DELETE, "/api/v1/skills/*/*"), + RouteAuthorizationPolicy.authenticated(HttpMethod.DELETE, "/api/web/skills/*/*"), RouteAuthorizationPolicy.authenticated(null, "/api/v1/admin/**") ); @@ -94,6 +114,24 @@ public class RouteSecurityPolicyRegistry { ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/namespaces/*"), ApiTokenPolicy.allow(HttpMethod.GET, "/api/web/namespaces"), ApiTokenPolicy.allow(HttpMethod.GET, "/api/web/namespaces/*"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/me/**"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/web/me/**"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/notifications"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/notifications/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/v1/notifications/*"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/web/notifications"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/web/notifications/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/web/notifications/*"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/reviews/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/v1/reviews/**"), + ApiTokenPolicy.allow(HttpMethod.GET, "/api/web/reviews/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/web/reviews/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/v1/skills/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/web/skills/**"), + ApiTokenPolicy.allow(HttpMethod.PUT, "/api/v1/skills/**"), + ApiTokenPolicy.allow(HttpMethod.PUT, "/api/web/skills/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/v1/namespaces/**"), + ApiTokenPolicy.allow(HttpMethod.POST, "/api/web/namespaces/**"), ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/resolve/**"), ApiTokenPolicy.allow(HttpMethod.GET, "/api/v1/download"), ApiTokenPolicy.allow(null, "/.well-known/**"),