restructured auto-credentials (#74)

* restructured auto-credentials

* fix clone

---------

Co-authored-by: Jai A <jaiagr+gpg@pm.me>
Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
Wyatt Verchere 2023-04-07 14:43:21 -07:00 committed by GitHub
parent 34005dd2e2
commit 764d75181f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 82 additions and 48 deletions

View File

@ -57,6 +57,7 @@ pub async fn authenticate(
Ok(credentials) Ok(credentials)
} }
/// Refresh some credentials using Hydra, if needed /// Refresh some credentials using Hydra, if needed
/// This is the primary desired way to get credentials, as it will also refresh them. /// This is the primary desired way to get credentials, as it will also refresh them.
#[tracing::instrument] #[tracing::instrument]

View File

@ -1,5 +1,9 @@
//! Theseus profile management interface //! Theseus profile management interface
use crate::{launcher::download, state::MinecraftChild}; use crate::{
auth::{self, refresh},
launcher::download,
state::MinecraftChild,
};
pub use crate::{ pub use crate::{
state::{JavaSettings, Profile}, state::{JavaSettings, Profile},
State, State,
@ -91,10 +95,33 @@ pub async fn sync(path: &Path) -> crate::Result<()> {
} }
} }
/// Run Minecraft using a profile /// Run Minecraft using a profile and the default credentials, logged in credentials,
/// failing with an error if no credentials are available
#[tracing::instrument(skip_all)]
pub async fn run(path: &Path) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
let state = State::get().await?;
// Get default account and refresh credentials (preferred way to log in)
let default_account = state.settings.read().await.default_user;
let credentials = if let Some(default_account) = default_account {
refresh(default_account, false).await?
} else {
// If no default account, try to use a logged in account
let users = auth::users().await?;
let last_account = users.iter().next();
if let Some(last_account) = last_account {
refresh(last_account.id, false).await?
} else {
return Err(crate::ErrorKind::NoCredentialsError.as_error());
}
};
run_credentials(path, &credentials).await
}
/// Run Minecraft using a profile, and credentials for authentication
/// Returns Arc pointer to RwLock to Child /// Returns Arc pointer to RwLock to Child
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn run( pub async fn run_credentials(
path: &Path, path: &Path,
credentials: &crate::auth::Credentials, credentials: &crate::auth::Credentials,
) -> crate::Result<Arc<RwLock<MinecraftChild>>> { ) -> crate::Result<Arc<RwLock<MinecraftChild>>> {

View File

@ -76,6 +76,9 @@ pub enum ErrorKind {
#[error("Could not create profile: {0}")] #[error("Could not create profile: {0}")]
ProfileCreationError(#[from] profile_create::ProfileCreationError), ProfileCreationError(#[from] profile_create::ProfileCreationError),
#[error("User is not logged in, no credentials available!")]
NoCredentialsError,
#[error("JRE error: {0}")] #[error("JRE error: {0}")]
JREError(#[from] crate::util::jre::JREError), JREError(#[from] crate::util::jre::JREError),

View File

@ -47,7 +47,7 @@ struct ProfileInfoJSON {
} }
// Login information // Login information
#[derive(Encode, Decode, Serialize, Deserialize)] #[derive(Encode, Decode, Serialize, Deserialize, Clone, Debug)]
pub struct Credentials { pub struct Credentials {
#[bincode(with_serde)] #[bincode(with_serde)]
pub id: uuid::Uuid, pub id: uuid::Uuid,

View File

@ -332,7 +332,7 @@ impl ProfileRun {
.await?; .await?;
let credentials = auth::refresh(id, false).await?; let credentials = auth::refresh(id, false).await?;
let proc_lock = profile::run(&path, &credentials).await?; let proc_lock = profile::run_credentials(&path, &credentials).await?;
let mut proc = proc_lock.write().await; let mut proc = proc_lock.write().await;
process::wait_for(&mut proc).await?; process::wait_for(&mut proc).await?;

View File

@ -27,13 +27,13 @@ pub async fn profile_list(
Ok(res) Ok(res)
} }
// Run Minecraft using a profile // Run minecraft using a profile using the default credentials
// Returns a u32 representing the PID, which can be used to poll // Returns a u32 representing the PID, which can be used to poll
// for the actual Child in the state. // for the actual Child in the state.
// invoke('profile_run') // invoke('profile_run', path)
#[tauri::command] #[tauri::command]
pub async fn profile_run(path: &Path, credentials: Credentials) -> Result<u32> { pub async fn profile_run(path: &Path) -> Result<u32> {
let proc_lock = profile::run(path, &credentials).await?; let proc_lock = profile::run(path).await?;
let pid = proc_lock.read().await.child.id().ok_or_else(|| { let pid = proc_lock.read().await.child.id().ok_or_else(|| {
theseus::Error::from(theseus::ErrorKind::LauncherError( theseus::Error::from(theseus::ErrorKind::LauncherError(
"Process failed to stay open.".to_string(), "Process failed to stay open.".to_string(),
@ -42,14 +42,38 @@ pub async fn profile_run(path: &Path, credentials: Credentials) -> Result<u32> {
Ok(pid) Ok(pid)
} }
// Run Minecraft using a profile, and wait for the result // Run Minecraft using a profile using the default credentials, and wait for the result
// invoke('profile_run_wait', path, credentials) // invoke('profile_run_wait', path)
#[tauri::command] #[tauri::command]
pub async fn profile_run_wait( pub async fn profile_run_wait(path: &Path) -> Result<()> {
path: &Path, let proc_lock = profile::run(path).await?;
credentials: Credentials, let mut proc = proc_lock.write().await;
) -> Result<()> { Ok(process::wait_for(&mut proc).await?)
let proc_lock = profile::run(path, &credentials).await?; }
// Run Minecraft using a profile using chosen credentials
// Returns a u32 representing the PID, which can be used to poll
// for the actual Child in the state.
// invoke('profile_run_credentials', {path, credentials})')
#[tauri::command]
pub async fn profile_run_credentials(path: &Path, credentials: Credentials) -> Result<u32> {
let proc_lock = profile::run_credentials(path, &credentials).await?;
let pid = proc_lock.read().await.child.id().ok_or_else(|| {
theseus::Error::from(theseus::ErrorKind::LauncherError(
"Process failed to stay open.".to_string(),
))
})?;
Ok(pid)
}
// Run Minecraft using a profile using the chosen credentials, and wait for the result
// invoke('profile_run_wait', {path, credentials)
#[tauri::command]
pub async fn profile_run_wait_credentials(
path: &Path,
credentials: Credentials,
) -> Result<()> {
let proc_lock = profile::run_credentials(path, &credentials).await?;
let mut proc = proc_lock.write().await; let mut proc = proc_lock.write().await;
Ok(process::wait_for(&mut proc).await?) Ok(process::wait_for(&mut proc).await?)
} }

View File

@ -25,6 +25,8 @@ fn main() {
api::profile::profile_list, api::profile::profile_list,
api::profile::profile_run, api::profile::profile_run,
api::profile::profile_run_wait, api::profile::profile_run_wait,
api::profile::profile_run_credentials,
api::profile::profile_run_wait_credentials,
api::pack::pack_install_version_id, api::pack::pack_install_version_id,
api::pack::pack_install_file, api::pack::pack_install_file,
api::auth::auth_authenticate_begin_flow, api::auth::auth_authenticate_begin_flow,

View File

@ -64,39 +64,16 @@ async fn main() -> theseus::Result<()> {
.await?; .await?;
State::sync().await?; State::sync().await?;
// Attempt to get the default user, if it exists, and refresh their credentials
let default_user_uuid = {
let settings = st.settings.read().await;
settings.default_user
};
let credentials = if let Some(uuid) = default_user_uuid {
println!("Attempting to refresh existing authentication.");
auth::refresh(uuid, false).await
} else {
println!("Freshly authenticating.");
authenticate_run().await
};
// Check attempt to get Credentials
// If successful, run the profile and store the RwLock to the process // Attempt to run game
let proc_lock = match credentials { if auth::users().await?.len() == 0 {
Ok(credentials) => { println!("No users found, authenticating.");
println!("Preparing to run Minecraft."); authenticate_run().await?; // could take credentials from here direct, but also deposited in state users
profile::run(&canonicalize(&profile_path)?, &credentials).await }
}
Err(e) => { // Run a profile, running minecraft and store the RwLock to the process
// If Hydra could not be accessed, for testing, attempt to load credentials from disk and do the same let proc_lock = profile::run(&canonicalize(&profile_path)?).await?;
println!("Could not authenticate: {}.\nAttempting stored authentication.",e);
let users = auth::users().await.expect(
"Could not access any stored users- state was dropped.",
);
let credentials = users
.first()
.expect("Hydra failed, and no stored users were found.");
println!("Preparing to run Minecraft.");
profile::run(&canonicalize(&profile_path)?, credentials).await
}
}?;
let pid = proc_lock let pid = proc_lock
.read() .read()