From 18d3d4a1c3da0ec2a1a6aced30ef736c3394a9d2 Mon Sep 17 00:00:00 2001 From: Samuel Cristobal Date: Thu, 3 Apr 2025 12:52:01 +0200 Subject: [PATCH] Improve deletion and revocation of local Hex API key --- CHANGELOG.md | 3 +++ compiler-cli/src/hex.rs | 30 ++++++++++++++++------------ compiler-cli/src/hex/auth.rs | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c50bcb0e02..8bfe1d5adf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,9 @@ - Deprecate `HEXPM_USER` and `HEXPM_PASS` in favour of `HEXPM_API_KEY`. ([Samuel Cristobal](https://github.com/scristobal)) +- Improved Hex API key removal CLI process. + ([Samuel Cristobal](https://github.com/scristobal)) + ### Language server - The language server now allows renaming of functions, constants, diff --git a/compiler-cli/src/hex.rs b/compiler-cli/src/hex.rs index a5ceaa142b6..3cd7c23d673 100644 --- a/compiler-cli/src/hex.rs +++ b/compiler-cli/src/hex.rs @@ -1,6 +1,7 @@ mod auth; use crate::{cli, http::HttpClient}; +use auth::EncryptedApiKey; use gleam_core::{ Error, Result, hex::{self, RetirementReason}, @@ -93,29 +94,32 @@ pub fn revert( pub(crate) fn authenticate() -> Result<()> { let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime"); - let http = HttpClient::new(); let config = hexpm::Config::new(); let mut auth = HexAuthentication::new(&runtime, config.clone()); let previous = auth.read_stored_api_key()?; - if previous.is_some() { - let question = "You already have a local Hex API token. Would you like to replace it + if let Some(EncryptedApiKey { name, .. }) = previous { + let question = "You already have a local Hex API key. Would you like to replace it with a new one?"; if !cli::confirm(question)? { return Ok(()); } + match auth.remove_stored_api_key() { + Err(e) => match e { + Error::Hex(_) => println!( + "\nWe were unable to revoke Hex API key {name}. You should revoke it manually at https://hex.pm", + ), + e => println!( + "\nWe were unable to delete your existing Hex API key due to an unexpected error: {e}" + ), + }, + _ => println!("\nYour Hex API key {name} was deleted and revoked succesfully"), + } + + println!("Let's create a new Hex API key.") } - let new_key = auth.create_and_store_api_key()?; + let _ = auth.create_and_store_api_key()?; - if let Some(previous) = previous { - println!("Deleting previous key `{}` from Hex", previous.name); - runtime.block_on(hex::remove_api_key( - &previous.name, - &config, - &new_key.unencrypted, - &http, - ))?; - } Ok(()) } diff --git a/compiler-cli/src/hex/auth.rs b/compiler-cli/src/hex/auth.rs index 7d03f00a1ec..ee38cbf9cf8 100644 --- a/compiler-cli/src/hex/auth.rs +++ b/compiler-cli/src/hex/auth.rs @@ -148,6 +148,44 @@ encrypt your Hex API key. encrypted: encrypted.to_string(), })) } + + /// Try to remove and revoke existing Hex API key, will return: + /// + /// - Error: operation failed + /// - Ok(None): operation successfull but there was no key stored, eg. wrong file format + /// - Ok(Some(String)): operation successfull, plus the name of key + pub fn remove_stored_api_key(&mut self) -> Result> { + let path = global_hexpm_credentials_path(); + + let Some(EncryptedApiKey { name, .. }) = self.read_stored_api_key()? else { + return Ok(None); + }; + + println!( + "\nWe are going to delete and revoke your existing Hex API key. +In order to do so, we need to use your current local password." + ); + + let Some(UnencryptedApiKey { unencrypted }) = self.read_and_decrypt_stored_api_key()? + else { + return Ok(None); + }; + + println!("\nDeleting local Hex API key from disk..."); + crate::fs::delete_file(&path)?; + println!("File {path} deleted successfully."); + + println!("\nRevoking Hex API key from Hex..."); + self.runtime.block_on(hex::remove_api_key( + &name, + &self.hex_config, + &unencrypted, + &self.http, + ))?; + println!("Key {name} revoked successfully."); + + Ok(Some(name.to_string())) + } } impl Drop for HexAuthentication<'_> {