Skip to content

Commit 07cac75

Browse files
committed
feat: stabilize credential-process and registry-auth
1 parent 4518131 commit 07cac75

File tree

24 files changed

+550
-448
lines changed

24 files changed

+550
-448
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
# cargo-credential-1password
22

3-
This is the implementation for the Cargo credential helper for [1password].
4-
See the [credential-process] documentation for how to use this.
3+
A Cargo [credential provider] for [1password].
4+
5+
`cargo-credential-1password` uses the 1password `op` CLI to store the token. You must
6+
install the `op` CLI from the [1password
7+
website](https://1password.com/downloads/command-line/). You must run `op signin`
8+
at least once with the appropriate arguments (such as `op signin my.1password.com [email protected]`),
9+
unless you provide the sign-in-address and email arguments. The master password will be required on each request
10+
unless the appropriate `OP_SESSION` environment variable is set. It supports
11+
the following command-line arguments:
12+
* `--account`: The account shorthand name to use.
13+
* `--vault`: The vault name to use.
14+
* `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`.
15+
* `--email`: The email address to sign in with.
516

617
[1password]: https://1password.com/
7-
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
18+
[credential provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html

credential/cargo-credential-libsecret/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
This is the implementation for the Cargo credential helper for [GNOME libsecret].
44
See the [credential-process] documentation for how to use this.
55

6+
This credential provider is built-in to cargo as `cargo:libsecret`.
7+
68
[GNOME libsecret]: https://wiki.gnome.org/Projects/Libsecret
7-
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
9+
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html

credential/cargo-credential-macos-keychain/README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@
33
This is the implementation for the Cargo credential helper for [macOS Keychain].
44
See the [credential-process] documentation for how to use this.
55

6+
This credential provider is built-in to cargo as `cargo:macos-keychain`.
7+
68
[macOS Keychain]: https://support.apple.com/guide/keychain-access/welcome/mac
7-
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
9+
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html
10+

credential/cargo-credential-wincred/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
This is the implementation for the Cargo credential helper for [Windows Credential Manager].
44
See the [credential-process] documentation for how to use this.
55

6+
This credential provider is built-in to cargo as `cargo:wincred`.
7+
68
[Windows Credential Manager]: https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0
7-
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
9+
[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html

credential/cargo-credential/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ provides an interface to store tokens for authorizing access to a registry
55
such as https://crates.io/.
66

77
Documentation about credential processes may be found at
8-
https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
8+
https://doc.rust-lang.org/nightly/cargo/reference/credential-provider-protocol.html
99

1010
Example implementations may be found at
1111
https://github.com/rust-lang/cargo/tree/master/credential

src/cargo/core/features.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,6 @@ unstable_cli_options!(
729729
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"),
730730
codegen_backend: bool = ("Enable the `codegen-backend` option in profiles in .cargo/config.toml file"),
731731
config_include: bool = ("Enable the `include` key in config files"),
732-
credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"),
733732
direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"),
734733
doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
735734
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
@@ -745,7 +744,6 @@ unstable_cli_options!(
745744
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
746745
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
747746
publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
748-
registry_auth: bool = ("Authentication for alternative registries"),
749747
rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"),
750748
rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"),
751749
script: bool = ("Enable support for single-file, `.rs` packages"),
@@ -819,6 +817,12 @@ const STABILIZED_TERMINAL_WIDTH: &str =
819817

820818
const STABILISED_SPARSE_REGISTRY: &str = "The sparse protocol is now the default for crates.io";
821819

820+
const STABILIZED_CREDENTIAL_PROCESS: &str =
821+
"Authentication with a credential provider is always available.";
822+
823+
const STABILIZED_REGISTRY_AUTH: &str =
824+
"Authenticated registries are available if a credential provider is configured.";
825+
822826
fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
823827
where
824828
D: serde::Deserializer<'de>,
@@ -1084,6 +1088,8 @@ impl CliUnstable {
10841088
"sparse-registry" => stabilized_warn(k, "1.68", STABILISED_SPARSE_REGISTRY),
10851089
"terminal-width" => stabilized_warn(k, "1.68", STABILIZED_TERMINAL_WIDTH),
10861090
"doctest-in-workspace" => stabilized_warn(k, "1.72", STABILIZED_DOCTEST_IN_WORKSPACE),
1091+
"credential-process" => stabilized_warn(k, "1.74", STABILIZED_CREDENTIAL_PROCESS),
1092+
"registry-auth" => stabilized_warn(k, "1.74", STABILIZED_REGISTRY_AUTH),
10871093

10881094
// Unstable features
10891095
// Sorted alphabetically:
@@ -1101,7 +1107,6 @@ impl CliUnstable {
11011107
}
11021108
"codegen-backend" => self.codegen_backend = parse_empty(k, v)?,
11031109
"config-include" => self.config_include = parse_empty(k, v)?,
1104-
"credential-process" => self.credential_process = parse_empty(k, v)?,
11051110
"direct-minimal-versions" => self.direct_minimal_versions = parse_empty(k, v)?,
11061111
"doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
11071112
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
@@ -1122,7 +1127,6 @@ impl CliUnstable {
11221127
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
11231128
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
11241129
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
1125-
"registry-auth" => self.registry_auth = parse_empty(k, v)?,
11261130
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
11271131
"rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?,
11281132
"separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,

src/cargo/lib.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,7 @@
9191
//! This is the `#[cargo_test]` proc-macro used by the test suite to define tests.
9292
//! - [`credential`](https://github.com/rust-lang/cargo/tree/master/credential)
9393
//! This subdirectory contains several packages for implementing the
94-
//! experimental
95-
//! [credential-process](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process)
96-
//! feature.
94+
//! [credential providers](https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html).
9795
//! - [`mdman`](https://github.com/rust-lang/cargo/tree/master/crates/mdman)
9896
//! ([nightly docs](https://doc.rust-lang.org/nightly/nightly-rustc/mdman/index.html)):
9997
//! This is a utility for generating cargo's man pages. See [Building the man

src/cargo/ops/registry/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ fn registry(
145145
None,
146146
operation,
147147
vec![],
148+
false,
148149
)?)
149150
} else {
150151
None

src/cargo/ops/registry/publish.rs

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
161161
None,
162162
operation,
163163
vec![],
164+
false,
164165
)?));
165166
}
166167

src/cargo/sources/registry/download.rs

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pub(super) fn download(
8585
None,
8686
Operation::Read,
8787
vec![],
88+
true,
8889
)?)
8990
} else {
9091
None

src/cargo/sources/registry/http_remote.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
547547
return Poll::Ready(Ok(LoadResponse::NotFound));
548548
}
549549
StatusCode::Unauthorized
550-
if !self.auth_required
551-
&& path == Path::new(RegistryConfig::NAME)
552-
&& self.config.cli_unstable().registry_auth =>
550+
if !self.auth_required && path == Path::new(RegistryConfig::NAME) =>
553551
{
554552
debug!(target: "network", "re-attempting request for config.json with authorization included.");
555553
self.fresh.remove(path);
@@ -612,10 +610,6 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
612610
}
613611
}
614612

615-
if !self.config.cli_unstable().registry_auth {
616-
self.auth_required = false;
617-
}
618-
619613
// Looks like we're going to have to do a network request.
620614
self.start_fetch()?;
621615

@@ -654,6 +648,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
654648
self.login_url.as_ref(),
655649
Operation::Read,
656650
self.auth_error_headers.clone(),
651+
true,
657652
)?;
658653
headers.append(&format!("Authorization: {}", authorization))?;
659654
trace!(target: "network", "including authorization for {}", full_url);
@@ -724,10 +719,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
724719
}
725720

726721
fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
727-
let mut cfg = ready!(self.config()?).clone();
728-
if !self.config.cli_unstable().registry_auth {
729-
cfg.auth_required = false;
730-
}
722+
let cfg = ready!(self.config()?).clone();
731723
Poll::Ready(Ok(Some(cfg)))
732724
}
733725

src/cargo/sources/registry/remote.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
307307
match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) {
308308
LoadResponse::Data { raw_data, .. } => {
309309
trace!("config loaded");
310-
let mut cfg: RegistryConfig = serde_json::from_slice(&raw_data)?;
311-
if !self.config.cli_unstable().registry_auth {
312-
cfg.auth_required = false;
313-
}
310+
let cfg: RegistryConfig = serde_json::from_slice(&raw_data)?;
314311
Poll::Ready(Ok(Some(cfg)))
315312
}
316313
_ => Poll::Ready(Ok(None)),

src/cargo/util/auth/mod.rs

+38-15
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ impl RegistryConfigExtended {
7777
fn credential_provider(
7878
config: &Config,
7979
sid: &SourceId,
80+
require_cred_provider_config: bool,
8081
show_warnings: bool,
8182
) -> CargoResult<Vec<Vec<String>>> {
8283
let warn = |message: String| {
@@ -88,7 +89,9 @@ fn credential_provider(
8889
};
8990

9091
let cfg = registry_credential_config_raw(config, sid)?;
92+
let mut global_provider_defined = true;
9193
let default_providers = || {
94+
global_provider_defined = false;
9295
if config.cli_unstable().asymmetric_token {
9396
// Enable the PASETO provider
9497
vec![
@@ -101,7 +104,7 @@ fn credential_provider(
101104
};
102105
let global_providers = config
103106
.get::<Option<Vec<Value<String>>>>("registry.global-credential-providers")?
104-
.filter(|p| !p.is_empty() && config.cli_unstable().credential_process)
107+
.filter(|p| !p.is_empty())
105108
.map(|p| {
106109
p.iter()
107110
.rev()
@@ -112,14 +115,14 @@ fn credential_provider(
112115
.unwrap_or_else(default_providers);
113116
tracing::debug!(?global_providers);
114117

115-
let providers = match cfg {
118+
match cfg {
116119
// If there's a specific provider configured for this registry, use it.
117120
Some(RegistryConfig {
118121
credential_provider: Some(provider),
119122
token,
120123
secret_key,
121124
..
122-
}) if config.cli_unstable().credential_process => {
125+
}) => {
123126
let provider = resolve_credential_alias(config, provider);
124127
if let Some(token) = token {
125128
if provider[0] != "cargo:token" {
@@ -139,7 +142,7 @@ fn credential_provider(
139142
))?;
140143
}
141144
}
142-
vec![provider]
145+
return Ok(vec![provider]);
143146
}
144147

145148
// Warning for both `token` and `secret-key`, stating which will be ignored
@@ -173,7 +176,6 @@ fn credential_provider(
173176
// One or both of the below individual warnings will trigger
174177
}
175178
}
176-
global_providers
177179
}
178180

179181
// Check if a `token` is configured that will be ignored.
@@ -191,7 +193,6 @@ fn credential_provider(
191193
token.definition
192194
))?;
193195
}
194-
global_providers
195196
}
196197

197198
// Check if a asymmetric token is configured that will be ignored.
@@ -210,13 +211,18 @@ fn credential_provider(
210211
token.definition
211212
))?;
212213
}
213-
global_providers
214214
}
215215

216216
// If we couldn't find a registry-specific provider, use the fallback provider list.
217-
None | Some(RegistryConfig { .. }) => global_providers,
217+
None | Some(RegistryConfig { .. }) => {}
218218
};
219-
Ok(providers)
219+
if !global_provider_defined && require_cred_provider_config {
220+
bail!(
221+
"authenticated registries require a credential-provider to be configured\n\
222+
see https://doc.rust-lang.org/cargo/reference/registry-authentication.html for details"
223+
);
224+
}
225+
Ok(global_providers)
220226
}
221227

222228
/// Get the credential configuration for a `SourceId`.
@@ -393,7 +399,7 @@ impl AuthorizationError {
393399
// Only display the _TOKEN environment variable suggestion if the `cargo:token` credential
394400
// provider is available for the source. Otherwise setting the environment variable will
395401
// have no effect.
396-
let display_token_env_help = credential_provider(config, &sid, false)?
402+
let display_token_env_help = credential_provider(config, &sid, false, false)?
397403
.iter()
398404
.any(|p| p.first().map(String::as_str) == Some("cargo:token"));
399405
Ok(AuthorizationError {
@@ -476,6 +482,7 @@ fn credential_action(
476482
action: Action<'_>,
477483
headers: Vec<String>,
478484
args: &[&str],
485+
require_cred_provider_config: bool,
479486
) -> CargoResult<CredentialResponse> {
480487
let name = if sid.is_crates_io() {
481488
Some(CRATES_IO_REGISTRY)
@@ -487,7 +494,7 @@ fn credential_action(
487494
name,
488495
headers,
489496
};
490-
let providers = credential_provider(config, sid, true)?;
497+
let providers = credential_provider(config, sid, require_cred_provider_config, true)?;
491498
let mut any_not_found = false;
492499
for provider in providers {
493500
let args: Vec<&str> = provider
@@ -549,8 +556,15 @@ pub fn auth_token(
549556
login_url: Option<&Url>,
550557
operation: Operation<'_>,
551558
headers: Vec<String>,
559+
require_cred_provider_config: bool,
552560
) -> CargoResult<String> {
553-
match auth_token_optional(config, sid, operation, headers)? {
561+
match auth_token_optional(
562+
config,
563+
sid,
564+
operation,
565+
headers,
566+
require_cred_provider_config,
567+
)? {
554568
Some(token) => Ok(token.expose()),
555569
None => Err(AuthorizationError::new(
556570
config,
@@ -568,6 +582,7 @@ fn auth_token_optional(
568582
sid: &SourceId,
569583
operation: Operation<'_>,
570584
headers: Vec<String>,
585+
require_cred_provider_config: bool,
571586
) -> CargoResult<Option<Secret<String>>> {
572587
tracing::trace!("token requested for {}", sid.display_registry_name());
573588
let mut cache = config.credential_cache();
@@ -588,7 +603,14 @@ fn auth_token_optional(
588603
}
589604
}
590605

591-
let credential_response = credential_action(config, sid, Action::Get(operation), headers, &[]);
606+
let credential_response = credential_action(
607+
config,
608+
sid,
609+
Action::Get(operation),
610+
headers,
611+
&[],
612+
require_cred_provider_config,
613+
);
592614
if let Some(e) = credential_response.as_ref().err() {
593615
if let Some(e) = e.downcast_ref::<cargo_credential::Error>() {
594616
if matches!(e, cargo_credential::Error::NotFound) {
@@ -627,7 +649,7 @@ fn auth_token_optional(
627649

628650
/// Log out from the given registry.
629651
pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> {
630-
let credential_response = credential_action(config, sid, Action::Logout, vec![], &[]);
652+
let credential_response = credential_action(config, sid, Action::Logout, vec![], &[], false);
631653
if let Some(e) = credential_response.as_ref().err() {
632654
if let Some(e) = e.downcast_ref::<cargo_credential::Error>() {
633655
if matches!(e, cargo_credential::Error::NotFound) {
@@ -656,7 +678,8 @@ pub fn login(
656678
options: LoginOptions<'_>,
657679
args: &[&str],
658680
) -> CargoResult<()> {
659-
let credential_response = credential_action(config, sid, Action::Login(options), vec![], args)?;
681+
let credential_response =
682+
credential_action(config, sid, Action::Login(options), vec![], args, false)?;
660683
let CredentialResponse::Login = credential_response else {
661684
bail!("credential provider produced unexpected response for `login` request: {credential_response:?}")
662685
};

src/doc/src/SUMMARY.md

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
* [Source Replacement](reference/source-replacement.md)
3737
* [External Tools](reference/external-tools.md)
3838
* [Registries](reference/registries.md)
39+
* [Registry Authentication](reference/registry-authentication.md)
40+
* [Credential Provider Protocol](reference/credential-provider-protocol.md)
3941
* [Running a Registry](reference/running-a-registry.md)
4042
* [Registry Index](reference/registry-index.md)
4143
* [Registry Web API](reference/registry-web-api.md)

0 commit comments

Comments
 (0)