diff --git a/src/Files.App/Data/Contexts/Window/IWindowContext.cs b/src/Files.App/Data/Contexts/Window/IWindowContext.cs index e6271f95c25f..45d535b3b5b0 100644 --- a/src/Files.App/Data/Contexts/Window/IWindowContext.cs +++ b/src/Files.App/Data/Contexts/Window/IWindowContext.cs @@ -1,12 +1,16 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using System.ComponentModel; - namespace Files.App.Data.Contexts { public interface IWindowContext : INotifyPropertyChanged { bool IsCompactOverlay { get; } + + /// + bool IsRunningAsAdmin { get; } + + /// + bool CanDragAndDrop { get; } } } diff --git a/src/Files.App/Data/Contexts/Window/WindowContext.cs b/src/Files.App/Data/Contexts/Window/WindowContext.cs index 58140f8bf533..ce4b80015c0c 100644 --- a/src/Files.App/Data/Contexts/Window/WindowContext.cs +++ b/src/Files.App/Data/Contexts/Window/WindowContext.cs @@ -1,18 +1,30 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.UI.Windowing; namespace Files.App.Data.Contexts { + /// internal sealed class WindowContext : ObservableObject, IWindowContext { + private IWindowsSecurityService WindowsSecurityService = Ioc.Default.GetRequiredService(); + private bool isCompactOverlay; + /// public bool IsCompactOverlay => isCompactOverlay; + /// + public bool IsRunningAsAdmin { get; private set; } + + /// + public bool CanDragAndDrop { get; private set; } + public WindowContext() { + IsRunningAsAdmin = WindowsSecurityService.IsAppElevated(); + CanDragAndDrop = WindowsSecurityService.CanDragAndDrop(); + MainWindow.Instance.PresenterChanged += Window_PresenterChanged; } diff --git a/src/Files.App/Data/Contracts/IWindowsSecurityService.cs b/src/Files.App/Data/Contracts/IWindowsSecurityService.cs new file mode 100644 index 000000000000..b1d29250d101 --- /dev/null +++ b/src/Files.App/Data/Contracts/IWindowsSecurityService.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Data.Contracts +{ + /// + /// Provides service for security APIs on Windows. + /// + public interface IWindowsSecurityService + { + /// + /// Gets a value that indicates whether the application is elevated. + /// + /// Returns true if the application is elevated; otherwise, false. + bool IsAppElevated(); + + /// + /// Gets a value that indicates whether the application can drag & drop. + /// + /// + /// Drag & drop onto an elevated app is not allowed (just crashes) due to UIPI. + ///
+ ///
+ /// For more info, visit: + ///
+ /// + ///
+ ///
+ ///
+ /// Returns true if the application can drag & drop; otherwise, false. + bool CanDragAndDrop(); + + /// + /// Gets a value that indicates whether the application needs to be elevated for some operations. + /// + /// + /// True if the application needs to be elevated for some operations; otherwise, false. + bool IsElevationRequired(string path); + } +} diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index d05d49735bd4..5c0daa8e4e56 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -166,6 +166,7 @@ public static IHost ConfigureHost() // Services .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/src/Files.App/Helpers/Environment/ElevationHelpers.cs b/src/Files.App/Helpers/Environment/ElevationHelpers.cs deleted file mode 100644 index 2770e09eb7ae..000000000000 --- a/src/Files.App/Helpers/Environment/ElevationHelpers.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Security.Principal; - -namespace Files.App.Helpers -{ - public static class ElevationHelpers - { - public static bool IsElevationRequired(string filePath) - { - if (string.IsNullOrEmpty(filePath)) - return false; - - return Win32PInvoke.IsElevationRequired(filePath); - } - - public static bool IsAppRunAsAdmin() - { - using WindowsIdentity identity = WindowsIdentity.GetCurrent(); - return new WindowsPrincipal(identity).IsInRole(new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null)); - } - } -} diff --git a/src/Files.App/Services/Windows/WindowsSecurityService.cs b/src/Files.App/Services/Windows/WindowsSecurityService.cs new file mode 100644 index 000000000000..1c05f8c7bfa5 --- /dev/null +++ b/src/Files.App/Services/Windows/WindowsSecurityService.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Services +{ + /// + public sealed class WindowsSecurityService : IWindowsSecurityService + { + /// + public unsafe bool IsAppElevated() + { + var identity = System.Security.Principal.WindowsIdentity.GetCurrent(); + var principal = new System.Security.Principal.WindowsPrincipal(identity); + return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator); + } + + /// + public unsafe bool CanDragAndDrop() + { + return !IsAppElevated(); + } + + /// + public bool IsElevationRequired(string path) + { + if (string.IsNullOrEmpty(path)) + return false; + + return Win32PInvoke.IsElevationRequired(path); + } + } +} diff --git a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs index 1151223e4505..4dc46487e888 100644 --- a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs +++ b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs @@ -18,6 +18,7 @@ public sealed partial class TabBar : BaseTabBar, INotifyPropertyChanged private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); private readonly IAppearanceSettingsService AppearanceSettingsService = Ioc.Default.GetRequiredService(); + private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); // Fields @@ -44,12 +45,8 @@ public sealed partial class TabBar : BaseTabBar, INotifyPropertyChanged public bool ShowTabActionsButton => AppearanceSettingsService.ShowTabActions; - // Dragging makes the app crash when run as admin. - // For more information: - // - https://github.com/files-community/Files/issues/12390 - // - https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669 public bool AllowTabsDrag - => !ElevationHelpers.IsAppRunAsAdmin(); + => WindowContext.CanDragAndDrop; public Rectangle DragArea => DragAreaRectangle; @@ -172,7 +169,7 @@ private void TabView_TabStripDragOver(object sender, DragEventArgs e) { if (e.DataView.Properties.ContainsKey(TabPathIdentifier)) { - HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin(); + HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop; e.AcceptedOperation = DataPackageOperation.Move; e.DragUIOverride.Caption = "TabStripDragAndDropUIOverrideCaption".GetLocalizedResource(); @@ -187,12 +184,12 @@ private void TabView_TabStripDragOver(object sender, DragEventArgs e) private void TabView_DragLeave(object sender, DragEventArgs e) { - HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin(); + HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop; } private async void TabView_TabStripDrop(object sender, DragEventArgs e) { - HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin(); + HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop; if (!(sender is TabView tabStrip)) return; diff --git a/src/Files.App/ViewModels/ShellViewModel.cs b/src/Files.App/ViewModels/ShellViewModel.cs index d491dae1aff4..dcae759b69ee 100644 --- a/src/Files.App/ViewModels/ShellViewModel.cs +++ b/src/Files.App/ViewModels/ShellViewModel.cs @@ -55,6 +55,7 @@ public sealed class ShellViewModel : ObservableObject, IDisposable private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService(); private readonly ISizeProvider folderSizeProvider = Ioc.Default.GetRequiredService(); private readonly IStorageCacheService fileListCache = Ioc.Default.GetRequiredService(); + private readonly IWindowsSecurityService WindowsSecurityService = Ioc.Default.GetRequiredService(); // Only used for Binding and ApplyFilesAndFoldersChangesAsync, don't manipulate on this! public BulkConcurrentObservableCollection FilesAndFolders { get; } @@ -1268,9 +1269,7 @@ private bool CheckElevationRights(ListedItem item) if (item.SyncStatusUI.LoadSyncStatus) return false; - return item.IsShortcut - ? ElevationHelpers.IsElevationRequired(((ShortcutItem)item).TargetPath) - : ElevationHelpers.IsElevationRequired(item.ItemPath); + return WindowsSecurityService.IsElevationRequired(item.IsShortcut ? ((ShortcutItem)item).TargetPath : item.ItemPath); } public async Task LoadGitPropertiesAsync(GitItem gitItem) diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index 9ca1d77258b1..077581742188 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -39,6 +39,7 @@ public abstract class BaseLayoutPage : Page, IBaseLayoutPage, INotifyPropertyCha protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!; protected ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); public InfoPaneViewModel InfoPaneViewModel { get; } = Ioc.Default.GetRequiredService(); + protected readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); // ViewModels @@ -82,10 +83,8 @@ public CurrentInstanceViewModel? InstanceViewModel public static AppModel AppModel => App.AppModel; - // NOTE: Dragging makes the app crash when run as admin. (#12390) - // For more information, visit https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669 public bool AllowItemDrag - => !ElevationHelpers.IsAppRunAsAdmin(); + => WindowContext.CanDragAndDrop; public CommandBarFlyout ItemContextMenuFlyout { get; set; } = new() { diff --git a/src/Files.App/Views/MainPage.xaml.cs b/src/Files.App/Views/MainPage.xaml.cs index 812b763361e7..6106a9ef5bb5 100644 --- a/src/Files.App/Views/MainPage.xaml.cs +++ b/src/Files.App/Views/MainPage.xaml.cs @@ -12,10 +12,7 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Navigation; -using Sentry; -using System.Data; using Windows.ApplicationModel; -using Windows.ApplicationModel.DataTransfer; using Windows.Foundation.Metadata; using Windows.Graphics; using Windows.Services.Store; @@ -28,15 +25,10 @@ public sealed partial class MainPage : Page { private IGeneralSettingsService generalSettingsService { get; } = Ioc.Default.GetRequiredService(); public IUserSettingsService UserSettingsService { get; } - + private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); public ICommandManager Commands { get; } - - public IWindowContext WindowContext { get; } - public SidebarViewModel SidebarAdaptiveViewModel { get; } - public MainPageViewModel ViewModel { get; } - public StatusCenterViewModel OngoingTasksViewModel { get; } public static AppModel AppModel @@ -44,8 +36,6 @@ public static AppModel AppModel private bool keyReleased = true; - private bool isAppRunningAsAdmin => ElevationHelpers.IsAppRunAsAdmin(); - private DispatcherQueueTimer _updateDateDisplayTimer; public MainPage() @@ -55,7 +45,6 @@ public MainPage() // Dependency Injection UserSettingsService = Ioc.Default.GetRequiredService(); Commands = Ioc.Default.GetRequiredService(); - WindowContext = Ioc.Default.GetRequiredService(); SidebarAdaptiveViewModel = Ioc.Default.GetRequiredService(); SidebarAdaptiveViewModel.PaneFlyout = (MenuFlyout)Resources["SidebarContextMenu"]; ViewModel = Ioc.Default.GetRequiredService(); @@ -307,7 +296,7 @@ private void Page_Loaded(object sender, RoutedEventArgs e) if ( AppLifecycleHelper.AppEnvironment is not AppEnvironment.Dev && - isAppRunningAsAdmin && + WindowContext.IsRunningAsAdmin && UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt ) { diff --git a/src/Files.App/Views/Settings/TagsPage.xaml.cs b/src/Files.App/Views/Settings/TagsPage.xaml.cs index 55d9c49af679..cf6176ddf5ab 100644 --- a/src/Files.App/Views/Settings/TagsPage.xaml.cs +++ b/src/Files.App/Views/Settings/TagsPage.xaml.cs @@ -12,6 +12,8 @@ namespace Files.App.Views.Settings { public sealed partial class TagsPage : Page { + private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); + private string oldTagName = string.Empty; // Will be null unless the user has edited any tag @@ -19,9 +21,8 @@ public sealed partial class TagsPage : Page private FlyoutBase? deleteItemFlyout; - // See issue #12390 on Github. Dragging makes the app crash when run as admin. - // Further reading: https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669 - public bool AllowItemsDrag => !ElevationHelpers.IsAppRunAsAdmin(); + public bool AllowItemsDrag + => WindowContext.CanDragAndDrop; public TagsPage() {