warg_cli/commands/
login.rs1use crate::commands::config::{keyring_backend_help, keyring_backend_parser};
2use anyhow::{bail, Context, Result};
3use clap::Args;
4use dialoguer::{theme::ColorfulTheme, Confirm, Password};
5use p256::ecdsa::SigningKey;
6use rand_core::OsRng;
7use warg_client::keyring::Keyring;
8use warg_client::{Config, RegistryUrl};
9
10use super::CommonOptions;
11
12#[derive(Args)]
14pub struct LoginCommand {
15 #[clap(flatten)]
17 pub common: CommonOptions,
18
19 #[clap(value_name = "URL")]
21 #[arg(hide = true)]
22 pub registry_url: Option<String>,
23
24 #[clap(long)]
26 pub ignore_federation_hints: bool,
27
28 #[clap(long)]
30 pub disable_auto_accept_federation_hints: bool,
31
32 #[clap(long)]
35 pub disable_auto_package_init: bool,
36
37 #[clap(long, value_name = "KEYRING_BACKEND", value_parser = keyring_backend_parser, long_help = keyring_backend_help())]
39 pub keyring_backend: Option<String>,
40}
41
42impl LoginCommand {
43 pub async fn exec(mut self) -> Result<()> {
45 if self.registry_url.is_some() {
46 if self.common.registry.is_some() {
47 bail!("Registry URL provided in two different arguments. Use only one.");
48 }
49 self.common.registry = self.registry_url;
50 }
51 let mut config = self.common.read_config()?;
52 let mut registry_url = &self
53 .common
54 .registry
55 .as_ref()
56 .map(RegistryUrl::new)
57 .transpose()?
58 .map(|u| u.to_string());
59 config.ignore_federation_hints = self.ignore_federation_hints;
60 config.disable_auto_accept_federation_hints = self.disable_auto_accept_federation_hints;
61 config.disable_auto_package_init = self.disable_auto_package_init;
62
63 if self.keyring_backend.is_some() {
65 config.keyring_backend = self.keyring_backend;
66 }
67
68 if registry_url.is_none() && config.home_url.is_none() {
69 bail!("Please set your registry: warg login --registry <registry-url>");
70 }
71
72 let mut changing_home_registry = false;
73
74 if registry_url.is_some()
75 && registry_url != &config.home_url
76 && Confirm::with_theme(&ColorfulTheme::default())
77 .with_prompt(format!(
78 "Set `{registry}` as your home (or default) registry?",
79 registry = registry_url.as_deref().unwrap(),
80 ))
81 .default(true)
82 .interact()?
83 {
84 config.home_url.clone_from(registry_url);
85 config.write_to_file(&Config::default_config_path()?)?;
86
87 changing_home_registry = true;
89 } else if registry_url.is_none() {
90 registry_url = &config.home_url;
91 }
92
93 let keyring = Keyring::from_config(&config)?;
94 config.keyring_auth = true;
95
96 let client = if *registry_url == config.home_url {
97 self.common.create_client(&config).await?
98 } else {
99 let mut config = config.clone();
100 config.home_url.clone_from(registry_url);
101 self.common.create_client(&config).await?
102 };
103
104 let registry_url = Some(client.url().to_string());
107
108 if changing_home_registry {
109 client.reset_namespaces().await?;
110 client.reset_registry().await?;
111 }
112
113 let prompt = format!(
114 "Enter auth token for registry: {registry}",
115 registry = client.url().registry_domain(),
116 );
117
118 if config.keys.is_empty() {
119 config.keys.insert("default".to_string());
120 let key = SigningKey::random(&mut OsRng).into();
121 keyring.set_signing_key(None, &key, &mut config.keys, registry_url.as_deref())?;
122 let public_key = key.public_key();
123 let token = Password::with_theme(&ColorfulTheme::default())
124 .with_prompt(prompt)
125 .interact()
126 .context("failed to read token")?;
127 keyring.set_auth_token(&RegistryUrl::new(registry_url.as_deref().unwrap())?, &token)?;
128 config.write_to_file(&Config::default_config_path()?)?;
129 println!("Auth token was set successfully, and generated default key.");
130 println!("Public Key: {public_key}");
131 return Ok(());
132 }
133
134 let token = Password::with_theme(&ColorfulTheme::default())
135 .with_prompt(prompt)
136 .interact()
137 .context("failed to read token")?;
138 keyring.set_auth_token(&RegistryUrl::new(registry_url.as_deref().unwrap())?, &token)?;
139 config.write_to_file(&Config::default_config_path()?)?;
140 println!("Auth token was set successfully.");
141
142 if let Ok(private_key) = keyring.get_signing_key(
143 self.common.registry.as_deref(),
144 &config.keys,
145 registry_url.as_deref(),
146 ) {
147 println!("\nSigning key is still available:");
148 let public_key = private_key.public_key();
149 println!("Key ID: {}", public_key.fingerprint());
150 println!("Public Key: {public_key}");
151 }
152
153 Ok(())
154 }
155}