app-cloudep-permission-server/pkg/usecase/casbin_redis_rbac.go

203 lines
5.1 KiB
Go

package usecase
import (
"context"
"fmt"
"log"
"net/http"
"time"
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain"
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/permission"
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/repository"
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/usecase"
"code.30cm.net/digimon/library-go/errs"
"code.30cm.net/digimon/library-go/errs/code"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
"github.com/zeromicro/go-zero/core/logx"
)
type RBACUseCaseParam struct {
ModulePath string
permissionRepo repository.PermissionRepository
roleRepo repository.RoleRepository
rolePermission repository.RolePermissionRepository
RBACRedisAdapter repository.RBACAdapter
// role permission 之類的
}
type RBACUseCase struct {
permissionRepo repository.PermissionRepository
roleRepo repository.RoleRepository
adapter repository.RBACAdapter
rolePermission repository.RolePermissionRepository
instance *casbin.Enforcer
}
func NewRBACUseCase(param RBACUseCaseParam) usecase.RBACUseCase {
result := &RBACUseCase{
adapter: param.RBACRedisAdapter,
permissionRepo: param.permissionRepo,
roleRepo: param.roleRepo,
rolePermission: param.rolePermission,
}
// 1. 讀取 RBAC 模型 ->
m, err := model.NewModelFromFile(param.ModulePath)
if err != nil {
log.Fatalf("failed to load model: %v", err)
}
// 3. 創建 Casbin Enforcer
enforcer, err := casbin.NewEnforcer(m, result.adapter)
if err != nil {
log.Fatalf("failed to init Enforcer: %v", err)
}
result.instance = enforcer
return result
}
func (use *RBACUseCase) Check(ctx context.Context, role, path, method string) (usecase.CheckRolePermissionStatus, error) {
ok, p, err := use.instance.EnforceEx(role, path, method)
if err != nil {
e := errs.ForbiddenL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: fmt.Sprintf("role: %s, path: %s, method: %s", role, path, method)},
{Key: "func", Value: "casbin.EnforceEx"},
{Key: "err", Value: err.Error()},
},
"failed to get permission")
return usecase.CheckRolePermissionStatus{}, e
}
status := usecase.CheckRolePermissionStatus{
Allow: ok,
}
// 檢查是否有明碼查詢權限
if role == permission.AdminRoleUID {
status.Select.PlainCode = true
} else if ok && method == http.MethodGet {
policy, err := use.instance.GetModel().HasPolicy("p", "p", []string{
role, path, method, p[3] + ".plain_code",
})
if err != nil {
return usecase.CheckRolePermissionStatus{}, err
}
status.Select.PlainCode = policy
}
limit := 4
if len(p) >= limit {
status.Select.PermissionName = p[3]
}
return status, nil
}
func (use *RBACUseCase) LoadPolicy(ctx context.Context) error {
// 取得所有permission -> permission tree 拿到所有節點,
permissions, err := use.permissionRepo.GetAll(ctx, nil)
if err != nil {
return fmt.Errorf("permissionRepo.AllStatus error: %w", err)
}
// 建立樹,我只要開啟的 Permission
tree := GeneratePermissionTree(permissions)
openMaps, err := tree.filterOpenNodes()
if err != nil {
return fmt.Errorf("GeneratePermissionTree.filterOpenNodes error: %w", err)
}
// 全部permission
permissionMap := make(map[string]entity.Permission, len(permissions))
for k, v := range openMaps {
permissionMap[k] = v
}
// 全部角色
roles, err := use.roleRepo.All(ctx, nil)
if err != nil {
return fmt.Errorf("roleRepo.All error: %w", err)
}
roleMap := make(map[string]entity.Role, len(roles))
for _, v := range roles {
tmpValue := v
roleMap[v.ID.Hex()] = *tmpValue
}
// 根據角色組合權限表
for _, role := range roles {
rolePermission, err := use.rolePermission.Get(ctx, role.ID.Hex())
if err != nil {
e := errs.DatabaseErrorWithScopeL(
code.CloudEPPermission,
domain.FailedToGetRolePermission,
logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: fmt.Sprintf("role: %s", role.ID.Hex())},
{Key: "func", Value: "RolePermissionRepo.Get"},
{Key: "err", Value: err.Error()},
},
"failed to get rolePermission")
return e
}
for _, rp := range rolePermission {
r, ok := roleMap[rp.RoleID]
if !ok {
logx.Errorf(fmt.Sprintf("role_id: %s not found", rp.RoleID))
continue
}
p, ok := permissionMap[rp.PermissionID]
if !ok {
logx.Errorf(fmt.Sprintf("permission_id: %s not found", rp.PermissionID))
continue
}
if p.HTTPPath == "" || p.HTTPMethod == "" {
continue
}
_, err = use.instance.AddPolicy(r.Name, p.HTTPPath, p.HTTPMethod, p.Name)
if err != nil {
return err
}
}
}
err = use.instance.LoadPolicy()
if err != nil {
return err
}
return nil
}
func (use *RBACUseCase) SyncPolicy(ctx context.Context, cron time.Duration) {
t := time.NewTicker(cron)
for {
select {
case <-t.C:
if err := use.LoadPolicy(ctx); err == nil {
logx.Info("LoadPolicy success")
}
case <-ctx.Done():
t.Stop()
logx.Info("exit Policy success")
return
}
}
}