From 202ee13b8414854d7decdf13217373659e5776b2 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Thu, 10 Apr 2025 17:39:05 +0800 Subject: [PATCH] plugin service permissions are dynamically updated and stored in the database's permission list following service registration or exit --- .../app/network/controller/pluginService.go | 60 +++++++++++++++ cmd/server/app/network/gateway.go | 35 ++++++++- cmd/server/app/service/auth/casbin_plugin.go | 42 +++++++++++ pkg/global/global.go | 5 ++ sdk/go-micro/gateway/caddy.go | 18 ++++- sdk/go-micro/gateway/service.go | 74 +++++++++++++++++++ sdk/go-micro/registry/etcd.go | 14 ++-- sdk/go-micro/registry/registry.go | 17 +++-- sdk/go-micro/registry/service.go | 6 +- 9 files changed, 255 insertions(+), 16 deletions(-) create mode 100644 cmd/server/app/network/controller/pluginService.go create mode 100644 cmd/server/app/service/auth/casbin_plugin.go create mode 100644 sdk/go-micro/gateway/service.go diff --git a/cmd/server/app/network/controller/pluginService.go b/cmd/server/app/network/controller/pluginService.go new file mode 100644 index 00000000..b8697879 --- /dev/null +++ b/cmd/server/app/network/controller/pluginService.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2024.All rights reserved. + * PilotGo licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: zhanghan + * Date: Thu Apr 10 16:18:53 2025 +0800 + */ +package controller + +import ( + "fmt" + + "gitee.com/openeuler/PilotGo/pkg/global" + "gitee.com/openeuler/PilotGo/sdk/response" + "github.com/gin-gonic/gin" +) + +// 查询插件服务清单 +func GetPluginServices(c *gin.Context) { + pluginServices := global.GW.GetAllServices() + + response.Success(c, pluginServices, "插件查询成功") +} + +// 分页查询插件服务清单 +func GetPluginServicesPaged(c *gin.Context) { + query := &response.PaginationQ{} + err := c.ShouldBindQuery(query) + if err != nil { + response.Fail(c, nil, err.Error()) + return + } + + pluginServices := global.GW.GetAllServices() + data, err := response.DataPaging(query, pluginServices, len(pluginServices)) + if err != nil { + response.Fail(c, nil, err.Error()) + return + } + response.DataPagination(c, data, len(pluginServices), query) +} + +// 停用/启动插件服务 +func TogglePluginService(c *gin.Context) { + param := struct { + ServiceName string `json:"serviceName"` + Enable bool `json:"enable"` + }{} + if err := c.BindJSON(¶m); err != nil { + response.Fail(c, nil, "参数错误") + return + } + + if err := global.GW.SetServiceStatus(param.ServiceName, param.Enable); err != nil { + response.Fail(c, nil, err.Error()) + return + } + + response.Success(c, nil, fmt.Sprintf("插件信息更新成功,当前%v服务状态为: %v", param.ServiceName, global.GW.GetServiceStatus(param.ServiceName))) +} diff --git a/cmd/server/app/network/gateway.go b/cmd/server/app/network/gateway.go index 3aa5faa2..6376d0bc 100644 --- a/cmd/server/app/network/gateway.go +++ b/cmd/server/app/network/gateway.go @@ -9,6 +9,7 @@ package network import ( "context" + "encoding/json" "fmt" "net" "net/http" @@ -17,6 +18,9 @@ import ( "gitee.com/openeuler/PilotGo/cmd/server/app/cmd/options" "gitee.com/openeuler/PilotGo/cmd/server/app/network/websocket" + "gitee.com/openeuler/PilotGo/cmd/server/app/service/auth" + "gitee.com/openeuler/PilotGo/pkg/global" + "gitee.com/openeuler/PilotGo/sdk/common" "gitee.com/openeuler/PilotGo/sdk/go-micro/gateway" "gitee.com/openeuler/PilotGo/sdk/go-micro/registry" "gitee.com/openeuler/PilotGo/sdk/logger" @@ -120,10 +124,35 @@ func startGateway(ctx context.Context, conf *options.HttpServer, addr *net.TCPAd return fmt.Errorf("failed to initialize registry: %v", err) } - gw := gateway.NewCaddyGateway(reg, conf.Addr) + watchCallback := func(eventType registry.EventType, service *registry.ServiceInfo) { + if perms, ok := service.Metadata["permissions"]; ok { + + jsonData, err := json.Marshal(perms) + if err != nil { + return + } + var permissions []common.Permission + if err := json.Unmarshal(jsonData, &permissions); err != nil { + return + } + + switch eventType { + case registry.EventTypePut: + if err := auth.AddPluginServicePermission("admin", permissions, service.ServiceName); err != nil { + logger.Error("Failed to add permissions for service %s: %v", service.ServiceName, err) + } + case registry.EventTypeDelete: + if err := auth.DeletePluginServicePermission(permissions, service.ServiceName); err != nil { + logger.Error("Failed to remove permissions for service %s: %v", service.ServiceName, err) + } + } + } + } + + global.GW = gateway.NewCaddyGateway(reg, conf.Addr, watchCallback) go func() { - if err := gw.Run(); err != nil { + if err := global.GW.Run(); err != nil { logger.Error("Gateway encountered an error: %v", err) } }() @@ -132,7 +161,7 @@ func startGateway(ctx context.Context, conf *options.HttpServer, addr *net.TCPAd <-ctx.Done() logger.Info("Gateway stopped successfully") - if err := gw.Stop(); err != nil { + if err := global.GW.Stop(); err != nil { logger.Error("Failed to stop Gateway: %v", err) } else { logger.Info("Gateway stopped successfully") diff --git a/cmd/server/app/service/auth/casbin_plugin.go b/cmd/server/app/service/auth/casbin_plugin.go new file mode 100644 index 00000000..ad3a5d6f --- /dev/null +++ b/cmd/server/app/service/auth/casbin_plugin.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2024.All rights reserved. + * PilotGo licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: zhanghan + * Date: Thu Apr 10 16:18:53 2025 +0800 + */ +package auth + +import ( + "gitee.com/openeuler/PilotGo/sdk/common" + "gitee.com/openeuler/PilotGo/sdk/logger" +) + +// 添加插件服务权限到列表中 +func AddPluginServicePermission(role string, permissions []common.Permission, serviceName string) error { + //TODO;先添加到列表中可以展示 + for _, v := range permissions { + ok, err := addPolicy(role, v.Resource, v.Operate, serviceName) + if err != nil { + logger.Error("init plugin-admin policy failed:%s", err) + } + if !ok { + logger.Debug("plugin-admin %s permission already exists: %s", v.Operate, v.Resource) + } + } + return nil +} + +// 删除插件服务权限 +func DeletePluginServicePermission(permissions []common.Permission, serviceName string) error { + for _, v := range permissions { + ok, err := G_Enfocer.RemoveFilteredPolicy(1, v.Resource, v.Operate, serviceName) + if err != nil { + logger.Error("delete plugin policy failed:%s", err) + } + if !ok { + logger.Debug("delete plugin %s permission failed: %s", v.Operate, v.Resource) + } + } + return nil +} diff --git a/pkg/global/global.go b/pkg/global/global.go index fe2db5f3..1cade87d 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -7,6 +7,8 @@ */ package global +import "gitee.com/openeuler/PilotGo/sdk/go-micro/gateway" + const ( // 新注册机器添加到部门根节点 UncateloguedDepartId = 1 @@ -20,3 +22,6 @@ const ( // 消息提醒缓存 RemindMsgSize = 20 ) + +// caddy gateway +var GW *gateway.CaddyGateway diff --git a/sdk/go-micro/gateway/caddy.go b/sdk/go-micro/gateway/caddy.go index d4b66ca0..c6492ad9 100644 --- a/sdk/go-micro/gateway/caddy.go +++ b/sdk/go-micro/gateway/caddy.go @@ -26,6 +26,8 @@ import ( _ "github.com/caddyserver/caddy/v2/modules/standard" ) +type ServiceEventCallback func(eventType registry.EventType, service *registry.ServiceInfo) + type CaddyGateway struct { registry registry.Registry services map[string][]*registry.ServiceInfo @@ -33,15 +35,17 @@ type CaddyGateway struct { serviceLock *sync.RWMutex cancel context.CancelFunc httpAddr string + watchCallback ServiceEventCallback } -func NewCaddyGateway(reg registry.Registry, httpAddr string) *CaddyGateway { +func NewCaddyGateway(reg registry.Registry, httpAddr string, watchCB ServiceEventCallback) *CaddyGateway { return &CaddyGateway{ registry: reg, services: make(map[string][]*registry.ServiceInfo), serviceStatus: make(map[string]bool), serviceLock: &sync.RWMutex{}, httpAddr: httpAddr, + watchCallback: watchCB, } } func (g *CaddyGateway) Run() error { @@ -131,6 +135,10 @@ func (g *CaddyGateway) generateCaddyConfig() (*caddy.Config, error) { basePath, basePath + "/*", basePath + "/*/*", + "/plugin_manage", + "/plugin_manage/*", + "/plugin_manage/*/*", + "/plugin_manage/*/*/*", }) if err != nil { return nil, fmt.Errorf("failed to marshal matcher config: %v", err) @@ -278,8 +286,16 @@ func (g *CaddyGateway) watchServices(ctx context.Context) error { return } g.addService(&service) + + if g.watchCallback != nil { + g.watchCallback(event.Type, &service) + } logger.Info("Service added/updated: %s at %s:%s", service.ServiceName, service.Address, service.Port) case registry.EventTypeDelete: + if g.watchCallback != nil { + g.watchCallback(event.Type, g.GetService(event.Key)) + } + g.removeService(event.Key) logger.Info("Service removed: %s", event.Key) } diff --git a/sdk/go-micro/gateway/service.go b/sdk/go-micro/gateway/service.go new file mode 100644 index 00000000..3b2054bf --- /dev/null +++ b/sdk/go-micro/gateway/service.go @@ -0,0 +1,74 @@ +package gateway + +import ( + "errors" + "fmt" + + "gitee.com/openeuler/PilotGo/sdk/go-micro/registry" +) + +// 设置服务状态 +func (g *CaddyGateway) SetServiceStatus(serviceName string, status bool) error { + if _, ok := g.serviceStatus[serviceName]; !ok { + return errors.New("service not found") + } + g.serviceLock.Lock() + g.serviceStatus[serviceName] = status + g.serviceLock.Unlock() + + if err := g.updateCaddyConfig(); err != nil { + return fmt.Errorf("failed to reload caddy config: %w", err) + } + return nil +} + +// 获取服务状态 +func (g *CaddyGateway) GetServiceStatus(serviceName string) bool { + g.serviceLock.RLock() + defer g.serviceLock.RUnlock() + return g.serviceStatus[serviceName] +} + +// 获取某个服务 +func (g *CaddyGateway) GetService(key string) *registry.ServiceInfo { + g.serviceLock.Lock() + defer g.serviceLock.Unlock() + + var service *registry.ServiceInfo + for _, services := range g.services { + for _, s := range services { + if fmt.Sprintf("/services/%s", s.ServiceName) == key { + service = s + break + } + } + } + return service +} + +// 获取所有服务 +func (g *CaddyGateway) GetAllServices() []map[string]interface{} { + g.serviceLock.RLock() + defer g.serviceLock.RUnlock() + + result := make([]map[string]interface{}, 0) + + for serviceName, services := range g.services { + if len(services) == 0 { + continue + } + + svc := services[0] // 只取第一个实例 + result = append(result, map[string]interface{}{ + "serviceName": svc.ServiceName, + "address": svc.Address, + "port": svc.Port, + "version": svc.Metadata["version"], + "status": g.serviceStatus[serviceName], + "extentions": svc.Metadata["extentions"], + "permissions": svc.Metadata["permissions"], + }) + } + + return result +} diff --git a/sdk/go-micro/registry/etcd.go b/sdk/go-micro/registry/etcd.go index 15faaca4..94e8041f 100644 --- a/sdk/go-micro/registry/etcd.go +++ b/sdk/go-micro/registry/etcd.go @@ -98,15 +98,19 @@ func (e *etcdRegistry) Deregister() error { return nil } -func (e *etcdRegistry) Get(key string) (string, error) { - resp, err := e.client.Get(e.ctx, "/services/"+key) +func (e *etcdRegistry) Get(key string) (*ServiceInfo, error) { + resp, err := e.client.Get(e.ctx, key) if err != nil { - return "", err + return &ServiceInfo{}, err } if len(resp.Kvs) == 0 { - return "", nil + return &ServiceInfo{}, nil } - return string(resp.Kvs[0].Value), nil + var service ServiceInfo + if err := json.Unmarshal(resp.Kvs[0].Value, &service); err != nil { + return &ServiceInfo{}, err + } + return &service, nil } func (e *etcdRegistry) Put(key string, value string) error { diff --git a/sdk/go-micro/registry/registry.go b/sdk/go-micro/registry/registry.go index 86e61250..c26a44e7 100644 --- a/sdk/go-micro/registry/registry.go +++ b/sdk/go-micro/registry/registry.go @@ -11,14 +11,16 @@ import ( "context" "errors" "time" + + "gitee.com/openeuler/PilotGo/sdk/common" ) // ServiceInfo represents the basic information of a service type ServiceInfo struct { - ServiceName string `json:"serviceName"` - Address string `json:"address"` - Port string `json:"port"` - Metadata map[string]string `json:"metadata,omitempty"` + ServiceName string `json:"serviceName"` + Address string `json:"address"` + Port string `json:"port"` + Metadata map[string]interface{} `json:"metadata,omitempty"` } // Options represents the configuration options for service registry @@ -26,8 +28,11 @@ type Options struct { Endpoints []string // etcd address ServiceName string // 服务地址 ServiceAddr string // 服务名称 - Version string // 服务版本 DialTimeout time.Duration // 超时时间 + + Version string // 服务版本 + Extentions []common.Extention // 用于平台扩展点功能 + Permissions []common.Permission //用于权限校验 } // EventType represents the type of service registry events @@ -55,7 +60,7 @@ type Registry interface { // Deregister removes a service registration Deregister() error // Get retrieves service information - Get(key string) (string, error) + Get(key string) (*ServiceInfo, error) // Put stores service information Put(key string, value string) error // Delete removes service information diff --git a/sdk/go-micro/registry/service.go b/sdk/go-micro/registry/service.go index 20d180e7..45b93407 100644 --- a/sdk/go-micro/registry/service.go +++ b/sdk/go-micro/registry/service.go @@ -37,7 +37,11 @@ func NewServiceRegistrar(opts *Options) (Registry, error) { ServiceName: opts.ServiceName, Address: strings.Split(opts.ServiceAddr, ":")[0], Port: strings.Split(opts.ServiceAddr, ":")[1], - Metadata: map[string]string{"version": opts.Version}, + Metadata: map[string]interface{}{ + "version": opts.Version, + "extentions": opts.Extentions, + "permissions": opts.Permissions, + }, } sr := &ServiceRegistrar{ -- Gitee