203 lines
5.1 KiB
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
|
|
}
|
|
}
|
|
}
|