refactor: 授权凭证优化

This commit is contained in:
meilin.huang
2024-04-10 23:17:20 +08:00
parent 40b6e603fc
commit 4ef8d27b1e
14 changed files with 360 additions and 129 deletions

View File

@@ -86,7 +86,6 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
tagIds := param.TagIds
authCerts := param.AuthCerts
resourceType := tagentity.TagTypeMachine
authCertTagType := tagentity.TagTypeMachineAuthCert
oldMachine := &entity.Machine{
Ip: me.Ip,
@@ -119,11 +118,10 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
return err
}
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
ResourceCode: me.Code,
ResourceType: resourceType,
AuthCertTagType: authCertTagType,
AuthCerts: authCerts,
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: me.Code,
ResourceType: resourceType,
AuthCerts: authCerts,
})
}
@@ -152,11 +150,10 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
return err
}
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
ResourceCode: oldMachine.Code,
ResourceType: resourceType,
AuthCertTagType: authCertTagType,
AuthCerts: authCerts,
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: oldMachine.Code,
ResourceType: resourceType,
AuthCerts: authCerts,
})
}
@@ -216,7 +213,7 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
ResourceType: resourceType,
})
}, func(ctx context.Context) error {
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: machine.Code,
ResourceType: resourceType,
})

View File

@@ -6,8 +6,6 @@ import (
"mayfly-go/internal/tag/domain/entity"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/req"
"mayfly-go/pkg/utils/collx"
"strings"
"github.com/may-fly/cast"
)
@@ -51,25 +49,11 @@ func (c *ResourceAuthCert) SaveAuthCert(rc *req.Ctx) {
acForm.Ciphertext = "***"
rc.ReqParam = acForm
biz.ErrIsNil(c.ResourceAuthCertApp.SavePulbicAuthCert(rc.MetaCtx, ac))
biz.ErrIsNil(c.ResourceAuthCertApp.SaveAuthCert(rc.MetaCtx, ac))
}
func (c *ResourceAuthCert) Delete(rc *req.Ctx) {
idsStr := rc.PathParam("id")
ids := strings.Split(idsStr, ",")
acIds := make([]uint64, 0)
acNames := make([]string, 0)
for _, v := range ids {
id := cast.ToUint64(v)
rac, err := c.ResourceAuthCertApp.GetById(new(entity.ResourceAuthCert), id)
biz.ErrIsNil(err, "存在错误授权凭证id")
biz.IsTrue(rac.Type == entity.AuthCertTypePublic, "只允许删除公共授权凭证")
biz.IsTrue(c.ResourceAuthCertApp.CountByCond(&entity.ResourceAuthCert{Ciphertext: rac.Name}) == 0, "[%s]该授权凭证已被关联", rac.Name)
acIds = append(acIds, id)
acNames = append(acNames, rac.Name)
}
rc.ReqParam = acNames
biz.ErrIsNil(c.ResourceAuthCertApp.DeleteByWheres(rc.MetaCtx, collx.M{"id in ?": acIds}))
id := rc.PathParamInt("id")
rc.ReqParam = id
biz.ErrIsNil(c.ResourceAuthCertApp.DeleteAuthCert(rc.MetaCtx, cast.ToUint64(id)))
}

View File

@@ -102,8 +102,14 @@ func (p *TagTree) TagResources(rc *req.Ctx) {
func (p *TagTree) CountTagResource(rc *req.Ctx) {
tagPath := rc.Query("tagPath")
accountId := rc.GetLoginAccount().Id
machienAuthCerts := p.ResourceAuthCertApp.GetAccountAuthCert(accountId, entity.TagTypeMachineAuthCert, tagPath)
machineCodes := collx.ArrayMap(machienAuthCerts, func(ac *entity.ResourceAuthCert) string {
return ac.ResourceCode
})
rc.ResData = collx.M{
"machine": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMachine, tagPath)),
"machine": len(collx.ArrayDeduplicate(machineCodes)),
"db": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, tagPath)),
"redis": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, tagPath)),
"mongo": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, tagPath)),

View File

@@ -10,14 +10,12 @@ import (
"mayfly-go/pkg/utils/collx"
)
type SaveAuthCertParam struct {
type RelateAuthCertParam struct {
ResourceCode string
// 资源标签类型
ResourceType entity.TagType
// 授权凭证类型
AuthCertTagType entity.TagType
// 空数组则为删除该资源绑定的授权凭证
AuthCerts []*entity.ResourceAuthCert
}
@@ -25,11 +23,13 @@ type SaveAuthCertParam struct {
type ResourceAuthCert interface {
base.App[*entity.ResourceAuthCert]
// SaveAuthCert 保存资源授权凭证信息,不可放于事务中
SaveAuthCert(ctx context.Context, param *SaveAuthCertParam) error
// RelateAuthCert 保存资源授权凭证信息,不可放于事务中
RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error
// SavePublicAuthCert 保存公共授权凭证信息
SavePulbicAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
// SaveAuthCert 保存授权凭证信息
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
DeleteAuthCert(ctx context.Context, id uint64) error
// GetAuthCert 根据授权凭证名称获取授权凭证
GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error)
@@ -56,23 +56,25 @@ func (r *resourceAuthCertAppImpl) InjectResourceAuthCertRepo(resourceAuthCertRep
r.Repo = resourceAuthCertRepo
}
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *SaveAuthCertParam) error {
func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *RelateAuthCertParam) error {
resourceCode := params.ResourceCode
resourceType := int8(params.ResourceType)
resourceAuthCerts := params.AuthCerts
authCertTagType := params.AuthCertTagType
authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType))
if authCertTagType == 0 {
return errorx.NewBiz("资源授权凭证所属标签类型不能为空")
}
if resourceCode == "" {
return errorx.NewBiz("资源授权凭证的资源编号不能为空")
}
if resourceType == 0 {
return errorx.NewBiz("资源类型不能为空")
}
// 删除授权信息
if len(resourceAuthCerts) == 0 {
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-删除所有关联的授权凭证信息", resourceType, resourceCode)
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-删除所有关联的授权凭证信息", resourceType, resourceCode)
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}); err != nil {
return err
}
@@ -114,9 +116,10 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
var oldAuthCert []*entity.ResourceAuthCert
r.ListByCond(&entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}, &oldAuthCert)
// 新增、删除以及不变的授权凭证名
var adds, dels, unmodifys []string
if len(oldAuthCert) == 0 {
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
adds = collx.MapKeys(name2AuthCert)
} else {
oldNames := collx.ArrayMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
@@ -134,7 +137,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
// 处理新增的授权凭证
if len(addAuthCerts) > 0 {
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, adds)
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, adds)
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
return err
}
@@ -150,7 +153,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
if len(resourceTagIds) > 0 {
// 保存授权凭证类型的资源标签
for _, authCert := range addAuthCerts {
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, authCert.Name, resourceTagIds)
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, authCert.Name, resourceTagIds)
if err := r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{
ResourceCode: authCert.Name,
ResourceType: authCertTagType,
@@ -164,7 +167,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
}
for _, del := range dels {
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, del)
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, del)
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil {
return err
}
@@ -177,39 +180,75 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
}
}
for _, unmodify := range unmodifys {
unmodifyAc := name2AuthCert[unmodify]
if unmodifyAc.Id == 0 {
continue
}
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
// 置空用户名不允许修改TagTree的name关联了该值修改了会造成数据不一致懒得处理该同步。要修改用户名可通过重新删除旧凭证后添加一个授权凭证或通过关联公共凭证公共凭证可修改用户名
if unmodifyAc.CiphertextType != entity.AuthCertCiphertextTypePublic {
unmodifyAc.Username = ""
}
if len(unmodifys) > 0 {
// 旧凭证名 -> 旧凭证
oldName2AuthCert := collx.ArrayToMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
return ac.Name
})
for _, unmodify := range unmodifys {
unmodifyAc := name2AuthCert[unmodify]
if unmodifyAc.Id == 0 {
continue
}
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
return err
oldAuthCert := oldName2AuthCert[unmodify]
if !unmodifyAc.HasChanged(oldAuthCert) {
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证[%s]未发生字段变更", resourceType, resourceCode, unmodify)
continue
}
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
if oldAuthCert.Username != unmodifyAc.Username {
if err := r.updateAuthCertTagName(ctx, unmodify, authCertTagType, unmodifyAc.Username); err != nil {
logx.WarnfContext(ctx, "授权凭证[%s]修改了用户名-同步更新授权凭证标签名失败", unmodify)
}
}
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
return err
}
}
}
return nil
}
func (r *resourceAuthCertAppImpl) SavePulbicAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
rac.Type = entity.AuthCertTypePublic
rac.CiphertextEncrypt()
rac.ResourceType = 0
rac.ResourceCode = "-"
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
if rac.Id == 0 {
if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 {
return errorx.NewBiz("授权凭证的名称不能重复[%s]", rac.Name)
}
return r.Insert(ctx, rac)
return r.addAuthCert(ctx, rac)
}
// 名称置空,防止被更新
rac.Name = ""
return r.UpdateById(ctx, rac)
return r.updateAuthCert(ctx, rac)
}
func (r *resourceAuthCertAppImpl) DeleteAuthCert(ctx context.Context, id uint64) error {
rac, err := r.GetById(new(entity.ResourceAuthCert), id)
if err != nil {
return errorx.NewBiz("授权凭证不存在")
}
if rac.Type == entity.AuthCertTypePublic {
if r.CountByCond(&entity.ResourceAuthCert{Ciphertext: rac.Name}) > 0 {
return errorx.NewBiz("该公共授权凭证[%s]已被关联", rac.Name)
}
// 公共授权凭证直接删除即可
return r.DeleteById(ctx, id)
}
if r.CountByCond(&entity.ResourceAuthCert{ResourceCode: rac.ResourceCode, ResourceType: rac.ResourceType}) <= 1 {
return errorx.NewBiz("资源至少需要绑定一个授权凭证,无法删除该凭证[%s]", rac.Name)
}
return r.Tx(ctx,
func(ctx context.Context) error {
// 删除对应授权凭证标签
return r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
ResourceCode: rac.Name,
})
},
func(ctx context.Context) error {
return r.DeleteById(ctx, id)
})
}
func (r *resourceAuthCertAppImpl) GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error) {
@@ -290,6 +329,109 @@ func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthC
}
}
// addAuthCert 添加授权凭证
func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 {
return errorx.NewBiz("授权凭证的名称不能重复[%s]", rac.Name)
}
// 公共凭证
if rac.Type == entity.AuthCertTypePublic {
rac.ResourceCode = "-"
rac.CiphertextEncrypt()
rac.Type = -2
return r.Insert(ctx, rac)
}
resourceCode := rac.ResourceCode
resourceType := rac.ResourceType
// 获取资源编号对应的资源标签信息
var resourceTags []*entity.TagTree
r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags)
// 资源标签id相当于父tag id
resourceTagIds := collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 {
return tag.Id
})
if len(resourceTagIds) == 0 {
return errorx.NewBiz("资源标签不存在[%s], 请检查资源编号是否正确", resourceCode)
}
authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType))
// 如果密文类型不为公共凭证,则进行加密。公共凭证密文内容存的是明文的公共凭证名
if rac.CiphertextType != entity.AuthCertCiphertextTypePublic {
rac.CiphertextEncrypt()
}
return r.Tx(ctx, func(ctx context.Context) error {
logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagIds)
return r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{
ResourceCode: rac.Name,
ResourceType: authCertTagType,
ResourceName: rac.Username,
TagIds: resourceTagIds,
})
}, func(ctx context.Context) error {
return r.Insert(ctx, rac)
})
}
// updateAuthCert 更新授权凭证
func (r *resourceAuthCertAppImpl) updateAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
oldRac, err := r.GetById(new(entity.ResourceAuthCert), rac.Id)
if err != nil {
return errorx.NewBiz("该授权凭证不存在")
}
if !oldRac.HasChanged(rac) {
return nil
}
if oldRac.Type == entity.AuthCertTypePublic {
// 如果旧凭证为公共凭证,则不允许修改凭证类型
if rac.Type != entity.AuthCertTypePublic {
return errorx.NewBiz("公共授权凭证不允许修改凭证类型")
}
if rac.CiphertextType == entity.AuthCertCiphertextTypePublic {
return errorx.NewBiz("公共授权凭证不允许绑定其他公共授权凭证")
}
} else {
if rac.Type == entity.AuthCertTypePublic {
return errorx.NewBiz("非公共授权凭证不允许修改为公共凭证")
}
// 修改了用户名,则需要同步更新对应授权凭证标签里的名称
if rac.Username != oldRac.Username {
if err := r.updateAuthCertTagName(ctx, oldRac.Name, getResourceAuthCertTagType(entity.TagType(oldRac.ResourceType)), rac.Username); err != nil {
return errorx.NewBiz("同步更新授权凭证标签名称失败")
}
}
}
// 防止误更新
rac.Name = ""
rac.ResourceCode = ""
rac.ResourceType = 0
return r.UpdateById(ctx, rac)
}
// updateAuthCertTagName 同步更新授权凭证的标签名,防止标签展示不一致
func (r *resourceAuthCertAppImpl) updateAuthCertTagName(ctx context.Context, authCertName string, autyCertTagType entity.TagType, newTagName string) error {
return r.tagTreeApp.UpdateByWheres(ctx, &entity.TagTree{Name: newTagName}, collx.M{"code = ?": authCertName, "type = ?": autyCertTagType})
}
// getResourceAuthCertTagType 根据资源类型,获取对应的授权凭证标签类型
func getResourceAuthCertTagType(resourceType entity.TagType) entity.TagType {
if resourceType == entity.TagTypeMachine {
return entity.TagTypeMachineAuthCert
}
if resourceType == entity.TagTypeDb {
return entity.TagTypeDbAuthCert
}
return -2
}
// 解密授权凭证信息
func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {

View File

@@ -152,19 +152,30 @@ func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, query *entity.
if len(accountTagPaths) == 0 {
accountTagPaths = tagPaths
} else {
accountTagPaths = collx.ArrayFilter[string](tagPaths, func(s string) bool {
for _, v := range accountTagPaths {
// 要过滤的权限需要在用户拥有的子标签下, accountTagPath: test/ tagPath: test/test1/ -> true
if strings.HasPrefix(v, s) {
queryPaths := collx.ArrayFilter[string](tagPaths, func(tagPath string) bool {
for _, acPath := range accountTagPaths {
// 查询条件: a/b/ 有权的a/ 查询结果应该是 a/b/
if strings.HasPrefix(tagPath, acPath) {
return true
}
}
return false
})
acPaths := collx.ArrayFilter[string](accountTagPaths, func(acPath string) bool {
for _, tagPath := range tagPaths {
// 查询条件: a/ 有权的a/b/ 查询结果应该是 a/b/
if strings.HasPrefix(acPath, tagPath) {
return true
}
}
return false
})
accountTagPaths = append(queryPaths, acPaths...)
}
}
// tagResourceQuery.CodePathLike = tagPath
tagResourceQuery.Codes = query.Codes
tagResourceQuery.CodePathLikes = accountTagPaths
p.ListByQuery(tagResourceQuery, &tagResources)

View File

@@ -97,6 +97,19 @@ func (m *ResourceAuthCert) GetExtraString(key string) string {
return cast.ToString(m.Extra[key])
}
// HasChanged 与指定授权凭证比较是否有变更
func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool {
if rac == nil {
return true
}
return m.Username != rac.Username ||
m.Ciphertext != rac.Ciphertext ||
m.CiphertextType != rac.CiphertextType ||
m.Remark != rac.Remark ||
m.Type != rac.Type ||
m.GetExtraString(ExtraKeyPassphrase) != rac.GetExtraString(ExtraKeyPassphrase)
}
// 密文类型
type AuthCertCiphertextType int8

View File

@@ -30,6 +30,8 @@ const (
TagTypeRedis TagType = TagType(consts.TagResourceTypeRedis)
TagTypeMongo TagType = TagType(consts.TagResourceTypeMongo)
// ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证)
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
TagTypeDbAuthCert TagType = 21 // DB-授权凭证
)

View File

@@ -0,0 +1,22 @@
package structx
import "reflect"
// DiffFields 比较两个结构体中指定字段的属性值是否发生改变,并返回改变的字段名
func DiffFields[T any](t1 T, t2 T, fieldsToCompare ...string) []string {
var changedFields []string
oldValue := reflect.ValueOf(t1)
newValue := reflect.ValueOf(t2)
for _, fieldName := range fieldsToCompare {
oldFieldValue := oldValue.FieldByName(fieldName)
newFieldValue := newValue.FieldByName(fieldName)
if !reflect.DeepEqual(oldFieldValue.Interface(), newFieldValue.Interface()) {
changedFields = append(changedFields, fieldName)
}
}
return changedFields
}