Skip to content

Commit d86beb5

Browse files
committed
login: allow passing additional args to provider
1 parent f137594 commit d86beb5

File tree

7 files changed

+97
-14
lines changed

7 files changed

+97
-14
lines changed

src/bin/cargo/commands/login.rs

+12
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,28 @@ pub fn cli() -> Command {
77
.about("Log in to a registry.")
88
.arg(Arg::new("token").action(ArgAction::Set))
99
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
10+
.arg(
11+
Arg::new("args")
12+
.help("Arguments for the credential provider (unstable)")
13+
.num_args(0..)
14+
.last(true),
15+
)
1016
.arg_quiet()
1117
.after_help("Run `cargo help login` for more detailed information.\n")
1218
}
1319

1420
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
1521
let registry = args.registry(config)?;
22+
let extra_args = args
23+
.get_many::<String>("args")
24+
.unwrap_or_default()
25+
.map(String::as_str)
26+
.collect::<Vec<_>>();
1627
ops::registry_login(
1728
config,
1829
args.get_one::<String>("token").map(|s| s.as_str().into()),
1930
registry.as_deref(),
31+
&extra_args,
2032
)?;
2133
Ok(())
2234
}

src/cargo/ops/registry/login.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub fn registry_login(
2121
config: &Config,
2222
token_from_cmdline: Option<Secret<&str>>,
2323
reg: Option<&str>,
24+
args: &[&str],
2425
) -> CargoResult<()> {
2526
let source_ids = get_source_id(config, None, reg)?;
2627

@@ -50,6 +51,6 @@ pub fn registry_login(
5051
login_url: login_url.as_deref(),
5152
};
5253

53-
auth::login(config, &source_ids.original, options)?;
54+
auth::login(config, &source_ids.original, options, args)?;
5455
Ok(())
5556
}

src/cargo/util/auth/mod.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ fn credential_action(
429429
sid: &SourceId,
430430
action: Action<'_>,
431431
headers: Vec<String>,
432+
args: &[&str],
432433
) -> CargoResult<CredentialResponse> {
433434
let name = if sid.is_crates_io() {
434435
Some(CRATES_IO_REGISTRY)
@@ -442,7 +443,11 @@ fn credential_action(
442443
};
443444
let providers = credential_provider(config, sid)?;
444445
for provider in providers {
445-
let args: Vec<&str> = provider.iter().map(String::as_str).collect();
446+
let args: Vec<&str> = provider
447+
.iter()
448+
.map(String::as_str)
449+
.chain(args.iter().map(|s| *s))
450+
.collect();
446451
let process = args[0];
447452
tracing::debug!("attempting credential provider: {args:?}");
448453
let provider: Box<dyn Credential> = match process {
@@ -529,7 +534,7 @@ fn auth_token_optional(
529534
}
530535
}
531536

532-
let credential_response = credential_action(config, sid, Action::Get(operation), headers);
537+
let credential_response = credential_action(config, sid, Action::Get(operation), headers, &[]);
533538
if let Some(e) = credential_response.as_ref().err() {
534539
if let Some(e) = e.downcast_ref::<cargo_credential::Error>() {
535540
if matches!(e, cargo_credential::Error::NotFound) {
@@ -568,7 +573,7 @@ fn auth_token_optional(
568573

569574
/// Log out from the given registry.
570575
pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> {
571-
let credential_response = credential_action(config, sid, Action::Logout, vec![]);
576+
let credential_response = credential_action(config, sid, Action::Logout, vec![], &[]);
572577
if let Some(e) = credential_response.as_ref().err() {
573578
if let Some(e) = e.downcast_ref::<cargo_credential::Error>() {
574579
if matches!(e, cargo_credential::Error::NotFound) {
@@ -591,8 +596,13 @@ pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> {
591596
}
592597

593598
/// Log in to the given registry.
594-
pub fn login(config: &Config, sid: &SourceId, options: LoginOptions<'_>) -> CargoResult<()> {
595-
let credential_response = credential_action(config, sid, Action::Login(options), vec![])?;
599+
pub fn login(
600+
config: &Config,
601+
sid: &SourceId,
602+
options: LoginOptions<'_>,
603+
args: &[&str],
604+
) -> CargoResult<()> {
605+
let credential_response = credential_action(config, sid, Action::Login(options), vec![], args)?;
596606
let CredentialResponse::Login = credential_response else {
597607
bail!("credential provider produced unexpected response for `login` request: {credential_response:?}")
598608
};

src/cargo/util/credential/paseto.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use anyhow::Context;
44
use cargo_credential::{
55
Action, CacheControl, Credential, CredentialResponse, Error, Operation, RegistryInfo, Secret,
66
};
7+
use clap::Command;
78
use pasetors::{
89
keys::{AsymmetricKeyPair, AsymmetricPublicKey, AsymmetricSecretKey, Generate},
910
paserk::FormatAsPaserk,
@@ -14,7 +15,7 @@ use url::Url;
1415
use crate::{
1516
core::SourceId,
1617
ops::RegistryCredentialConfig,
17-
util::{auth::registry_credential_config_raw, config},
18+
util::{auth::registry_credential_config_raw, command_prelude::opt, config},
1819
Config,
1920
};
2021

@@ -60,7 +61,7 @@ impl<'a> Credential for PasetoCredential<'a> {
6061
&self,
6162
registry: &RegistryInfo<'_>,
6263
action: &Action<'_>,
63-
_args: &[&str],
64+
args: &[&str],
6465
) -> Result<CredentialResponse, Error> {
6566
let index_url = Url::parse(registry.index_url).context("parsing index url")?;
6667
let sid = if let Some(name) = registry.name {
@@ -71,6 +72,13 @@ impl<'a> Credential for PasetoCredential<'a> {
7172

7273
let reg_cfg = registry_credential_config_raw(self.config, &sid)?;
7374

75+
let matches = Command::new("cargo:paseto")
76+
.no_binary_name(true)
77+
.arg(opt("key-subject", "Set the key subject for this registry").value_name("SUBJECT"))
78+
.try_get_matches_from(args)
79+
.map_err(Box::new)?;
80+
let key_subject = matches.get_one("key-subject").map(String::as_str);
81+
7482
match action {
7583
Action::Get(operation) => {
7684
let Some(reg_cfg) = reg_cfg else {
@@ -163,6 +171,7 @@ impl<'a> Credential for PasetoCredential<'a> {
163171
})
164172
}
165173
Action::Login(options) => {
174+
let old_key_subject = reg_cfg.and_then(|cfg| cfg.secret_key_subject);
166175
let new_token;
167176
let secret_key: Secret<String>;
168177
if let Some(key) = &options.token {
@@ -180,7 +189,13 @@ impl<'a> Credential for PasetoCredential<'a> {
180189
} else {
181190
return Err("not a validly formatted PASERK secret key".into());
182191
}
183-
new_token = RegistryCredentialConfig::AsymmetricKey((secret_key, None));
192+
new_token = RegistryCredentialConfig::AsymmetricKey((
193+
secret_key,
194+
match key_subject {
195+
Some(key_subject) => Some(key_subject.to_string()),
196+
None => old_key_subject,
197+
},
198+
));
184199
config::save_credentials(self.config, Some(new_token), &sid)?;
185200
Ok(CredentialResponse::Login)
186201
}

tests/testsuite/cargo_login/help/stdout.log

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
Log in to a registry.
22

3-
Usage: cargo[EXE] login [OPTIONS] [token]
3+
Usage: cargo login [OPTIONS] [token] [-- [args]...]
44

55
Arguments:
6-
[token]
6+
[token]
7+
[args]... Arguments for the credential provider (unstable)
78

89
Options:
910
--registry <REGISTRY> Registry to use

tests/testsuite/credential_process.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,19 @@ Caused by:
185185
fn login() {
186186
let registry = registry::RegistryBuilder::new()
187187
.no_configure_token()
188-
.credential_provider(&[&build_provider("test-cred", r#"{"Ok": {"kind": "login"}}"#)])
188+
.credential_provider(&[
189+
&build_provider("test-cred", r#"{"Ok": {"kind": "login"}}"#),
190+
"cfg1",
191+
"--cfg2",
192+
])
189193
.build();
190194

191-
cargo_process("login -Z credential-process abcdefg")
195+
cargo_process("login -Z credential-process abcdefg -- cmd3 --cmd4")
192196
.masquerade_as_nightly_cargo(&["credential-process"])
193197
.replace_crates_io(registry.index_url())
194198
.with_stderr(
195199
r#"[UPDATING] [..]
196-
{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[..]","args":[]}
200+
{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[..]","args":["cfg1","--cfg2","cmd3","--cmd4"]}
197201
"#,
198202
)
199203
.run();

tests/testsuite/login.rs

+40
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,24 @@ Caused by:
189189
);
190190
}
191191

192+
#[cargo_test]
193+
fn bad_asymmetric_token_args() {
194+
let registry = RegistryBuilder::new()
195+
.credential_provider(&["cargo:paseto"])
196+
.no_configure_token()
197+
.build();
198+
199+
// These cases are kept brief as the implementation is covered by clap, so this is only smoke testing that we have clap configured correctly.
200+
cargo_process("login -Zcredential-process -- --key-subject")
201+
.masquerade_as_nightly_cargo(&["credential-process"])
202+
.replace_crates_io(registry.index_url())
203+
.with_stderr_contains(
204+
" error: a value is required for '--key-subject <SUBJECT>' but none was supplied",
205+
)
206+
.with_status(101)
207+
.run();
208+
}
209+
192210
#[cargo_test]
193211
fn login_with_no_cargo_dir() {
194212
// Create a config in the root directory because `login` requires the
@@ -203,6 +221,28 @@ fn login_with_no_cargo_dir() {
203221
assert_eq!(credentials, "[registry]\ntoken = \"foo\"\n");
204222
}
205223

224+
#[cargo_test]
225+
fn login_with_asymmetric_token_and_subject_on_stdin() {
226+
let registry = RegistryBuilder::new()
227+
.credential_provider(&["cargo:paseto"])
228+
.no_configure_token()
229+
.build();
230+
let credentials = credentials_toml();
231+
cargo_process("login -v -Z credential-process -- --key-subject=foo")
232+
.masquerade_as_nightly_cargo(&["credential-process"])
233+
.replace_crates_io(registry.index_url())
234+
.with_stderr_contains(
235+
"\
236+
k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ",
237+
)
238+
.with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36")
239+
.run();
240+
let credentials = fs::read_to_string(&credentials).unwrap();
241+
assert!(credentials.starts_with("[registry]\n"));
242+
assert!(credentials.contains("secret-key-subject = \"foo\"\n"));
243+
assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n"));
244+
}
245+
206246
#[cargo_test]
207247
fn login_with_differently_sized_token() {
208248
// Verify that the configuration file gets properly truncated.

0 commit comments

Comments
 (0)