credential_shadow.go 1.4 KB

123456789101112131415161718192021222324252627282930313233
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. )
  6. // resolveCredentialAccount 解析影子账号到其母账号,用于凭据/Token 透传。
  7. // - 普通账号(非影子):直接返回自身。
  8. // - 影子账号:通过 repo 取母账号,校验母账号存在且为 OpenAI OAuth 类型,否则返回错误。
  9. // 设计为包级函数(非任何 service 的方法),以便 OpenAIGatewayService / OpenAIQuotaService /
  10. // AccountUsageService 等不同接收者共享同一实现。
  11. func resolveCredentialAccount(ctx context.Context, repo AccountRepository, account *Account) (*Account, error) {
  12. if account == nil || !account.IsShadow() {
  13. return account, nil
  14. }
  15. parent, err := repo.GetByID(ctx, *account.ParentAccountID)
  16. if err != nil {
  17. return nil, fmt.Errorf("resolve spark shadow parent %d: %w", *account.ParentAccountID, err)
  18. }
  19. if parent == nil {
  20. return nil, fmt.Errorf("spark shadow parent %d not found", *account.ParentAccountID)
  21. }
  22. // 防御:创建路径已禁二级影子(G6),此处再挡一层——畸形数据/手工 DB 写出的影子→影子链
  23. // 会让凭据解析停在无凭据的一级影子(只解一层),fail-closed 比静默返回坏母更安全(外审第6轮)。
  24. if parent.IsShadow() {
  25. return nil, fmt.Errorf("spark shadow parent %d is itself a shadow", parent.ID)
  26. }
  27. if !parent.IsOpenAIOAuth() {
  28. return nil, fmt.Errorf("spark shadow parent %d is not OpenAI OAuth", parent.ID)
  29. }
  30. return parent, nil
  31. }