//! ref: composer/src/Composer/Util/Forgejo.php use crate::config::Config; use crate::downloader::transport_exception::TransportException; use crate::io::io_interface; use crate::io::io_interface::IOInterface; use crate::util::http_downloader::HttpDownloader; #[derive(Debug)] pub struct Forgejo { io: Box, config: std::rc::Rc>, http_downloader: std::rc::Rc>, } impl Forgejo { pub fn new( io: Box, config: std::rc::Rc>, http_downloader: std::rc::Rc>, ) -> Self { Self { io, config, http_downloader, } } /// Authorizes a Forgejo domain interactively pub fn authorize_o_auth_interactively( &mut self, origin_url: &str, message: Option<&str>, ) -> anyhow::Result> { if let Some(message) = message { self.io.write_error3(message, true, io_interface::NORMAL); } let url = format!("https://{}/user/settings/applications", origin_url); self.io.write_error3( "Setup a personal access token with repository:read permissions on:", true, io_interface::NORMAL, ); self.io.write_error3(&url, true, io_interface::NORMAL); let local_auth_config = self.config.borrow().get_local_auth_config_source(); self.io.write_error3( &format!( "Tokens will be stored in plain text in \"{}\" for future use by Composer.", local_auth_config .as_ref() .map(|s| format!("{} OR ", s.get_name())) .unwrap_or_default() + self.config.borrow().get_auth_config_source().get_name() ), true, io_interface::NORMAL, ); self.io.write_error3( "For additional information, check https://getcomposer.org/doc/articles/authentication-for-private-packages.md#forgejo-token", true, io_interface::NORMAL, ); let mut store_in_local_auth_config = false; if local_auth_config.is_some() { store_in_local_auth_config = self.io.ask_confirmation( "A local auth config source was found, do you want to store the token there?", true, ); } let username = self.io.ask("Username: ", None).trim().to_string(); let token = self .io .ask_and_hide_answer("Token (hidden): ") .trim() .to_string(); let add_token_manually = format!( "You can also add it manually later by using \"composer config --global --auth forgejo-token.{} \"", origin_url ); if token.is_empty() || username.is_empty() { self.io.write_error3( "No username/token given, aborting.", true, io_interface::NORMAL, ); self.io .write_error3(&add_token_manually, true, io_interface::NORMAL); return Ok(Ok(false)); } self.io .set_authentication(origin_url.to_string(), username.clone(), token.clone()); match self.http_downloader.borrow_mut().get( &format!("https://{}/api/v1/version", origin_url), indexmap::indexmap! { "retry-auth-failure".to_string() => false.into(), }, ) { Ok(_) => {} Err(e) => { if [403, 401, 404].contains(&e.get_code()) { self.io.write_error3( "Invalid access token provided.", true, io_interface::NORMAL, ); self.io .write_error3(&add_token_manually, true, io_interface::NORMAL); return Ok(Ok(false)); } return Ok(Err(e)); } } // store value in local/user config let local_auth_config = self.config.borrow().get_local_auth_config_source(); let auth_config_source = if store_in_local_auth_config { local_auth_config .as_ref() .unwrap_or_else(|| self.config.borrow().get_auth_config_source()) } else { self.config.borrow().get_auth_config_source() }; self.config .borrow() .get_config_source() .remove_config_setting(&format!("forgejo-token.{}", origin_url)); auth_config_source.add_config_setting( &format!("forgejo-token.{}", origin_url), indexmap::indexmap! { "username".to_string() => username.into(), "token".to_string() => token.into(), }, ); self.io.write_error3( "Token stored successfully.", true, io_interface::NORMAL, ); Ok(Ok(true)) } }