[CFX-7585] Stop reporting server errors and bad endpoint schemes as credential problems - #772
Conversation
|
🎫 Jira: |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 489b510. Configure here.
|
bugbot run |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (3)
cmd/auth/check/cmd.go:148
- This pre-validation bypasses the shared reporter, so a
.envendpoint such asftp://...only emits the generic “Invalid DATAROBOT_ENDPOINT” message and drops the unsupported-scheme reason. That contradicts the PR/docs claim that all three credential sources use the same classification and source-specific scheme message. Let verification fail and route the error throughReportUnjudged, as the block below already does.
if err := auth.ValidateEndpoint(dotenvEndpoint); err != nil {
fmt.Fprintln(w, tui.BaseTextStyle.Render("❌ Invalid DATAROBOT_ENDPOINT in '.env'."))
fmt.Fprint(w, tui.BaseTextStyle.Render("Run "))
fmt.Fprint(w, tui.InfoStyle.Render("dr dotenv update"))
fmt.Fprintln(w, tui.BaseTextStyle.Render(" to fix the configuration."))
cmd/auth/check/cmd.go:71
- When no stored endpoint is configured,
GetAPIKeyreturns a*url.Errorfor the relative/version/request, so this now printsCould not connect to : unsupported protocol scheme ""immediately after the correct “No DataRobot URL configured” message. Guard this classification with the already-computeddatarobotHostso the missing-URL case does not masquerade as a transport failure.
if !auth.ReportUnjudged(w, viperx.GetString(config.DataRobotURL), auth.StoredEndpointName, err) {
internal/auth/auth.go:323
- This duplicates the newly exported
config.HTTPStatusTextimplementation verbatim. Using that helper here and removing the local copy keeps unknown-status formatting defined in one place and prevents the two user-facing paths from drifting.
// httpStatusText renders "HTTP 503 Service Unavailable", dropping the name for
// codes Go does not know (Cloudflare 520-527) so no dangling space is left.
func httpStatusText(code int) string {
if name := http.StatusText(code); name != "" {
return fmt.Sprintf("HTTP %d %s", code, name)
… problems Why: - Every non-200 from the version check collapsed to "invalid token", so a 404, 429, or 5xx told the user to unset a token the server never judged. - Any parseable scheme passed the endpoint checks, so ftp://host reported as "Could not connect", blaming the network for an endpoint the CLI cannot use. Changes: - VerifyToken returns config.HTTPStatusError carrying the status code. - ReportEnvCredentialsError blames the token only on 401/403; any other status reads "<host> answered HTTP nnn, so the CLI could not verify your credentials". - ValidateEndpoint requires http or https. SchemeHostOnly is untouched, so set-url and export keep accepting what they always did. - Tests pin the status preservation, its consumers (GetAPIKey, the dotenv leg of dr auth check), and the classifier split.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ef08abb. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
docs/development/authentication.md:32
- This says every 2xx response verifies successfully, but
VerifyTokenaccepts only HTTP 200 and returnsHTTPStatusErrorfor all other statuses. Describe this as a non-200 status so the developer documentation matches the actual contract.
1. **Checks environment credentials first**: A complete `DATAROBOT_ENDPOINT` (or `DATAROBOT_API_ENDPOINT`) and `DATAROBOT_API_TOKEN` pair takes precedence over the config file. If the pair fails verification, the command fails with the reason (timeout, malformed endpoint, unreachable endpoint, a non-2xx status from the instance, or an invalid token; only a 401 or 403 blames the token). It never falls back to the stored profile and never starts the login flow, because that would silently run the command against a different DataRobot instance than the one requested.
ajalon1
left a comment
There was a problem hiding this comment.
👨🏽🚀 Nice add. I'm actually surprised we didn't handle this properly before, since auth is some of the oldest code in the CLI.
LGTM
|
👨🏽🚀 But you still deserve a 🤖 pass. Verdict: approve — one suggestion worth considering for a follow-up. The core change is correct and well-tested: PraiseThe 401/403 vs. everything-else split is the right call, verified end-to-end. Before this PR, a 503 from the instance told the user to The scheme check is placed in 🤖 Classification chain in
|
|
go ahead and ignore the govulncheck, that's a new version of Go available |
… the stdout test The '.env' leg and the login-flow fallback still blame the token for any non-200. One-line comments say so and that the split is a follow-up, per the review's ask to make the scoping decision visible in the code. The stdout restore in TestVerifyDotenvToken_StatusErrorKeepsMessage also runs via t.Cleanup, so a panic inside verifyDotenvToken cannot leave stdout piped.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 54b5b4a. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (3)
internal/auth/auth_test.go:340
- This output test checks only fragments, so duplicated text, wrong line structure, or other unexpected output would go unnoticed. Compare the full two-line status message to verify the new user-facing format.
assert.Contains(t, buf.String(), fmt.Sprintf("answered HTTP %d", status))
assert.Contains(t, buf.String(), "https://app.example.com")
assert.NotContains(t, buf.String(), "unset DATAROBOT_API_TOKEN")
docs/development/authentication.md:32
- The implementation treats every status except 200 as a verification failure, including 2xx responses such as 204. Describing this as only “non-2xx” makes the documented authentication contract disagree with
VerifyToken; use “non-200” here.
1. **Checks environment credentials first**: A complete `DATAROBOT_ENDPOINT` (or `DATAROBOT_API_ENDPOINT`) and `DATAROBOT_API_TOKEN` pair takes precedence over the config file. If the pair fails verification, the command fails with the reason (timeout, malformed endpoint, unreachable endpoint, a non-2xx status from the instance, or an invalid token; only a 401 or 403 blames the token). It never falls back to the stored profile and never starts the login flow, because that would silently run the command against a different DataRobot instance than the one requested.
internal/auth/auth_test.go:326
- These substring assertions do not pin the exact 401/403 wording as intended: extra or malformed output would still pass. Assert the complete rendered message so changes to line breaks or the unset instruction are caught.
This issue also appears on line 338 of the same file.
assert.Contains(t, buf.String(), "DATAROBOT_API_TOKEN environment variable is invalid or expired")
assert.Contains(t, buf.String(), "unset DATAROBOT_API_TOKEN")
Summary
drtold users to throw away a working API token whenever the version check came back as anything other than 200. A 404 from a mistyped endpoint, a 429, or a 503 all printed "DATAROBOT_API_TOKEN is invalid or expired, unset it". Only 401 and 403 still say that; any other status now names what the instance answered. And an endpoint scheme the CLI cannot use (ftp://host) reads as a bad endpoint instead of a network failure. Both were suppressed review findings on #751, which made them visible on every command.Notes for review
The scheme check went into
ValidateEndpoint, not into theSchemeHostOnlyhelper thatset-urlandexportshare, so those keep accepting what they always did.Deliberately out of scope, same defect family, follow-up material:
dr auth check's.envand stored-profile legs still call every failure a stale token or missing key,EnsureAuthenticatedstill starts the login flow when the stored profile's instance answers 5xx, anddr auth set-url ftp://hoststill writes the endpoint unvalidated.Output
Technical Changes
internal/config/auth.go:VerifyTokenreturns*HTTPStatusError{StatusCode}instead of a flatinvalid token.internal/auth/auth.go:ValidateEndpointrequires http or https; the classifier's fallthrough splits on the status.GetAPIKey, the.envleg ofdr auth check), and 401/403 keeping the exact [CFX-7263] Fail on bad env credentials instead of using the stored profile #751 wording.docs/: the two new messages.Note
Medium Risk
Changes shared auth verification and error reporting used on every authenticated command; behavior is narrower (better classification) but mistakes could mis-route users on edge HTTP statuses.
Overview
Fixes misleading “invalid or expired API token” messages when environment credentials fail for reasons other than rejected auth.
VerifyTokennow returns*HTTPStatusErrorwith the real status instead of a generic invalid-token error.ReportEnvCredentialsError(used byEnsureAuthenticatedanddr auth checkfor env vars) only tells users to unsetDATAROBOT_API_TOKENon 401 or 403; for other non-2xx responses it reports that the instance answered HTTP n and points at endpoint/instance health.ValidateEndpointadditionally rejects schemes other than http/https (e.g.ftp://) as a bad endpoint, not a connection failure.Docs and tests cover status preservation,
GetAPIKey, scheme validation, and the new user-facing copy. The.envpath indr auth checkand stored-profile login behavior are unchanged for non-401 failures (called out in code as follow-up).Reviewed by Cursor Bugbot for commit 54b5b4a. Configure here.