Fix: Issue with invalid apikey parameter in service MCP path

This commit is contained in:
Liujian
2025-04-14 11:57:06 +08:00
parent fb31ecc012
commit b703ddaae8
12 changed files with 192 additions and 21 deletions
+29 -9
View File
@@ -6,6 +6,8 @@ import (
"strings"
"sync"
application_authorization "github.com/APIParkLab/APIPark/module/application-authorization"
mcp_server "github.com/APIParkLab/APIPark/mcp-server"
"github.com/APIParkLab/APIPark/module/mcp"
"github.com/APIParkLab/APIPark/module/system"
@@ -18,11 +20,12 @@ import (
var _ IMcpController = (*imlMcpController)(nil)
type imlMcpController struct {
settingModule system.ISettingModule `autowired:""`
mcpModule mcp.IMcpModule `autowired:""`
sessionKeys sync.Map
server http.Handler
openServer http.Handler
settingModule system.ISettingModule `autowired:""`
authorizationModule application_authorization.IAuthorizationModule `autowired:""`
mcpModule mcp.IMcpModule `autowired:""`
sessionKeys sync.Map
server http.Handler
openServer http.Handler
}
var mcpDefaultConfig = `{
@@ -85,11 +88,12 @@ func (i *imlMcpController) GlobalMCPHandle(ctx *gin.Context) {
}
func (i *imlMcpController) GlobalHandleSSE(ctx *gin.Context) {
i.handleSSE(ctx, i.openServer)
apikey := ctx.Request.URL.Query().Get("apikey")
i.handleSSE(ctx, i.openServer, apikey)
}
func (i *imlMcpController) handleSSE(ctx *gin.Context, server http.Handler) {
apikey := ctx.Request.URL.Query().Get("apikey")
func (i *imlMcpController) handleSSE(ctx *gin.Context, server http.Handler, apikey string) {
writer := &ResponseWriter{
Writer: ctx.Writer,
sessionId: make(chan string),
@@ -120,7 +124,23 @@ func (i *imlMcpController) MCPHandle(ctx *gin.Context) {
}
func (i *imlMcpController) ServiceHandleSSE(ctx *gin.Context) {
i.handleSSE(ctx, mcp_server.DefaultMCPServer())
apikey := ctx.Request.URL.Query().Get("apikey")
serviceId := ctx.Param("serviceId")
if serviceId == "" {
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid service id", "success": "fail"})
return
}
ok, err := i.authorizationModule.CheckAPIKeyAuthorization(ctx, serviceId, apikey)
if err != nil {
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": err.Error(), "success": "fail"})
return
}
if !ok {
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid apikey", "success": "fail"})
return
}
i.handleSSE(ctx, mcp_server.DefaultMCPServer(), apikey)
}
func (i *imlMcpController) ServiceHandleMessage(ctx *gin.Context) {
+1
View File
@@ -16,6 +16,7 @@ type IAPIKeyController interface {
Search(ctx *gin.Context, keyword string) ([]*system_apikey_dto.Item, error)
SimpleList(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error)
MyAPIKeys(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error)
MyAPIKeysByService(ctx *gin.Context, serviceId string) ([]*system_apikey_dto.SimpleItem, error)
}
func init() {
+4
View File
@@ -12,6 +12,10 @@ type imlAPIKeyController struct {
apikeyModule system_apikey.IAPIKeyModule `autowired:""`
}
func (i *imlAPIKeyController) MyAPIKeysByService(ctx *gin.Context, serviceId string) ([]*system_apikey_dto.SimpleItem, error) {
return i.apikeyModule.MyAPIKeysByService(ctx, serviceId)
}
func (i *imlAPIKeyController) MyAPIKeys(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error) {
return i.apikeyModule.MyAPIKeys(ctx)
}
@@ -3,7 +3,7 @@ package auth_driver
import (
"encoding/json"
"fmt"
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
)
@@ -83,6 +83,6 @@ func generateStruct[T any](cfg interface{}) (*T, error) {
return nil, err
}
}
return result, nil
}
@@ -3,9 +3,9 @@ package oauth2
import (
"encoding/json"
"strconv"
auth_driver "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver"
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
)
@@ -33,7 +33,7 @@ func (cfg *Config) ID() string {
}
func (cfg *Config) Valid() ([]byte, error) {
if cfg.HashSecret && !cfg.Hashed {
// 未加密
secret, err := hashSecret([]byte(cfg.ClientSecret), 0, 0, 0)
@@ -48,9 +48,9 @@ func (cfg *Config) Valid() ([]byte, error) {
}
func (cfg *Config) Detail() []application_authorization_dto.DetailItem {
redirectURLs, _ := json.Marshal(cfg.RedirectUrls)
return []application_authorization_dto.DetailItem{
{Key: "客户端ID", Value: cfg.ClientId},
{Key: "客户端密钥", Value: cfg.ClientSecret},
@@ -2,9 +2,10 @@ package application_authorization
import (
"context"
"github.com/APIParkLab/APIPark/module/system"
"reflect"
"github.com/APIParkLab/APIPark/module/system"
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
"github.com/APIParkLab/APIPark/gateway"
@@ -31,6 +32,9 @@ type IAuthorizationModule interface {
Detail(ctx context.Context, appId string, aid string) ([]application_authorization_dto.DetailItem, error)
// Info 获取项目鉴权详情
Info(ctx context.Context, appId string, aid string) (*application_authorization_dto.Authorization, error)
CheckAPIKeyAuthorization(ctx context.Context, serviceId string, apikey string) (bool, error)
//ExportAll(ctx context.Context) ([]*application_authorization_dto.ExportAuthorization, error)
}
+71
View File
@@ -7,6 +7,8 @@ import (
"fmt"
"time"
"github.com/APIParkLab/APIPark/service/subscribe"
application_authorization "github.com/APIParkLab/APIPark/service/application-authorization"
"github.com/eolinker/eosc/log"
@@ -36,11 +38,80 @@ var (
type imlAuthorizationModule struct {
serviceService service.IServiceService `autowired:""`
subscribeService subscribe.ISubscribeService `autowired:""`
authorizationService application_authorization.IAuthorizationService `autowired:""`
clusterService cluster.IClusterService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlAuthorizationModule) CheckAPIKeyAuthorization(ctx context.Context, serviceId string, apikey string) (bool, error) {
list, err := i.subscribeService.ListBySubscribeStatus(ctx, serviceId, subscribe.ApplyStatusSubscribe)
if err != nil {
return false, err
}
if len(list) < 1 {
return false, fmt.Errorf("no application found")
}
appIds := utils.SliceToSlice(list, func(s *subscribe.Subscribe) string {
return s.Application
})
authorizations, err := i.authorizationService.ListByApp(ctx, appIds...)
if err != nil {
return false, err
}
for _, a := range authorizations {
if a.Type != "apikey" {
continue
}
cfg := make(map[string]interface{})
if a.Config != "" {
json.Unmarshal([]byte(a.Config), &cfg)
}
if cfg["apikey"] == apikey {
return true, nil
}
}
return false, nil
}
func (i *imlAuthorizationModule) AuthorizationsByService(ctx context.Context, serviceId string, authorizationType string) ([]*application_authorization_dto.Authorization, error) {
list, err := i.subscribeService.ListBySubscribeStatus(ctx, serviceId, subscribe.ApplyStatusSubscribe)
if err != nil {
return nil, err
}
if len(list) < 1 {
return nil, fmt.Errorf("no application found")
}
appIds := utils.SliceToSlice(list, func(s *subscribe.Subscribe) string {
return s.Application
})
authorizations, err := i.authorizationService.ListByApp(ctx, appIds...)
if err != nil {
return nil, err
}
result := make([]*application_authorization_dto.Authorization, 0, len(authorizations))
for _, a := range authorizations {
if authorizationType != "" && a.Type != authorizationType {
continue
}
cfg := make(map[string]interface{})
if a.Config != "" {
json.Unmarshal([]byte(a.Config), &cfg)
}
result = append(result, &application_authorization_dto.Authorization{
UUID: a.UUID,
Name: a.Name,
Driver: a.Type,
Position: a.Position,
TokenName: a.TokenName,
Config: cfg,
ExpireTime: a.ExpireTime,
HideCredential: a.HideCredential,
})
}
return result, nil
}
func (i *imlAuthorizationModule) ExportAll(ctx context.Context) ([]*application_authorization_dto.ExportAuthorization, error) {
list, err := i.authorizationService.List(ctx)
if err != nil {
+65
View File
@@ -3,8 +3,11 @@ package system_apikey
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/APIParkLab/APIPark/service/subscribe"
application_authorization "github.com/APIParkLab/APIPark/service/application-authorization"
"github.com/APIParkLab/APIPark/service/service"
@@ -34,9 +37,71 @@ type imlAPIKeyModule struct {
teamMemberService team_member.ITeamMemberService `autowired:""`
serviceService service.IServiceService `autowired:""`
applicationAuthorizationService application_authorization.IAuthorizationService `autowired:""`
subscribeService subscribe.ISubscribeService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlAPIKeyModule) MyAPIKeysByService(ctx context.Context, serviceId string) ([]*system_apikey_dto.SimpleItem, error) {
list, err := i.subscribeService.ListBySubscribeStatus(ctx, serviceId, subscribe.ApplyStatusSubscribe)
if err != nil {
return nil, err
}
if len(list) < 1 {
return nil, fmt.Errorf("no subscriber found")
}
appMap := utils.SliceToMapO(list, func(a *subscribe.Subscribe) (string, struct{}) {
return a.Application, struct{}{}
})
members, err := i.teamMemberService.Members(ctx, nil, nil)
if err != nil {
return nil, err
}
if len(members) == 0 {
return nil, nil
}
teamIds := utils.SliceToSlice(members, func(m *team_member.Member) string {
return m.Come
})
apps, err := i.serviceService.AppListByTeam(ctx, teamIds...)
if err != nil {
return nil, err
}
appIds := utils.SliceToSlice(apps, func(a *service.Service) string {
return a.Id
}, func(s *service.Service) bool {
if _, ok := appMap[s.Id]; !ok {
return false
}
return true
})
if len(appIds) < 1 {
return nil, fmt.Errorf("no app found")
}
auths, err := i.applicationAuthorizationService.ListByApp(ctx, appIds...)
if err != nil {
return nil, err
}
result := make([]*system_apikey_dto.SimpleItem, 0, len(auths))
for _, a := range auths {
if a.Type != "apikey" {
continue
}
m := make(map[string]string)
json.Unmarshal([]byte(a.Config), &m)
if m["apikey"] == "" {
continue
}
result = append(result, &system_apikey_dto.SimpleItem{
Id: a.UUID,
Name: a.Name,
Value: m["apikey"],
Expired: a.ExpireTime,
})
}
return result, nil
}
func (i *imlAPIKeyModule) MyAPIKeys(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) {
members, err := i.teamMemberService.Members(ctx, nil, nil)
if err != nil {
+1
View File
@@ -18,6 +18,7 @@ type IAPIKeyModule interface {
Search(ctx context.Context, keyword string) ([]*system_apikey_dto.Item, error)
SimpleList(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error)
MyAPIKeys(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error)
MyAPIKeysByService(ctx context.Context, serviceId string) ([]*system_apikey_dto.SimpleItem, error)
}
func init() {
+2 -1
View File
@@ -23,7 +23,8 @@ func (p *plugin) systemApikeyApis() []pm3.Api {
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/system/apikey", []string{"context", "query:apikey", "body"}, nil, p.systemAPIKeyController.Update),
pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/system/apikey", []string{"context", "query:apikey"}, nil, p.systemAPIKeyController.Delete),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/system/apikeys", []string{"context", "query:keyword"}, []string{"apikeys"}, p.systemAPIKeyController.Search),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/my/apikeys/", []string{"context"}, []string{"apikeys"}, p.systemAPIKeyController.MyAPIKeys),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/my/apikeys", []string{"context"}, []string{"apikeys"}, p.systemAPIKeyController.MyAPIKeys),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/my/apikeys/:serviceId", []string{"context", "path:serviceId"}, []string{"apikeys"}, p.systemAPIKeyController.MyAPIKeysByService),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/system/apikeys", []string{"context"}, []string{"apikeys"}, p.systemAPIKeyController.SimpleList),
}
}
+5 -1
View File
@@ -14,12 +14,16 @@ import (
func (p *plugin) mcpAPIs() []pm3.Api {
globalMessagePath := fmt.Sprintf("/openapi/v1/%s/message", strings.Trim(mcp_server.GlobalBasePath, "/"))
serviceMessagePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/message", strings.Trim(mcp_server.ServiceBasePath, "/"))
serviceSSEPath := fmt.Sprintf("/openapi/v1/%s/:serviceId/sse", strings.Trim(mcp_server.ServiceBasePath, "/"))
ignore.IgnorePath("openapi", http.MethodPost, globalMessagePath)
ignore.IgnorePath("openapi", http.MethodGet, serviceSSEPath)
ignore.IgnorePath("openapi", http.MethodPost, serviceMessagePath)
return []pm3.Api{
pm3.CreateApiSimple(http.MethodGet, fmt.Sprintf("/openapi/v1/%s/sse", strings.Trim(mcp_server.GlobalBasePath, "/")), p.mcpController.GlobalHandleSSE),
pm3.CreateApiSimple(http.MethodPost, globalMessagePath, p.mcpController.GlobalHandleMessage),
pm3.CreateApiSimple(http.MethodGet, fmt.Sprintf("/openapi/v1/%s/:serviceId/sse", strings.Trim(mcp_server.ServiceBasePath, "/")), p.mcpController.ServiceHandleSSE),
pm3.CreateApiSimple(http.MethodGet, serviceSSEPath, p.mcpController.ServiceHandleSSE),
pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.ServiceHandleMessage),
}
}
+2 -2
View File
@@ -21,8 +21,8 @@ type ISubscribeService interface {
MySubscribeServices(ctx context.Context, application string, serviceIDs []string) ([]*Subscribe, error)
UpdateSubscribeStatus(ctx context.Context, application string, service string, status int) error
ListBySubscribeStatus(ctx context.Context, projectId string, status int) ([]*Subscribe, error)
SubscribersByProject(ctx context.Context, projectIds ...string) ([]*Subscribe, error)
ListBySubscribeStatus(ctx context.Context, serviceId string, status int) ([]*Subscribe, error)
SubscribersByProject(ctx context.Context, serviceIds ...string) ([]*Subscribe, error)
Subscribers(ctx context.Context, project string, status int) ([]*Subscribe, error)
SubscriptionsByApplication(ctx context.Context, applicationIds ...string) ([]*Subscribe, error)
}