mirror of https://github.com/astral-sh/uv
Add support for credentials in URLs to `uv auth` (#15554)
Allows cases like `uv auth login https://username:password@example.com` for coherence with the rest of our interfaces.
This commit is contained in:
parent
4ad5ae5e6f
commit
a1cc12af2b
|
|
@ -84,6 +84,9 @@ impl KeyringProvider {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ensure we strip credentials from the URL before storing
|
||||||
|
let url = url.without_credentials();
|
||||||
|
|
||||||
match &self.backend {
|
match &self.backend {
|
||||||
KeyringProviderBackend::Native => {
|
KeyringProviderBackend::Native => {
|
||||||
self.store_native(url.as_str(), username, password).await?;
|
self.store_native(url.as_str(), username, password).await?;
|
||||||
|
|
@ -116,6 +119,9 @@ impl KeyringProvider {
|
||||||
/// Only [`KeyringProviderBackend::Native`] is supported at this time.
|
/// Only [`KeyringProviderBackend::Native`] is supported at this time.
|
||||||
#[instrument(skip_all, fields(url = % url.to_string(), username))]
|
#[instrument(skip_all, fields(url = % url.to_string(), username))]
|
||||||
pub async fn remove(&self, url: &DisplaySafeUrl, username: &str) -> Result<(), Error> {
|
pub async fn remove(&self, url: &DisplaySafeUrl, username: &str) -> Result<(), Error> {
|
||||||
|
// Ensure we strip credentials from the URL before storing
|
||||||
|
let url = url.without_credentials();
|
||||||
|
|
||||||
match &self.backend {
|
match &self.backend {
|
||||||
KeyringProviderBackend::Native => {
|
KeyringProviderBackend::Native => {
|
||||||
self.remove_native(url.as_str(), username).await?;
|
self.remove_native(url.as_str(), username).await?;
|
||||||
|
|
|
||||||
|
|
@ -19,24 +19,6 @@ pub(crate) async fn login(
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let url = service.url();
|
let url = service.url();
|
||||||
let display_url = username
|
|
||||||
.as_ref()
|
|
||||||
.map(|username| format!("{username}@{url}"))
|
|
||||||
.unwrap_or_else(|| url.to_string());
|
|
||||||
|
|
||||||
let username = if let Some(username) = username {
|
|
||||||
username
|
|
||||||
} else if token.is_some() {
|
|
||||||
String::from("__token__")
|
|
||||||
} else {
|
|
||||||
let term = Term::stderr();
|
|
||||||
if term.is_term() {
|
|
||||||
let prompt = "username: ";
|
|
||||||
uv_console::username(prompt, &term)?
|
|
||||||
} else {
|
|
||||||
bail!("No username provided; did you mean to provide `--username` or `--token`?");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Be helpful about incompatible `keyring-provider` settings
|
// Be helpful about incompatible `keyring-provider` settings
|
||||||
let Some(keyring_provider) = &keyring_provider else {
|
let Some(keyring_provider) = &keyring_provider else {
|
||||||
|
|
@ -55,14 +37,65 @@ pub(crate) async fn login(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: It would be preferable to accept the value of --password or --token
|
// Extract credentials from URL if present
|
||||||
// from stdin, perhaps checking here for `-` as an indicator to read stdin. We
|
let url_credentials = Credentials::from_url(url);
|
||||||
// could then warn if the password is provided as a plaintext argument.
|
let url_username = url_credentials.as_ref().and_then(|c| c.username());
|
||||||
let password = if let Some(password) = password {
|
let url_password = url_credentials.as_ref().and_then(|c| c.password());
|
||||||
password
|
|
||||||
} else if let Some(token) = token {
|
let username = match (username, url_username) {
|
||||||
token
|
(Some(cli), Some(url)) => {
|
||||||
|
bail!(
|
||||||
|
"Cannot specify a username both via the URL and CLI; found `--username {cli}` and `{url}`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(cli), None) => Some(cli),
|
||||||
|
(None, Some(url)) => Some(url.to_string()),
|
||||||
|
(None, None) => {
|
||||||
|
// When using `--token`, we'll use a `__token__` placeholder username
|
||||||
|
if token.is_some() {
|
||||||
|
Some("__token__".to_string())
|
||||||
} else {
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure that a username is not provided when using a token
|
||||||
|
if token.is_some() {
|
||||||
|
if let Some(username) = &username {
|
||||||
|
if username != "__token__" {
|
||||||
|
bail!("When using `--token`, a username cannot not be provided; found: {username}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for a username if not provided
|
||||||
|
let username = if let Some(username) = username {
|
||||||
|
username
|
||||||
|
} else {
|
||||||
|
let term = Term::stderr();
|
||||||
|
if term.is_term() {
|
||||||
|
let prompt = "username: ";
|
||||||
|
uv_console::username(prompt, &term)?
|
||||||
|
} else {
|
||||||
|
bail!("No username provided; did you mean to provide `--username` or `--token`?");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let password = match (password, url_password, token) {
|
||||||
|
(Some(_), Some(_), _) => {
|
||||||
|
bail!("Cannot specify a password both via the URL and CLI");
|
||||||
|
}
|
||||||
|
(Some(_), None, Some(_)) => {
|
||||||
|
bail!("Cannot specify a password via `--password` when using `--token`");
|
||||||
|
}
|
||||||
|
(None, Some(_), Some(_)) => {
|
||||||
|
bail!("Cannot include a password in the URL when using `--token`")
|
||||||
|
}
|
||||||
|
(Some(cli), None, None) => cli,
|
||||||
|
(None, Some(url), None) => url.to_string(),
|
||||||
|
(None, None, Some(token)) => token,
|
||||||
|
(None, None, None) => {
|
||||||
let term = Term::stderr();
|
let term = Term::stderr();
|
||||||
if term.is_term() {
|
if term.is_term() {
|
||||||
let prompt = "password: ";
|
let prompt = "password: ";
|
||||||
|
|
@ -70,6 +103,13 @@ pub(crate) async fn login(
|
||||||
} else {
|
} else {
|
||||||
bail!("No password provided; did you mean to provide `--password` or `--token`?");
|
bail!("No password provided; did you mean to provide `--password` or `--token`?");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_url = if username == "__token__" {
|
||||||
|
url.without_credentials().to_string()
|
||||||
|
} else {
|
||||||
|
format!("{username}@{}", url.without_credentials())
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(zanieb): Add support for other authentication schemes here, e.g., `Credentials::Bearer`
|
// TODO(zanieb): Add support for other authentication schemes here, e.g., `Credentials::Bearer`
|
||||||
|
|
@ -77,6 +117,5 @@ pub(crate) async fn login(
|
||||||
provider.store(url, &credentials).await?;
|
provider.store(url, &credentials).await?;
|
||||||
|
|
||||||
writeln!(printer.stderr(), "Logged in to {display_url}")?;
|
writeln!(printer.stderr(), "Logged in to {display_url}")?;
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use std::{borrow::Cow, fmt::Write};
|
use std::fmt::Write;
|
||||||
|
use uv_auth::Credentials;
|
||||||
use uv_configuration::{KeyringProviderType, Service};
|
use uv_configuration::{KeyringProviderType, Service};
|
||||||
use uv_preview::Preview;
|
use uv_preview::Preview;
|
||||||
|
|
||||||
|
|
@ -16,13 +17,27 @@ pub(crate) async fn logout(
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let url = service.url();
|
let url = service.url();
|
||||||
let display_url = username
|
|
||||||
.as_ref()
|
// Extract credentials from URL if present
|
||||||
.map(|username| format!("{username}@{url}"))
|
let url_credentials = Credentials::from_url(url);
|
||||||
.unwrap_or_else(|| url.to_string());
|
let url_username = url_credentials.as_ref().and_then(|c| c.username());
|
||||||
let username = username
|
|
||||||
.map(Cow::Owned)
|
let username = match (username, url_username) {
|
||||||
.unwrap_or(Cow::Borrowed("__token__"));
|
(Some(cli), Some(url)) => {
|
||||||
|
bail!(
|
||||||
|
"Cannot specify a username both via the URL and CLI; found `--username {cli}` and `{url}`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(cli), None) => cli,
|
||||||
|
(None, Some(url)) => url.to_string(),
|
||||||
|
(None, None) => "__token__".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_url = if username == "__token__" {
|
||||||
|
url.without_credentials().to_string()
|
||||||
|
} else {
|
||||||
|
format!("{username}@{}", url.without_credentials())
|
||||||
|
};
|
||||||
|
|
||||||
// Unlike login, we'll default to the native provider if none is requested since it's the only
|
// Unlike login, we'll default to the native provider if none is requested since it's the only
|
||||||
// valid option and it doesn't matter if the credentials are available in subsequent commands.
|
// valid option and it doesn't matter if the credentials are available in subsequent commands.
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@ use std::fmt::Write;
|
||||||
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
|
|
||||||
|
use uv_auth::Credentials;
|
||||||
use uv_configuration::{KeyringProviderType, Service};
|
use uv_configuration::{KeyringProviderType, Service};
|
||||||
use uv_preview::Preview;
|
use uv_preview::Preview;
|
||||||
|
|
||||||
use crate::{Printer, commands::ExitStatus};
|
use crate::{commands::ExitStatus, printer::Printer};
|
||||||
|
|
||||||
/// Show the token that will be used for a service.
|
/// Show the token that will be used for a service.
|
||||||
pub(crate) async fn token(
|
pub(crate) async fn token(
|
||||||
|
|
@ -15,6 +16,7 @@ pub(crate) async fn token(
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
|
let url = service.url();
|
||||||
// Determine the keyring provider to use
|
// Determine the keyring provider to use
|
||||||
let Some(keyring_provider) = &keyring_provider else {
|
let Some(keyring_provider) = &keyring_provider else {
|
||||||
bail!("Retrieving credentials requires setting a `keyring-provider`");
|
bail!("Retrieving credentials requires setting a `keyring-provider`");
|
||||||
|
|
@ -23,21 +25,36 @@ pub(crate) async fn token(
|
||||||
bail!("Cannot retrieve credentials with `keyring-provider = {keyring_provider}`");
|
bail!("Cannot retrieve credentials with `keyring-provider = {keyring_provider}`");
|
||||||
};
|
};
|
||||||
|
|
||||||
let url = service.url();
|
// Extract credentials from URL if present
|
||||||
let display_url = username
|
let url_credentials = Credentials::from_url(url);
|
||||||
.as_ref()
|
let url_username = url_credentials.as_ref().and_then(|c| c.username());
|
||||||
.map(|username| format!("{username}@{url}"))
|
|
||||||
.unwrap_or_else(|| url.to_string());
|
let username = match (username, url_username) {
|
||||||
|
(Some(cli), Some(url)) => {
|
||||||
|
bail!(
|
||||||
|
"Cannot specify a username both via the URL and CLI; found `--username {cli}` and `{url}`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(cli), None) => cli,
|
||||||
|
(None, Some(url)) => url.to_string(),
|
||||||
|
(None, None) => "__token__".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_url = if username == "__token__" {
|
||||||
|
url.without_credentials().to_string()
|
||||||
|
} else {
|
||||||
|
format!("{username}@{}", url.without_credentials())
|
||||||
|
};
|
||||||
|
|
||||||
let credentials = provider
|
let credentials = provider
|
||||||
.fetch(url, Some(username.as_deref().unwrap_or("__token__")))
|
.fetch(url, Some(&username))
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("Failed to fetch credentials for {display_url}"))?;
|
.with_context(|| format!("Failed to fetch credentials for {display_url}"))?;
|
||||||
|
|
||||||
let Some(password) = credentials.password() else {
|
let Some(password) = credentials.password() else {
|
||||||
bail!(
|
bail!(
|
||||||
"No {} found for {display_url}",
|
"No {} found for {display_url}",
|
||||||
if username.is_some() {
|
if username != "__token__" {
|
||||||
"password"
|
"password"
|
||||||
} else {
|
} else {
|
||||||
"token"
|
"token"
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,41 @@ fn token_native_keyring() -> Result<()> {
|
||||||
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
|
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
|
||||||
");
|
");
|
||||||
|
|
||||||
|
context
|
||||||
|
.auth_logout()
|
||||||
|
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--username")
|
||||||
|
.arg("public")
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
// Retrieve token using URL with embedded username (no --username needed)
|
||||||
|
uv_snapshot!(context.auth_token()
|
||||||
|
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
|
||||||
|
");
|
||||||
|
|
||||||
|
// Conflict between --username and URL username is rejected
|
||||||
|
uv_snapshot!(context.auth_token()
|
||||||
|
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--username")
|
||||||
|
.arg("different")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Cannot specify a username both via the URL and CLI; found `--username different` and `public`
|
||||||
|
");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,7 +334,7 @@ fn token_subprocess_keyring() {
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Failed to fetch credentials for https://****@pypi-proxy.fly.dev/basic-auth/simple
|
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -327,9 +362,9 @@ fn token_subprocess_keyring() {
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Keyring request for __token__@https://public@pypi-proxy.fly.dev/basic-auth/simple
|
Keyring request for public@https://public@pypi-proxy.fly.dev/basic-auth/simple
|
||||||
Keyring request for __token__@pypi-proxy.fly.dev
|
Keyring request for public@pypi-proxy.fly.dev
|
||||||
error: Failed to fetch credentials for https://****@pypi-proxy.fly.dev/basic-auth/simple
|
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -341,14 +376,14 @@ fn token_subprocess_keyring() {
|
||||||
.arg("subprocess")
|
.arg("subprocess")
|
||||||
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
|
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
|
||||||
.env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
|
.env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
|
||||||
success: false
|
success: true
|
||||||
exit_code: 2
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
heron
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Keyring request for __token__@https://public@pypi-proxy.fly.dev/basic-auth/simple
|
Keyring request for public@https://public@pypi-proxy.fly.dev/basic-auth/simple
|
||||||
Keyring request for __token__@pypi-proxy.fly.dev
|
Keyring request for public@pypi-proxy.fly.dev
|
||||||
error: Failed to fetch credentials for https://****@pypi-proxy.fly.dev/basic-auth/simple
|
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -361,14 +396,12 @@ fn token_subprocess_keyring() {
|
||||||
.arg("public")
|
.arg("public")
|
||||||
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
|
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
|
||||||
.env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
|
.env(EnvVars::PATH, venv_bin_path(&context.venv)), @r"
|
||||||
success: true
|
success: false
|
||||||
exit_code: 0
|
exit_code: 2
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
heron
|
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Keyring request for public@https://public@pypi-proxy.fly.dev/basic-auth/simple
|
error: Cannot specify a username both via the URL and CLI; found `--username public` and `public`
|
||||||
Keyring request for public@pypi-proxy.fly.dev
|
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -618,6 +651,62 @@ fn logout_native_keyring() -> Result<()> {
|
||||||
Logged out of public@https://pypi-proxy.fly.dev/basic-auth/simple
|
Logged out of public@https://pypi-proxy.fly.dev/basic-auth/simple
|
||||||
");
|
");
|
||||||
|
|
||||||
|
// Login again
|
||||||
|
context
|
||||||
|
.auth_login()
|
||||||
|
.arg("https://pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--username")
|
||||||
|
.arg("public")
|
||||||
|
.arg("--password")
|
||||||
|
.arg("heron")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Logout with a username in the URL
|
||||||
|
uv_snapshot!(context.auth_logout()
|
||||||
|
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Logged out of public@https://pypi-proxy.fly.dev/basic-auth/simple
|
||||||
|
");
|
||||||
|
|
||||||
|
// Conflict between --username and a URL username is rejected
|
||||||
|
uv_snapshot!(context.auth_logout()
|
||||||
|
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--username")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Cannot specify a username both via the URL and CLI; found `--username foo` and `public`
|
||||||
|
");
|
||||||
|
|
||||||
|
// Conflict between --token and a URL username is rejected
|
||||||
|
uv_snapshot!(context.auth_login()
|
||||||
|
.arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
|
||||||
|
.arg("--token")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: When using `--token`, a username cannot not be provided; found: public
|
||||||
|
");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -666,7 +755,7 @@ fn logout_token_native_keyring() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn login_url_parsing() {
|
fn login_native_keyring_url() {
|
||||||
let context = TestContext::new_with_versions(&[]).with_real_home();
|
let context = TestContext::new_with_versions(&[]).with_real_home();
|
||||||
|
|
||||||
// A domain-only service name gets https:// prepended
|
// A domain-only service name gets https:// prepended
|
||||||
|
|
@ -758,4 +847,79 @@ fn login_url_parsing() {
|
||||||
|
|
||||||
For more information, try '--help'.
|
For more information, try '--help'.
|
||||||
");
|
");
|
||||||
|
|
||||||
|
// URL with embedded credentials works
|
||||||
|
uv_snapshot!(context.auth_login()
|
||||||
|
.arg("https://test:password@example.com/simple")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Logged in to test@https://example.com/simple
|
||||||
|
");
|
||||||
|
|
||||||
|
// URL with embedded username and separate password works
|
||||||
|
uv_snapshot!(context.auth_login()
|
||||||
|
.arg("https://test@example.com/simple")
|
||||||
|
.arg("--password")
|
||||||
|
.arg("password")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Logged in to test@https://example.com/simple
|
||||||
|
");
|
||||||
|
|
||||||
|
// Conflict between --username and URL username is rejected
|
||||||
|
uv_snapshot!(context.auth_login()
|
||||||
|
.arg("https://test@example.com/simple")
|
||||||
|
.arg("--username")
|
||||||
|
.arg("different")
|
||||||
|
.arg("--password")
|
||||||
|
.arg("password")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Cannot specify a username both via the URL and CLI; found `--username different` and `test`
|
||||||
|
");
|
||||||
|
|
||||||
|
// Conflict between --password and URL password is rejected
|
||||||
|
uv_snapshot!(context.auth_login()
|
||||||
|
.arg("https://test:password@example.com/simple")
|
||||||
|
.arg("--password")
|
||||||
|
.arg("different")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Cannot specify a password both via the URL and CLI
|
||||||
|
");
|
||||||
|
|
||||||
|
// Conflict between --token and URL credentials is rejected
|
||||||
|
uv_snapshot!(context.auth_login()
|
||||||
|
.arg("https://test:password@example.com/simple")
|
||||||
|
.arg("--token")
|
||||||
|
.arg("some-token")
|
||||||
|
.arg("--keyring-provider")
|
||||||
|
.arg("native"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: When using `--token`, a username cannot not be provided; found: test
|
||||||
|
");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue