refactor(access): 统一分层白名单权限语义

取消跨主体专属占用,按租户、用户组、用户、当前 API Key 和 scope 分层求交,并在任务落库前统一校验候选。\n\n增加旧 allow 规则归档清理迁移、脱敏审计工具和回滚运行手册,补齐主体隔离、deny 优先及列表与运行时一致性测试。
This commit is contained in:
2026-08-03 15:43:49 +08:00
parent c9393af43a
commit 7376d6fab6
20 changed files with 1490 additions and 513 deletions
+32 -15
View File
@@ -25,21 +25,7 @@ type EstimateResult struct {
}
func (s *Service) Estimate(ctx context.Context, kind string, model string, body map[string]any, user *auth.User) (EstimateResult, error) {
body = normalizeRequest(kind, body)
modelType := modelTypeFromKind(kind, body)
candidates, err := s.store.ListModelCandidates(ctx, model, modelType, user)
if err != nil {
return EstimateResult{}, err
}
candidates, err = filterCandidatesByRequestedPlatform(candidates, body)
if err != nil {
return EstimateResult{}, err
}
candidates, _, err = filterRuntimeCandidatesByRequest(kind, model, modelType, body, candidates)
if err != nil {
return EstimateResult{}, err
}
candidates, _, err = filterRuntimeCandidatesByOutputTokens(kind, model, modelType, body, candidates)
candidates, body, err := s.candidatesForRequest(ctx, kind, model, body, user)
if err != nil {
return EstimateResult{}, err
}
@@ -58,6 +44,37 @@ func (s *Service) Estimate(ctx context.Context, kind string, model string, body
return buildEstimateResult(estimates, pricingRequestFingerprint(kind, model, body))
}
// ValidateModelAccess resolves the same permission-filtered candidates used by
// execution before a task row is created. This keeps authorization failures
// out of task history while execution still re-resolves candidates to avoid
// using stale routing or capacity state.
func (s *Service) ValidateModelAccess(ctx context.Context, kind string, model string, body map[string]any, user *auth.User) error {
_, _, err := s.candidatesForRequest(ctx, kind, model, body, user)
return err
}
func (s *Service) candidatesForRequest(ctx context.Context, kind string, model string, body map[string]any, user *auth.User) ([]store.RuntimeModelCandidate, map[string]any, error) {
body = normalizeRequest(kind, body)
modelType := modelTypeFromKind(kind, body)
candidates, err := s.store.ListModelCandidates(ctx, model, modelType, user)
if err != nil {
return nil, body, err
}
candidates, err = filterCandidatesByRequestedPlatform(candidates, body)
if err != nil {
return nil, body, err
}
candidates, _, err = filterRuntimeCandidatesByRequest(kind, model, modelType, body, candidates)
if err != nil {
return nil, body, err
}
candidates, _, err = filterRuntimeCandidatesByOutputTokens(kind, model, modelType, body, candidates)
if err != nil {
return nil, body, err
}
return candidates, body, nil
}
func (s *Service) estimatedBillings(ctx context.Context, user *auth.User, kind string, body map[string]any, candidate store.RuntimeModelCandidate) []any {
usage := clients.Usage{InputTokens: estimateRequestTokens(body)}
if isTextGenerationKind(kind) {