From 07cac7524e342b8d6eb034eb762aa44944c87e89 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 6 Sep 2023 14:00:46 -0500 Subject: [PATCH] feat: stabilize credential-process and registry-auth --- .../cargo-credential-1password/README.md | 17 +- .../cargo-credential-libsecret/README.md | 4 +- .../cargo-credential-macos-keychain/README.md | 5 +- credential/cargo-credential-wincred/README.md | 4 +- credential/cargo-credential/README.md | 2 +- src/cargo/core/features.rs | 12 +- src/cargo/lib.rs | 4 +- src/cargo/ops/registry/mod.rs | 1 + src/cargo/ops/registry/publish.rs | 1 + src/cargo/sources/registry/download.rs | 1 + src/cargo/sources/registry/http_remote.rs | 14 +- src/cargo/sources/registry/remote.rs | 5 +- src/cargo/util/auth/mod.rs | 53 ++- src/doc/src/SUMMARY.md | 2 + src/doc/src/reference/config.md | 51 ++- .../reference/credential-provider-protocol.md | 237 ++++++++++++++ src/doc/src/reference/registries.md | 6 + .../src/reference/registry-authentication.md | 103 ++++++ src/doc/src/reference/registry-index.md | 17 + src/doc/src/reference/unstable.md | 303 +----------------- tests/testsuite/credential_process.rs | 112 ++----- tests/testsuite/login.rs | 16 +- tests/testsuite/registry_auth.rs | 22 +- tests/testsuite/search.rs | 6 +- 24 files changed, 550 insertions(+), 448 deletions(-) create mode 100644 src/doc/src/reference/credential-provider-protocol.md create mode 100644 src/doc/src/reference/registry-authentication.md diff --git a/credential/cargo-credential-1password/README.md b/credential/cargo-credential-1password/README.md index 7cc15e05b132..3648efe4bda6 100644 --- a/credential/cargo-credential-1password/README.md +++ b/credential/cargo-credential-1password/README.md @@ -1,7 +1,18 @@ # cargo-credential-1password -This is the implementation for the Cargo credential helper for [1password]. -See the [credential-process] documentation for how to use this. +A Cargo [credential provider] for [1password]. + +`cargo-credential-1password` uses the 1password `op` CLI to store the token. You must +install the `op` CLI from the [1password +website](https://1password.com/downloads/command-line/). You must run `op signin` +at least once with the appropriate arguments (such as `op signin my.1password.com user@example.com`), +unless you provide the sign-in-address and email arguments. The master password will be required on each request +unless the appropriate `OP_SESSION` environment variable is set. It supports +the following command-line arguments: +* `--account`: The account shorthand name to use. +* `--vault`: The vault name to use. +* `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`. +* `--email`: The email address to sign in with. [1password]: https://1password.com/ -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html diff --git a/credential/cargo-credential-libsecret/README.md b/credential/cargo-credential-libsecret/README.md index f169323e0615..d3a094a825da 100644 --- a/credential/cargo-credential-libsecret/README.md +++ b/credential/cargo-credential-libsecret/README.md @@ -3,5 +3,7 @@ This is the implementation for the Cargo credential helper for [GNOME libsecret]. See the [credential-process] documentation for how to use this. +This credential provider is built-in to cargo as `cargo:libsecret`. + [GNOME libsecret]: https://wiki.gnome.org/Projects/Libsecret -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html diff --git a/credential/cargo-credential-macos-keychain/README.md b/credential/cargo-credential-macos-keychain/README.md index 554116b55728..8e5159a088d9 100644 --- a/credential/cargo-credential-macos-keychain/README.md +++ b/credential/cargo-credential-macos-keychain/README.md @@ -3,5 +3,8 @@ This is the implementation for the Cargo credential helper for [macOS Keychain]. See the [credential-process] documentation for how to use this. +This credential provider is built-in to cargo as `cargo:macos-keychain`. + [macOS Keychain]: https://support.apple.com/guide/keychain-access/welcome/mac -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html + diff --git a/credential/cargo-credential-wincred/README.md b/credential/cargo-credential-wincred/README.md index 8c8d1878924d..b675d7bbf84b 100644 --- a/credential/cargo-credential-wincred/README.md +++ b/credential/cargo-credential-wincred/README.md @@ -3,5 +3,7 @@ This is the implementation for the Cargo credential helper for [Windows Credential Manager]. See the [credential-process] documentation for how to use this. +This credential provider is built-in to cargo as `cargo:wincred`. + [Windows Credential Manager]: https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0 -[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html diff --git a/credential/cargo-credential/README.md b/credential/cargo-credential/README.md index 2311f3e57f55..d87d41bb8c8e 100644 --- a/credential/cargo-credential/README.md +++ b/credential/cargo-credential/README.md @@ -5,7 +5,7 @@ provides an interface to store tokens for authorizing access to a registry such as https://crates.io/. Documentation about credential processes may be found at -https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process +https://doc.rust-lang.org/nightly/cargo/reference/credential-provider-protocol.html Example implementations may be found at https://github.com/rust-lang/cargo/tree/master/credential diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index ea6560edea7a..454de0d1c976 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -729,7 +729,6 @@ unstable_cli_options!( check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"), codegen_backend: bool = ("Enable the `codegen-backend` option in profiles in .cargo/config.toml file"), config_include: bool = ("Enable the `include` key in config files"), - credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"), direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"), doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"), dual_proc_macros: bool = ("Build proc-macros for both the host and the target"), @@ -745,7 +744,6 @@ unstable_cli_options!( panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"), profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"), publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"), - registry_auth: bool = ("Authentication for alternative registries"), rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"), rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"), script: bool = ("Enable support for single-file, `.rs` packages"), @@ -819,6 +817,12 @@ const STABILIZED_TERMINAL_WIDTH: &str = const STABILISED_SPARSE_REGISTRY: &str = "The sparse protocol is now the default for crates.io"; +const STABILIZED_CREDENTIAL_PROCESS: &str = + "Authentication with a credential provider is always available."; + +const STABILIZED_REGISTRY_AUTH: &str = + "Authenticated registries are available if a credential provider is configured."; + fn deserialize_build_std<'de, D>(deserializer: D) -> Result>, D::Error> where D: serde::Deserializer<'de>, @@ -1084,6 +1088,8 @@ impl CliUnstable { "sparse-registry" => stabilized_warn(k, "1.68", STABILISED_SPARSE_REGISTRY), "terminal-width" => stabilized_warn(k, "1.68", STABILIZED_TERMINAL_WIDTH), "doctest-in-workspace" => stabilized_warn(k, "1.72", STABILIZED_DOCTEST_IN_WORKSPACE), + "credential-process" => stabilized_warn(k, "1.74", STABILIZED_CREDENTIAL_PROCESS), + "registry-auth" => stabilized_warn(k, "1.74", STABILIZED_REGISTRY_AUTH), // Unstable features // Sorted alphabetically: @@ -1101,7 +1107,6 @@ impl CliUnstable { } "codegen-backend" => self.codegen_backend = parse_empty(k, v)?, "config-include" => self.config_include = parse_empty(k, v)?, - "credential-process" => self.credential_process = parse_empty(k, v)?, "direct-minimal-versions" => self.direct_minimal_versions = parse_empty(k, v)?, "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?, "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?, @@ -1122,7 +1127,6 @@ impl CliUnstable { "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?, "profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?, "publish-timeout" => self.publish_timeout = parse_empty(k, v)?, - "registry-auth" => self.registry_auth = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?, "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index d562667e9059..d80a993b6dd7 100644 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -91,9 +91,7 @@ //! This is the `#[cargo_test]` proc-macro used by the test suite to define tests. //! - [`credential`](https://github.com/rust-lang/cargo/tree/master/credential) //! This subdirectory contains several packages for implementing the -//! experimental -//! [credential-process](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process) -//! feature. +//! [credential providers](https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html). //! - [`mdman`](https://github.com/rust-lang/cargo/tree/master/crates/mdman) //! ([nightly docs](https://doc.rust-lang.org/nightly/nightly-rustc/mdman/index.html)): //! This is a utility for generating cargo's man pages. See [Building the man diff --git a/src/cargo/ops/registry/mod.rs b/src/cargo/ops/registry/mod.rs index 7d4e99f6bcb4..e990d1714826 100644 --- a/src/cargo/ops/registry/mod.rs +++ b/src/cargo/ops/registry/mod.rs @@ -145,6 +145,7 @@ fn registry( None, operation, vec![], + false, )?) } else { None diff --git a/src/cargo/ops/registry/publish.rs b/src/cargo/ops/registry/publish.rs index 6e43ca2dbcc6..868347287af8 100644 --- a/src/cargo/ops/registry/publish.rs +++ b/src/cargo/ops/registry/publish.rs @@ -161,6 +161,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { None, operation, vec![], + false, )?)); } diff --git a/src/cargo/sources/registry/download.rs b/src/cargo/sources/registry/download.rs index 08940b3a1db5..9bf838625c15 100644 --- a/src/cargo/sources/registry/download.rs +++ b/src/cargo/sources/registry/download.rs @@ -85,6 +85,7 @@ pub(super) fn download( None, Operation::Read, vec![], + true, )?) } else { None diff --git a/src/cargo/sources/registry/http_remote.rs b/src/cargo/sources/registry/http_remote.rs index 1346b6995ab8..9fe76333b19d 100644 --- a/src/cargo/sources/registry/http_remote.rs +++ b/src/cargo/sources/registry/http_remote.rs @@ -547,9 +547,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { return Poll::Ready(Ok(LoadResponse::NotFound)); } StatusCode::Unauthorized - if !self.auth_required - && path == Path::new(RegistryConfig::NAME) - && self.config.cli_unstable().registry_auth => + if !self.auth_required && path == Path::new(RegistryConfig::NAME) => { debug!(target: "network", "re-attempting request for config.json with authorization included."); self.fresh.remove(path); @@ -612,10 +610,6 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { } } - if !self.config.cli_unstable().registry_auth { - self.auth_required = false; - } - // Looks like we're going to have to do a network request. self.start_fetch()?; @@ -654,6 +648,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { self.login_url.as_ref(), Operation::Read, self.auth_error_headers.clone(), + true, )?; headers.append(&format!("Authorization: {}", authorization))?; trace!(target: "network", "including authorization for {}", full_url); @@ -724,10 +719,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { } fn config(&mut self) -> Poll>> { - let mut cfg = ready!(self.config()?).clone(); - if !self.config.cli_unstable().registry_auth { - cfg.auth_required = false; - } + let cfg = ready!(self.config()?).clone(); Poll::Ready(Ok(Some(cfg))) } diff --git a/src/cargo/sources/registry/remote.rs b/src/cargo/sources/registry/remote.rs index 89927181f643..ac83e3a52921 100644 --- a/src/cargo/sources/registry/remote.rs +++ b/src/cargo/sources/registry/remote.rs @@ -307,10 +307,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) { LoadResponse::Data { raw_data, .. } => { trace!("config loaded"); - let mut cfg: RegistryConfig = serde_json::from_slice(&raw_data)?; - if !self.config.cli_unstable().registry_auth { - cfg.auth_required = false; - } + let cfg: RegistryConfig = serde_json::from_slice(&raw_data)?; Poll::Ready(Ok(Some(cfg))) } _ => Poll::Ready(Ok(None)), diff --git a/src/cargo/util/auth/mod.rs b/src/cargo/util/auth/mod.rs index cc8128d6ca4c..0fb3e50882a5 100644 --- a/src/cargo/util/auth/mod.rs +++ b/src/cargo/util/auth/mod.rs @@ -77,6 +77,7 @@ impl RegistryConfigExtended { fn credential_provider( config: &Config, sid: &SourceId, + require_cred_provider_config: bool, show_warnings: bool, ) -> CargoResult>> { let warn = |message: String| { @@ -88,7 +89,9 @@ fn credential_provider( }; let cfg = registry_credential_config_raw(config, sid)?; + let mut global_provider_defined = true; let default_providers = || { + global_provider_defined = false; if config.cli_unstable().asymmetric_token { // Enable the PASETO provider vec![ @@ -101,7 +104,7 @@ fn credential_provider( }; let global_providers = config .get::>>>("registry.global-credential-providers")? - .filter(|p| !p.is_empty() && config.cli_unstable().credential_process) + .filter(|p| !p.is_empty()) .map(|p| { p.iter() .rev() @@ -112,14 +115,14 @@ fn credential_provider( .unwrap_or_else(default_providers); tracing::debug!(?global_providers); - let providers = match cfg { + match cfg { // If there's a specific provider configured for this registry, use it. Some(RegistryConfig { credential_provider: Some(provider), token, secret_key, .. - }) if config.cli_unstable().credential_process => { + }) => { let provider = resolve_credential_alias(config, provider); if let Some(token) = token { if provider[0] != "cargo:token" { @@ -139,7 +142,7 @@ fn credential_provider( ))?; } } - vec![provider] + return Ok(vec![provider]); } // Warning for both `token` and `secret-key`, stating which will be ignored @@ -173,7 +176,6 @@ fn credential_provider( // One or both of the below individual warnings will trigger } } - global_providers } // Check if a `token` is configured that will be ignored. @@ -191,7 +193,6 @@ fn credential_provider( token.definition ))?; } - global_providers } // Check if a asymmetric token is configured that will be ignored. @@ -210,13 +211,18 @@ fn credential_provider( token.definition ))?; } - global_providers } // If we couldn't find a registry-specific provider, use the fallback provider list. - None | Some(RegistryConfig { .. }) => global_providers, + None | Some(RegistryConfig { .. }) => {} }; - Ok(providers) + if !global_provider_defined && require_cred_provider_config { + bail!( + "authenticated registries require a credential-provider to be configured\n\ + see https://doc.rust-lang.org/cargo/reference/registry-authentication.html for details" + ); + } + Ok(global_providers) } /// Get the credential configuration for a `SourceId`. @@ -393,7 +399,7 @@ impl AuthorizationError { // Only display the _TOKEN environment variable suggestion if the `cargo:token` credential // provider is available for the source. Otherwise setting the environment variable will // have no effect. - let display_token_env_help = credential_provider(config, &sid, false)? + let display_token_env_help = credential_provider(config, &sid, false, false)? .iter() .any(|p| p.first().map(String::as_str) == Some("cargo:token")); Ok(AuthorizationError { @@ -476,6 +482,7 @@ fn credential_action( action: Action<'_>, headers: Vec, args: &[&str], + require_cred_provider_config: bool, ) -> CargoResult { let name = if sid.is_crates_io() { Some(CRATES_IO_REGISTRY) @@ -487,7 +494,7 @@ fn credential_action( name, headers, }; - let providers = credential_provider(config, sid, true)?; + let providers = credential_provider(config, sid, require_cred_provider_config, true)?; let mut any_not_found = false; for provider in providers { let args: Vec<&str> = provider @@ -549,8 +556,15 @@ pub fn auth_token( login_url: Option<&Url>, operation: Operation<'_>, headers: Vec, + require_cred_provider_config: bool, ) -> CargoResult { - match auth_token_optional(config, sid, operation, headers)? { + match auth_token_optional( + config, + sid, + operation, + headers, + require_cred_provider_config, + )? { Some(token) => Ok(token.expose()), None => Err(AuthorizationError::new( config, @@ -568,6 +582,7 @@ fn auth_token_optional( sid: &SourceId, operation: Operation<'_>, headers: Vec, + require_cred_provider_config: bool, ) -> CargoResult>> { tracing::trace!("token requested for {}", sid.display_registry_name()); let mut cache = config.credential_cache(); @@ -588,7 +603,14 @@ fn auth_token_optional( } } - let credential_response = credential_action(config, sid, Action::Get(operation), headers, &[]); + let credential_response = credential_action( + config, + sid, + Action::Get(operation), + headers, + &[], + require_cred_provider_config, + ); if let Some(e) = credential_response.as_ref().err() { if let Some(e) = e.downcast_ref::() { if matches!(e, cargo_credential::Error::NotFound) { @@ -627,7 +649,7 @@ fn auth_token_optional( /// Log out from the given registry. pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> { - let credential_response = credential_action(config, sid, Action::Logout, vec![], &[]); + let credential_response = credential_action(config, sid, Action::Logout, vec![], &[], false); if let Some(e) = credential_response.as_ref().err() { if let Some(e) = e.downcast_ref::() { if matches!(e, cargo_credential::Error::NotFound) { @@ -656,7 +678,8 @@ pub fn login( options: LoginOptions<'_>, args: &[&str], ) -> CargoResult<()> { - let credential_response = credential_action(config, sid, Action::Login(options), vec![], args)?; + let credential_response = + credential_action(config, sid, Action::Login(options), vec![], args, false)?; let CredentialResponse::Login = credential_response else { bail!("credential provider produced unexpected response for `login` request: {credential_response:?}") }; diff --git a/src/doc/src/SUMMARY.md b/src/doc/src/SUMMARY.md index 273936559854..9ebd7915bb9c 100644 --- a/src/doc/src/SUMMARY.md +++ b/src/doc/src/SUMMARY.md @@ -36,6 +36,8 @@ * [Source Replacement](reference/source-replacement.md) * [External Tools](reference/external-tools.md) * [Registries](reference/registries.md) + * [Registry Authentication](reference/registry-authentication.md) + * [Credential Provider Protocol](reference/credential-provider-protocol.md) * [Running a Registry](reference/running-a-registry.md) * [Registry Index](reference/registry-index.md) * [Registry Web API](reference/registry-web-api.md) diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index 6d0dba29c918..8d5becd1c4cb 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -298,7 +298,9 @@ Cargo will search `PATH` for its executable. Configuration values with sensitive information are stored in the `$CARGO_HOME/credentials.toml` file. This file is automatically created and updated -by [`cargo login`] and [`cargo logout`]. It follows the same format as Cargo config files. +by [`cargo login`] and [`cargo logout`] when using the `cargo:token` credential provider. + +It follows the same format as Cargo config files. ```toml [registry] @@ -537,6 +539,24 @@ directory. This option is deprecated and unused. Cargo always has pipelining enabled. +### `[credential-alias]` +* Type: string or array of strings +* Default: empty +* Environment: `CARGO_CREDENTIAL_ALIAS_` + +The `[credential-alias]` table defines credential provider aliases. +These aliases can be referenced as an element of the `registry.global-credential-providers` +array, or as a credential provider for a specific registry +under `registries..credential-provider`. + +For example, to define an alias called `my-alias`: + +```toml +[credential-alias] +my-alias = ["/usr/bin/cargo-creds", "--argument"] +``` +See [Registry Authentication](registry-authentication.md) for more information. + ### `[doc]` The `[doc]` table defines options for the [`cargo doc`] command. @@ -947,6 +967,21 @@ commands like [`cargo publish`] that require authentication. Can be overridden with the `--token` command-line option. +#### `registries..credential-provider` +* Type: string or array of path and arguments +* Default: none +* Environment: `CARGO_REGISTRIES__CREDENTIAL_PROVIDER` + +Specifies the credential provider for the given registry. If not set, the +providers in `registry.global-credential-providers` will be used. + +If specified as a string, path and arguments will be split on spaces. For +paths or arguments that contain spaces, use an array. + +If the value exists in the `[credential-alias]` table, the alias will be used. + +See [Registry Authentication](registry-authentication.md) for more information. + #### `registries.crates-io.protocol` * Type: string * Default: `sparse` @@ -991,6 +1026,20 @@ commands like [`cargo publish`] that require authentication. Can be overridden with the `--token` command-line option. +#### `registry.global-credential-providers` +* Type: array +* Default: `["cargo:token"]` +* Environment: `CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS` + +Specifies the list of global credential providers. If credential provider is not set +for a specific registry using `registries..credential-provider`, Cargo will use +the credential providers in this list. Providers toward the end of the list have precedence. + +Path and arguments are split on spaces. If the path or arguments contains spaces, the credential +provider should be defined in the `credential-alias` table and referenced here by its alias. + +See [Registry Authentication](registry-authentication.md) for more information. + ### `[source]` The `[source]` table defines the registry sources available. See [Source diff --git a/src/doc/src/reference/credential-provider-protocol.md b/src/doc/src/reference/credential-provider-protocol.md new file mode 100644 index 000000000000..de46cd9c71cc --- /dev/null +++ b/src/doc/src/reference/credential-provider-protocol.md @@ -0,0 +1,237 @@ +# Credential Provider Protocol +This document describes information for building a Cargo credential provider. For information on +setting up or using a credential provider, see [Registry Authentication](registry-authentication.md). + +When using an external credential provider, Cargo communicates with the credential +provider using stdin/stdout messages passed as single lines of JSON. + +Cargo will always execute the credential provider with the `--cargo-plugin` argument. +This enables a credential provider executable to have additional functionality beyond +what Cargo needs. Additional arguments are included in the JSON via the `args` field. + +## JSON messages +The JSON messages in this document have newlines added for readability. +Actual messages must not contain newlines. + +### Credential hello +* Sent by: credential provider +* Purpose: used to identify the supported protocols on process startup +```javascript +{ + "v":[1] +} +``` + +Requests sent by Cargo will include a `v` field set to one of the versions listed here. +If Cargo does not support any of the versions offered by the credential provider, it will issue an +error and shut down the credential process. + +### Registry information +* Sent by: Cargo +Not a message by itself. Included in all messages sent by Cargo as the `registry` field. +```javascript +{ + // Index URL of the registry + "index-url":"https://github.com/rust-lang/crates.io-index", + // Name of the registry in configuration (optional) + "name": "crates-io" + // HTTP headers received from attempting to access an authenticated registry (optional) + "headers": ["WWW-Authenticate: cargo"] +} +``` + +### Login request +* Sent by: Cargo +* Purpose: collect and store credentials +```javascript +{ + // Protocol version + "v":1, + // Action to perform: login + "kind":"login", + // Registry information (see Registry information) + "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"}, + // User-specified token from stdin or command line (optional) + "token": "" + // URL that the user could visit to get a token (optional) + "login-url": "http://registry-url/login" + // Additional command-line args (optional) + "args":[] +} +``` + +If the `token` field is set, than the credential provider should use the token provided. If +the `token` is not set, then the credential provider should prompt the user for a token. + +In addition to the arguments that may be passed to the credential provider in +configuration, `cargo login` also supports passing additional command line args +via `cargo login -- `. These additional arguments will be included +in the `args` field after any args from Cargo configuration. + +### Read request +* Sent by: Cargo +* Purpose: Get the credential for reading crate information +```javascript +{ + // Protocol version + "v":1, + // Request kind: get credentials + "kind":"get", + // Action to perform: read crate information + "operation":"read", + // Registry information (see Registry information) + "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"}, + // Additional command-line args (optional) + "args":[] +} +``` + +### Publish request +* Sent by: Cargo +* Purpose: Get the credential for publishing a crate +```javascript +{ + // Protocol version + "v":1, + // Request kind: get credentials + "kind":"get", + // Action to perform: publish crate + "operation":"publish", + // Crate name + "name":"sample", + // Crate version + "vers":"0.1.0", + // Crate checksum + "cksum":"...", + // Registry information (see Registry information) + "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"}, + // Additional command-line args (optional) + "args":[] +} +``` + +### Get success response +* Sent by: credential provider +* Purpose: Gives the credential to Cargo +```javascript +{"Ok":{ + // Response kind: this was a get request + "kind":"get", + // Token to send to the registry + "token":"...", + // Cache control. Can be one of the following: + // * "never": do not cache + // * "session": cache for the current cargo session + // * "expires": cache for the current cargo session until expiration + "cache":"expires", + // Unix timestamp (only for "cache": "expires") + "expiration":1693942857, + // Is the token operation independent? + "operation_independent":true +}} +``` + +The `token` will be sent to the registry as the value of the `Authorization` HTTP header. + +`operation_independent` indicates whether the token can be cached across different +operations (such as publishing or fetching). In general this should be `true` unless +the provider wants to generate tokens that are scoped to specific operations. + +### Login success response +* Sent by: credential provider +* Purpose: Indicates the login was successful +```javascript +{"Ok":{ + // Response kind: this was a login request + "kind":"login" +}} +``` + +### Logout success response +* Sent by: credential provider +* Purpose: Indicates the logout was successful +```javascript +{"Ok":{ + // Response kind: this was a logout request + "kind":"logout" +}} +``` + +### Failure response (URL not supported) +* Sent by: credential provider +* Purpose: Gives error information to Cargo +```javascript +{"Err":{ + "kind":"url-not-supported" +}} +``` +Sent if the credential provider is designed +to only handle specific registry URLs, and the given URL +is not supported. Cargo will attempt another provider if +available. + +### Failure response (not found) +* Sent by: credential provider +* Purpose: Gives error information to Cargo +```javascript +{"Err":{ + // Error: The credential could not be found in the provider. + "kind":"not-found" +}} +``` +Sent if the credential could not be found. This is expected for +`get` requests where the credential is not available, or `logout` +requests where there is nothing found to erase. + +### Failure response (operation not supported) +* Sent by: credential provider +* Purpose: Gives error information to Cargo +```javascript +{"Err":{ + // Error: The credential could not be found in the provider. + "kind":"operation-not-supported" +}} +``` +Sent if the credential provider does not support the requested operation. +If a provider only supports `get` and a `login` is requested, the +provider should respond with this error. + +### Failure response (other) +* Sent by: credential provider +* Purpose: Gives error information to Cargo +```javascript +{"Err":{ + // Error: something else has failed + "kind":"other", + // Error message string to be displayed + "message": "free form string error message", + // Detailed cause chain for the error (optional) + "caused-by": ["cause 1", "cause 2"] +}} +``` + +## Example communication to request a token for reading: +1. Cargo spawns the credential process, capturing stdin and stdout. +2. Credential process sends the Hello message to Cargo + ```javascript + { "v": [1] } + ``` +3. Cargo sends the CredentialRequest message to the credential process (newlines added for readability). + ```javascript + { + "v": 1, + "kind": "get", + "operation": "read", + "registry":{"index-url":"sparse+https://registry-url/index/"} + } + ``` +4. Credential process sends the CredentialResponse to Cargo (newlines added for readability). + ```javascript + { + "token": "...", + "cache": "session", + "operation_independent": true + } + ``` +5. Cargo closes the stdin pipe to the credential provider and it exits. +6. Cargo uses the token for the remainder of the session (until Cargo exits) when interacting with this registry. diff --git a/src/doc/src/reference/registries.md b/src/doc/src/reference/registries.md index 74c81943579a..f0554be980ed 100644 --- a/src/doc/src/reference/registries.md +++ b/src/doc/src/reference/registries.md @@ -11,6 +11,10 @@ support publishing new crates directly from Cargo. If you are implementing a registry server, see [Running a Registry] for more details about the protocol between Cargo and a registry. +If you're using a registry that requires authentication, see [Registry Authentication]. +If you are implementing a credential provider, see [Credential Provider Protocol] +for details. + ## Using an Alternate Registry To use a registry other than [crates.io], the name and index URL of the @@ -117,6 +121,8 @@ controlled via the [`registries.crates-io.protocol`] config key. [Source Replacement]: source-replacement.md [Running a Registry]: running-a-registry.md +[Credential Provider Protocol]: credential-provider-protocol.md +[Registry Authentication]: registry-authentication.md [`cargo publish`]: ../commands/cargo-publish.md [`cargo package`]: ../commands/cargo-package.md [`cargo login`]: ../commands/cargo-login.md diff --git a/src/doc/src/reference/registry-authentication.md b/src/doc/src/reference/registry-authentication.md new file mode 100644 index 000000000000..130fd32cfa22 --- /dev/null +++ b/src/doc/src/reference/registry-authentication.md @@ -0,0 +1,103 @@ +# Registry Authentication +Cargo authenticates to registries with through credential providers. These +credential providers are external executables or built-in providers that Cargo +uses to store and retreive credentials. + +Using alternative registries with authentication *requires* a credential provider to be configured +to avoid unknowningly storing unecrypted credentials on disk. For historical reasons, public +(non-authenticated) registres do not require credential provider configuration and the `cargo:token` +provider is used if no providers are configured. + +Cargo also includes platform-specific providers that use the operating system to securely store +tokens. The `cargo:token` provider is also included which stores credentials in unencrypted plain +text in the [credentials](config.md#credentials) file. + +## Recommended configuration +It's recommended to configure a global credential provider list in `$CARGO_HOME/config.toml` +which defaults to: +* Windows: `%USERPROFILE%\.cargo\config.toml` +* Unix: `~/.cargo/config.toml` + +This recommended configuration uses the operating system provider, with a fallback to `cargo:token` +to look in Cargo's [credentials](config.md#credentials) file or environment variables. + +Some private registries may also recommend a registry-specific credential-provider. Check your +registry's documentation to see if this is the case. + +### macOS configuration +```toml +# ~/.cargo/config.toml +[registry] +global-credential-providers = ["cargo:token", "cargo:macos-keychain"] +``` + +### Linux (libsecret) configuration +```toml +# ~/.cargo/config.toml +[registry] +global-credential-providers = ["cargo:token", "cargo:libsecret"] +``` + +### Windows configuration +```toml +# %USERPROFILE%\.cargo\config.toml +[registry] +global-credential-providers = ["cargo:token", "cargo:wincred"] +``` + +See [`registry.global-credential-providers`](config.md#registryglobal-credential-providers) +for more details. + +## Built-in providers +Cargo includes several built-in credential providers. + +### `cargo:token` +Uses Cargo's [credentials](config.md#credentials) file to store tokens unencrypted in plain text. +When retreiving tokens, checks the `CARGO_REGISTRIES__TOKEN` environment variable. +If this credential provider is not listed, then the `*_TOKEN` environment variables will not work. + +### `cargo:wincred` +Uses the Windows Credential Manager to store tokens. + +The credentials are stored as `cargo-registry:` in the Credential Manager +under "Windows Credentials". + +### `cargo:macos-keychain` +Uses the macOS Keychain to store tokens. + +The Keychain Access app can be used to view stored tokens. + +### `cargo:libsecret` +Uses [libsecret](https://wiki.gnome.org/Projects/Libsecret) to store tokens. + +On GNOME, credentials can be viewed using [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring). + +### `cargo:token-from-stdout ` +Launch a subprocess that returns a token on stdout. Newlines will be trimmed. +* The process inherits the user's stdin and stderr. +* It should exit 0 on success, and nonzero on error. +* [`cargo login`] and [`cargo logout`] are not supported and return an error if used. + +The following environment variables will be provided to the executed command: + +* `CARGO` --- Path to the `cargo` binary executing the command. +* `CARGO_REGISTRY_INDEX_URL` --- The URL of the registry index. +* `CARGO_REGISTRY_NAME_OPT` --- Optional name of the registry. Should not be used as a lookup key. + +[`cargo login`]: ../commands/cargo-login.md +[`cargo logout`]: ../commands/cargo-logout.md + +## External providers +For external providers that follow Cargo's [credential provider protocol](credential-provider-protocol.md) +use the path to the executable (or the executable name if on the `PATH`). + +For example, to install [cargo-credential-1password](https://crates.io/crates/cargo-credential-1password) +from crates.io do the following: + +Install the provider with `cargo install cargo-credential-1password` + +In the config, add to (or create) `registry.global-credential-providers`: +```toml +[registry] +global-credential-providers = ["cargo:token", "cargo-credential-1password --email you@example.com"] +``` diff --git a/src/doc/src/reference/registry-index.md b/src/doc/src/reference/registry-index.md index 7f331b826c13..67af8d3cd34b 100644 --- a/src/doc/src/reference/registry-index.md +++ b/src/doc/src/reference/registry-index.md @@ -35,6 +35,9 @@ The keys are: - `api`: This is the base URL for the web API. This key is optional, but if it is not specified, commands such as [`cargo publish`] will not work. The web API is described below. +- `auth-required`: indicates whether this is a private registry that requires + all operations to be authenticated including API requests, crate downloads + and sparse index updates. ## Download Endpoint @@ -43,6 +46,8 @@ Cargo supports https, http, and file URLs, HTTP redirects, HTTP1 and HTTP2. The exact specifics of TLS support depend on the platform that Cargo is running on, the version of Cargo, and how it was compiled. +If `auth-required: true` is set in `config.json`, the `Authorization` header +will be included with http(s) download requests. ## Index files The rest of the index repository contains one file for each package, where the @@ -274,6 +279,18 @@ The sparse protocol downloads each index file using an individual HTTP request. this results in a large number of small HTTP requests, performance is significantly improved with a server that supports pipelining and HTTP/2. +#### Sparse authentication +Cargo will attempt to fetch the `config.json` file before +fetching any other files. If the server responds with an HTTP 401, then Cargo will assume +that the registry requires authentication and re-attempt the request for `config.json` +with the authentication token included. + +On authentication failure (or a missing authentication token) the server may include a +`www-authenticate` header with a `Cargo login_url=""` challenge to indicate where the user +can go to get a token. + +Registries that require authentication must set `auth-required: true` in `config.json`. + #### Caching Cargo caches the crate metadata files, and captures the `ETag` or `Last-Modified` HTTP header from the server for each entry. When refreshing crate metadata, Cargo diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 9767d5dc3b26..1a270929a062 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -102,9 +102,8 @@ For the latest nightly, see the [nightly version] of this page. * [config-include](#config-include) --- Adds the ability for config files to include other files. * [`cargo config`](#cargo-config) --- Adds a new subcommand for viewing config files. * Registries - * [credential-process](#credential-process) --- Adds support for fetching registry tokens from an external authentication program. * [publish-timeout](#publish-timeout) --- Controls the timeout between uploading the crate and being available in the index - * [registry-auth](#registry-auth) --- Adds support for authenticated registries, and generate registry authentication tokens using asymmetric cryptography. + * [asymmetric-token](#asymmetric-token) --- Adds support for authentication tokens using asymmetric cryptography (`cargo:paseto` provider). * Other * [gitoxide](#gitoxide) --- Use `gitoxide` instead of `git2` for a set of operations. * [script](#script) --- Enable support for single-file `.rs` packages. @@ -934,30 +933,6 @@ It requires the `-Zpublish-timeout` command-line options to be set. timeout = 300 # in seconds ``` -## registry-auth -* Tracking Issue: [10474](https://github.com/rust-lang/cargo/issues/10474) -* RFC: [#3139](https://github.com/rust-lang/rfcs/pull/3139) - -Enables Cargo to include the authorization token for API requests, crate downloads -and sparse index updates by adding a configuration option to config.json -in the registry index. - -To use this feature, the registry server must include `"auth-required": true` in -`config.json`, and you must pass the `-Z registry-auth` flag on the Cargo command line. - -When using the sparse protocol, Cargo will attempt to fetch the `config.json` file before -fetching any other files. If the server responds with an HTTP 401, then Cargo will assume -that the registry requires authentication and re-attempt the request for `config.json` -with the authentication token included. - -On authentication failure (or missing authentication token) the server MAY include a -`WWW-Authenticate` header with a `Cargo login_url` challenge to indicate where the user -can go to get a token. - -``` -WWW-Authenticate: Cargo login_url="https://test-registry-login/me -``` - ## asymmetric-token * Tracking Issue: [10519](https://github.com/rust-lang/cargo/issues/10519) * RFC: [#3231](https://github.com/rust-lang/rfcs/pull/3231) @@ -998,267 +973,6 @@ The "footer" (which is part of the signature) will be a JSON string in UTF-8 and PASETO includes the message that was signed, so the server does not have to reconstruct the exact string from the request in order to check the signature. The server does need to check that the signature is valid for the string in the PASETO and that the contents of that string matches the request. If a claim should be expected for the request but is missing in the PASETO then the request must be rejected. -## credential-process -* Tracking Issue: [#8933](https://github.com/rust-lang/cargo/issues/8933) -* RFC: [#2730](https://github.com/rust-lang/rfcs/pull/2730) - -The `credential-process` feature adds a config setting to fetch registry -authentication tokens by calling an external process. - -To use this feature, you must pass the `-Z credential-process` flag on the -command-line. - -### `credential-process` Configuration - -To configure which process to run to fetch the token, specify the process in -the `registry` table in a [config file] with spaces separating arguments. If the -path to the provider or its arguments contain spaces, then it mused be defined in -the `credential-alias` table and referenced instead. - -```toml -[registry] -global-credential-providers = ["/usr/bin/cargo-creds"] -``` - -The provider at the end of the list will be attempted first. This ensures -that when config files are merged, files closer to the project (and ultimatly -environment variables) have precedence. - -In this example, the `my-provider` provider will be attempted first, and if -it cannot provide credentials, then the `cargo:token` provider will be used. - -```toml -[registry] -global-credential-providers = ['cargo:token', 'my-provider'] -``` - -If you want to use a different provider for a specific registry, it can be -specified in the `registries` table: - -```toml -[registries.my-registry] -credential-provider = "/usr/bin/cargo-creds" -``` - -The credential provider for crates.io can be specified as: - -```toml -[registry] -credential-provider = "/usr/bin/cargo-creds" -``` - -The value can be a string with spaces separating arguments or it can be a TOML -array of strings. - -For commonly-used providers, or providers that need to contain spaces in the arguments -or path, the `credential-alias` table can be used. These aliases can be referenced -in `credential-provider` or `global-credential-providers`. - -```toml -[credential-alias] -my-alias = ["/usr/bin/cargo-creds", "--argument"] - -[registry] -global-credential-providers = ["cargo:token", "my-alias"] -``` - -### Built-in providers - -Cargo now includes several built-in credential providers. These providers are -executed within the Cargo process. They are identified with the `cargo:` prefix. - -* `cargo:token` - Uses Cargo's config and `credentials.toml` to store the token (default). -* `cargo:wincred` - Uses the Windows Credential Manager to store the token. -* `cargo:macos-keychain` - Uses the macOS Keychain to store the token. -* `cargo:libsecret` - Uses [libsecret](https://wiki.gnome.org/Projects/Libsecret) to store tokens on Linux systems. -* `cargo:token-from-stdout ` - Launch a subprocess that returns a token - on stdout. Newlines will be trimmed. The process inherits the user's stdin and stderr. - It should exit 0 on success, and nonzero on error. - - With this form, [`cargo login`] and [`cargo logout`] are not supported and - return an error if used. - - The following environment variables will be provided to the executed command: - - * `CARGO` --- Path to the `cargo` binary executing the command. - * `CARGO_REGISTRY_INDEX_URL` --- The URL of the registry index. - * `CARGO_REGISTRY_NAME_OPT` --- Optional name of the registry. Should not be used as a storage key. Not always available. - -* `cargo:paseto` - implements asymmetric token support (RFC3231) as a credential provider. Requires `-Zasymmetric-token`. - - -`cargo-credential-1password` uses the 1password `op` CLI to store the token. You must -install the `op` CLI from the [1password -website](https://1password.com/downloads/command-line/). You must run `op -signin` at least once with the appropriate arguments (such as `op signin -my.1password.com user@example.com`), unless you provide the sign-in-address -and email arguments. The master password will be required on each request -unless the appropriate `OP_SESSION` environment variable is set. It supports -the following command-line arguments: -* `--account`: The account shorthand name to use. -* `--vault`: The vault name to use. -* `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`. -* `--email`: The email address to sign in with. - -Install the provider with `cargo install cargo-credential-1password` -In the config, add it to `global-credential-providers`: -```toml -[registry] -global-credential-providers = ["cargo-credential-1password"] -``` - -### JSON Interface -When using an external credential provider, Cargo communicates with the credential -provider using stdin/stdout messages passed as a single line of JSON. - -Cargo will always execute the credential provider with the `--cargo-plugin` argument. -This enables a credential provider executable to have additional functionality beyond -how Cargo uses it. - -The messages here have additional newlines added for readability. -Actual messages must not contain newlines. - -#### Credential hello -* Sent by: credential provider -* Purpose: used to identify the supported protocols on process startup -```javascript -{ - "v":[1] -} -``` - -#### Login request -* Sent by: Cargo -* Purpose: collect and store credentials -```javascript -{ - // Protocol version - "v":1, - // Action to perform: login - "kind":"login", - // Registry information - "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"}, -} -``` - -#### Read request -* Sent by: Cargo -* Purpose: Get the credential for reading crate information -```javascript -{ - // Protocol version - "v":1, - // Request kind: get credentials - "kind":"get", - // Action to perform: read crate information - "operation":"read", - // Registry information - "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"}, - // Additional command-line args (optional) - "args":[] -} -``` - -#### Publish request -* Sent by: Cargo -* Purpose: Get the credential for publishing a crate -```javascript -{ - // Protocol version - "v":1, - // Request kind: get credentials - "kind":"get", - // Action to perform: publish crate - "operation":"publish", - // Crate name - "name":"sample", - // Crate version - "vers":"0.1.0", - // Crate checksum - "cksum":"...", - // Registry information - "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"}, - // Additional command-line args (optional) - "args":[] -} -``` - -#### Success response -* Sent by: credential process -* Purpose: Gives the credential to Cargo -```javascript -{"Ok":{ - // Response kind: this was a get request kind - "kind":"get", - // Token to send to the registry - "token":"...", - // Cache control. Can be one of the following: - // * "never" - // * "session" - // * "expires" - "cache":"expires", - // Unix timestamp (only for "cache": "expires") - "expiration":1693942857, - // Is the token operation independent? - "operation_independent":true -}} -``` - -#### Failure response -* Sent by: credential process -* Purpose: Gives error information to Cargo -```javascript -{"Err":{ - // Error: the credential provider does not support the - // registry - "kind":"url-not-supported", - - // Error: The credential could not be found in the provider. - // using `cargo login --registry ...`. - "kind":"not-found", - - // Error: something else has failed - "kind":"other", - "detail": "free form string error message" -}} -``` - -#### Example communication to request a token for reading: -1. Cargo spawns the credential process, capturing stdin and stdout. -2. Credential process sends the Hello message to Cargo - ```javascript - { "v": [1] } - ``` -3. Cargo sends the CredentialRequest message to the credential process (newlines added for readability). - ```javascript - { - "v": 1, - "kind": "get", - "operation": "read", - "registry":{"index-url":"sparse+https://registry-url/index/", "name":"ado2"}, - "args":[] - } - ``` -4. Credential process sends the CredentialResponse to Cargo (newlines added for readability). - ```javascript - { - "token": "...", - "cache": "session", - "operation_independent": false - } - ``` -5. Credential process exits -6. Cargo uses the token for the remainder of the session (until Cargo exits) when interacting with this registry. - -[`cargo login`]: ../commands/cargo-login.md -[`cargo logout`]: ../commands/cargo-logout.md -[`cargo publish`]: ../commands/cargo-publish.md -[`cargo owner`]: ../commands/cargo-owner.md -[`cargo yank`]: ../commands/cargo-yank.md -[`credentials.toml` file]: config.md#credentials -[crates.io]: https://crates.io/ -[config file]: config.md - ## `cargo config` * Original Issue: [#2362](https://github.com/rust-lang/cargo/issues/2362) @@ -1872,7 +1586,7 @@ See [Registry Protocols](registries.md#registry-protocols) for more information. The [`cargo logout`] command has been stabilized in the 1.70 release. [target triple]: ../appendix/glossary.md#target '"target" (glossary)' - +[`cargo logout`]: ../commands/cargo-logout.md ## `doctest-in-workspace` @@ -1886,3 +1600,16 @@ for more information about the working directory for compiling and running tests The `--keep-going` option has been stabilized in the 1.74 release. See the [`--keep-going` flag](../commands/cargo-build.html#option-cargo-build---keep-going) in `cargo build` as an example for more details. + +## credential-process + +The `-Z credential-process` feature has been stabilized in the 1.74 release. + +See [Registry Authentication](registry-authentication.md) documentation for details. + +## registry-auth + +The `-Z registry-auth` feature has been stabilized in the 1.74 release with the additional +requirement that a credential-provider is configured. + +See [Registry Authentication](registry-authentication.md) documentation for details. diff --git a/tests/testsuite/credential_process.rs b/tests/testsuite/credential_process.rs index 55a568dff18f..8dc6c7eb114c 100644 --- a/tests/testsuite/credential_process.rs +++ b/tests/testsuite/credential_process.rs @@ -7,63 +7,6 @@ fn toml_bin(proj: &Project, name: &str) -> String { proj.bin(name).display().to_string().replace('\\', "\\\\") } -#[cargo_test] -fn gated() { - let _alternative = registry::RegistryBuilder::new() - .alternative() - .no_configure_token() - .build(); - - let cratesio = registry::RegistryBuilder::new() - .no_configure_token() - .build(); - - let p = project() - .file( - ".cargo/config", - r#" - [registry] - credential-provider = ["false"] - "#, - ) - .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) - .file("src/lib.rs", "") - .build(); - - p.cargo("publish --no-verify") - .replace_crates_io(cratesio.index_url()) - .masquerade_as_nightly_cargo(&["credential-process"]) - .with_status(101) - .with_stderr( - "\ -[UPDATING] [..] -[ERROR] no token found, please run `cargo login` -or use environment variable CARGO_REGISTRY_TOKEN -", - ) - .run(); - - p.change_file( - ".cargo/config", - r#" - [registry.alternative] - credential-process = "false" - "#, - ); - - p.cargo("publish --no-verify --registry alternative") - .masquerade_as_nightly_cargo(&["credential-process"]) - .with_status(101) - .with_stderr( - "\ -[UPDATING] [..] -[ERROR] no token found for `alternative`, please run `cargo login --registry alternative` -or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN -", - ) - .run(); -} - /// Setup for a test that will issue a command that needs to fetch a token. /// /// This does the following: @@ -125,8 +68,7 @@ fn publish() { // Checks that credential-process is used for `cargo publish`. let (p, _t) = get_token_test(); - p.cargo("publish --no-verify --registry alternative -Z credential-process -Z registry-auth") - .masquerade_as_nightly_cargo(&["credential-process"]) + p.cargo("publish --no-verify --registry alternative") .with_stderr( r#"[UPDATING] [..] {"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read"} @@ -151,9 +93,8 @@ fn basic_unsupported() { .credential_provider(&["cargo:token-from-stdout", "false"]) .build(); - cargo_process("login -Z credential-process abcdefg") + cargo_process("login abcdefg") .replace_crates_io(registry.index_url()) - .masquerade_as_nightly_cargo(&["credential-process"]) .with_status(101) .with_stderr( "\ @@ -166,9 +107,8 @@ Caused by: ) .run(); - cargo_process("logout -Z credential-process") + cargo_process("logout") .replace_crates_io(registry.index_url()) - .masquerade_as_nightly_cargo(&["credential-process"]) .with_status(101) .with_stderr( "\ @@ -192,8 +132,7 @@ fn login() { ]) .build(); - cargo_process("login -Z credential-process abcdefg -- cmd3 --cmd4") - .masquerade_as_nightly_cargo(&["credential-process"]) + cargo_process("login abcdefg -- cmd3 --cmd4") .replace_crates_io(registry.index_url()) .with_stderr( r#"[UPDATING] [..] @@ -213,8 +152,7 @@ fn logout() { )]) .build(); - cargo_process("logout -Z credential-process") - .masquerade_as_nightly_cargo(&["credential-process"]) + cargo_process("logout") .replace_crates_io(server.index_url()) .with_stderr( r#"{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"logout"} @@ -227,8 +165,7 @@ fn logout() { fn yank() { let (p, _t) = get_token_test(); - p.cargo("yank --version 0.1.0 --registry alternative -Zcredential-process -Zregistry-auth") - .masquerade_as_nightly_cargo(&["credential-process"]) + p.cargo("yank --version 0.1.0 --registry alternative") .with_stderr( r#"[UPDATING] [..] {"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read"} @@ -243,8 +180,7 @@ fn yank() { fn owner() { let (p, _t) = get_token_test(); - p.cargo("owner --add username --registry alternative -Zcredential-process -Zregistry-auth") - .masquerade_as_nightly_cargo(&["credential-process"]) + p.cargo("owner --add username --registry alternative") .with_stderr( r#"[UPDATING] [..] {"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read"} @@ -278,8 +214,7 @@ fn invalid_token_output() { .file("src/lib.rs", "") .build(); - p.cargo("publish --no-verify --registry alternative -Z credential-process") - .masquerade_as_nightly_cargo(&["credential-process"]) + p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr( "\ @@ -333,8 +268,7 @@ fn not_found() { .build(); // should not suggest a _TOKEN environment variable since the cargo:token provider isn't available. - cargo_process("install -v foo -Zcredential-process -Zregistry-auth") - .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"]) + cargo_process("install -v foo") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr( @@ -373,8 +307,7 @@ fn all_not_found() { .unwrap(); // should not suggest a _TOKEN environment variable since the cargo:token provider isn't available. - cargo_process("install -v foo -Zcredential-process -Zregistry-auth") - .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"]) + cargo_process("install -v foo") .replace_crates_io(server.index_url()) .with_status(101) .with_stderr( @@ -413,8 +346,7 @@ fn all_not_supported() { ) .unwrap(); - cargo_process("install -v foo -Zcredential-process -Zregistry-auth") - .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"]) + cargo_process("install -v foo") .replace_crates_io(server.index_url()) .with_status(101) .with_stderr( @@ -461,8 +393,7 @@ fn multiple_providers() { ) .unwrap(); - cargo_process("login -Z credential-process -v abcdefg") - .masquerade_as_nightly_cargo(&["credential-process"]) + cargo_process("login -v abcdefg") .replace_crates_io(server.index_url()) .with_stderr( r#"[UPDATING] [..] @@ -481,8 +412,8 @@ fn both_token_and_provider() { .credential_provider(&["cargo:paseto"]) .build(); - cargo_process("login -Z credential-process -Z asymmetric-token") - .masquerade_as_nightly_cargo(&["credential-process", "asymmetric-token"]) + cargo_process("login -Z asymmetric-token") + .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(server.index_url()) .with_stderr( r#"[UPDATING] [..] @@ -508,8 +439,7 @@ fn registry_provider_overrides_global() { ) .unwrap(); - cargo_process("login -Z credential-process -v abcdefg") - .masquerade_as_nightly_cargo(&["credential-process"]) + cargo_process("login -v abcdefg") .env("CARGO_REGISTRY_CREDENTIAL_PROVIDER", "cargo:token") .replace_crates_io(server.index_url()) .with_stderr( @@ -619,8 +549,7 @@ You may press ctrl-c [..] // The output should contain two JSON messages from the provider in boths cases: // The first because the credential is expired, the second because the provider // indicated that the token was non-operation-independent. - p.cargo("publish -Z credential-process --registry alternative --no-verify") - .masquerade_as_nightly_cargo(&["credential-process"]) + p.cargo("publish --registry alternative --no-verify") .with_stderr(output) .run(); @@ -636,8 +565,7 @@ You may press ctrl-c [..] ), ); - p.cargo("publish -Z credential-process --registry alternative --no-verify") - .masquerade_as_nightly_cargo(&["credential-process"]) + p.cargo("publish --registry alternative --no-verify") .with_stderr(output) .run(); } @@ -687,8 +615,7 @@ fn basic_provider() { .build(); Package::new("bar", "0.0.1").alternative(true).publish(); - p.cargo("check -Z credential-process -Z registry-auth") - .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"]) + p.cargo("check") .with_stderr( "\ [UPDATING] `alternative` index @@ -731,8 +658,7 @@ fn unsupported_version() { .credential_provider(&[&provider]) .build(); - cargo_process("login -Z credential-process abcdefg") - .masquerade_as_nightly_cargo(&["credential-process"]) + cargo_process("login abcdefg") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr( diff --git a/tests/testsuite/login.rs b/tests/testsuite/login.rs index 9b84c541e477..0eec734cebb2 100644 --- a/tests/testsuite/login.rs +++ b/tests/testsuite/login.rs @@ -197,8 +197,8 @@ fn bad_asymmetric_token_args() { .build(); // These cases are kept brief as the implementation is covered by clap, so this is only smoke testing that we have clap configured correctly. - cargo_process("login -Zcredential-process -Zasymmetric-token -- --key-subject") - .masquerade_as_nightly_cargo(&["credential-process", "asymmetric-token"]) + cargo_process("login -Zasymmetric-token -- --key-subject") + .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .with_stderr_contains( " error: a value is required for '--key-subject ' but none was supplied", @@ -228,8 +228,8 @@ fn login_with_asymmetric_token_and_subject_on_stdin() { .no_configure_token() .build(); let credentials = credentials_toml(); - cargo_process("login -v -Z credential-process -Z asymmetric-token -- --key-subject=foo") - .masquerade_as_nightly_cargo(&["credential-process"]) + cargo_process("login -v -Z asymmetric-token -- --key-subject=foo") + .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .with_stderr_contains( "\ @@ -286,8 +286,8 @@ fn login_with_asymmetric_token_on_stdin() { .no_configure_token() .build(); let credentials = credentials_toml(); - cargo_process("login -vZ credential-process -Z asymmetric-token --registry alternative") - .masquerade_as_nightly_cargo(&["credential-process", "asymmetric-token"]) + cargo_process("login -v -Z asymmetric-token --registry alternative") + .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr( "\ [UPDATING] [..] @@ -308,8 +308,8 @@ fn login_with_generate_asymmetric_token() { .no_configure_token() .build(); let credentials = credentials_toml(); - cargo_process("login -Z credential-process -Z asymmetric-token --registry alternative") - .masquerade_as_nightly_cargo(&["credential-process", "asymmetric-token"]) + cargo_process("login -Z asymmetric-token --registry alternative") + .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr("[UPDATING] `alternative` index\nk3.public.[..]") .run(); let credentials = fs::read_to_string(&credentials).unwrap(); diff --git a/tests/testsuite/registry_auth.rs b/tests/testsuite/registry_auth.rs index 9f127669df3f..df2bdf565d79 100644 --- a/tests/testsuite/registry_auth.rs +++ b/tests/testsuite/registry_auth.rs @@ -6,10 +6,12 @@ use cargo_test_support::{project, Execs, Project}; fn cargo(p: &Project, s: &str) -> Execs { let mut e = p.cargo(s); - e.masquerade_as_nightly_cargo(&["registry-auth", "credential-process", "asymmetric-token"]) - .arg("-Zregistry-auth") - .arg("-Zcredential-process") + e.masquerade_as_nightly_cargo(&["asymmetric-token"]) .arg("-Zasymmetric-token"); + e.env( + "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS", + "cargo:paseto cargo:token", + ); e } @@ -44,7 +46,7 @@ static SUCCESS_OUTPUT: &'static str = "\ "; #[cargo_test] -fn requires_nightly() { +fn requires_credential_provider() { let _registry = RegistryBuilder::new() .alternative() .auth_required() @@ -56,14 +58,14 @@ fn requires_nightly() { .with_status(101) .with_stderr( r#"[UPDATING] `alternative` index -[DOWNLOADING] crates ... -error: failed to download from `[..]/dl/bar/0.0.1/download` +error: failed to download `bar v0.0.1 (registry `alternative`)` Caused by: - failed to get successful HTTP response from `[..]` (127.0.0.1), got 401 - body: - Unauthorized message from server. -"#, + unable to get packages from source + +Caused by: + authenticated registries require a credential-provider to be configured + see https://doc.rust-lang.org/cargo/reference/registry-authentication.html for details"#, ) .run(); } diff --git a/tests/testsuite/search.rs b/tests/testsuite/search.rs index dd54a5bcf4bf..4c3155c8fb2b 100644 --- a/tests/testsuite/search.rs +++ b/tests/testsuite/search.rs @@ -173,8 +173,7 @@ fn colored_results() { fn auth_required_failure() { let server = setup().auth_required().no_configure_token().build(); - cargo_process("-Zregistry-auth search postgres") - .masquerade_as_nightly_cargo(&["registry-auth"]) + cargo_process("search postgres") .replace_crates_io(server.index_url()) .with_status(101) .with_stderr_contains("[ERROR] no token found, please run `cargo login`") @@ -185,8 +184,7 @@ fn auth_required_failure() { fn auth_required() { let server = setup().auth_required().build(); - cargo_process("-Zregistry-auth search postgres") - .masquerade_as_nightly_cargo(&["registry-auth"]) + cargo_process("search postgres") .replace_crates_io(server.index_url()) .with_stdout_contains(SEARCH_RESULTS) .run();