补充鉴权逻辑

This commit is contained in:
yumaojun03 2025-06-08 16:14:19 +08:00
parent 9eb977cfbf
commit 298cadf2e2
17 changed files with 1056 additions and 4 deletions

View File

@ -0,0 +1,5 @@
package namespace
const (
DEFAULT_NS_NAME = "default"
)

View File

@ -0,0 +1,31 @@
package impl
import (
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/datasource"
)
func init() {
ioc.Controller().Registry(&NameSpaceServiceImpl{})
}
var _ namespace.Service = (*NameSpaceServiceImpl)(nil)
type NameSpaceServiceImpl struct {
ioc.ObjectImpl
}
func (i *NameSpaceServiceImpl) Init() error {
if datasource.Get().AutoMigrate {
err := datasource.DB().AutoMigrate(&namespace.Namespace{})
if err != nil {
return err
}
}
return nil
}
func (i *NameSpaceServiceImpl) Name() string {
return namespace.AppName
}

View File

@ -0,0 +1,18 @@
package impl_test
import (
"context"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"122.51.31.227/go-course/go18/devcloud/mcenter/test"
)
var (
impl namespace.Service
ctx = context.Background()
)
func init() {
test.DevelopmentSetUp()
impl = namespace.GetService()
}

View File

@ -0,0 +1,93 @@
package impl
import (
"context"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/ioc/config/datasource"
"github.com/infraboard/mcube/v2/types"
"gorm.io/gorm"
)
// 创建空间
func (i *NameSpaceServiceImpl) CreateNamespace(ctx context.Context, in *namespace.CreateNamespaceRequest) (*namespace.Namespace, error) {
if err := in.Validate(); err != nil {
return nil, err
}
ins := namespace.NewNamespace()
ins.CreateNamespaceRequest = *in
if err := datasource.DBFromCtx(ctx).
Create(ins).
Error; err != nil {
return nil, err
}
return ins, nil
}
// 查询空间
func (i *NameSpaceServiceImpl) QueryNamespace(ctx context.Context, in *namespace.QueryNamespaceRequest) (*types.Set[*namespace.Namespace], error) {
set := types.New[*namespace.Namespace]()
query := datasource.DBFromCtx(ctx).Model(&namespace.Namespace{})
err := query.Count(&set.Total).Error
if err != nil {
return nil, err
}
err = query.
Order("created_at desc").
Offset(int(in.ComputeOffset())).
Limit(int(in.PageSize)).
Find(&set.Items).
Error
if err != nil {
return nil, err
}
return set, nil
}
// 查询空间详情
func (i *NameSpaceServiceImpl) DescribeNamespace(ctx context.Context, in *namespace.DescribeNamespaceRequest) (*namespace.Namespace, error) {
query := datasource.DBFromCtx(ctx)
ins := &namespace.Namespace{}
if err := query.Where("id = ?", in.Id).First(ins).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, exception.NewNotFound("namespace %d not found", in.Id)
}
return nil, err
}
return ins, nil
}
// 更新空间
func (i *NameSpaceServiceImpl) UpdateNamespace(ctx context.Context, in *namespace.UpdateNamespaceRequest) (*namespace.Namespace, error) {
descReq := namespace.NewDescribeNamespaceRequest()
descReq.SetId(in.Id)
ins, err := i.DescribeNamespace(ctx, descReq)
if err != nil {
return nil, err
}
ins.CreateNamespaceRequest = in.CreateNamespaceRequest
return ins, datasource.DBFromCtx(ctx).Where("id = ?", in.Id).Updates(ins).Error
}
// 删除空间
func (i *NameSpaceServiceImpl) DeleteNamespace(ctx context.Context, in *namespace.DeleteNamespaceRequest) (*namespace.Namespace, error) {
descReq := namespace.NewDescribeNamespaceRequest()
descReq.SetId(in.Id)
ins, err := i.DescribeNamespace(ctx, descReq)
if err != nil {
return nil, err
}
return ins, datasource.DBFromCtx(ctx).
Where("id = ?", in.Id).
Delete(&namespace.Namespace{}).
Error
}

View File

@ -0,0 +1,28 @@
package impl_test
import (
"testing"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
)
func TestQueryNamespace(t *testing.T) {
req := namespace.NewQueryNamespaceRequest()
set, err := impl.QueryNamespace(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}
func TestCreateNamespace(t *testing.T) {
req := namespace.NewCreateNamespaceRequest()
req.Name = namespace.DEFAULT_NS_NAME
req.Description = "默认空间"
req.OwnerUserId = 1
set, err := impl.CreateNamespace(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}

View File

@ -0,0 +1,90 @@
package namespace
import (
"context"
"github.com/infraboard/mcube/v2/http/request"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/types"
"github.com/infraboard/modules/iam/apps"
)
const (
AppName = "namespace"
)
func GetService() Service {
return ioc.Controller().Get(AppName).(Service)
}
type Service interface {
// 创建空间
CreateNamespace(context.Context, *CreateNamespaceRequest) (*Namespace, error)
// 查询空间
QueryNamespace(context.Context, *QueryNamespaceRequest) (*types.Set[*Namespace], error)
// 查询空间详情
DescribeNamespace(context.Context, *DescribeNamespaceRequest) (*Namespace, error)
// 更新空间
UpdateNamespace(context.Context, *UpdateNamespaceRequest) (*Namespace, error)
// 删除空间
DeleteNamespace(context.Context, *DeleteNamespaceRequest) (*Namespace, error)
}
func NewQueryNamespaceRequest() *QueryNamespaceRequest {
return &QueryNamespaceRequest{
PageRequest: *request.NewDefaultPageRequest(),
NamespaceIds: []uint64{},
}
}
type QueryNamespaceRequest struct {
request.PageRequest
NamespaceIds []uint64 `json:"namespace_ids"`
}
func (r *QueryNamespaceRequest) AddNamespaceIds(ids ...uint64) {
for _, id := range ids {
if !r.HasNamespaceIds(id) {
r.NamespaceIds = append(r.NamespaceIds, id)
}
}
}
func (r *QueryNamespaceRequest) HasNamespaceIds(namespaceId uint64) bool {
for i := range r.NamespaceIds {
if r.NamespaceIds[i] == namespaceId {
return true
}
}
return false
}
func NewDescribeNamespaceRequest() *DescribeNamespaceRequest {
return &DescribeNamespaceRequest{}
}
type DescribeNamespaceRequest struct {
apps.GetRequest
}
func (r *DescribeNamespaceRequest) SetNamespaceId(id uint64) *DescribeNamespaceRequest {
r.Id = id
return r
}
func NewUpdateNamespaceRequest() *UpdateNamespaceRequest {
return &UpdateNamespaceRequest{}
}
type UpdateNamespaceRequest struct {
apps.GetRequest
CreateNamespaceRequest
}
func NewDeleteNamespaceRequest() *DeleteNamespaceRequest {
return &DeleteNamespaceRequest{}
}
type DeleteNamespaceRequest struct {
apps.GetRequest
}

View File

@ -0,0 +1,62 @@
package namespace
import (
"github.com/infraboard/mcube/v2/ioc/config/validator"
"github.com/infraboard/mcube/v2/tools/pretty"
"github.com/infraboard/modules/iam/apps"
)
func NewNamespace() *Namespace {
return &Namespace{
ResourceMeta: *apps.NewResourceMeta(),
}
}
type Namespace struct {
// 基础数据
apps.ResourceMeta
// 空间属性
CreateNamespaceRequest
}
func (n *Namespace) IsOwner(ownerUserId uint64) bool {
return n.OwnerUserId == ownerUserId
}
func (n *Namespace) TableName() string {
return "namespaces"
}
func (n *Namespace) String() string {
return pretty.ToJSON(n)
}
func NewCreateNamespaceRequest() *CreateNamespaceRequest {
return &CreateNamespaceRequest{
Extras: map[string]string{},
Enabled: true,
}
}
type CreateNamespaceRequest struct {
// 父Namespace Id
ParentId uint64 `json:"parent_id" bson:"parent_id" gorm:"column:parent_id;type:uint;index" description:"父Namespace Id"`
// 全局唯一
Name string `json:"name" bson:"name" validate:"required" gorm:"column:name;type:varchar(200);not null;uniqueIndex" description:"空间名称" unique:"true"`
// 空间负责人
OwnerUserId uint64 `json:"owner_user_id" bson:"owner_user_id" gorm:"column:owner_user_id;type:uint;index;not null" description:" 空间负责人Id"`
// 禁用项目, 该项目所有人暂时都无法访问
Enabled bool `json:"enabled" bson:"enabled" gorm:"column:enabled;type:tinyint(1)" description:"是否启用"`
// 空间描述图片
Icon string `json:"icon" bson:"icon" gorm:"column:icon;type:varchar(200)" description:"空间图标"`
// 空间描述
Description string `json:"description" bson:"description" gorm:"column:description;type:text" description:"空间描述"`
// 标签
Label string `json:"label" gorm:"column:label;type:varchar(200);index" description:"标签"`
// 扩展信息
Extras map[string]string `json:"extras" bson:"extras" gorm:"column:extras;serializer:json;type:json" description:"扩展信息"`
}
func (r *CreateNamespaceRequest) Validate() error {
return validator.Validate(r)
}

View File

@ -0,0 +1,38 @@
package impl
import (
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/role"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/datasource"
)
func init() {
ioc.Controller().Registry(&PolicyServiceImpl{})
}
var _ policy.Service = (*PolicyServiceImpl)(nil)
type PolicyServiceImpl struct {
ioc.ObjectImpl
namespace namespace.Service
role role.Service
}
func (i *PolicyServiceImpl) Init() error {
if datasource.Get().AutoMigrate {
err := datasource.DB().AutoMigrate(&policy.Policy{})
if err != nil {
return err
}
}
i.namespace = namespace.GetService()
i.role = role.GetService()
return nil
}
func (i *PolicyServiceImpl) Name() string {
return policy.AppName
}

View File

@ -0,0 +1,18 @@
package impl_test
import (
"context"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy"
"122.51.31.227/go-course/go18/devcloud/mcenter/test"
)
var (
impl policy.Service
ctx = context.Background()
)
func init() {
test.DevelopmentSetUp()
impl = policy.GetService()
}

View File

@ -0,0 +1,107 @@
package impl
import (
"context"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/role"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/view"
"github.com/infraboard/mcube/v2/types"
)
// 查询用户可以访问的空间
func (i *PolicyServiceImpl) QueryNamespace(ctx context.Context, in *policy.QueryNamespaceRequest) (*types.Set[*namespace.Namespace], error) {
nsReq := namespace.NewQueryNamespaceRequest()
policies, err := i.QueryPolicy(ctx,
policy.NewQueryPolicyRequest().
SetSkipPage(true).
SetUserId(in.UserId).
SetExpired(false).
SetEnabled(true))
if err != nil {
return nil, err
}
policies.ForEach(func(t *policy.Policy) {
if t.NamespaceId != nil {
nsReq.AddNamespaceIds(*t.NamespaceId)
}
})
return i.namespace.QueryNamespace(ctx, nsReq)
}
// 查询用户可以访问的Api接口
// 找到用户可以访问的角色列表然后在找出角色对应的Api访问权限
func (i *PolicyServiceImpl) QueryEndpoint(ctx context.Context, in *policy.QueryEndpointRequest) (*types.Set[*endpoint.Endpoint], error) {
set := types.New[*endpoint.Endpoint]()
policies, err := i.QueryPolicy(ctx,
policy.NewQueryPolicyRequest().
SetSkipPage(true).
SetNamespaceId(in.NamespaceId).
SetUserId(in.UserId).
SetExpired(false).
SetEnabled(true))
if err != nil {
return nil, err
}
roleReq := role.NewQueryMatchedEndpointRequest()
policies.ForEach(func(t *policy.Policy) {
roleReq.Add(t.RoleId)
})
if policies.Len() > 0 {
set, err = role.GetService().QueryMatchedEndpoint(ctx, roleReq)
if err != nil {
return nil, err
}
}
return set, nil
}
// 校验Api接口权限
func (i *PolicyServiceImpl) ValidateEndpointPermission(ctx context.Context, in *policy.ValidateEndpointPermissionRequest) (*policy.ValidateEndpointPermissionResponse, error) {
resp := policy.NewValidateEndpointPermissionResponse(*in)
// 空间Owner有所有权限
ns, err := namespace.GetService().DescribeNamespace(ctx, namespace.NewDescribeNamespaceRequest().SetNamespaceId(in.NamespaceId))
if err != nil {
return nil, err
}
if ns.IsOwner(in.UserId) {
resp.HasPermission = true
return resp, nil
}
// 非空间管理员需要独立鉴权, 查询用户可以访问的API列表
endpointReq := policy.NewQueryEndpointRequest()
endpointReq.UserId = in.UserId
endpointReq.NamespaceId = in.NamespaceId
endpointSet, err := i.QueryEndpoint(ctx, endpointReq)
if err != nil {
return nil, err
}
for _, item := range endpointSet.Items {
if item.IsMatched(in.Service, in.Method, in.Path) {
resp.HasPermission = true
resp.Endpoint = item
break
}
}
return resp, nil
}
// 查询用户可以访问的菜单
func (i *PolicyServiceImpl) QueryMenu(ctx context.Context, in *policy.QueryMenuRequest) (*types.Set[*view.Menu], error) {
return nil, nil
}
// 校验Menu视图权限
func (i *PolicyServiceImpl) ValidatePagePermission(ctx context.Context, in *policy.ValidatePagePermissionRequest) (*policy.ValidatePagePermissionResponse, error) {
return nil, nil
}

View File

@ -0,0 +1,42 @@
package impl_test
import (
"testing"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy"
)
func TestQueryNamespace(t *testing.T) {
req := policy.NewQueryNamespaceRequest()
req.UserId = 1
set, err := impl.QueryNamespace(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}
func TestQueryEndpoint(t *testing.T) {
req := policy.NewQueryEndpointRequest()
req.UserId = 1
req.NamespaceId = 1
set, err := impl.QueryEndpoint(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}
func TestValidateEndpointPermission(t *testing.T) {
req := policy.NewValidateEndpointPermissionRequest()
req.UserId = 1
req.NamespaceId = 1
req.Service = "devcloud"
req.Method = "GET"
req.Path = "/api/devcloud/v1/users/"
set, err := impl.ValidateEndpointPermission(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}

View File

@ -0,0 +1,148 @@
package impl
import (
"context"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/role"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/view"
"github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/ioc/config/datasource"
"github.com/infraboard/mcube/v2/types"
"gorm.io/gorm"
)
// 创建策略
func (i *PolicyServiceImpl) CreatePolicy(ctx context.Context, in *policy.CreatePolicyRequest) (*policy.Policy, error) {
if err := in.Validate(); err != nil {
return nil, err
}
ins := policy.NewPolicy()
ins.CreatePolicyRequest = *in
if err := datasource.DBFromCtx(ctx).
Create(ins).
Error; err != nil {
return nil, err
}
return ins, nil
}
// 查询策略列表
func (i *PolicyServiceImpl) QueryPolicy(ctx context.Context, in *policy.QueryPolicyRequest) (*types.Set[*policy.Policy], error) {
set := types.New[*policy.Policy]()
query := datasource.DBFromCtx(ctx).Model(&policy.Policy{}).Order("created_at desc")
err := query.Count(&set.Total).Error
if err != nil {
return nil, err
}
if !in.SkipPage {
query = query.
Offset(int(in.ComputeOffset())).
Limit(int(in.PageSize))
}
if err = query.Find(&set.Items).Error; err != nil {
return nil, err
}
if in.WithUser {
userReq := user.NewQueryUserRequest()
set.ForEach(func(t *policy.Policy) {
userReq.AddUser(t.UserId)
})
userSet, err := user.GetService().QueryUser(ctx, userReq)
if err != nil {
return nil, err
}
set.ForEach(func(p *policy.Policy) {
p.User = userSet.Filter(func(t *user.User) bool {
return p.UserId == t.Id
}).First()
})
}
if in.WithRole {
roleReq := role.NewQueryRoleRequest()
set.ForEach(func(t *policy.Policy) {
roleReq.AddRoleId(t.RoleId)
})
roleSet, err := role.GetService().QueryRole(ctx, roleReq)
if err != nil {
return nil, err
}
set.ForEach(func(p *policy.Policy) {
p.Role = roleSet.Filter(func(t *role.Role) bool {
return p.RoleId == t.Id
}).First()
})
}
if in.WithNamespace {
nsReq := namespace.NewQueryNamespaceRequest()
set.ForEach(func(t *policy.Policy) {
if t.NamespaceId != nil {
nsReq.AddNamespaceIds(*t.NamespaceId)
}
})
nsSet, err := namespace.GetService().QueryNamespace(ctx, nsReq)
if err != nil {
return nil, err
}
set.ForEach(func(p *policy.Policy) {
if p.NamespaceId != nil {
p.Namespace = nsSet.Filter(func(t *namespace.Namespace) bool {
return *p.NamespaceId == t.Id
}).First()
}
})
}
return set, nil
}
// 查询详情
func (i *PolicyServiceImpl) DescribePolicy(ctx context.Context, in *policy.DescribePolicyRequest) (*policy.Policy, error) {
query := datasource.DBFromCtx(ctx)
ins := &policy.Policy{}
if err := query.Where("id =?", in.Id).First(ins).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, exception.NewNotFound("policy %d not found", in.Id)
}
return nil, err
}
return ins, nil
}
// 更新策略
func (i *PolicyServiceImpl) UpdatePolicy(ctx context.Context, in *policy.UpdatePolicyRequest) (*policy.Policy, error) {
descReq := policy.NewDescribePolicyRequest()
descReq.SetId(in.Id)
ins, err := i.DescribePolicy(ctx, descReq)
if err != nil {
return nil, err
}
ins.CreatePolicyRequest = in.CreatePolicyRequest
return ins, datasource.DBFromCtx(ctx).Where("id = ?", in.Id).Updates(ins).Error
}
// 删除策略
func (i *PolicyServiceImpl) DeletePolicy(ctx context.Context, in *policy.DeletePolicyRequest) (*policy.Policy, error) {
descReq := policy.NewDescribePolicyRequest()
descReq.SetId(in.Id)
ins, err := i.DescribePolicy(ctx, descReq)
if err != nil {
return nil, err
}
return ins, datasource.DBFromCtx(ctx).
Where("id = ?", in.Id).
Delete(&view.Menu{}).
Error
}

View File

@ -0,0 +1,31 @@
package impl_test
import (
"testing"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy"
)
func TestQueryPolicy(t *testing.T) {
req := policy.NewQueryPolicyRequest()
req.WithUser = true
req.WithRole = true
req.WithNamespace = true
set, err := impl.QueryPolicy(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}
func TestCreatePolicy(t *testing.T) {
req := policy.NewCreatePolicyRequest()
req.SetNamespaceId(1)
req.UserId = 1
req.RoleId = 1
set, err := impl.CreatePolicy(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(set)
}

View File

@ -0,0 +1,233 @@
package policy
import (
"context"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/view"
"github.com/infraboard/mcube/v2/http/request"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/tools/pretty"
"github.com/infraboard/mcube/v2/types"
"github.com/infraboard/modules/iam/apps"
)
const (
AppName = "policy"
)
func GetService() Service {
return ioc.Controller().Get(AppName).(Service)
}
type Service interface {
// 策略管理
PolicyService
// 权限查询, 整合用户多个角色的权限合集
PermissionService
}
type PolicyService interface {
// 创建策略
CreatePolicy(context.Context, *CreatePolicyRequest) (*Policy, error)
// 查询策略列表
QueryPolicy(context.Context, *QueryPolicyRequest) (*types.Set[*Policy], error)
// 查询详情
DescribePolicy(context.Context, *DescribePolicyRequest) (*Policy, error)
// 更新策略
UpdatePolicy(context.Context, *UpdatePolicyRequest) (*Policy, error)
// 删除策略
DeletePolicy(context.Context, *DeletePolicyRequest) (*Policy, error)
}
func NewQueryPolicyRequest() *QueryPolicyRequest {
return &QueryPolicyRequest{
PageRequest: request.NewDefaultPageRequest(),
}
}
type QueryPolicyRequest struct {
*request.PageRequest
// 忽略分页
SkipPage bool `json:"skip_page"`
// 关联用户Id
UserId *uint64 `json:"user_id"`
// 关联空间
NamespaceId *uint64 `json:"namespace_id"`
// 没有过期
Expired *bool `json:"expired"`
// 有没有启动
Enabled *bool `json:"active"`
// 关联查询出空间对象
WithNamespace bool `json:"with_namespace"`
// 关联查询出用户对象
WithUser bool `json:"with_user"`
// 关联查询角色对象
WithRole bool `json:"with_role"`
}
func (r *QueryPolicyRequest) SetNamespaceId(nsId uint64) *QueryPolicyRequest {
r.NamespaceId = &nsId
return r
}
func (r *QueryPolicyRequest) SetUserId(uid uint64) *QueryPolicyRequest {
r.UserId = &uid
return r
}
func (r *QueryPolicyRequest) SetExpired(v bool) *QueryPolicyRequest {
r.Expired = &v
return r
}
func (r *QueryPolicyRequest) SetEnabled(v bool) *QueryPolicyRequest {
r.Enabled = &v
return r
}
func (r *QueryPolicyRequest) SetSkipPage(v bool) *QueryPolicyRequest {
r.SkipPage = v
return r
}
func (r *QueryPolicyRequest) SetWithRole(v bool) *QueryPolicyRequest {
r.WithRole = v
return r
}
func (r *QueryPolicyRequest) SetWithUsers(v bool) *QueryPolicyRequest {
r.WithUser = v
return r
}
func (r *QueryPolicyRequest) SetWithUser(v bool) *QueryPolicyRequest {
r.WithNamespace = v
return r
}
func NewDescribePolicyRequest() *DescribePolicyRequest {
return &DescribePolicyRequest{}
}
type DescribePolicyRequest struct {
apps.GetRequest
}
type UpdatePolicyRequest struct {
apps.GetRequest
CreatePolicyRequest
}
func NewDeletePolicyRequest() *DeletePolicyRequest {
return &DeletePolicyRequest{}
}
type DeletePolicyRequest struct {
apps.GetRequest
}
type PermissionService interface {
// 查询用户可以访问的空间
QueryNamespace(context.Context, *QueryNamespaceRequest) (*types.Set[*namespace.Namespace], error)
// 查询用户可以访问的菜单
QueryMenu(context.Context, *QueryMenuRequest) (*types.Set[*view.Menu], error)
// 查询用户可以访问的Api接口
QueryEndpoint(context.Context, *QueryEndpointRequest) (*types.Set[*endpoint.Endpoint], error)
// 校验页面权限
ValidatePagePermission(context.Context, *ValidatePagePermissionRequest) (*ValidatePagePermissionResponse, error)
// 校验接口权限
ValidateEndpointPermission(context.Context, *ValidateEndpointPermissionRequest) (*ValidateEndpointPermissionResponse, error)
}
type ValidatePagePermissionRequest struct {
UserId uint64 `json:"user_id" form:"user_id"`
NamespaceId uint64 `json:"namespace_id" form:"namespace_id"`
Path string `json:"path" form:"path"`
}
func NewValidatePagePermissionResponse(req ValidatePagePermissionRequest) *ValidatePagePermissionResponse {
return &ValidatePagePermissionResponse{
ValidatePagePermissionRequest: req,
}
}
type ValidatePagePermissionResponse struct {
ValidatePagePermissionRequest
HasPermission bool `json:"has_permission"`
Page *view.Page `json:"page"`
}
func NewValidateEndpointPermissionRequest() *ValidateEndpointPermissionRequest {
return &ValidateEndpointPermissionRequest{}
}
type ValidateEndpointPermissionRequest struct {
UserId uint64 `json:"user_id" form:"user_id"`
NamespaceId uint64 `json:"namespace_id" form:"namespace_id"`
Service string `json:"service" form:"service"`
Path string `json:"path" form:"path"`
Method string `json:"method" form:"method"`
}
func NewValidateEndpointPermissionResponse(req ValidateEndpointPermissionRequest) *ValidateEndpointPermissionResponse {
return &ValidateEndpointPermissionResponse{
ValidateEndpointPermissionRequest: req,
}
}
type ValidateEndpointPermissionResponse struct {
ValidateEndpointPermissionRequest
HasPermission bool `json:"has_permission"`
Endpoint *endpoint.Endpoint `json:"endpoint"`
}
func (r *ValidateEndpointPermissionResponse) String() string {
return pretty.ToJSON(r)
}
func NewQueryNamespaceRequest() *QueryNamespaceRequest {
return &QueryNamespaceRequest{}
}
type QueryNamespaceRequest struct {
UserId uint64 `json:"user_id"`
NamespaceId uint64 `json:"namespace_id"`
}
func (r *QueryNamespaceRequest) SetUserId(v uint64) *QueryNamespaceRequest {
r.UserId = v
return r
}
func (r *QueryNamespaceRequest) SetNamespaceId(v uint64) *QueryNamespaceRequest {
r.NamespaceId = v
return r
}
func NewQueryMenuRequest() *QueryMenuRequest {
return &QueryMenuRequest{}
}
type QueryMenuRequest struct {
UserId uint64 `json:"user_id"`
NamespaceId uint64 `json:"namespace_id"`
}
func NewQueryEndpointRequest() *QueryEndpointRequest {
return &QueryEndpointRequest{}
}
type QueryEndpointRequest struct {
UserId uint64 `json:"user_id"`
NamespaceId uint64 `json:"namespace_id"`
}
func (r *QueryEndpointRequest) SetUserId(v uint64) *QueryEndpointRequest {
r.UserId = v
return r
}
func (r *QueryEndpointRequest) SetNamespaceId(v uint64) *QueryEndpointRequest {
r.NamespaceId = v
return r
}

View File

@ -0,0 +1,80 @@
package policy
import (
"time"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/role"
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
"github.com/infraboard/mcube/v2/ioc/config/validator"
"github.com/infraboard/mcube/v2/tools/pretty"
"github.com/infraboard/modules/iam/apps"
)
func NewPolicy() *Policy {
return &Policy{
ResourceMeta: *apps.NewResourceMeta(),
}
}
type Policy struct {
// 基础数据
apps.ResourceMeta
// 策略定义
CreatePolicyRequest
// 关联空间
Namespace *namespace.Namespace `json:"namespace,omitempty" gorm:"-"`
// 关联用户
User *user.User `json:"user,omitempty" gorm:"-"`
// 关联角色
Role *role.Role `json:"role,omitempty" gorm:"-"`
}
func (p *Policy) TableName() string {
return "policy"
}
func (p *Policy) String() string {
return pretty.ToJSON(p)
}
func NewCreatePolicyRequest() *CreatePolicyRequest {
return &CreatePolicyRequest{
Extras: map[string]string{},
Scope: map[string]string{},
Enabled: true,
ReadOnly: false,
}
}
type CreatePolicyRequest struct {
// 创建者
CreateBy uint64 `json:"create_by" bson:"create_by" gorm:"column:create_by;type:uint" description:"创建者" optional:"true"`
// 空间
NamespaceId *uint64 `json:"namespace_id" bson:"namespace_id" gorm:"column:namespace_id;type:varchar(200);index" description:"策略生效的空间Id" optional:"true"`
// 用户Id
UserId uint64 `json:"user_id" bson:"user_id" gorm:"column:user_id;type:uint;not null;index" validate:"required" description:"被授权的用户"`
// 角色Id
RoleId uint64 `json:"role_id" bson:"role_id" gorm:"column:role_id;type:uint;not null;index" validate:"required" description:"被关联的角色"`
// 访问范围, 需要提前定义scope, 比如环境
Scope map[string]string `json:"scope" bson:"scope" gorm:"column:scope;serializer:json;type:json" description:"数据访问的范围" optional:"true"`
// 策略过期时间
ExpiredTime *time.Time `json:"expired_time" bson:"expired_time" gorm:"column:expired_time;type:timestamp;index" description:"策略过期时间" optional:"true"`
// 只读策略, 不允许用户修改, 一般用于系统管理
ReadOnly bool `json:"read_only" bson:"read_only" gorm:"column:read_only;type:tinyint(1)" description:"只读策略, 不允许用户修改, 一般用于系统管理" optional:"true"`
// 该策略是否启用
Enabled bool `json:"enabled" bson:"enabled" gorm:"column:enabled;type:tinyint(1)" description:"该策略是否启用" optional:"true"`
// 策略标签
Label string `json:"label" gorm:"column:label;type:varchar(200);index" description:"策略标签" optional:"true"`
// 扩展信息
Extras map[string]string `json:"extras" bson:"extras" gorm:"column:extras;serializer:json;type:json" description:"扩展信息" optional:"true"`
}
func (r *CreatePolicyRequest) Validate() error {
return validator.Validate(r)
}
func (r *CreatePolicyRequest) SetNamespaceId(namespaceId uint64) *CreatePolicyRequest {
r.NamespaceId = &namespaceId
return r
}

View File

@ -7,7 +7,11 @@ import (
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/api" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/api"
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/impl" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/impl"
// 鉴权
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint/impl" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint/impl"
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace/impl"
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy/impl"
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/role/impl"
// 颁发器 // 颁发器
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/issuers" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/issuers"

View File

@ -1,18 +1,42 @@
<mxfile host="65bd71144e"> <mxfile host="65bd71144e">
<diagram id="prDIldifm9lRc8bqSxY8" name="第 1 页"> <diagram id="prDIldifm9lRc8bqSxY8" name="第 1 页">
<mxGraphModel dx="892" dy="476" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="892" dy="439" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="0"/> <mxCell id="0"/>
<mxCell id="1" parent="0"/> <mxCell id="1" parent="0"/>
<mxCell id="2" value="Role" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="2" value="Role" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="210" y="230" width="120" height="60" as="geometry"/> <mxGeometry x="210" y="230" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="3" value="Api Permission" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="6" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="3" target="5">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="12" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="3" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="Api Permission&lt;div&gt;mapping&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="390" y="120" width="120" height="60" as="geometry"/> <mxGeometry x="390" y="120" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="4" value="Api Permission" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="9" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="4" target="7">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="13" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="4" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="4" value="View Permission&lt;div&gt;mapping&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="390" y="340" width="120" height="60" as="geometry"/> <mxGeometry x="390" y="340" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="5" value="Endpont" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="610" y="120" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="8">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="7" value="Menu" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="610" y="340" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="8" value="Page" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="770" y="340" width="120" height="60" as="geometry"/>
</mxCell>
</root> </root>
</mxGraphModel> </mxGraphModel>
</diagram> </diagram>