cargo/util/credential/
token.rs1use anyhow::Context as _;
4use cargo_credential::{Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo};
5use url::Url;
6
7use crate::{
8 GlobalContext, context, ops::RegistryCredentialConfig,
9 util::auth::registry_credential_config_raw, workspace::SourceId,
10};
11
12pub struct TokenCredential<'a> {
13 gctx: &'a GlobalContext,
14}
15
16impl<'a> TokenCredential<'a> {
17 pub fn new(gctx: &'a GlobalContext) -> Self {
18 Self { gctx }
19 }
20}
21
22impl<'a> Credential for TokenCredential<'a> {
23 fn perform(
24 &self,
25 registry: &RegistryInfo<'_>,
26 action: &Action<'_>,
27 _args: &[&str],
28 ) -> Result<CredentialResponse, Error> {
29 let index_url = Url::parse(registry.index_url).context("parsing index url")?;
30 let sid = if let Some(name) = registry.name {
31 SourceId::for_alt_registry(&index_url, name)
32 } else {
33 SourceId::for_registry(&index_url)
34 }?;
35 let previous_token = registry_credential_config_raw(self.gctx, &sid)?.and_then(|c| c.token);
36
37 match action {
38 Action::Get(_) => {
39 let token = previous_token.ok_or_else(|| Error::NotFound)?.val;
40 Ok(CredentialResponse::Get {
41 token,
42 cache: CacheControl::Session,
43 operation_independent: true,
44 })
45 }
46 Action::Login(options) => {
47 let new_token = cargo_credential::read_token(options, registry)?
50 .map(|line| line.replace("cargo login", "").trim().to_string());
51
52 crates_io::check_token(new_token.as_ref().expose()).map_err(Box::new)?;
53 context::save_credentials(
54 self.gctx,
55 Some(RegistryCredentialConfig::Token(new_token)),
56 &sid,
57 )?;
58 let _ = self.gctx.shell().status(
59 "Login",
60 format!("token for `{}` saved", sid.display_registry_name()),
61 );
62 Ok(CredentialResponse::Login)
63 }
64 Action::Logout => {
65 if previous_token.is_none() {
66 return Err(Error::NotFound);
67 }
68 let reg_name = sid.display_registry_name();
69 context::save_credentials(self.gctx, None, &sid)?;
70 let _ = self.gctx.shell().status(
71 "Logout",
72 format!("token for `{reg_name}` has been removed from local storage"),
73 );
74 let location = if sid.is_crates_io() {
75 "<https://crates.io/me>".to_string()
76 } else {
77 format!("the `{reg_name}` website")
83 };
84 eprintln!(
85 "note: This does not revoke the token on the registry server.\n \
86 If you need to revoke the token, visit {location} and follow the instructions there."
87 );
88 Ok(CredentialResponse::Logout)
89 }
90 _ => Err(Error::OperationNotSupported),
91 }
92 }
93}