mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
refactor: extract login to its own module
This commit is contained in:
parent
d39c6504fa
commit
048267934b
160
src/cargo/ops/registry/login.rs
Normal file
160
src/cargo/ops/registry/login.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//! Interacts with the registry [login API][1].
|
||||||
|
//!
|
||||||
|
//! This doesn't really call any web API at this moment. Instead, it's just an
|
||||||
|
//! operation for `cargo login`.
|
||||||
|
//!
|
||||||
|
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#login
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use anyhow::bail;
|
||||||
|
use anyhow::Context as _;
|
||||||
|
use pasetors::keys::AsymmetricKeyPair;
|
||||||
|
use pasetors::keys::Generate as _;
|
||||||
|
use pasetors::paserk::FormatAsPaserk;
|
||||||
|
|
||||||
|
use crate::drop_println;
|
||||||
|
use crate::ops::RegistryCredentialConfig;
|
||||||
|
use crate::sources::CRATES_IO_DOMAIN;
|
||||||
|
use crate::util::auth;
|
||||||
|
use crate::util::auth::paserk_public_from_paserk_secret;
|
||||||
|
use crate::util::auth::AuthorizationError;
|
||||||
|
use crate::util::auth::Secret;
|
||||||
|
use crate::CargoResult;
|
||||||
|
use crate::Config;
|
||||||
|
|
||||||
|
use super::get_source_id;
|
||||||
|
|
||||||
|
pub fn registry_login(
|
||||||
|
config: &Config,
|
||||||
|
token: Option<Secret<&str>>,
|
||||||
|
reg: Option<&str>,
|
||||||
|
generate_keypair: bool,
|
||||||
|
secret_key_required: bool,
|
||||||
|
key_subject: Option<&str>,
|
||||||
|
) -> CargoResult<()> {
|
||||||
|
let source_ids = get_source_id(config, None, reg)?;
|
||||||
|
let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
|
||||||
|
|
||||||
|
let login_url = match super::registry(config, token.clone(), None, reg, false, None) {
|
||||||
|
Ok((registry, _)) => Some(format!("{}/me", registry.host())),
|
||||||
|
Err(e) if e.is::<AuthorizationError>() => e
|
||||||
|
.downcast::<AuthorizationError>()
|
||||||
|
.unwrap()
|
||||||
|
.login_url
|
||||||
|
.map(|u| u.to_string()),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
let new_token;
|
||||||
|
if generate_keypair || secret_key_required || key_subject.is_some() {
|
||||||
|
if !config.cli_unstable().registry_auth {
|
||||||
|
let flag = if generate_keypair {
|
||||||
|
"generate-keypair"
|
||||||
|
} else if secret_key_required {
|
||||||
|
"secret-key"
|
||||||
|
} else if key_subject.is_some() {
|
||||||
|
"key-subject"
|
||||||
|
} else {
|
||||||
|
unreachable!("how did we get here");
|
||||||
|
};
|
||||||
|
bail!(
|
||||||
|
"the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\
|
||||||
|
See https://github.com/rust-lang/cargo/issues/10519 for more \
|
||||||
|
information about the `{flag}` flag."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert!(token.is_none());
|
||||||
|
// we are dealing with asymmetric tokens
|
||||||
|
let (old_secret_key, old_key_subject) = match ®_cfg {
|
||||||
|
RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => {
|
||||||
|
(Some(old_secret_key), old_key_subject.clone())
|
||||||
|
}
|
||||||
|
_ => (None, None),
|
||||||
|
};
|
||||||
|
let secret_key: Secret<String>;
|
||||||
|
if generate_keypair {
|
||||||
|
assert!(!secret_key_required);
|
||||||
|
let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap();
|
||||||
|
secret_key = Secret::default().map(|mut key| {
|
||||||
|
FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap();
|
||||||
|
key
|
||||||
|
});
|
||||||
|
} else if secret_key_required {
|
||||||
|
assert!(!generate_keypair);
|
||||||
|
drop_println!(config, "please paste the API secret key below");
|
||||||
|
secret_key = Secret::default()
|
||||||
|
.map(|mut line| {
|
||||||
|
let input = io::stdin();
|
||||||
|
input
|
||||||
|
.lock()
|
||||||
|
.read_line(&mut line)
|
||||||
|
.with_context(|| "failed to read stdin")
|
||||||
|
.map(|_| line.trim().to_string())
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
} else {
|
||||||
|
secret_key = old_secret_key
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?;
|
||||||
|
}
|
||||||
|
if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) {
|
||||||
|
drop_println!(config, "{}", &p);
|
||||||
|
} else {
|
||||||
|
bail!("not a validly formatted PASERK secret key");
|
||||||
|
}
|
||||||
|
new_token = RegistryCredentialConfig::AsymmetricKey((
|
||||||
|
secret_key,
|
||||||
|
match key_subject {
|
||||||
|
Some(key_subject) => Some(key_subject.to_string()),
|
||||||
|
None => old_key_subject,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
new_token = RegistryCredentialConfig::Token(match token {
|
||||||
|
Some(token) => token.owned(),
|
||||||
|
None => {
|
||||||
|
if let Some(login_url) = login_url {
|
||||||
|
drop_println!(
|
||||||
|
config,
|
||||||
|
"please paste the token found on {} below",
|
||||||
|
login_url
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
drop_println!(
|
||||||
|
config,
|
||||||
|
"please paste the token for {} below",
|
||||||
|
source_ids.original.display_registry_name()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut line = String::new();
|
||||||
|
let input = io::stdin();
|
||||||
|
input
|
||||||
|
.lock()
|
||||||
|
.read_line(&mut line)
|
||||||
|
.with_context(|| "failed to read stdin")?;
|
||||||
|
// Automatically remove `cargo login` from an inputted token to
|
||||||
|
// allow direct pastes from `registry.host()`/me.
|
||||||
|
Secret::from(line.replace("cargo login", "").trim().to_string())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(tok) = new_token.as_token() {
|
||||||
|
crates_io::check_token(tok.as_ref().expose())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ®_cfg == &new_token {
|
||||||
|
config.shell().status("Login", "already logged in")?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
auth::login(config, &source_ids.original, new_token)?;
|
||||||
|
|
||||||
|
config.shell().status(
|
||||||
|
"Login",
|
||||||
|
format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)),
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -2,29 +2,25 @@
|
|||||||
//!
|
//!
|
||||||
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html
|
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html
|
||||||
|
|
||||||
|
mod login;
|
||||||
mod publish;
|
mod publish;
|
||||||
mod search;
|
mod search;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::{self, BufRead};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, format_err, Context as _};
|
use anyhow::{bail, format_err, Context as _};
|
||||||
use crates_io::{self, Registry};
|
use crates_io::{self, Registry};
|
||||||
use curl::easy::{Easy, InfoType, SslOpt, SslVersion};
|
use curl::easy::{Easy, InfoType, SslOpt, SslVersion};
|
||||||
use log::{log, Level};
|
use log::{log, Level};
|
||||||
use pasetors::keys::{AsymmetricKeyPair, Generate};
|
|
||||||
use pasetors::paserk::FormatAsPaserk;
|
|
||||||
|
|
||||||
use crate::core::source::Source;
|
use crate::core::source::Source;
|
||||||
use crate::core::{SourceId, Workspace};
|
use crate::core::{SourceId, Workspace};
|
||||||
use crate::sources::{RegistrySource, SourceConfigMap, CRATES_IO_DOMAIN};
|
use crate::sources::{RegistrySource, SourceConfigMap};
|
||||||
use crate::util::auth::{
|
use crate::util::auth::{self, Secret};
|
||||||
paserk_public_from_paserk_secret, Secret, {self, AuthorizationError},
|
|
||||||
};
|
|
||||||
use crate::util::config::{Config, SslVersionConfig, SslVersionConfigRange};
|
use crate::util::config::{Config, SslVersionConfig, SslVersionConfigRange};
|
||||||
use crate::util::errors::CargoResult;
|
use crate::util::errors::CargoResult;
|
||||||
use crate::util::important_paths::find_root_manifest_for_wd;
|
use crate::util::important_paths::find_root_manifest_for_wd;
|
||||||
@ -32,6 +28,7 @@ use crate::util::network;
|
|||||||
use crate::util::IntoUrl;
|
use crate::util::IntoUrl;
|
||||||
use crate::{drop_print, drop_println, version};
|
use crate::{drop_print, drop_println, version};
|
||||||
|
|
||||||
|
pub use self::login::registry_login;
|
||||||
pub use self::publish::publish;
|
pub use self::publish::publish;
|
||||||
pub use self::publish::PublishOpts;
|
pub use self::publish::PublishOpts;
|
||||||
pub use self::search::search;
|
pub use self::search::search;
|
||||||
@ -353,138 +350,6 @@ impl HttpTimeout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn registry_login(
|
|
||||||
config: &Config,
|
|
||||||
token: Option<Secret<&str>>,
|
|
||||||
reg: Option<&str>,
|
|
||||||
generate_keypair: bool,
|
|
||||||
secret_key_required: bool,
|
|
||||||
key_subject: Option<&str>,
|
|
||||||
) -> CargoResult<()> {
|
|
||||||
let source_ids = get_source_id(config, None, reg)?;
|
|
||||||
let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
|
|
||||||
|
|
||||||
let login_url = match registry(config, token.clone(), None, reg, false, None) {
|
|
||||||
Ok((registry, _)) => Some(format!("{}/me", registry.host())),
|
|
||||||
Err(e) if e.is::<AuthorizationError>() => e
|
|
||||||
.downcast::<AuthorizationError>()
|
|
||||||
.unwrap()
|
|
||||||
.login_url
|
|
||||||
.map(|u| u.to_string()),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
let new_token;
|
|
||||||
if generate_keypair || secret_key_required || key_subject.is_some() {
|
|
||||||
if !config.cli_unstable().registry_auth {
|
|
||||||
let flag = if generate_keypair {
|
|
||||||
"generate-keypair"
|
|
||||||
} else if secret_key_required {
|
|
||||||
"secret-key"
|
|
||||||
} else if key_subject.is_some() {
|
|
||||||
"key-subject"
|
|
||||||
} else {
|
|
||||||
unreachable!("how did we get here");
|
|
||||||
};
|
|
||||||
bail!(
|
|
||||||
"the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\
|
|
||||||
See https://github.com/rust-lang/cargo/issues/10519 for more \
|
|
||||||
information about the `{flag}` flag."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
assert!(token.is_none());
|
|
||||||
// we are dealing with asymmetric tokens
|
|
||||||
let (old_secret_key, old_key_subject) = match ®_cfg {
|
|
||||||
RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => {
|
|
||||||
(Some(old_secret_key), old_key_subject.clone())
|
|
||||||
}
|
|
||||||
_ => (None, None),
|
|
||||||
};
|
|
||||||
let secret_key: Secret<String>;
|
|
||||||
if generate_keypair {
|
|
||||||
assert!(!secret_key_required);
|
|
||||||
let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap();
|
|
||||||
secret_key = Secret::default().map(|mut key| {
|
|
||||||
FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap();
|
|
||||||
key
|
|
||||||
});
|
|
||||||
} else if secret_key_required {
|
|
||||||
assert!(!generate_keypair);
|
|
||||||
drop_println!(config, "please paste the API secret key below");
|
|
||||||
secret_key = Secret::default()
|
|
||||||
.map(|mut line| {
|
|
||||||
let input = io::stdin();
|
|
||||||
input
|
|
||||||
.lock()
|
|
||||||
.read_line(&mut line)
|
|
||||||
.with_context(|| "failed to read stdin")
|
|
||||||
.map(|_| line.trim().to_string())
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
} else {
|
|
||||||
secret_key = old_secret_key
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?;
|
|
||||||
}
|
|
||||||
if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) {
|
|
||||||
drop_println!(config, "{}", &p);
|
|
||||||
} else {
|
|
||||||
bail!("not a validly formatted PASERK secret key");
|
|
||||||
}
|
|
||||||
new_token = RegistryCredentialConfig::AsymmetricKey((
|
|
||||||
secret_key,
|
|
||||||
match key_subject {
|
|
||||||
Some(key_subject) => Some(key_subject.to_string()),
|
|
||||||
None => old_key_subject,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
new_token = RegistryCredentialConfig::Token(match token {
|
|
||||||
Some(token) => token.owned(),
|
|
||||||
None => {
|
|
||||||
if let Some(login_url) = login_url {
|
|
||||||
drop_println!(
|
|
||||||
config,
|
|
||||||
"please paste the token found on {} below",
|
|
||||||
login_url
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
drop_println!(
|
|
||||||
config,
|
|
||||||
"please paste the token for {} below",
|
|
||||||
source_ids.original.display_registry_name()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut line = String::new();
|
|
||||||
let input = io::stdin();
|
|
||||||
input
|
|
||||||
.lock()
|
|
||||||
.read_line(&mut line)
|
|
||||||
.with_context(|| "failed to read stdin")?;
|
|
||||||
// Automatically remove `cargo login` from an inputted token to
|
|
||||||
// allow direct pastes from `registry.host()`/me.
|
|
||||||
Secret::from(line.replace("cargo login", "").trim().to_string())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(tok) = new_token.as_token() {
|
|
||||||
crates_io::check_token(tok.as_ref().expose())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ®_cfg == &new_token {
|
|
||||||
config.shell().status("Login", "already logged in")?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
auth::login(config, &source_ids.original, new_token)?;
|
|
||||||
|
|
||||||
config.shell().status(
|
|
||||||
"Login",
|
|
||||||
format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> {
|
pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> {
|
||||||
let source_ids = get_source_id(config, None, reg)?;
|
let source_ids = get_source_id(config, None, reg)?;
|
||||||
let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
|
let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user