Files
ai_site/platform/internal/agentcap/modpath.go
2026-07-31 10:31:17 +08:00

115 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package agentcap
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"strings"
"time"
)
const ModulePathPrefix = "ajzm1_"
// ModulePathClaim 写入加密路径 token。根据用户/智能体 ID 生成路径,公开侧可用平台密钥解开。
type ModulePathClaim struct {
TenantID int64 `json:"t"`
OwnerID int64 `json:"o"` // user_id 或 agent_id宇恒侧用户
Slug string `json:"s"`
IssuedAt int64 `json:"iat"`
}
func modulePathKey(masterSecret string) []byte {
sum := sha256.Sum256([]byte("aijianzhan-module-path-v1|" + masterSecret))
return sum[:]
}
// SealModulePath 根据用户 ID及租户、模块 slug加密返回
// - token单段密文可作 URL 段)
// - filePath逻辑文件路径 m/{token}(不含明文用户 id / slug
func SealModulePath(masterSecret string, tenantID, ownerID int64, slug string) (token, filePath string, err error) {
slug = strings.TrimSpace(slug)
if slug == "" {
return "", "", fmt.Errorf("slug required")
}
if ownerID <= 0 {
return "", "", fmt.Errorf("owner id required")
}
if tenantID <= 0 {
return "", "", fmt.Errorf("tenant id required")
}
claim := ModulePathClaim{
TenantID: tenantID,
OwnerID: ownerID,
Slug: slug,
IssuedAt: time.Now().UTC().Unix(),
}
raw, err := json.Marshal(claim)
if err != nil {
return "", "", err
}
key := modulePathKey(masterSecret)
block, err := aes.NewCipher(key)
if err != nil {
return "", "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", "", err
}
ciphertext := gcm.Seal(nil, nonce, raw, nil)
packed := append(nonce, ciphertext...)
token = ModulePathPrefix + base64.RawURLEncoding.EncodeToString(packed)
filePath = "m/" + token
return token, filePath, nil
}
// OpenModulePath 解密路径 token得到租户、用户、模块 slug。
func OpenModulePath(masterSecret, token string) (*ModulePathClaim, error) {
token = strings.TrimSpace(token)
token = strings.TrimPrefix(token, "/")
if strings.HasPrefix(token, "m/") {
token = strings.TrimPrefix(token, "m/")
}
if !strings.HasPrefix(token, ModulePathPrefix) {
return nil, fmt.Errorf("invalid module path token")
}
packed, err := base64.RawURLEncoding.DecodeString(strings.TrimPrefix(token, ModulePathPrefix))
if err != nil {
return nil, fmt.Errorf("invalid module path token encoding")
}
key := modulePathKey(masterSecret)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
ns := gcm.NonceSize()
if len(packed) < ns+1 {
return nil, fmt.Errorf("invalid module path token length")
}
plain, err := gcm.Open(nil, packed[:ns], packed[ns:], nil)
if err != nil {
return nil, fmt.Errorf("module path decrypt failed")
}
var claim ModulePathClaim
if err := json.Unmarshal(plain, &claim); err != nil {
return nil, err
}
if claim.TenantID <= 0 || claim.OwnerID <= 0 || claim.Slug == "" {
return nil, fmt.Errorf("module path claim incomplete")
}
return &claim, nil
}