desktop: Implement asking for permission before accessing files

This implements the filesystem access mode setting and its three options.
When set to Ask, the filesystem access dialog will be shown to the user.
This commit is contained in:
Kamil Jarosz 2024-09-11 23:36:03 +02:00
parent 61c4005785
commit 6b6c4d4909
2 changed files with 59 additions and 3 deletions

View File

@ -3,13 +3,17 @@ use ruffle_frontend_utils::backends::navigator::NavigatorInterface;
use std::fs::File;
use std::io;
use std::io::ErrorKind;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use tokio::sync::oneshot;
use url::Url;
use winit::event_loop::EventLoopProxy;
use crate::cli::FilesystemAccessMode;
use crate::custom_event::RuffleEvent;
use crate::gui::dialogs::filesystem_access_dialog::{
FilesystemAccessDialogConfiguration, FilesystemAccessDialogResult,
};
use crate::gui::dialogs::network_access_dialog::{
NetworkAccessDialogConfiguration, NetworkAccessDialogResult,
};
@ -20,14 +24,46 @@ use crate::util::open_url;
pub struct DesktopNavigatorInterface {
// Arc + Mutex due to macOS
event_loop: Arc<Mutex<EventLoopProxy<RuffleEvent>>>,
filesystem_access_mode: FilesystemAccessMode,
// TODO Make this more generic, maybe a manager?
allowed_paths: Arc<Mutex<Vec<PathBuf>>>,
}
impl DesktopNavigatorInterface {
pub fn new(event_loop: EventLoopProxy<RuffleEvent>) -> Self {
pub fn new(
event_loop: EventLoopProxy<RuffleEvent>,
movie_path: Option<PathBuf>,
filesystem_access_mode: FilesystemAccessMode,
) -> Self {
Self {
event_loop: Arc::new(Mutex::new(event_loop)),
allowed_paths: Arc::new(Mutex::new(if let Some(movie_path) = movie_path {
vec![movie_path]
} else {
Vec::new()
})),
filesystem_access_mode,
}
}
async fn ask_for_filesystem_access(&self, path: &Path) -> bool {
let (notifier, receiver) = oneshot::channel();
let _ = self
.event_loop
.lock()
.expect("Non-poisoned event loop")
.send_event(RuffleEvent::OpenDialog(DialogDescriptor::FilesystemAccess(
FilesystemAccessDialogConfiguration::new(
notifier,
self.allowed_paths.clone(),
path.to_path_buf(),
),
)));
receiver.await == Ok(FilesystemAccessDialogResult::Allow)
}
}
impl NavigatorInterface for DesktopNavigatorInterface {
@ -45,6 +81,18 @@ impl NavigatorInterface for DesktopNavigatorInterface {
}
async fn open_file(&self, path: &Path) -> io::Result<File> {
let path = &path.canonicalize()?;
let allow = match self.filesystem_access_mode {
FilesystemAccessMode::Allow => true,
FilesystemAccessMode::Deny => false,
FilesystemAccessMode::Ask => self.ask_for_filesystem_access(path).await,
};
if !allow {
return Err(ErrorKind::PermissionDenied.into());
}
File::open(path).or_else(|e| {
if cfg!(feature = "sandbox") {
use rfd::FileDialog;

View File

@ -2,6 +2,7 @@ use crate::backends::{
CpalAudioBackend, DesktopExternalInterfaceProvider, DesktopFSCommandProvider,
DesktopNavigatorInterface, DesktopUiBackend,
};
use crate::cli::FilesystemAccessMode;
use crate::custom_event::RuffleEvent;
use crate::gui::{FilePicker, MovieView};
use crate::preferences::GlobalPreferences;
@ -45,6 +46,7 @@ pub struct LaunchOptions {
pub save_directory: PathBuf,
pub cache_directory: PathBuf,
pub open_url_mode: OpenURLMode,
pub filesystem_access_mode: FilesystemAccessMode,
pub gamepad_button_mapping: HashMap<GamepadButton, KeyCode>,
pub avm2_optimizer_enabled: bool,
}
@ -93,6 +95,7 @@ impl From<&GlobalPreferences> for LaunchOptions {
save_directory: value.cli.save_directory.clone(),
cache_directory: value.cli.cache_directory.clone(),
open_url_mode: value.cli.open_url_mode,
filesystem_access_mode: value.cli.filesystem_access_mode,
socket_allowed: HashSet::from_iter(value.cli.socket_allow.iter().cloned()),
tcp_connections: value.cli.tcp_connections,
gamepad_button_mapping: HashMap::from_iter(value.cli.gamepad_button.iter().cloned()),
@ -198,6 +201,7 @@ impl ActivePlayer {
save_directory: opt.save_directory.clone(),
cache_directory: opt.cache_directory.clone(),
open_url_mode: opt.open_url_mode,
filesystem_access_mode: opt.filesystem_access_mode,
gamepad_button_mapping: opt.gamepad_button_mapping.clone(),
avm2_optimizer_enabled: opt.avm2_optimizer_enabled,
})
@ -221,7 +225,11 @@ impl ActivePlayer {
opt.socket_allowed.clone(),
opt.tcp_connections.unwrap_or(SocketMode::Ask),
Rc::new(content),
DesktopNavigatorInterface::new(event_loop.clone()),
DesktopNavigatorInterface::new(
event_loop.clone(),
movie_url.to_file_path().ok(),
opt.filesystem_access_mode,
),
);
if cfg!(feature = "external_video") && preferences.openh264_enabled() {