补充权限验证

This commit is contained in:
yumaojun03 2025-03-23 10:39:44 +08:00
parent 903a3d8520
commit 89d648e5a6
11 changed files with 270 additions and 65 deletions

View File

@ -1,6 +1,7 @@
{
"go.testEnvFile": "${workspaceFolder}/etc/unit_test.env",
"go.testEnvVars": {
"CmdbConfigPath": "${workspaceFolder}/cmdb/etc/application.toml"
"CmdbConfigPath": "${workspaceFolder}/cmdb/etc/application.toml",
"McenterConfigPath": "${workspaceFolder}/mcenter/etc/application.toml"
}
}

View File

@ -9,7 +9,9 @@ import (
"github.com/infraboard/mcube/v2/http/restful/response"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/gorestful"
"github.com/infraboard/modules/iam/apps/endpoint"
"gitlab.com/go-course-project/go17/devcloud-mini/cmdb/apps/secret"
permission "gitlab.com/go-course-project/go17/devcloud-mini/mcenter/permisson"
)
func init() {
@ -31,7 +33,12 @@ func (r *SecretApiHandler) Init() error {
tags := []string{"凭证管理"}
ws.Route(ws.GET("").To(r.QuerySecret).Doc("凭证列表").
ws.Route(ws.GET("").To(r.QuerySecret).
Metadata(permission.Auth(true)).
Metadata(permission.Permission(true)).
Metadata(endpoint.META_RESOURCE_KEY, "secret").
Metadata(endpoint.META_ACTION_KEY, "list").
Doc("凭证列表").
Param(ws.QueryParameter("page_size", "分页大小").DataType("intger")).
Param(ws.QueryParameter("page_number", "页码").DataType("intger")).
Param(ws.QueryParameter("keywords", "关键字过滤").DataType("string")).
@ -41,7 +48,12 @@ func (r *SecretApiHandler) Init() error {
Returns(404, "Not Found", exception.NewNotFound("")))
// :id -> {id}
ws.Route(ws.GET("/{id}").To(r.DescribeSecret).Doc("凭证详情").
ws.Route(ws.GET("/{id}").To(r.DescribeSecret).
Metadata(permission.Auth(true)).
Metadata(permission.Permission(true)).
Metadata(endpoint.META_RESOURCE_KEY, "secret").
Metadata(endpoint.META_ACTION_KEY, "get").
Doc("凭证详情").
Param(ws.PathParameter("id", "凭证Id").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(secret.Secret{}).

View File

@ -1,6 +1,8 @@
[app]
name = "cmdb"
description = "cmdb"
internal_address = "http://127.0.0.1:8020"
internal_token = "bar3TjDvMxITfrGrVLvv3ujF"
[http]
# 开启GRPC服务

View File

@ -1,6 +1,8 @@
[app]
name = "mcenter"
description = "mcenter"
internal_address = "http://127.0.0.1:8020"
internal_token = "bar3TjDvMxITfrGrVLvv3ujF"
[http]
# 开启GRPC服务
@ -18,3 +20,4 @@
username = "root"
password = "123456"
debug = true
auto_migrate = true

View File

@ -1,15 +1,67 @@
# 给接入用户中心的服务提供的SDK 中间件
```go
import (
permission "gitlab.com/go-course-project/go17/devcloud-mini/mcenter/permisson"
"github.com/infraboard/modules/iam/apps/endpoint"
)
ws.Route(ws.GET("").To(r.QuerySecret).
Metadata(permission.Auth(true)).
Metadata(permission.Permission(true)).
Metadata(endpoint.META_RESOURCE_KEY, "secret").
Metadata(endpoint.META_ACTION_KEY, "list")
)
```
## 中间件逻辑
把这2个部分替换为RPC 就可以给其他服务使用了
```go
tk, err := c.token.ValiateToken(r.Request.Context(), token.NewValiateTokenRequest(v))
set, err := c.policy.QueryPolicy(r.Request.Context(),
policy.NewQueryPolicyRequest().
SetNamespaceId(tk.NamespaceId).
SetUserId(tk.UserId).
SetExpired(false).
SetEnabled(true).
SetWithRole(true),
)
// http://127.0.0.1:8020/api/mcenter/v1/token/validate
func (c *Checker) ValiateToken(ctx context.Context, in *token.ValiateTokenRequest) (*token.Token, error) {
tk := token.NewToken()
resp, err := resty.New().
SetBaseURL(application.Get().InternalAddress).
SetAuthToken(application.Get().InternalToken).
R().
WithContext(ctx).
SetContentType("application/json").
SetBody(in).
SetResult(tk).
Post("/api/mcenter/v1/token/validate")
if err != nil {
return nil, err
}
if resp.StatusCode()/100 != 2 {
return nil, exception.NewUnauthorized("[%d] token校验异常: %s", resp.StatusCode(), resp.String())
}
return tk, nil
}
```
```go
// 查询策略列表
// /api/mcenter/v1/permission/check
func (c *Checker) ValidateEndpointPermission(ctx context.Context, in *policy.ValidateEndpointPermissionRequest) (*policy.ValidateEndpointPermissionResponse, error) {
ins := policy.NewValidateEndpointPermissionResponse(*in)
resp, err := resty.New().
SetBaseURL(application.Get().InternalAddress).
SetAuthToken(application.Get().InternalToken).
SetDebug(false).
R().
WithContext(ctx).
SetBody(in).
SetResult(ins).
Post("/api/mcenter/v1/permission/check")
if err != nil {
return nil, err
}
if resp.StatusCode()/100 != 2 {
return nil, exception.NewPermissionDeny("[%d] token鉴权异常: %s", resp.StatusCode(), resp.String())
}
return ins, nil
}
```

View File

@ -7,12 +7,14 @@ import (
"github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/http/restful/response"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/application"
"github.com/infraboard/mcube/v2/ioc/config/gorestful"
"github.com/infraboard/mcube/v2/ioc/config/log"
"github.com/infraboard/modules/iam/apps/endpoint"
"github.com/infraboard/modules/iam/apps/policy"
"github.com/infraboard/modules/iam/apps/token"
"github.com/rs/zerolog"
"resty.dev/v3"
)
func Auth(v bool) (string, bool) {
@ -31,12 +33,13 @@ func init() {
ioc.Config().Registry(&Checker{})
}
func GetPermissionChecker() *Checker {
return ioc.Config().Get("permission_checker").(*Checker)
}
type Checker struct {
ioc.ObjectImpl
log *zerolog.Logger
token token.Service
policy policy.Service
}
func (c *Checker) Name() string {
@ -49,8 +52,6 @@ func (c *Checker) Priority() int {
func (c *Checker) Init() error {
c.log = log.Sub(c.Name())
c.token = token.GetService()
c.policy = policy.GetService()
// 注册认证中间件
gorestful.RootRouter().Filter(c.Check)
@ -83,7 +84,7 @@ func (c *Checker) CheckToken(r *restful.Request) (*token.Token, error) {
return nil, exception.NewUnauthorized("请先登录")
}
tk, err := c.token.ValiateToken(r.Request.Context(), token.NewValiateTokenRequest(v))
tk, err := c.ValiateToken(r.Request.Context(), token.NewValiateTokenRequest(v))
if err != nil {
return nil, err
}
@ -93,43 +94,73 @@ func (c *Checker) CheckToken(r *restful.Request) (*token.Token, error) {
return tk, nil
}
// http://127.0.0.1:8020/api/mcenter/v1/token/validate
func (c *Checker) ValiateToken(ctx context.Context, in *token.ValiateTokenRequest) (*token.Token, error) {
tk := token.NewToken()
resp, err := resty.New().
SetBaseURL(application.Get().InternalAddress).
SetAuthToken(application.Get().InternalToken).
R().
WithContext(ctx).
SetContentType("application/json").
SetBody(in).
SetResult(tk).
Post("/api/mcenter/v1/token/validate")
if err != nil {
return nil, err
}
if resp.StatusCode()/100 != 2 {
return nil, exception.NewUnauthorized("[%d] token校验异常: %s", resp.StatusCode(), resp.String())
}
if tk.NamespaceId == 0 {
tk.NamespaceId = 1
}
return tk, nil
}
func (c *Checker) CheckPolicy(r *restful.Request, tk *token.Token, route *endpoint.RouteEntry) error {
if tk.IsAdmin {
return nil
}
set, err := c.policy.QueryPolicy(r.Request.Context(),
policy.NewQueryPolicyRequest().
SetNamespaceId(tk.NamespaceId).
SetUserId(tk.UserId).
SetExpired(false).
SetEnabled(true).
SetWithRole(true),
)
if err != nil {
return exception.NewInternalServerError(err.Error())
}
// 角色校验
if route.HasRequiredRole() {
for i := range set.Items {
p := set.Items[i]
if route.IsRequireRole(p.Role.Name) {
return nil
}
}
}
// API权限校验
if route.RequiredPerm {
for i := range set.Items {
p := set.Items[i]
if p.Role == nil {
return exception.NewInternalServerError("policy role is nil")
}
permReq := policy.NewValidateEndpointPermissionRequest()
permReq.UserId = tk.UserId
permReq.NamespaceId = tk.NamespaceId
permReq.Service = application.Get().AppName
permReq.Method = route.Method
permReq.Path = route.Path
resp, err := c.ValidateEndpointPermission(r.Request.Context(), permReq)
if err != nil {
return err
}
if !resp.HasPermission {
return exception.NewPermissionDeny("无权限")
}
}
return nil
}
// 查询策略列表
// /api/mcenter/v1/permission/check
func (c *Checker) ValidateEndpointPermission(ctx context.Context, in *policy.ValidateEndpointPermissionRequest) (*policy.ValidateEndpointPermissionResponse, error) {
ins := policy.NewValidateEndpointPermissionResponse(*in)
resp, err := resty.New().
SetBaseURL(application.Get().InternalAddress).
SetAuthToken(application.Get().InternalToken).
SetDebug(true).
R().
WithContext(ctx).
SetBody(in).
SetResult(ins).
Post("/api/mcenter/v1/permission/check")
if err != nil {
return nil, err
}
if resp.StatusCode()/100 != 2 {
return nil, exception.NewPermissionDeny("[%d] token鉴权异常: %s", resp.StatusCode(), resp.String())
}
return ins, nil
}

View File

@ -0,0 +1,57 @@
package permission_test
import (
"context"
"testing"
"github.com/infraboard/modules/iam/apps/endpoint"
"github.com/infraboard/modules/iam/apps/policy"
"github.com/infraboard/modules/iam/apps/token"
permission "gitlab.com/go-course-project/go17/devcloud-mini/mcenter/permisson"
"gitlab.com/go-course-project/go17/devcloud-mini/mcenter/test"
)
func TestValiateToken(t *testing.T) {
tk, err := permission.GetPermissionChecker().
ValiateToken(context.Background(), token.NewValiateTokenRequest("fY2dsHqkSBNATR1jTHsXIpXo"))
if err != nil {
t.Fatal(err)
}
t.Log(tk)
}
func TestValidateEndpointPermission(t *testing.T) {
req := policy.NewValidateEndpointPermissionRequest()
req.UserId = 5
req.NamespaceId = 1
req.Service = "cmdb"
req.Method = "GET"
req.Path = "/api/cmdb/v1/secret"
resp, err := permission.GetPermissionChecker().
ValidateEndpointPermission(context.Background(), req)
if err != nil {
t.Fatal(err)
}
t.Log(resp)
}
func TestRegistryEndpoint(t *testing.T) {
req := endpoint.NewRegistryEndpointRequest()
req.AddItem(&endpoint.RouteEntry{
Service: "cmdb",
Resource: "secret",
Action: "delete",
Method: "DELETE",
Path: "/api/cmdb/v1/secret/{id}",
})
resp, err := permission.GetApiRegister().
RegistryEndpoint(context.Background(), req)
if err != nil {
t.Fatal(err)
}
t.Log(resp)
}
func init() {
test.SetUp()
}

View File

@ -3,18 +3,33 @@ package permission
import (
"context"
"github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/application"
"github.com/infraboard/mcube/v2/ioc/config/gorestful"
"github.com/infraboard/mcube/v2/types"
"github.com/infraboard/modules/iam/apps/endpoint"
"resty.dev/v3"
)
func init() {
ioc.Api().Registry(&ApiRegister{})
}
func GetApiRegister() *ApiRegister {
return ioc.Api().Get("api_register").(*ApiRegister)
}
type ApiRegister struct {
ioc.ObjectImpl
endpoint endpoint.Service
}
func (c *ApiRegister) Name() string {
return "api_register"
}
func (i *ApiRegister) Priority() int {
return -100
}
func (a *ApiRegister) Init() error {
@ -22,13 +37,35 @@ func (a *ApiRegister) Init() error {
entries := endpoint.NewEntryFromRestfulContainer(gorestful.RootRouter())
req := endpoint.NewRegistryEndpointRequest()
req.AddItem(entries...)
_, err := a.endpoint.RegistryEndpoint(context.Background(), req)
_, err := a.RegistryEndpoint(context.Background(), req)
if err != nil {
return err
}
return nil
}
func (c *ApiRegister) Name() string {
return "api_register"
// 注册API接口(RPC --> REST SDK)
// 自己的 注册API接口
// restful client: github.com/go-resty/resty/v2
// http://127.0.0.1:8020/api/mcenter/v1/endpoint
func (a *ApiRegister) RegistryEndpoint(ctx context.Context, in *endpoint.RegistryEndpointRequest) (*types.Set[*endpoint.Endpoint], error) {
set := types.New[*endpoint.Endpoint]()
resp, err := resty.New().
SetDebug(true).
SetBaseURL(application.Get().InternalAddress).
SetAuthToken(application.Get().InternalToken).
R().
WithContext(ctx).
SetContentType("application/json").
SetBody(in.Items).
SetResult(set).
Post("/api/mcenter/v1/endpoint")
if err != nil {
return nil, err
}
if resp.StatusCode()/100 != 2 {
return nil, exception.NewPermissionDeny("[%d] API注册异常: %s", resp.StatusCode(), resp.String())
}
return set, nil
}

View File

@ -0,0 +1,13 @@
package test
import (
"fmt"
"os"
"github.com/infraboard/mcube/v2/ioc"
)
func SetUp() {
fmt.Println(os.Getenv("McenterConfigPath"))
ioc.DevelopmentSetupWithPath(os.Getenv("McenterConfigPath"))
}

7
go.mod
View File

@ -9,8 +9,8 @@ require (
github.com/go-playground/validator/v10 v10.20.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/infraboard/mcube/v2 v2.0.51
github.com/infraboard/modules v0.0.3
github.com/infraboard/mcube/v2 v2.0.52
github.com/infraboard/modules v0.0.8
github.com/rs/zerolog v1.32.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1116
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse v1.0.1115
@ -111,7 +111,7 @@ require (
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
@ -123,5 +123,6 @@ require (
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.28.0 // indirect
resty.dev/v3 v3.0.0-beta.2
sigs.k8s.io/yaml v1.4.0 // indirect
)

20
go.sum
View File

@ -125,16 +125,10 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/infraboard/mcenter v0.0.45 h1:zFVmurkjGXb582hEJS0YSLjyl4TcimwSCeEbjXaJ6/M=
github.com/infraboard/mcenter v0.0.45/go.mod h1:YsGG69OTCgqsAb0VYr7pLNASElVtimX1WQ/ZzANQ9MI=
github.com/infraboard/mcube/v2 v2.0.51 h1:QWgC6yo6qfx5xvU25MPdoQhkPuzjwrn8YfrQxOonL/8=
github.com/infraboard/mcube/v2 v2.0.51/go.mod h1:gnr0xPPDPHvCS6JAzvdjqJ62J2+vUZTkobomjTXKsx0=
github.com/infraboard/modules v0.0.0-20241201140557-2383c703e617 h1:+xhfA9EXxu7iW6sb9DrG/ZHiCM31PH9aHQ31v3Xywck=
github.com/infraboard/modules v0.0.0-20241201140557-2383c703e617/go.mod h1:v5qcwHvGOg6UsmKPVgAN3sn+XLgD9uYu/PqEWU129ck=
github.com/infraboard/modules v0.0.0-20250316032722-447e1c651a75 h1:yuK6RWI23duw2nbGAa673puBk8lwaF7DKH55UmtgLjE=
github.com/infraboard/modules v0.0.0-20250316032722-447e1c651a75/go.mod h1:v5qcwHvGOg6UsmKPVgAN3sn+XLgD9uYu/PqEWU129ck=
github.com/infraboard/modules v0.0.2 h1:LyIKcSp4J/p96JHm59ZtDr3CF1yWN09RttiPmRgoYHM=
github.com/infraboard/modules v0.0.2/go.mod h1:v5qcwHvGOg6UsmKPVgAN3sn+XLgD9uYu/PqEWU129ck=
github.com/infraboard/modules v0.0.3 h1:OTsW1K7htTnNZUMNHJOcHIL4Tp218LaGPk4HX3Cuas0=
github.com/infraboard/modules v0.0.3/go.mod h1:v5qcwHvGOg6UsmKPVgAN3sn+XLgD9uYu/PqEWU129ck=
github.com/infraboard/mcube/v2 v2.0.52 h1:cOzVjTz2LlIMvz1CCXrRwyX6uaF0JKxc7MBIRMLipFY=
github.com/infraboard/mcube/v2 v2.0.52/go.mod h1:gnr0xPPDPHvCS6JAzvdjqJ62J2+vUZTkobomjTXKsx0=
github.com/infraboard/modules v0.0.8 h1:ytzkjMbRuFb6FoI3Md6xS5hITjFqIvhIMMBvMQUGuE4=
github.com/infraboard/modules v0.0.8/go.mod h1:2ENrxqKlWvkf9WgO9FvXw3bUHWtPut71WSwDvVga/PI=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
@ -324,8 +318,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -406,6 +400,8 @@ modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
resty.dev/v3 v3.0.0-beta.2 h1:xu4mGAdbCLuc3kbk7eddWfWm4JfhwDtdapwss5nCjnQ=
resty.dev/v3 v3.0.0-beta.2/go.mod h1:OgkqiPvTDtOuV4MGZuUDhwOpkY8enjOsjjMzeOHefy4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=