mirror of
				https://github.com/veops/oneterm.git
				synced 2025-11-01 03:12:39 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			136 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package service
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/gin-gonic/gin"
 | |
| 	"github.com/samber/lo"
 | |
| 	"github.com/veops/oneterm/internal/acl"
 | |
| 	"github.com/veops/oneterm/internal/model"
 | |
| 	"github.com/veops/oneterm/internal/repository"
 | |
| 	"github.com/veops/oneterm/internal/schedule"
 | |
| 	"gorm.io/gorm"
 | |
| )
 | |
| 
 | |
| // AssetService handles asset business logic
 | |
| type AssetService struct {
 | |
| 	repo repository.AssetRepository
 | |
| }
 | |
| 
 | |
| // NewAssetService creates a new asset service
 | |
| func NewAssetService() *AssetService {
 | |
| 	return &AssetService{
 | |
| 		repo: repository.NewAssetRepository(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // GetById retrieves an asset by its ID
 | |
| func (s *AssetService) GetById(ctx context.Context, id int) (*model.Asset, error) {
 | |
| 	return s.repo.GetById(ctx, id)
 | |
| }
 | |
| 
 | |
| // PreprocessAssetData preprocesses asset data before saving
 | |
| func (s *AssetService) PreprocessAssetData(asset *model.Asset) {
 | |
| 	asset.Ip = strings.TrimSpace(asset.Ip)
 | |
| 	asset.Protocols = lo.Map(asset.Protocols, func(s string, _ int) string { return strings.TrimSpace(s) })
 | |
| 	if asset.Authorization == nil {
 | |
| 		asset.Authorization = make(model.AuthorizationMap)
 | |
| 	}
 | |
| 
 | |
| 	// Handle backward compatibility: convert old format to new format
 | |
| 	// This handles cases where frontend still sends old format: Map[int, Slice[int]]
 | |
| 	s.ensureAuthorizationFormat(asset)
 | |
| }
 | |
| 
 | |
| // ensureAuthorizationFormat ensures asset.Authorization is in the correct V2 format
 | |
| // Handles backward compatibility with old V1 format
 | |
| func (s *AssetService) ensureAuthorizationFormat(asset *model.Asset) {
 | |
| 	// Check if we need to convert from old format to new format
 | |
| 	// This is needed for backward compatibility
 | |
| 	for accountId, authData := range asset.Authorization {
 | |
| 		// If permissions is nil, set default permissions (connect only for V1 compatibility)
 | |
| 		if authData.Permissions == nil {
 | |
| 			authData.Permissions = &model.AuthPermissions{
 | |
| 				Connect:      true,  // Default: allow connect (V1 behavior)
 | |
| 				FileUpload:   false, // Default: deny file upload
 | |
| 				FileDownload: false, // Default: deny file download
 | |
| 				Copy:         false, // Default: deny copy
 | |
| 				Paste:        false, // Default: deny paste
 | |
| 				Share:        false, // Default: deny share
 | |
| 			}
 | |
| 			asset.Authorization[accountId] = authData
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // AttachNodeChain attaches node chain to assets
 | |
| func (s *AssetService) AttachNodeChain(ctx context.Context, assets []*model.Asset) error {
 | |
| 	return s.repo.AttachNodeChain(ctx, assets)
 | |
| }
 | |
| 
 | |
| // BuildQuery constructs asset query with basic filters
 | |
| func (s *AssetService) BuildQuery(ctx *gin.Context) (*gorm.DB, error) {
 | |
| 	return s.repo.BuildQuery(ctx)
 | |
| }
 | |
| 
 | |
| // FilterByParentId filters assets by parent ID
 | |
| func (s *AssetService) FilterByParentId(db *gorm.DB, parentId int) (*gorm.DB, error) {
 | |
| 	return s.repo.FilterByParentId(db, parentId)
 | |
| }
 | |
| 
 | |
| // GetAssetIdsByAuthorization gets asset IDs by authorization using efficient V2 method
 | |
| func (s *AssetService) GetAssetIdsByAuthorization(ctx *gin.Context) ([]int, []int, []int, error) {
 | |
| 	// Use efficient V2 method: get authorized resource IDs from ACL, then find V2 rules
 | |
| 	authV2Service := NewAuthorizationV2Service()
 | |
| 	return authV2Service.GetAuthorizationScopeByACL(ctx)
 | |
| }
 | |
| 
 | |
| // GetIdsByAuthorizationIds extracts node IDs, asset IDs, and account IDs from authorization IDs
 | |
| func (s *AssetService) GetIdsByAuthorizationIds(ctx *gin.Context, authorizationIds []*model.AuthorizationIds) ([]int, []int, []int) {
 | |
| 	return s.repo.GetIdsByAuthorizationIds(ctx, authorizationIds)
 | |
| }
 | |
| 
 | |
| // GetAssetIdsByNodeAccount gets asset IDs by node IDs and account IDs
 | |
| func (s *AssetService) GetAssetIdsByNodeAccount(ctx context.Context, nodeIds, accountIds []int) ([]int, error) {
 | |
| 	return s.repo.GetAssetIdsByNodeAccount(ctx, nodeIds, accountIds)
 | |
| }
 | |
| 
 | |
| // UpdateConnectables updates asset connectability status
 | |
| func (s *AssetService) UpdateConnectables(ids ...int) error {
 | |
| 	return schedule.UpdateAssetConnectables(ids...)
 | |
| }
 | |
| 
 | |
| // BuildQueryWithAuthorization constructs asset query with integrated V2 authorization filter
 | |
| func (s *AssetService) BuildQueryWithAuthorization(ctx *gin.Context) (*gorm.DB, error) {
 | |
| 	// Start with base query
 | |
| 	db, err := s.repo.BuildQuery(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	currentUser, _ := acl.GetSessionFromCtx(ctx)
 | |
| 
 | |
| 	// Administrators have access to all assets
 | |
| 	if acl.IsAdmin(currentUser) {
 | |
| 		return db, nil
 | |
| 	}
 | |
| 
 | |
| 	// Apply V2 authorization filter directly at database level
 | |
| 	authV2Service := NewAuthorizationV2Service()
 | |
| 	_, assetIds, _, err := authV2Service.GetAuthorizationScopeByACL(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Filter by authorized asset IDs at database level (much more efficient)
 | |
| 	if len(assetIds) == 0 {
 | |
| 		// No access to any assets
 | |
| 		db = db.Where("1 = 0") // Returns empty result set efficiently
 | |
| 	} else {
 | |
| 		db = db.Where("id IN ?", assetIds)
 | |
| 	}
 | |
| 
 | |
| 	return db, nil
 | |
| }
 | 
