refactor: extract login to its own module

This commit is contained in:
Weihang Lo 2023-06-20 15:06:56 +01:00
parent d39c6504fa
commit 048267934b
No known key found for this signature in database
GPG Key ID: D7DBF189825E82E7
2 changed files with 165 additions and 140 deletions

View 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 &reg_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 &reg_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(())
}

View File

@ -2,29 +2,25 @@
//!
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html
mod login;
mod publish;
mod search;
use std::collections::HashSet;
use std::io::{self, BufRead};
use std::path::PathBuf;
use std::str;
use std::task::Poll;
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 curl::easy::{Easy, InfoType, SslOpt, SslVersion};
use log::{log, Level};
use pasetors::keys::{AsymmetricKeyPair, Generate};
use pasetors::paserk::FormatAsPaserk;
use crate::core::source::Source;
use crate::core::{SourceId, Workspace};
use crate::sources::{RegistrySource, SourceConfigMap, CRATES_IO_DOMAIN};
use crate::util::auth::{
paserk_public_from_paserk_secret, Secret, {self, AuthorizationError},
};
use crate::sources::{RegistrySource, SourceConfigMap};
use crate::util::auth::{self, Secret};
use crate::util::config::{Config, SslVersionConfig, SslVersionConfigRange};
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
@ -32,6 +28,7 @@ use crate::util::network;
use crate::util::IntoUrl;
use crate::{drop_print, drop_println, version};
pub use self::login::registry_login;
pub use self::publish::publish;
pub use self::publish::PublishOpts;
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 &reg_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 &reg_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<()> {
let source_ids = get_source_id(config, None, reg)?;
let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;