diff --git a/controller/mcp/iml.go b/controller/mcp/iml.go index 2aa4d660..a7ebedf3 100644 --- a/controller/mcp/iml.go +++ b/controller/mcp/iml.go @@ -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) { diff --git a/controller/system-apikey/apikey.go b/controller/system-apikey/apikey.go index c59fc03e..0fb0d17c 100644 --- a/controller/system-apikey/apikey.go +++ b/controller/system-apikey/apikey.go @@ -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() { diff --git a/controller/system-apikey/iml.go b/controller/system-apikey/iml.go index 7afdd451..0e505445 100644 --- a/controller/system-apikey/iml.go +++ b/controller/system-apikey/iml.go @@ -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) } diff --git a/module/application-authorization/auth-driver/auth.go b/module/application-authorization/auth-driver/auth.go index 5d454ff3..f0ae7151 100644 --- a/module/application-authorization/auth-driver/auth.go +++ b/module/application-authorization/auth-driver/auth.go @@ -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 } diff --git a/module/application-authorization/auth-driver/oauth2/oauth2.go b/module/application-authorization/auth-driver/oauth2/oauth2.go index 1cfd3d8d..efd9f4d0 100644 --- a/module/application-authorization/auth-driver/oauth2/oauth2.go +++ b/module/application-authorization/auth-driver/oauth2/oauth2.go @@ -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}, diff --git a/module/application-authorization/authorization.go b/module/application-authorization/authorization.go index 56026253..315a5149 100644 --- a/module/application-authorization/authorization.go +++ b/module/application-authorization/authorization.go @@ -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) } diff --git a/module/application-authorization/iml.go b/module/application-authorization/iml.go index 391eb16d..d14ec5ae 100644 --- a/module/application-authorization/iml.go +++ b/module/application-authorization/iml.go @@ -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 { diff --git a/module/system-apikey/iml.go b/module/system-apikey/iml.go index 3d778952..d26bf9de 100644 --- a/module/system-apikey/iml.go +++ b/module/system-apikey/iml.go @@ -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 { diff --git a/module/system-apikey/module.go b/module/system-apikey/module.go index f80e2a4d..bb91fd89 100644 --- a/module/system-apikey/module.go +++ b/module/system-apikey/module.go @@ -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() { diff --git a/plugins/core/system.go b/plugins/core/system.go index 669ac648..176efc07 100644 --- a/plugins/core/system.go +++ b/plugins/core/system.go @@ -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), } } diff --git a/plugins/openapi/mcp.go b/plugins/openapi/mcp.go index 29b52606..4c9233de 100644 --- a/plugins/openapi/mcp.go +++ b/plugins/openapi/mcp.go @@ -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), } } diff --git a/service/subscribe/service.go b/service/subscribe/service.go index 6cc0f903..ea158a74 100644 --- a/service/subscribe/service.go +++ b/service/subscribe/service.go @@ -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) }