package store import ( "encoding/json" "errors" "git.ipao.vip/rogee/go-sip/internal/ai" "git.ipao.vip/rogee/go-sip/internal/contract" ) // LoadAuthorizedAI never treats a cached configuration as permission to run. // The latest received reply governs admission; rejection, revocation and expiry // cannot fall back to an earlier successful reply. func (s *Store) LoadAuthorizedAI(tenantID, tenantKey, version string) (ai.Snapshot, []byte, error) { snapshot, err := s.LoadAIConfig(tenantID, tenantKey, version) if err != nil { return ai.Snapshot{}, nil, err } var raw []byte if err := s.db.QueryRow(`SELECT r.response FROM ai_mq_requests r JOIN outbox o ON o.event_id=r.message_id WHERE r.tenant_id=? AND r.tenant_key=? AND r.agent_version_id=? AND r.response IS NOT NULL ORDER BY o.id DESC LIMIT 1`, tenantID, tenantKey, version).Scan(&raw); err != nil { return ai.Snapshot{}, nil, err } response, err := contract.DecodeService(raw) if err != nil { return ai.Snapshot{}, nil, err } if response.Status != "ok" { return ai.Snapshot{}, nil, errors.New("latest AI configuration authorization request was rejected") } var payload struct { Authorization json.RawMessage `json:"authorization"` } if err := json.Unmarshal(response.Payload, &payload); err != nil { return ai.Snapshot{}, nil, err } authorization, err := ai.ValidateAuthorization(payload.Authorization, snapshot, tenantID, tenantKey, s.now()) if err != nil { return ai.Snapshot{}, nil, err } // Revocation is a permanent fact for this grant identity, even if a later // correlated request returns an older non-revoked representation. rows, err := s.db.Query(`SELECT json_extract(response,'$.payload.authorization') FROM ai_mq_requests WHERE tenant_id=? AND tenant_key=? AND agent_version_id=? AND json_extract(response,'$.payload.authorization.authorization_id')=? AND json_extract(response,'$.payload.authorization.revoked')=1`, tenantID, tenantKey, version, authorization.AuthorizationID) if err != nil { return ai.Snapshot{}, nil, err } defer rows.Close() for rows.Next() { var revokedRaw []byte if err := rows.Scan(&revokedRaw); err != nil { return ai.Snapshot{}, nil, err } if _, err := ai.DecodeBoundAuthorization(revokedRaw, snapshot, tenantID, tenantKey); err != nil { return ai.Snapshot{}, nil, err } return ai.Snapshot{}, nil, errors.New("AI authorization has a persisted revocation") } if err := rows.Err(); err != nil { return ai.Snapshot{}, nil, err } return snapshot, append([]byte(nil), payload.Authorization...), nil }