Skip to content

Commit 4518131

Browse files
committed
Auto merge of #12644 - arlosi:cred-no-env, r=weihanglo
fix: don't print _TOKEN suggestion when not applicable ### What does this PR try to resolve? When a registry token cannot be found, or a token is invalid, cargo displays an error recommending either `cargo login` or the appropriate environment variable. For example: ``` error: no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN ``` With `-Z credential-process`, if `cargo:token` is not in `registry.global-credential-providers` or `registries.<NAME>.credential-provider` the suggested environment variable will not work. Fixes #12642 by not including the `_TOKEN` environment variable if `cargo:token` is not enabled as a credential provider. ### How should we test and review this PR? Two tests `not_found` and `all_not_found` cover this case by having a registry-specific and global credential providers respectively that return `not-found`. Other tests that emit this error are not affected, since they still have the `cargo:token` provider available.
2 parents 5c77b5d + 803fd69 commit 4518131

File tree

3 files changed

+105
-33
lines changed

3 files changed

+105
-33
lines changed

src/cargo/sources/registry/http_remote.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -588,12 +588,13 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
588588
}
589589
.into());
590590
if self.auth_required {
591-
return Poll::Ready(err.context(auth::AuthorizationError {
592-
sid: self.source_id.clone(),
593-
default_registry: self.config.default_registry()?,
594-
login_url: self.login_url.clone(),
595-
reason: auth::AuthorizationErrorReason::TokenRejected,
596-
}));
591+
let auth_error = auth::AuthorizationError::new(
592+
self.config,
593+
self.source_id,
594+
self.login_url.clone(),
595+
auth::AuthorizationErrorReason::TokenRejected,
596+
)?;
597+
return Poll::Ready(err.context(auth_error));
597598
} else {
598599
return Poll::Ready(err);
599600
}

src/cargo/util/auth/mod.rs

+67-26
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,19 @@ impl RegistryConfigExtended {
7474
}
7575

7676
/// Get the list of credential providers for a registry source.
77-
fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<String>>> {
77+
fn credential_provider(
78+
config: &Config,
79+
sid: &SourceId,
80+
show_warnings: bool,
81+
) -> CargoResult<Vec<Vec<String>>> {
82+
let warn = |message: String| {
83+
if show_warnings {
84+
config.shell().warn(message)
85+
} else {
86+
Ok(())
87+
}
88+
};
89+
7890
let cfg = registry_credential_config_raw(config, sid)?;
7991
let default_providers = || {
8092
if config.cli_unstable().asymmetric_token {
@@ -111,7 +123,7 @@ fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<S
111123
let provider = resolve_credential_alias(config, provider);
112124
if let Some(token) = token {
113125
if provider[0] != "cargo:token" {
114-
config.shell().warn(format!(
126+
warn(format!(
115127
"{sid} has a token configured in {} that will be ignored \
116128
because this registry is configured to use credential-provider `{}`",
117129
token.definition, provider[0],
@@ -120,7 +132,7 @@ fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<S
120132
}
121133
if let Some(secret_key) = secret_key {
122134
if provider[0] != "cargo:paseto" {
123-
config.shell().warn(format!(
135+
warn(format!(
124136
"{sid} has a secret-key configured in {} that will be ignored \
125137
because this registry is configured to use credential-provider `{}`",
126138
secret_key.definition, provider[0],
@@ -145,14 +157,14 @@ fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<S
145157
match (token_pos, paseto_pos) {
146158
(Some(token_pos), Some(paseto_pos)) => {
147159
if token_pos < paseto_pos {
148-
config.shell().warn(format!(
160+
warn(format!(
149161
"{sid} has a `secret_key` configured in {} that will be ignored \
150162
because a `token` is also configured, and the `cargo:token` provider is \
151163
configured with higher precedence",
152164
secret_key.definition
153165
))?;
154166
} else {
155-
config.shell().warn(format!("{sid} has a `token` configured in {} that will be ignored \
167+
warn(format!("{sid} has a `token` configured in {} that will be ignored \
156168
because a `secret_key` is also configured, and the `cargo:paseto` provider is \
157169
configured with higher precedence", token.definition))?;
158170
}
@@ -172,7 +184,7 @@ fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<S
172184
.iter()
173185
.any(|p| p.first().map(String::as_str) == Some("cargo:token"))
174186
{
175-
config.shell().warn(format!(
187+
warn(format!(
176188
"{sid} has a token configured in {} that will be ignored \
177189
because the `cargo:token` credential provider is not listed in \
178190
`registry.global-credential-providers`",
@@ -191,7 +203,7 @@ fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<S
191203
.iter()
192204
.any(|p| p.first().map(String::as_str) == Some("cargo:paseto"))
193205
{
194-
config.shell().warn(format!(
206+
warn(format!(
195207
"{sid} has a secret-key configured in {} that will be ignored \
196208
because the `cargo:paseto` credential provider is not listed in \
197209
`registry.global-credential-providers`",
@@ -360,14 +372,40 @@ impl fmt::Display for AuthorizationErrorReason {
360372
#[derive(Debug)]
361373
pub struct AuthorizationError {
362374
/// Url that was attempted
363-
pub sid: SourceId,
375+
sid: SourceId,
364376
/// The `registry.default` config value.
365-
pub default_registry: Option<String>,
377+
default_registry: Option<String>,
366378
/// Url where the user could log in.
367379
pub login_url: Option<Url>,
368380
/// Specific reason indicating what failed
369-
pub reason: AuthorizationErrorReason,
381+
reason: AuthorizationErrorReason,
382+
/// Should the _TOKEN environment variable name be included when displaying this error?
383+
display_token_env_help: bool,
370384
}
385+
386+
impl AuthorizationError {
387+
pub fn new(
388+
config: &Config,
389+
sid: SourceId,
390+
login_url: Option<Url>,
391+
reason: AuthorizationErrorReason,
392+
) -> CargoResult<Self> {
393+
// Only display the _TOKEN environment variable suggestion if the `cargo:token` credential
394+
// provider is available for the source. Otherwise setting the environment variable will
395+
// have no effect.
396+
let display_token_env_help = credential_provider(config, &sid, false)?
397+
.iter()
398+
.any(|p| p.first().map(String::as_str) == Some("cargo:token"));
399+
Ok(AuthorizationError {
400+
sid,
401+
default_registry: config.default_registry()?,
402+
login_url,
403+
reason,
404+
display_token_env_help,
405+
})
406+
}
407+
}
408+
371409
impl Error for AuthorizationError {}
372410
impl fmt::Display for AuthorizationError {
373411
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -377,20 +415,23 @@ impl fmt::Display for AuthorizationError {
377415
} else {
378416
""
379417
};
380-
write!(
381-
f,
382-
"{}, please run `cargo login{args}`\nor use environment variable CARGO_REGISTRY_TOKEN",
383-
self.reason
384-
)
418+
write!(f, "{}, please run `cargo login{args}`", self.reason)?;
419+
if self.display_token_env_help {
420+
write!(f, "\nor use environment variable CARGO_REGISTRY_TOKEN")?;
421+
}
422+
Ok(())
385423
} else if let Some(name) = self.sid.alt_registry_key() {
386424
let key = ConfigKey::from_str(&format!("registries.{name}.token"));
387425
write!(
388426
f,
389-
"{} for `{}`, please run `cargo login --registry {name}`\nor use environment variable {}",
427+
"{} for `{}`, please run `cargo login --registry {name}`",
390428
self.reason,
391429
self.sid.display_registry_name(),
392-
key.as_env_key(),
393-
)
430+
)?;
431+
if self.display_token_env_help {
432+
write!(f, "\nor use environment variable {}", key.as_env_key())?;
433+
}
434+
Ok(())
394435
} else if self.reason == AuthorizationErrorReason::TokenMissing {
395436
write!(
396437
f,
@@ -416,7 +457,7 @@ my-registry = {{ index = "{}" }}
416457
}
417458
}
418459

419-
// Store a token in the cache for future calls.
460+
/// Store a token in the cache for future calls.
420461
pub fn cache_token_from_commandline(config: &Config, sid: &SourceId, token: Secret<&str>) {
421462
let url = sid.canonical_url();
422463
config.credential_cache().insert(
@@ -446,7 +487,7 @@ fn credential_action(
446487
name,
447488
headers,
448489
};
449-
let providers = credential_provider(config, sid)?;
490+
let providers = credential_provider(config, sid, true)?;
450491
let mut any_not_found = false;
451492
for provider in providers {
452493
let args: Vec<&str> = provider
@@ -511,12 +552,12 @@ pub fn auth_token(
511552
) -> CargoResult<String> {
512553
match auth_token_optional(config, sid, operation, headers)? {
513554
Some(token) => Ok(token.expose()),
514-
None => Err(AuthorizationError {
515-
sid: sid.clone(),
516-
default_registry: config.default_registry()?,
517-
login_url: login_url.cloned(),
518-
reason: AuthorizationErrorReason::TokenMissing,
519-
}
555+
None => Err(AuthorizationError::new(
556+
config,
557+
*sid,
558+
login_url.cloned(),
559+
AuthorizationErrorReason::TokenMissing,
560+
)?
520561
.into()),
521562
}
522563
}

tests/testsuite/credential_process.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,36 @@ fn build_provider(name: &str, response: &str) -> String {
320320
toml_bin(&cred_proj, name)
321321
}
322322

323+
#[cargo_test]
324+
fn not_found() {
325+
let registry = registry::RegistryBuilder::new()
326+
.no_configure_token()
327+
.http_index()
328+
.auth_required()
329+
.credential_provider(&[&build_provider(
330+
"not_found",
331+
r#"{"Err": {"kind": "not-found"}}"#,
332+
)])
333+
.build();
334+
335+
// should not suggest a _TOKEN environment variable since the cargo:token provider isn't available.
336+
cargo_process("install -v foo -Zcredential-process -Zregistry-auth")
337+
.masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
338+
.replace_crates_io(registry.index_url())
339+
.with_status(101)
340+
.with_stderr(
341+
r#"[UPDATING] [..]
342+
[CREDENTIAL] [..]not_found[..] get crates-io
343+
{"v":1[..]
344+
[ERROR] failed to query replaced source registry `crates-io`
345+
346+
Caused by:
347+
no token found, please run `cargo login`
348+
"#,
349+
)
350+
.run();
351+
}
352+
323353
#[cargo_test]
324354
fn all_not_found() {
325355
let server = registry::RegistryBuilder::new()
@@ -342,6 +372,7 @@ fn all_not_found() {
342372
)
343373
.unwrap();
344374

375+
// should not suggest a _TOKEN environment variable since the cargo:token provider isn't available.
345376
cargo_process("install -v foo -Zcredential-process -Zregistry-auth")
346377
.masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
347378
.replace_crates_io(server.index_url())
@@ -354,7 +385,6 @@ fn all_not_found() {
354385
355386
Caused by:
356387
no token found, please run `cargo login`
357-
or use environment variable CARGO_REGISTRY_TOKEN
358388
"#,
359389
)
360390
.run();

0 commit comments

Comments
 (0)