Skip to content

Commit c576119

Browse files
fix(updater): Run elevated task only if server tell us (#2357)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent 97bc52e commit c576119

File tree

2 files changed

+127
-37
lines changed

2 files changed

+127
-37
lines changed

Diff for: .changes/tauri-updater-windows.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
- Do not run the updater with UAC task if server don't tell us. (Allow toggling server-side)
6+
- 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)

Diff for: core/tauri/src/updater/core.rs

+121-37
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ pub struct RemoteRelease {
4343
pub body: Option<String>,
4444
/// Optional signature for the current platform
4545
pub signature: Option<String>,
46+
#[cfg(target_os = "windows")]
47+
/// Optional: Windows only try to use elevated task
48+
pub with_elevated_task: bool,
4649
}
4750

4851
impl RemoteRelease {
@@ -70,10 +73,11 @@ impl RemoteRelease {
7073
};
7174

7275
// pub_date is required default is: `N/A` if not provided by the remote JSON
73-
let date = match release.get("pub_date") {
74-
Some(pub_date) => pub_date.as_str().unwrap_or("N/A").to_string(),
75-
None => "N/A".to_string(),
76-
};
76+
let date = release
77+
.get("pub_date")
78+
.and_then(|v| v.as_str())
79+
.unwrap_or("N/A")
80+
.to_string();
7781

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

8892
let download_url;
93+
#[cfg(target_os = "windows")]
94+
let mut with_elevated_task = false;
8995

9096
match release.get("platforms") {
9197
//
@@ -118,6 +124,13 @@ impl RemoteRelease {
118124
Error::RemoteMetadata("Unable to extract `url` from remote server`".into())
119125
})?
120126
.to_string();
127+
#[cfg(target_os = "windows")]
128+
{
129+
with_elevated_task = current_target_data
130+
.get("with_elevated_task")
131+
.and_then(|v| v.as_bool())
132+
.unwrap_or_default();
133+
}
121134
} else {
122135
// make sure we have an available platform from the static
123136
return Err(Error::RemoteMetadata("Platform not available".into()));
@@ -134,6 +147,13 @@ impl RemoteRelease {
134147
Error::RemoteMetadata("Unable to extract `url` from remote server`".into())
135148
})?
136149
.to_string();
150+
#[cfg(target_os = "windows")]
151+
{
152+
with_elevated_task = match release.get("with_elevated_task") {
153+
Some(with_elevated_task) => with_elevated_task.as_bool().unwrap_or(false),
154+
None => false,
155+
};
156+
}
137157
}
138158
}
139159
// Return our formatted release
@@ -143,6 +163,8 @@ impl RemoteRelease {
143163
download_url,
144164
body,
145165
signature,
166+
#[cfg(target_os = "windows")]
167+
with_elevated_task,
146168
})
147169
}
148170
}
@@ -343,6 +365,8 @@ impl<'a> UpdateBuilder<'a> {
343365
download_url: final_release.download_url,
344366
body: final_release.body,
345367
signature: final_release.signature,
368+
#[cfg(target_os = "windows")]
369+
with_elevated_task: final_release.with_elevated_task,
346370
})
347371
}
348372
}
@@ -371,6 +395,10 @@ pub struct Update {
371395
download_url: String,
372396
/// Signature announced
373397
signature: Option<String>,
398+
#[cfg(target_os = "windows")]
399+
/// Optional: Windows only try to use elevated task
400+
/// Default to false
401+
with_elevated_task: bool,
374402
}
375403

376404
impl Update {
@@ -465,6 +493,9 @@ impl Update {
465493
// we copy the files depending of the operating system
466494
// we run the setup, appimage re-install or overwrite the
467495
// macos .app
496+
#[cfg(target_os = "windows")]
497+
copy_files_and_run(tmp_dir, extract_path, self.with_elevated_task)?;
498+
#[cfg(not(target_os = "windows"))]
468499
copy_files_and_run(tmp_dir, extract_path)?;
469500
// We are done!
470501
Ok(())
@@ -525,7 +556,11 @@ fn copy_files_and_run(tmp_dir: tempfile::TempDir, extract_path: PathBuf) -> Resu
525556
// Update server can provide a custom EXE (installer) who can run any task.
526557
#[cfg(target_os = "windows")]
527558
#[allow(clippy::unnecessary_wraps)]
528-
fn copy_files_and_run(tmp_dir: tempfile::TempDir, _extract_path: PathBuf) -> Result {
559+
fn copy_files_and_run(
560+
tmp_dir: tempfile::TempDir,
561+
_extract_path: PathBuf,
562+
with_elevated_task: bool,
563+
) -> Result {
529564
use crate::api::file::Move;
530565

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

545580
exit(0);
546581
} else if found_path.extension() == Some(OsStr::new("msi")) {
547-
if let Some(bin_name) = std::env::current_exe()
548-
.ok()
549-
.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
550-
.and_then(|s| s.into_string().ok())
551-
{
552-
let product_name = bin_name.replace(".exe", "");
553-
554-
// Check if there is a task that enables the updater to skip the UAC prompt
555-
let update_task_name = format!("Update {} - Skip UAC", product_name);
556-
if let Ok(status) = Command::new("schtasks")
557-
.arg("/QUERY")
558-
.arg("/TN")
559-
.arg(update_task_name.clone())
560-
.status()
582+
if with_elevated_task {
583+
if let Some(bin_name) = std::env::current_exe()
584+
.ok()
585+
.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
586+
.and_then(|s| s.into_string().ok())
561587
{
562-
if status.success() {
563-
// Rename the MSI to the match file name the Skip UAC task is expecting it to be
564-
let temp_msi = tmp_path.with_file_name(bin_name).with_extension("msi");
565-
Move::from_source(&found_path)
566-
.to_dest(&temp_msi)
567-
.expect("Unable to move update MSI");
568-
let exit_status = Command::new("schtasks")
569-
.arg("/RUN")
570-
.arg("/TN")
571-
.arg(update_task_name)
572-
.status()
573-
.expect("failed to start updater task");
574-
575-
if exit_status.success() {
576-
// Successfully launched task that skips the UAC prompt
577-
exit(0);
588+
let product_name = bin_name.replace(".exe", "");
589+
590+
// Check if there is a task that enables the updater to skip the UAC prompt
591+
let update_task_name = format!("Update {} - Skip UAC", product_name);
592+
if let Ok(status) = Command::new("schtasks")
593+
.arg("/QUERY")
594+
.arg("/TN")
595+
.arg(update_task_name.clone())
596+
.status()
597+
{
598+
if status.success() {
599+
// Rename the MSI to the match file name the Skip UAC task is expecting it to be
600+
let temp_msi = tmp_path.with_file_name(bin_name).with_extension("msi");
601+
Move::from_source(&found_path)
602+
.to_dest(&temp_msi)
603+
.expect("Unable to move update MSI");
604+
let exit_status = Command::new("schtasks")
605+
.arg("/RUN")
606+
.arg("/TN")
607+
.arg(update_task_name)
608+
.status()
609+
.expect("failed to start updater task");
610+
611+
if exit_status.success() {
612+
// Successfully launched task that skips the UAC prompt
613+
exit(0);
614+
}
578615
}
616+
// Failed to run update task. Following UAC Path
579617
}
580-
581-
// Failed to run update task. Following UAC Path
582618
}
583619
}
584620

@@ -834,6 +870,27 @@ mod test {
834870
)
835871
}
836872

873+
fn generate_sample_with_elevated_task_platform_json(
874+
version: &str,
875+
public_signature: &str,
876+
download_url: &str,
877+
with_elevated_task: bool,
878+
) -> String {
879+
format!(
880+
r#"
881+
{{
882+
"name": "v{}",
883+
"notes": "This is the latest version! Once updated you shouldn't see this prompt.",
884+
"pub_date": "2020-06-25T14:14:19Z",
885+
"signature": "{}",
886+
"url": "{}",
887+
"with_elevated_task": "{}"
888+
}}
889+
"#,
890+
version, public_signature, download_url, with_elevated_task
891+
)
892+
}
893+
837894
fn generate_sample_bad_json() -> String {
838895
r#"{
839896
"version": "v0.0.3",
@@ -963,6 +1020,33 @@ mod test {
9631020
assert!(updater.should_update);
9641021
}
9651022

1023+
#[test]
1024+
fn simple_http_updater_with_elevated_task() {
1025+
let _m = mockito::mock("GET", "/win64/1.0.0")
1026+
.with_status(200)
1027+
.with_header("content-type", "application/json")
1028+
.with_body(generate_sample_with_elevated_task_platform_json(
1029+
"2.0.0",
1030+
"SampleTauriKey",
1031+
"https://tauri.studio",
1032+
true,
1033+
))
1034+
.create();
1035+
1036+
let check_update = block!(builder()
1037+
.current_version("1.0.0")
1038+
.url(format!(
1039+
"{}/win64/{{{{current_version}}}}",
1040+
mockito::server_url()
1041+
))
1042+
.build());
1043+
1044+
assert!(check_update.is_ok());
1045+
let updater = check_update.expect("Can't check update");
1046+
1047+
assert!(updater.should_update);
1048+
}
1049+
9661050
#[test]
9671051
fn http_updater_uptodate() {
9681052
let _m = mockito::mock("GET", "/darwin/10.0.0")

0 commit comments

Comments
 (0)