mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
feat: implement token overrides
This commit is contained in:
parent
a2865523a0
commit
30c9be8366
11 changed files with 159 additions and 41 deletions
|
@ -64,7 +64,7 @@ pub fn get_token_login(
|
|||
) -> anyhow::Result<String> {
|
||||
let response = reqwest
|
||||
.get("https://api.github.com/user")
|
||||
.header("Authorization", format!("Bearer {access_token}"))
|
||||
.header("Authorization", access_token)
|
||||
.send()
|
||||
.context("failed to send user request")?
|
||||
.error_for_status()
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
use crate::cli::{
|
||||
auth::{get_token_login, set_token},
|
||||
config::read_config,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
use pesde::{
|
||||
errors::ManifestReadError,
|
||||
source::{pesde::PesdePackageSource, traits::PackageSource},
|
||||
Project,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
use crate::cli::{
|
||||
auth::{get_token_login, set_token},
|
||||
config::read_config,
|
||||
};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct LoginCommand {
|
||||
|
@ -19,6 +21,10 @@ pub struct LoginCommand {
|
|||
#[arg(short, long)]
|
||||
index: Option<String>,
|
||||
|
||||
/// Whether to not prefix the token with `Bearer `
|
||||
#[arg(short, long, conflicts_with = "token")]
|
||||
no_bearer: bool,
|
||||
|
||||
/// The token to use for authentication, skipping login
|
||||
#[arg(short, long, conflicts_with = "index")]
|
||||
token: Option<String>,
|
||||
|
@ -184,8 +190,16 @@ impl LoginCommand {
|
|||
None => self.authenticate_device_flow(&project, &reqwest)?,
|
||||
};
|
||||
|
||||
let token = if self.no_bearer {
|
||||
println!("set token");
|
||||
token
|
||||
} else {
|
||||
let token = format!("Bearer {token}");
|
||||
println!("logged in as {}", get_token_login(&reqwest, &token)?.bold());
|
||||
|
||||
token
|
||||
};
|
||||
|
||||
set_token(Some(&token))?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -3,6 +3,7 @@ use pesde::Project;
|
|||
|
||||
mod login;
|
||||
mod logout;
|
||||
mod set_token_override;
|
||||
mod whoami;
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
|
@ -14,6 +15,8 @@ pub enum AuthCommands {
|
|||
/// Prints the username of the currently logged-in user
|
||||
#[clap(name = "whoami")]
|
||||
WhoAmI(whoami::WhoAmICommand),
|
||||
/// Sets a token override for a specific repository
|
||||
SetTokenOverride(set_token_override::SetTokenOverrideCommand),
|
||||
}
|
||||
|
||||
impl AuthCommands {
|
||||
|
@ -22,6 +25,7 @@ impl AuthCommands {
|
|||
AuthCommands::Login(login) => login.run(project, reqwest),
|
||||
AuthCommands::Logout(logout) => logout.run(),
|
||||
AuthCommands::WhoAmI(whoami) => whoami.run(reqwest),
|
||||
AuthCommands::SetTokenOverride(set_token_override) => set_token_override.run(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
31
src/cli/commands/auth/set_token_override.rs
Normal file
31
src/cli/commands/auth/set_token_override.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use crate::cli::config::{read_config, write_config};
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct SetTokenOverrideCommand {
|
||||
/// The repository to add the token to
|
||||
#[arg(index = 1, value_parser = crate::cli::parse_gix_url)]
|
||||
repository: gix::Url,
|
||||
|
||||
/// The token to set
|
||||
#[arg(index = 2)]
|
||||
token: Option<String>,
|
||||
}
|
||||
|
||||
impl SetTokenOverrideCommand {
|
||||
pub fn run(self) -> anyhow::Result<()> {
|
||||
let mut config = read_config()?;
|
||||
|
||||
if let Some(token) = self.token {
|
||||
println!("set token for {}", self.repository);
|
||||
config.token_overrides.insert(self.repository, token);
|
||||
} else {
|
||||
println!("removed token for {}", self.repository);
|
||||
config.token_overrides.remove(&self.repository);
|
||||
}
|
||||
|
||||
write_config(&config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -15,8 +17,16 @@ pub struct CliConfig {
|
|||
deserialize_with = "crate::util::deserialize_gix_url"
|
||||
)]
|
||||
pub scripts_repo: gix::Url,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub token: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "BTreeMap::is_empty",
|
||||
serialize_with = "crate::cli::serialize_string_url_map",
|
||||
deserialize_with = "crate::cli::deserialize_string_url_map"
|
||||
)]
|
||||
pub token_overrides: BTreeMap<gix::Url, String>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub last_checked_updates: Option<(chrono::DateTime<chrono::Utc>, semver::Version)>,
|
||||
|
@ -31,7 +41,9 @@ impl Default for CliConfig {
|
|||
scripts_repo: "https://github.com/daimond113/pesde-scripts"
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
|
||||
token: None,
|
||||
token_overrides: Default::default(),
|
||||
|
||||
last_checked_updates: None,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use std::{collections::HashSet, fs::create_dir_all, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use gix::bstr::BStr;
|
||||
use pesde::{
|
||||
lockfile::DownloadedGraph, names::PackageNames, source::version_id::VersionId, Project,
|
||||
};
|
||||
use serde::{ser::SerializeMap, Deserialize, Deserializer, Serializer};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
fs::create_dir_all,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use crate::cli::auth::get_token;
|
||||
|
||||
|
@ -168,3 +172,27 @@ impl<V: FromStr<Err = E>, E: Into<anyhow::Error>, N: FromStr<Err = F>, F: Into<a
|
|||
pub fn parse_gix_url(s: &str) -> Result<gix::Url, gix::url::parse::Error> {
|
||||
s.try_into()
|
||||
}
|
||||
|
||||
pub fn serialize_string_url_map<S: Serializer>(
|
||||
url: &BTreeMap<gix::Url, String>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(Some(url.len()))?;
|
||||
for (k, v) in url {
|
||||
map.serialize_entry(&k.to_bstring().to_string(), v)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
|
||||
pub fn deserialize_string_url_map<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<BTreeMap<gix::Url, String>, D::Error> {
|
||||
BTreeMap::<String, String>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
gix::Url::from_bytes(BStr::new(&k))
|
||||
.map(|k| (k, v))
|
||||
.map_err(serde::de::Error::custom)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
62
src/lib.rs
62
src/lib.rs
|
@ -7,7 +7,11 @@
|
|||
compile_error!("at least one of the features `roblox`, `lune`, or `luau` must be enabled");
|
||||
|
||||
use crate::lockfile::Lockfile;
|
||||
use std::path::{Path, PathBuf};
|
||||
use gix::sec::identity::Account;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// Downloading packages
|
||||
pub mod download;
|
||||
|
@ -43,8 +47,9 @@ pub(crate) const LINK_LIB_NO_FILE_FOUND: &str = "____pesde_no_export_file_found"
|
|||
/// Struct containing the authentication configuration
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct AuthConfig {
|
||||
github_token: Option<String>,
|
||||
git_credentials: Option<gix::sec::identity::Account>,
|
||||
default_token: Option<String>,
|
||||
token_overrides: HashMap<gix::Url, String>,
|
||||
git_credentials: Option<Account>,
|
||||
}
|
||||
|
||||
impl AuthConfig {
|
||||
|
@ -53,30 +58,51 @@ impl AuthConfig {
|
|||
AuthConfig::default()
|
||||
}
|
||||
|
||||
/// Access the GitHub token
|
||||
pub fn github_token(&self) -> Option<&str> {
|
||||
self.github_token.as_deref()
|
||||
/// Sets the default token
|
||||
pub fn with_default_token<S: AsRef<str>>(mut self, token: Option<S>) -> Self {
|
||||
self.default_token = token.map(|s| s.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Access the git credentials
|
||||
pub fn git_credentials(&self) -> Option<&gix::sec::identity::Account> {
|
||||
self.git_credentials.as_ref()
|
||||
}
|
||||
|
||||
/// Set the GitHub token
|
||||
pub fn with_github_token<S: AsRef<str>>(mut self, token: Option<S>) -> Self {
|
||||
self.github_token = token.map(|s| s.as_ref().to_string());
|
||||
/// Set the token overrides
|
||||
pub fn with_token_overrides<I: IntoIterator<Item = (gix::Url, S)>, S: AsRef<str>>(
|
||||
mut self,
|
||||
tokens: I,
|
||||
) -> Self {
|
||||
self.token_overrides = tokens
|
||||
.into_iter()
|
||||
.map(|(url, s)| (url, s.as_ref().to_string()))
|
||||
.collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the git credentials
|
||||
pub fn with_git_credentials(
|
||||
mut self,
|
||||
git_credentials: Option<gix::sec::identity::Account>,
|
||||
) -> Self {
|
||||
pub fn with_git_credentials(mut self, git_credentials: Option<Account>) -> Self {
|
||||
self.git_credentials = git_credentials;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the default token
|
||||
pub fn default_token(&self) -> Option<&str> {
|
||||
self.default_token.as_deref()
|
||||
}
|
||||
|
||||
/// Get the token overrides
|
||||
pub fn token_overrides(&self) -> &HashMap<gix::Url, String> {
|
||||
&self.token_overrides
|
||||
}
|
||||
|
||||
/// Get the git credentials
|
||||
pub fn git_credentials(&self) -> Option<&Account> {
|
||||
self.git_credentials.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn get_token(&self, url: &gix::Url) -> Option<&str> {
|
||||
self.token_overrides
|
||||
.get(url)
|
||||
.map(|s| s.as_str())
|
||||
.or(self.default_token.as_deref())
|
||||
}
|
||||
}
|
||||
|
||||
/// The main struct of the pesde library, representing a project
|
||||
|
|
|
@ -10,6 +10,7 @@ use pesde::{AuthConfig, Project, MANIFEST_FILE_NAME};
|
|||
|
||||
use crate::cli::{
|
||||
auth::get_token,
|
||||
config::read_config,
|
||||
home_dir,
|
||||
scripts::update_scripts_folder,
|
||||
version::{check_for_updates, current_version, get_or_download_version, max_installed_version},
|
||||
|
@ -140,7 +141,9 @@ fn run() -> anyhow::Result<()> {
|
|||
project_root_dir,
|
||||
data_dir,
|
||||
cas_dir,
|
||||
AuthConfig::new().with_github_token(token.as_ref()),
|
||||
AuthConfig::new()
|
||||
.with_default_token(token.clone())
|
||||
.with_token_overrides(read_config()?.token_overrides),
|
||||
);
|
||||
|
||||
let reqwest = {
|
||||
|
|
|
@ -277,9 +277,9 @@ impl PackageSource for PesdePackageSource {
|
|||
|
||||
let mut response = reqwest.get(url).header(ACCEPT, "application/octet-stream");
|
||||
|
||||
if let Some(token) = &project.auth_config.github_token {
|
||||
if let Some(token) = project.auth_config.get_token(&self.repo_url) {
|
||||
log::debug!("using token for pesde package download");
|
||||
response = response.header("Authorization", format!("Bearer {token}"));
|
||||
response = response.header("Authorization", token);
|
||||
}
|
||||
|
||||
let response = response.send()?.error_for_status()?;
|
||||
|
|
|
@ -184,9 +184,9 @@ impl PackageSource for WallyPackageSource {
|
|||
.unwrap_or("0.3.2"),
|
||||
);
|
||||
|
||||
if let Some(token) = &project.auth_config.github_token {
|
||||
if let Some(token) = project.auth_config.get_token(&self.repo_url) {
|
||||
log::debug!("using token for wally package download");
|
||||
response = response.header("Authorization", format!("Bearer {token}"));
|
||||
response = response.header("Authorization", token);
|
||||
}
|
||||
|
||||
let response = response.send()?.error_for_status()?;
|
||||
|
|
14
src/util.rs
14
src/util.rs
|
@ -30,6 +30,13 @@ pub fn serialize_gix_url<S: Serializer>(url: &gix::Url, serializer: S) -> Result
|
|||
serializer.serialize_str(&url.to_bstring().to_string())
|
||||
}
|
||||
|
||||
pub fn deserialize_gix_url<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<gix::Url, D::Error> {
|
||||
let s = String::deserialize(deserializer)?;
|
||||
gix::Url::from_bytes(BStr::new(&s)).map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
pub fn serialize_gix_url_map<S: Serializer>(
|
||||
url: &BTreeMap<String, gix::Url>,
|
||||
serializer: S,
|
||||
|
@ -41,13 +48,6 @@ pub fn serialize_gix_url_map<S: Serializer>(
|
|||
map.end()
|
||||
}
|
||||
|
||||
pub fn deserialize_gix_url<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<gix::Url, D::Error> {
|
||||
let s = String::deserialize(deserializer)?;
|
||||
gix::Url::from_bytes(BStr::new(&s)).map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
pub fn deserialize_gix_url_map<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<BTreeMap<String, gix::Url>, D::Error> {
|
||||
|
|
Loading…
Reference in a new issue