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