mirror of
				https://github.com/veops/oneterm.git
				synced 2025-10-31 02:46:29 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			168 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package controller
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"sort"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/gin-gonic/gin"
 | |
| 	"github.com/samber/lo"
 | |
| 	"go.uber.org/zap"
 | |
| 	"golang.org/x/sync/errgroup"
 | |
| 	"gorm.io/gorm"
 | |
| 
 | |
| 	"github.com/veops/oneterm/acl"
 | |
| 	"github.com/veops/oneterm/conf"
 | |
| 	mysql "github.com/veops/oneterm/db"
 | |
| 	"github.com/veops/oneterm/logger"
 | |
| 	"github.com/veops/oneterm/model"
 | |
| )
 | |
| 
 | |
| func HandleAuthorization(currentUser *acl.Session, tx *gorm.DB, action int, old, new *model.Asset) (err error) {
 | |
| 	ctx := context.Background()
 | |
| 	assetId := lo.TernaryF(new == nil, func() int { return old.Id }, func() int { return new.Id })
 | |
| 	mtx := &sync.Mutex{}
 | |
| 	eg := &errgroup.Group{}
 | |
| 	if action == model.ACTION_UPDATE {
 | |
| 		if sameAuthorization(old.Authorization, new.Authorization) {
 | |
| 			return
 | |
| 		}
 | |
| 		for id := range old.Authorization {
 | |
| 			if _, ok := new.Authorization[id]; ok {
 | |
| 				continue
 | |
| 			}
 | |
| 			accountId := id
 | |
| 			eg.Go(func() (err error) {
 | |
| 				a := &model.Authorization{}
 | |
| 				if err = mysql.DB.
 | |
| 					Model(a).
 | |
| 					Select("id", "resource_id").
 | |
| 					Where("asset_id = ? AND account_id = ?", assetId, accountId).
 | |
| 					First(a).
 | |
| 					Error; err != nil {
 | |
| 					return
 | |
| 				}
 | |
| 				if err = acl.DeleteResource(ctx, currentUser.GetUid(), a.ResourceId); err != nil {
 | |
| 					return
 | |
| 				}
 | |
| 				mtx.Lock()
 | |
| 				defer mtx.Unlock()
 | |
| 				err = tx.Delete(a, a.Id).Error
 | |
| 				return
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| 	as := lo.TernaryF(action == model.ACTION_DELETE,
 | |
| 		func() model.Map[int, model.Slice[int]] { return old.Authorization },
 | |
| 		func() model.Map[int, model.Slice[int]] { return new.Authorization })
 | |
| 	for k, v := range as {
 | |
| 		accountId := k
 | |
| 		curRids := lo.Uniq(v)
 | |
| 		eg.Go(func() (err error) {
 | |
| 			resourceId := 0
 | |
| 			if err = mysql.DB.
 | |
| 				Model(&model.Authorization{}).
 | |
| 				Select("resource_id").
 | |
| 				Where("asset_id = ? AND account_id = ?", assetId, accountId).
 | |
| 				First(&resourceId).
 | |
| 				Error; err != nil {
 | |
| 				notFount := errors.Is(err, gorm.ErrRecordNotFound)
 | |
| 				if !notFount || (notFount && action == model.ACTION_DELETE) {
 | |
| 					return
 | |
| 				}
 | |
| 				if resourceId, err = acl.CreateGrantAcl(ctx, currentUser, conf.GetResourceTypeName(conf.RESOURCE_AUTHORIZATION),
 | |
| 					fmt.Sprintf("%d-%d", assetId, accountId)); err != nil {
 | |
| 					return
 | |
| 				}
 | |
| 				mtx.Lock()
 | |
| 				if err = tx.Create(&model.Authorization{AssetId: assetId, AccountId: accountId, ResourceId: resourceId,
 | |
| 					CreatorId: currentUser.GetUid(), UpdaterId: currentUser.GetUid()}).Error; err != nil {
 | |
| 					return
 | |
| 				}
 | |
| 				mtx.Unlock()
 | |
| 			}
 | |
| 			switch action {
 | |
| 			case model.ACTION_CREATE:
 | |
| 				err = acl.BatchGrantRoleResource(ctx, currentUser.GetUid(), curRids, resourceId, []string{acl.READ})
 | |
| 			case model.ACTION_DELETE:
 | |
| 				err = acl.DeleteResource(ctx, currentUser.GetUid(), resourceId)
 | |
| 			case model.ACTION_UPDATE:
 | |
| 				var res map[string]*acl.ResourcePermissionsRespItem
 | |
| 				res, err = acl.GetResourcePermissions(ctx, resourceId)
 | |
| 				if err != nil {
 | |
| 					return
 | |
| 				}
 | |
| 				perms := make([]*acl.Perm, 0)
 | |
| 				for _, v := range res {
 | |
| 					perms = append(perms, v.Perms...)
 | |
| 				}
 | |
| 				preRids := lo.Map(lo.Filter(perms, func(p *acl.Perm, _ int) bool { return p.Name == acl.READ }), func(p *acl.Perm, _ int) int { return p.Rid })
 | |
| 				revokeRids := lo.Without(preRids, curRids...)
 | |
| 				if len(revokeRids) > 0 {
 | |
| 					if err = acl.BatchRevokeRoleResource(ctx, currentUser.GetUid(), revokeRids, resourceId, []string{acl.READ}); err != nil {
 | |
| 						return
 | |
| 					}
 | |
| 				}
 | |
| 				grantRids := lo.Without(curRids, preRids...)
 | |
| 				if len(grantRids) > 0 {
 | |
| 					err = acl.BatchGrantRoleResource(ctx, currentUser.GetUid(), grantRids, resourceId, []string{acl.READ})
 | |
| 				}
 | |
| 				return
 | |
| 			}
 | |
| 			return
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	err = eg.Wait()
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func sameAuthorization(old, new model.Map[int, model.Slice[int]]) bool {
 | |
| 	if len(old) != len(new) {
 | |
| 		return false
 | |
| 	}
 | |
| 	ks := lo.Uniq(append(lo.Keys(old), lo.Keys(new)...))
 | |
| 	for _, k := range ks {
 | |
| 		if len(old[k]) != len(new[k]) {
 | |
| 			return false
 | |
| 		}
 | |
| 		o, n := make([]int, 0, len(old[k])), make([]int, 0, len(new[k]))
 | |
| 		copy(o, old[k])
 | |
| 		copy(n, new[k])
 | |
| 		sort.Ints(o)
 | |
| 		sort.Ints(n)
 | |
| 		if !reflect.DeepEqual(o, n) {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func GetAutorizationResourceIds(ctx *gin.Context) (resourceIds []int, err error) {
 | |
| 	currentUser, _ := acl.GetSessionFromCtx(ctx)
 | |
| 	var rs []*acl.Resource
 | |
| 	rs, err = acl.GetRoleResources(ctx, currentUser.Acl.Rid, conf.RESOURCE_AUTHORIZATION)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	resourceIds = lo.Map(rs, func(r *acl.Resource, _ int) int { return r.ResourceId })
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func HasAuthorization(ctx *gin.Context) (ok bool) {
 | |
| 	currentUser, _ := acl.GetSessionFromCtx(ctx)
 | |
| 	rs, err := acl.GetRoleResources(ctx, currentUser.Acl.Rid, conf.RESOURCE_AUTHORIZATION)
 | |
| 	if err != nil {
 | |
| 		logger.L().Error("check authorization failed", zap.Error(err))
 | |
| 		return
 | |
| 	}
 | |
| 	k := fmt.Sprintf("%d-%d", ctx.Param("asset_id"), ctx.Param("account_id"))
 | |
| 	_, ok = lo.Find(rs, func(r *acl.Resource) bool { return k == r.Name })
 | |
| 	return
 | |
| }
 | 
