Skip to content

Commit

Permalink
修复高并发场景下鉴权CheckPermission方法导致内存溢出问题 (polarismesh#1086)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sad-polar-bear committed Apr 19, 2023
1 parent 3f9492e commit 475e488
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 54 deletions.
96 changes: 42 additions & 54 deletions auth/defaultauth/auth_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package defaultauth
import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/pkg/errors"
Expand Down Expand Up @@ -163,35 +164,21 @@ func (d *defaultAuthChecker) CheckPermission(authCtx *model.AcquireContext) (boo
return false, model.ErrorTokenDisabled
}

strategies, err := d.findStrategies(operatorInfo)
if err != nil {
log.Error("[Auth][Checker] find strategies when check permission", utils.ZapRequestID(reqId),
zap.Error(err), zap.Any("token", operatorInfo.String()))
return false, err
ok, err := d.doCheckPermission(authCtx)
if ok {
return ok, nil
}

authCtx.SetAttachment(model.OperatorLinkStrategy, strategies)

noResourceNeedCheck := d.removeNoStrategyResources(authCtx)
if !noResourceNeedCheck && len(strategies) == 0 {
log.Error("[Auth][Checker]", utils.ZapRequestID(reqId),
zap.String("msg", "need check resource is not empty, but strategies is empty"))
return false, errors.New("no permission")
}

log.Info("[Auth][Checker] check permission args", zap.Any("resources", authCtx.GetAccessResources()),
zap.Any("strategies", strategies))

ok, err := d.doCheckPermission(authCtx, strategies)
// 强制同步一次db中strategy数据到cache
err = d.cacheMgn.AuthStrategy().ForceSyncStrategy2Cache()
if err != nil {
log.Error("[Auth][Checker] check permission args", utils.ZapRequestID(reqId),
zap.String("method", authCtx.GetMethod()), zap.Any("resources", authCtx.GetAccessResources()),
zap.Any("strategies", strategies))
log.Error("[Auth][Checker] check permission when request arrive", utils.ZapRequestID(reqId),
zap.Error(err))
zap.String("method", authCtx.GetMethod()), zap.Any("resources", authCtx.GetAccessResources()))
log.Error("[Auth][Checker] force sync strategy to cache failed", utils.ZapRequestID(reqId), zap.Error(err))
return false, err
}

return ok, err
return d.doCheckPermission(authCtx)
}

func canDowngradeAnonymous(authCtx *model.AcquireContext, err error) bool {
Expand Down Expand Up @@ -481,45 +468,46 @@ func (d *defaultAuthChecker) removeNoStrategyResources(authCtx *model.AcquireCon
return noResourceNeedCheck
}

// doCheckPermission 执行权限检查
func (d *defaultAuthChecker) doCheckPermission(authCtx *model.AcquireContext,
strategies []*model.StrategyDetail) (bool, error) {

if len(strategies) == 0 {
return true, nil
func (d *defaultAuthChecker) isResourceEditable(
userid string,
resourceType apisecurity.ResourceType,
resEntries []model.ResourceEntry) bool {
for _, entry := range resEntries {
principal := model.Principal{
PrincipalID: userid,
PrincipalRole: model.PrincipalUser,
}
if !d.cacheMgn.AuthStrategy().IsResourceEditable(principal, resourceType, entry.ID) {
return false
}
}
return true
}

// doCheckPermission 执行权限检查
func (d *defaultAuthChecker) doCheckPermission(authCtx *model.AcquireContext) (bool, error) {

userId := utils.ParseUserID(authCtx.GetRequestContext())

var checkNamespace, checkSvc, checkCfgGroup bool

reqRes := authCtx.GetAccessResources()
var (
checkNamespace = false
checkService = true
checkConfigGroup = true
)

for _, rule := range strategies {
if !d.checkAction(rule.Action, authCtx.GetOperation()) {
continue
}
searchMaps := buildSearchMap(rule.Resources)
nsResEntries := reqRes[apisecurity.ResourceType_Namespaces]
svcResEntries := reqRes[apisecurity.ResourceType_Services]
cfgResEntries := reqRes[apisecurity.ResourceType_ConfigGroups]

// 检查 namespace
checkNamespace = checkAnyElementExist(userId, reqRes[apisecurity.ResourceType_Namespaces], searchMaps[0])
// 检查 service
if authCtx.GetModule() == model.DiscoverModule {
checkService = checkAnyElementExist(userId, reqRes[apisecurity.ResourceType_Services], searchMaps[1])
}
// 检查 config_group
if authCtx.GetModule() == model.ConfigModule {
checkConfigGroup = checkAnyElementExist(userId, reqRes[apisecurity.ResourceType_ConfigGroups], searchMaps[2])
}
checkNamespace = d.isResourceEditable(userId, apisecurity.ResourceType_Namespaces, nsResEntries)
checkSvc = d.isResourceEditable(userId, apisecurity.ResourceType_Services, svcResEntries)
checkCfgGroup = d.isResourceEditable(userId, apisecurity.ResourceType_ConfigGroups, cfgResEntries)

if checkNamespace && (checkService && checkConfigGroup) {
return true, nil
}
checkAllResEntries := checkNamespace && checkSvc && checkCfgGroup

var err error
if !checkAllResEntries {
err = fmt.Errorf("no permission")
}
return false, ErrorNotAllowedAccess

return checkAllResEntries, err
}

// checkAction 检查操作是否和策略匹配
Expand Down
7 changes: 7 additions & 0 deletions cache/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type StrategyCache interface {

// IsResourceEditable 判断该资源是否可以操作
IsResourceEditable(principal model.Principal, resType apisecurity.ResourceType, resId string) bool

// ForceSyncStrategy2Cache 强制同步鉴权策略到cache (串行)
ForceSyncStrategy2Cache() error
}

// strategyCache
Expand Down Expand Up @@ -132,6 +135,10 @@ func (sc *strategyCache) update() error {
return err
}

func (sc *strategyCache) ForceSyncStrategy2Cache() error {
return sc.update()
}

func (sc *strategyCache) realUpdate() (map[string]time.Time, int64, error) {
// 获取几秒前的全部数据
var (
Expand Down

0 comments on commit 475e488

Please sign in to comment.