History

fix(meta): require OAuth storage for DCA tokens and reject them in config meta-api-key

Kenny committed 2 months ago

Commit 1144ae71 parent d09042a5 files · +38 −29Browse files

Unified · Split

5 files
  1. internal/api/handlers/management/api_tools.go +14 −16
  2. internal/config/config_meta_test.go +3 −3
  3. internal/config/config_normalization.go +2 −2
  4. internal/runtime/executor/meta_executor.go +8 −8
  5. internal/runtime/executor/meta_executor_test.go +11 −0
internal/api/handlers/management/api_tools.go+14−16
@@ -384,22 +384,20 @@ func (h *Handler) resolveMetaToken(ctx context.Context, auth *coreauth.Auth, req · 383 unmodified lines
384 }384 }
385 385
386 var dcaToken string386 var dcaToken string
387 if auth.Metadata != nil {387 if !coreauth.IsConfigAPIKeyAuth(auth) {
388 if d, ok := auth.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" {388 if auth.Metadata != nil {
389 dcaToken = strings.TrimSpace(d)389 if d, ok := auth.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" {
390 } else if t, ok := auth.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") {390 dcaToken = strings.TrimSpace(d)
391 dcaToken = strings.TrimSpace(t)391 } else if t, ok := auth.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") {
392 } else if k, ok := auth.Metadata["api_key"].(string); ok && strings.HasPrefix(strings.TrimSpace(k), "dca:") {392 dcaToken = strings.TrimSpace(t)
393 dcaToken = strings.TrimSpace(k)393 }
394 }394 }
395 }395 if dcaToken == "" && auth.Attributes != nil {
396 if dcaToken == "" && auth.Attributes != nil {396 if d := strings.TrimSpace(auth.Attributes["dca_token"]); d != "" {
397 if d := strings.TrimSpace(auth.Attributes["dca_token"]); d != "" {397 dcaToken = d
398 dcaToken = d398 } else if t := strings.TrimSpace(auth.Attributes["access_token"]); strings.HasPrefix(t, "dca:") {
399 } else if t := strings.TrimSpace(auth.Attributes["access_token"]); strings.HasPrefix(t, "dca:") {399 dcaToken = t
400 dcaToken = t400 }
401 } else if k := strings.TrimSpace(auth.Attributes["api_key"]); strings.HasPrefix(k, "dca:") {
402 dcaToken = k
403 }401 }
404 }402 }
405 403
internal/config/config_meta_test.go+3−3
@@ -9,13 +9,13 @@ func TestMetaConfigDropsUnusableKeys(t *testing.T) { · 8 unmodified lines
9 - base-url: "https://api.meta.ai/v1"9 - base-url: "https://api.meta.ai/v1"
10 - headers: {X-Trace: placeholder}10 - headers: {X-Trace: placeholder}
11 - api-key: " LLM|valid "11 - api-key: " LLM|valid "
12 - api-key: "dca:recoverable"12 - api-key: "dca:requires-oauth-storage"
13`))13`))
14 if err != nil {14 if err != nil {
15 t.Fatal(err)15 t.Fatal(err)
16 }16 }
17 if len(cfg.MetaKey) != 2 {17 if len(cfg.MetaKey) != 1 {
18 t.Fatalf("got %d keys, want only the two usable credentials", len(cfg.MetaKey))18 t.Fatalf("got %d keys, want only the 1 valid API key (DCA tokens require OAuth storage)", len(cfg.MetaKey))
19 }19 }
20 if cfg.MetaKey[0].APIKey != "LLM|valid" || cfg.MetaKey[0].BaseURL != "https://api.meta.ai/v1" {20 if cfg.MetaKey[0].APIKey != "LLM|valid" || cfg.MetaKey[0].BaseURL != "https://api.meta.ai/v1" {
21 t.Fatalf("valid key not normalized: %#v", cfg.MetaKey[0])21 t.Fatalf("valid key not normalized: %#v", cfg.MetaKey[0])
internal/config/config_normalization.go+2−2
@@ -210,8 +210,8 @@ func sanitizeMetaKeyEntries(entries []MetaKey) []MetaKey { · 209 unmodified lines
210 for i := range entries {210 for i := range entries {
211 e := entries[i]211 e := entries[i]
212 e.APIKey = strings.TrimSpace(e.APIKey)212 e.APIKey = strings.TrimSpace(e.APIKey)
213 // The native Meta executor requires an API key or a DCA token in this field.213 // meta-api-key requires a valid API key. DCA tokens require OAuth storage (auths/*.json).
214 if e.APIKey == "" {214 if e.APIKey == "" || strings.HasPrefix(e.APIKey, "dca:") {
215 continue215 continue
216 }216 }
217 e.Prefix = normalizeModelPrefix(e.Prefix)217 e.Prefix = normalizeModelPrefix(e.Prefix)
internal/runtime/executor/meta_executor.go+8−8
@@ -254,7 +254,7 @@ func (e *MetaExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*c · 253 unmodified lines
254 254
255// ShouldPrepareRequestAuth reports true when a Meta auth has a DCA token but no usable API key.255// ShouldPrepareRequestAuth reports true when a Meta auth has a DCA token but no usable API key.
256func (e *MetaExecutor) ShouldPrepareRequestAuth(auth *cliproxyauth.Auth) bool {256func (e *MetaExecutor) ShouldPrepareRequestAuth(auth *cliproxyauth.Auth) bool {
257 if auth == nil {257 if auth == nil || cliproxyauth.IsConfigAPIKeyAuth(auth) {
258 return false258 return false
259 }259 }
260 _, token := metaCreds(auth)260 _, token := metaCreds(auth)
@@ -291,6 +291,12 @@ func (e *MetaExecutor) ensureAuth(ctx context.Context, auth *cliproxyauth.Auth) · 30 unmodified lines
291 }291 }
292 292
293 if token == "" {293 if token == "" {
294 if cliproxyauth.IsConfigAPIKeyAuth(auth) {
295 return nil, statusErr{
296 code: http.StatusUnauthorized,
297 msg: "meta executor: meta-api-key requires a valid API key (DCA tokens require OAuth storage)",
298 }
299 }
294 return nil, statusErr{300 return nil, statusErr{
295 code: http.StatusUnauthorized,301 code: http.StatusUnauthorized,
296 msg: "meta executor: missing API key or access token",302 msg: "meta executor: missing API key or access token",
@@ -326,7 +332,7 @@ func (e *MetaExecutor) enrichAuth(auth *cliproxyauth.Auth) *cliproxyauth.Auth { · 29 unmodified lines
326}332}
327 333
328func extractDCAToken(a *cliproxyauth.Auth) string {334func extractDCAToken(a *cliproxyauth.Auth) string {
329 if a == nil {335 if a == nil || cliproxyauth.IsConfigAPIKeyAuth(a) {
330 return ""336 return ""
331 }337 }
332 if a.Attributes != nil {338 if a.Attributes != nil {
@@ -336,9 +342,6 @@ func extractDCAToken(a *cliproxyauth.Auth) string { · 3 unmodified lines
336 if t := strings.TrimSpace(a.Attributes["access_token"]); strings.HasPrefix(t, "dca:") {342 if t := strings.TrimSpace(a.Attributes["access_token"]); strings.HasPrefix(t, "dca:") {
337 return t343 return t
338 }344 }
339 if k := strings.TrimSpace(a.Attributes["api_key"]); strings.HasPrefix(k, "dca:") {
340 return k
341 }
342 }345 }
343 if a.Metadata != nil {346 if a.Metadata != nil {
344 if d, ok := a.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" {347 if d, ok := a.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" {
@@ -347,9 +350,6 @@ func extractDCAToken(a *cliproxyauth.Auth) string { · 2 unmodified lines
347 if t, ok := a.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") {350 if t, ok := a.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") {
348 return strings.TrimSpace(t)351 return strings.TrimSpace(t)
349 }352 }
350 if k, ok := a.Metadata["api_key"].(string); ok && strings.HasPrefix(strings.TrimSpace(k), "dca:") {
351 return strings.TrimSpace(k)
352 }
353 }353 }
354 if a.Storage != nil {354 if a.Storage != nil {
355 if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil {355 if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil {
internal/runtime/executor/meta_executor_test.go+11−0
@@ -572,6 +572,17 @@ func TestMetaExecutor_RequestAuthPreparer(t *testing.T) { · 571 unmodified lines
572 t.Errorf("ShouldPrepareRequestAuth should be false when dca_token is missing")572 t.Errorf("ShouldPrepareRequestAuth should be false when dca_token is missing")
573 }573 }
574 574
575 authConfigDCA := &cliproxyauth.Auth{
576 Provider: "meta",
577 Attributes: map[string]string{
578 "api_key": "dca:invalid-for-config",
579 cliproxyauth.AttributeSource: "config:meta[0]",
580 },
581 }
582 if exec.ShouldPrepareRequestAuth(authConfigDCA) {
583 t.Errorf("ShouldPrepareRequestAuth should be false for config API key auths")
584 }
585
575 authDCAOnly := &cliproxyauth.Auth{586 authDCAOnly := &cliproxyauth.Auth{
576 ID: "meta-prep-test",587 ID: "meta-prep-test",
577 Provider: "meta",588 Provider: "meta",

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t