Skip to content

Commit

Permalink
fix(updater): Run elevated task only if server tell us (#2357)
Browse files Browse the repository at this point in the history
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
  • Loading branch information
lemarier and lucasfernog committed Aug 8, 2021
1 parent 97bc52e commit c576119
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 37 deletions.
6 changes: 6 additions & 0 deletions .changes/tauri-updater-windows.md
@@ -0,0 +1,6 @@
---
"tauri": patch
---

- Do not run the updater with UAC task if server don't tell us. (Allow toggling server-side)
- The updater expect a field named `with_elevated_task` with a `boolean` and will not run if the task is not installed first. (windows only)
158 changes: 121 additions & 37 deletions core/tauri/src/updater/core.rs
Expand Up @@ -43,6 +43,9 @@ pub struct RemoteRelease {
pub body: Option<String>,
/// Optional signature for the current platform
pub signature: Option<String>,
#[cfg(target_os = "windows")]
/// Optional: Windows only try to use elevated task
pub with_elevated_task: bool,
}

impl RemoteRelease {
Expand Down Expand Up @@ -70,10 +73,11 @@ impl RemoteRelease {
};

// pub_date is required default is: `N/A` if not provided by the remote JSON
let date = match release.get("pub_date") {
Some(pub_date) => pub_date.as_str().unwrap_or("N/A").to_string(),
None => "N/A".to_string(),
};
let date = release
.get("pub_date")
.and_then(|v| v.as_str())
.unwrap_or("N/A")
.to_string();

// body is optional to build our update
let body = release
Expand All @@ -86,6 +90,8 @@ impl RemoteRelease {
.map(|signature| signature.as_str().unwrap_or("").to_string());

let download_url;
#[cfg(target_os = "windows")]
let mut with_elevated_task = false;

match release.get("platforms") {
//
Expand Down Expand Up @@ -118,6 +124,13 @@ impl RemoteRelease {
Error::RemoteMetadata("Unable to extract `url` from remote server`".into())
})?
.to_string();
#[cfg(target_os = "windows")]
{
with_elevated_task = current_target_data
.get("with_elevated_task")
.and_then(|v| v.as_bool())
.unwrap_or_default();
}
} else {
// make sure we have an available platform from the static
return Err(Error::RemoteMetadata("Platform not available".into()));
Expand All @@ -134,6 +147,13 @@ impl RemoteRelease {
Error::RemoteMetadata("Unable to extract `url` from remote server`".into())
})?
.to_string();
#[cfg(target_os = "windows")]
{
with_elevated_task = match release.get("with_elevated_task") {
Some(with_elevated_task) => with_elevated_task.as_bool().unwrap_or(false),
None => false,
};
}
}
}
// Return our formatted release
Expand All @@ -143,6 +163,8 @@ impl RemoteRelease {
download_url,
body,
signature,
#[cfg(target_os = "windows")]
with_elevated_task,
})
}
}
Expand Down Expand Up @@ -343,6 +365,8 @@ impl<'a> UpdateBuilder<'a> {
download_url: final_release.download_url,
body: final_release.body,
signature: final_release.signature,
#[cfg(target_os = "windows")]
with_elevated_task: final_release.with_elevated_task,
})
}
}
Expand Down Expand Up @@ -371,6 +395,10 @@ pub struct Update {
download_url: String,
/// Signature announced
signature: Option<String>,
#[cfg(target_os = "windows")]
/// Optional: Windows only try to use elevated task
/// Default to false
with_elevated_task: bool,
}

impl Update {
Expand Down Expand Up @@ -465,6 +493,9 @@ impl Update {
// we copy the files depending of the operating system
// we run the setup, appimage re-install or overwrite the
// macos .app
#[cfg(target_os = "windows")]
copy_files_and_run(tmp_dir, extract_path, self.with_elevated_task)?;
#[cfg(not(target_os = "windows"))]
copy_files_and_run(tmp_dir, extract_path)?;
// We are done!
Ok(())
Expand Down Expand Up @@ -525,7 +556,11 @@ fn copy_files_and_run(tmp_dir: tempfile::TempDir, extract_path: PathBuf) -> Resu
// Update server can provide a custom EXE (installer) who can run any task.
#[cfg(target_os = "windows")]
#[allow(clippy::unnecessary_wraps)]
fn copy_files_and_run(tmp_dir: tempfile::TempDir, _extract_path: PathBuf) -> Result {
fn copy_files_and_run(
tmp_dir: tempfile::TempDir,
_extract_path: PathBuf,
with_elevated_task: bool,
) -> Result {
use crate::api::file::Move;

let paths = read_dir(&tmp_dir)?;
Expand All @@ -544,41 +579,42 @@ fn copy_files_and_run(tmp_dir: tempfile::TempDir, _extract_path: PathBuf) -> Res

exit(0);
} else if found_path.extension() == Some(OsStr::new("msi")) {
if let Some(bin_name) = std::env::current_exe()
.ok()
.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
.and_then(|s| s.into_string().ok())
{
let product_name = bin_name.replace(".exe", "");

// Check if there is a task that enables the updater to skip the UAC prompt
let update_task_name = format!("Update {} - Skip UAC", product_name);
if let Ok(status) = Command::new("schtasks")
.arg("/QUERY")
.arg("/TN")
.arg(update_task_name.clone())
.status()
if with_elevated_task {
if let Some(bin_name) = std::env::current_exe()
.ok()
.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
.and_then(|s| s.into_string().ok())
{
if status.success() {
// Rename the MSI to the match file name the Skip UAC task is expecting it to be
let temp_msi = tmp_path.with_file_name(bin_name).with_extension("msi");
Move::from_source(&found_path)
.to_dest(&temp_msi)
.expect("Unable to move update MSI");
let exit_status = Command::new("schtasks")
.arg("/RUN")
.arg("/TN")
.arg(update_task_name)
.status()
.expect("failed to start updater task");

if exit_status.success() {
// Successfully launched task that skips the UAC prompt
exit(0);
let product_name = bin_name.replace(".exe", "");

// Check if there is a task that enables the updater to skip the UAC prompt
let update_task_name = format!("Update {} - Skip UAC", product_name);
if let Ok(status) = Command::new("schtasks")
.arg("/QUERY")
.arg("/TN")
.arg(update_task_name.clone())
.status()
{
if status.success() {
// Rename the MSI to the match file name the Skip UAC task is expecting it to be
let temp_msi = tmp_path.with_file_name(bin_name).with_extension("msi");
Move::from_source(&found_path)
.to_dest(&temp_msi)
.expect("Unable to move update MSI");
let exit_status = Command::new("schtasks")
.arg("/RUN")
.arg("/TN")
.arg(update_task_name)
.status()
.expect("failed to start updater task");

if exit_status.success() {
// Successfully launched task that skips the UAC prompt
exit(0);
}
}
// Failed to run update task. Following UAC Path
}

// Failed to run update task. Following UAC Path
}
}

Expand Down Expand Up @@ -834,6 +870,27 @@ mod test {
)
}

fn generate_sample_with_elevated_task_platform_json(
version: &str,
public_signature: &str,
download_url: &str,
with_elevated_task: bool,
) -> String {
format!(
r#"
{{
"name": "v{}",
"notes": "This is the latest version! Once updated you shouldn't see this prompt.",
"pub_date": "2020-06-25T14:14:19Z",
"signature": "{}",
"url": "{}",
"with_elevated_task": "{}"
}}
"#,
version, public_signature, download_url, with_elevated_task
)
}

fn generate_sample_bad_json() -> String {
r#"{
"version": "v0.0.3",
Expand Down Expand Up @@ -963,6 +1020,33 @@ mod test {
assert!(updater.should_update);
}

#[test]
fn simple_http_updater_with_elevated_task() {
let _m = mockito::mock("GET", "/win64/1.0.0")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(generate_sample_with_elevated_task_platform_json(
"2.0.0",
"SampleTauriKey",
"https://tauri.studio",
true,
))
.create();

let check_update = block!(builder()
.current_version("1.0.0")
.url(format!(
"{}/win64/{{{{current_version}}}}",
mockito::server_url()
))
.build());

assert!(check_update.is_ok());
let updater = check_update.expect("Can't check update");

assert!(updater.should_update);
}

#[test]
fn http_updater_uptodate() {
let _m = mockito::mock("GET", "/darwin/10.0.0")
Expand Down

0 comments on commit c576119

Please sign in to comment.