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