cli/auth: address review nits on entire-api cell routing · Entire
cli/auth: address review nits on entire-api cell routing
3b9cf7a→main·
Soph·1w ago·2 files·+32 added/-12 removed
Follow-ups from the 715 review:
- Case-fold the home_jurisdiction claim from the login JWT before the strict [a-z0-9-] label check. The repo-target path already lowercases; the JWT path did not, so an uppercase claim would hard-fail instead of routing.
- List the cluster catalog in the home-jurisdiction fallback against the discovered login core (selected.CoreURL) rather than the templated jurisdiction core — the login JWT is signed by the former, so in a multi-core setup the templated core could reject it. The exchange core is unchanged.
- Add the ErrNoCellForJurisdiction sentinel and wrap the "no cell / no apiUrl" errors with it, so callers with a data-API fallback (activity, recap next) can degrade instead of failing when a region has no cell.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Sessions
e94d8b46f42cView transcript
Changes
2
cmd/entire/cli/auth
Mcell_data_api.go+26/-12
- Mcell_data_api_test.go+6
115 unmodified lines
116
117
118
119
119
120
121
122
123
124
125
126
183 unmodified lines
310
311
312
309
310
311
312
313
313
314
315
316
317
318
315
319
320
321
322
1 unmodified line
324
325
326
327
328
329
330
4 unmodified lines
335
336
337
333
334
338
339
340
341
342
343
344
1 unmodified line
346
347
348
342
349
350
351
352
153 unmodified lines
506
507
508
509
510
511
512
513
514
515
516
517
518
40 unmodified lines
559
560
561
548
562
563
550
564
565
566
567
115 unmodified lines
return nil, err
}
cellBaseURL, err := resolveTargetCellBaseURL(ctx, target, subject.dataOrigin, jurisdiction, coreURL, subject.loginJWT, subject.httpClient)
// The home-jurisdiction fallback lists the cluster catalog with loginJWT,
// which is signed by the discovered login core — so list there, not at the
// templated jurisdiction core (coreURL), which in a multi-core setup could
// differ and reject the token. coreURL still governs the token exchange below.
cellBaseURL, err := resolveTargetCellBaseURL(ctx, target, subject.dataOrigin, jurisdiction, subject.discoveredCore, subject.loginJWT, subject.httpClient)
if err != nil {
return nil, err
}
}
// resolveJurisdiction picks the jurisdiction to mint for: the explicit override
// (normalised to a lowercase DNS label) when non-empty, otherwise the subject
// token's home_jurisdiction claim. The result is validated as a DNS label before
// it is templated into URLs. Normalising the override means `--jurisdiction US`,
// `" us "` and `us` all resolve to `us`; the home-fallback claim is already a
// lowercase label so it is left as-is (an off-spec claim still fails the check).
// when non-empty, otherwise the subject token's home_jurisdiction claim. Either
// source is normalised to a lowercase DNS label and validated before it is
// templated into URLs — `--jurisdiction US`, `" us "` and `us` all resolve to
// `us`, and an uppercase home_jurisdiction claim routes instead of hard-failing
// the strict \[a-z0-9-\] label check.
func resolveJurisdiction(override, loginJWT string) (string, error) {
jurisdiction := strings.ToLower(strings.TrimSpace(override))
jurisdiction := strings.TrimSpace(override)
if jurisdiction == "" {
var err error
jurisdiction, err = HomeJurisdictionFromLoginJWT(loginJWT)
return "", err
}
}
jurisdiction = strings.ToLower(strings.TrimSpace(jurisdiction))
if jurisdiction == "" {
return "", errors.New("login token has no home_jurisdiction claim; cannot route to entire-api cell")
}
}
// resolveTargetCellBaseURL decides which cell origin to dial. See
// NewEntireAPICellClient's precedence doc.
func resolveTargetCellBaseURL(ctx context.Context, target *CellTarget, dataOrigin, jurisdiction, coreURL, loginJWT string, httpClient *http.Client) (string, error) {
// NewEntireAPICellClient's precedence doc. listCoreURL is the core the
// home-jurisdiction fallback lists the cluster catalog against; it must be a
// core that accepts loginJWT (i.e. the discovered login core).
func resolveTargetCellBaseURL(ctx context.Context, target *CellTarget, dataOrigin, jurisdiction, listCoreURL, loginJWT string, httpClient *http.Client) (string, error) {
if target != nil && strings.TrimSpace(target.BaseURL) != "" {
return strings.TrimRight(target.BaseURL, "/"), nil
}
}
// Already a cell URL, or a loopback local-dev host: keep it verbatim.
return strings.TrimRight(dataOrigin, "/"), nil
}
return resolveCellAPIBaseURL(ctx, coreURL, loginJWT, jurisdiction, httpClient)
return resolveCellAPIBaseURL(ctx, listCoreURL, loginJWT, jurisdiction, httpClient)
}
// isBFFOrigin reports whether origin is a BFF / apex host that fronts multiple
APIURL string `json:"apiUrl"`
}
// ErrNoCellForJurisdiction signals that the caller's home jurisdiction has no
// entire-api cell in the cluster catalog (or its row carries no apiUrl). It is
// not fatal: callers that also have a data-API path (e.g. activity/recap) treat
// it as "entire-api isn't serving this region yet" and fall back rather than
// failing the command. errors.Is unwraps it from the contextual message.
var ErrNoCellForJurisdiction = errors.New("no entire-api cell configured for jurisdiction")
// resolveCellAPIBaseURL is the home-jurisdiction fallback cell resolver: it
// lists the caller's clusters and picks the apiUrl for `jurisdiction` (default
// cluster first). It hand-parses GET /api/v1/clusters rather than reusing the
// 40 unmodified lines
if sawJurisdiction {
// A cluster row exists for the jurisdiction but carries no apiUrl —
// a schema/deploy problem, distinct from "no cell for jurisdiction".
return "", fmt.Errorf("cluster for jurisdiction %q advertises no apiUrl (entire-api cell not configured?)", jurisdiction)
return "", fmt.Errorf("%w %q: cluster advertises no apiUrl (entire-api cell not configured?)", ErrNoCellForJurisdiction, jurisdiction)
}
return "", fmt.Errorf("no entire-api cell configured for jurisdiction %q", jurisdiction)
return "", fmt.Errorf("%w %q", ErrNoCellForJurisdiction, jurisdiction)
}
chosen := matches[0]
for _, row := range matches {