diff --git a/src/Files.App/Filesystem/Security/AccessControlEntry.cs b/src/Files.App/Filesystem/Security/AccessControlEntry.cs
index 1c9b90c45149..6917635b3ae5 100644
--- a/src/Files.App/Filesystem/Security/AccessControlEntry.cs
+++ b/src/Files.App/Filesystem/Security/AccessControlEntry.cs
@@ -13,56 +13,144 @@
namespace Files.App.Filesystem.Security
{
///
- /// Represents an ACE.
+ /// Represents an access control entry (ACE).
///
public class AccessControlEntry : ObservableObject
{
- public bool IsFolder { get; set; }
-
+ ///
+ /// Whether the path indicates folder or not
+ ///
+ public bool IsFolder { get; private set; }
+
+ ///
+ /// The owner in the security descriptor (SD).
+ /// NULL if the security descriptor has no owner SID.
+ ///
public Principal Principal { get; set; }
- private AccessControlType _AccessControlType;
- public AccessControlType AccessControlType
+ ///
+ /// Whether the ACE is inherited or not
+ ///
+ public bool IsInherited { get; private set; }
+
+ ///
+ /// Whether the ACE is editable or not
+ ///
+ public bool IsEditable
+ => IsSelected && !IsInherited && false;
+
+ ///
+ /// AccessControlTypeHumanized
+ ///
+ public string AccessControlTypeHumanized
+ => AccessControlType switch
+ {
+ AccessControlEntryType.Allow => "Allow",
+ _ => "Deny" // AccessControlType.Deny
+ };
+
+ ///
+ /// AccessControlTypeGlyph
+ ///
+ public string AccessControlTypeGlyph
+ => AccessControlType switch
+ {
+ AccessControlEntryType.Allow => "\xE73E",
+ _ => "\xF140" // AccessControlType.Deny
+ };
+
+ ///
+ /// AccessMaskFlagsHumanized
+ ///
+ public string AccessMaskFlagsHumanized
{
- get => _AccessControlType;
- set
+ get
{
- // Update access control type glyph
- if (SetProperty(ref _AccessControlType, value))
- OnPropertyChanged(nameof(AccessControlTypeGlyph));
+ var accessMaskStrings = new List();
+
+ if (AccessMaskFlags == AccessMaskFlags.NULL)
+ accessMaskStrings.Add("None".GetLocalizedResource());
+
+ if (FullControlAccess)
+ accessMaskStrings.Add("SecurityFullControlLabel/Text".GetLocalizedResource());
+ else if (ModifyAccess)
+ accessMaskStrings.Add("SecurityModifyLabel/Text".GetLocalizedResource());
+ else if (ReadAndExecuteAccess)
+ accessMaskStrings.Add("SecurityReadAndExecuteLabel/Text".GetLocalizedResource());
+ else if (ReadAccess)
+ accessMaskStrings.Add("SecurityReadLabel/Text".GetLocalizedResource());
+
+ if (!FullControlAccess && !ModifyAccess && WriteAccess)
+ accessMaskStrings.Add("Write".GetLocalizedResource());
+
+ if (SpecialAccess)
+ accessMaskStrings.Add("SecuritySpecialLabel/Text".GetLocalizedResource());
+
+ return string.Join(", ", accessMaskStrings);
}
}
- private AccessMaskFlags _AccessMaskFlags;
- public AccessMaskFlags AccessMaskFlags
+ ///
+ /// IsInheritedHumanized
+ ///
+ public string IsInheritedHumanized
+ => IsInherited ? "Yes".GetLocalizedResource() : "No".GetLocalizedResource();
+
+ ///
+ /// InheritanceFlagsHumanized
+ ///
+ public string InheritanceFlagsHumanized
{
- get => _AccessMaskFlags;
- set
+ get
{
- if (SetProperty(ref _AccessMaskFlags, value))
- OnPropertyChanged(nameof(AccessMaskFlagsHumanized));
+ var inheritanceStrings = new List();
+
+ if (AccessControlEntryFlags == AccessControlEntryFlags.None ||
+ AccessControlEntryFlags == AccessControlEntryFlags.NoPropagateInherit)
+ inheritanceStrings.Add("SecurityAdvancedFlagsFolderLabel".GetLocalizedResource());
+
+ if (AccessControlEntryFlags.HasFlag(AccessControlEntryFlags.ContainerInherit))
+ inheritanceStrings.Add("SecurityAdvancedFlagsSubfoldersLabel".GetLocalizedResource());
+
+ if (AccessControlEntryFlags.HasFlag(AccessControlEntryFlags.ObjectInherit))
+ inheritanceStrings.Add("SecurityAdvancedFlagsFilesLabel".GetLocalizedResource());
+
+ // Capitalize the first letter
+ if (inheritanceStrings.Any())
+ inheritanceStrings[0] = char.ToUpperInvariant(inheritanceStrings[0].First()) + inheritanceStrings[0][1..];
+
+ return string.Join(", ", inheritanceStrings);
}
}
- private InheritanceFlags _InheritanceFlags;
- public InheritanceFlags InheritanceFlags
+ ///
+ /// AccessMaskItems
+ ///
+ public ObservableCollection AccessMaskItems { get; set; }
+
+ private AccessControlEntryType _AccessControlType;
+ public AccessControlEntryType AccessControlType
{
- get => _InheritanceFlags;
+ get => _AccessControlType;
set
{
- if (SetProperty(ref _InheritanceFlags, value))
- OnPropertyChanged(nameof(InheritanceFlagsHumanized));
+ if (SetProperty(ref _AccessControlType, value))
+ {
+ OnPropertyChanged(nameof(AccessControlTypeGlyph));
+ OnPropertyChanged(nameof(AccessControlTypeHumanized));
+ }
}
}
- private PropagationFlags _PropagationFlags;
- public PropagationFlags PropagationFlags
+ #region Access Mask Properties
+ private AccessMaskFlags _AccessMaskFlags;
+ public AccessMaskFlags AccessMaskFlags
{
- get => _PropagationFlags;
+ get => _AccessMaskFlags;
set
{
- if (SetProperty(ref _PropagationFlags, value))
- OnPropertyChanged(nameof(InheritanceFlagsHumanized));
+ if (SetProperty(ref _AccessMaskFlags, value))
+ OnPropertyChanged(nameof(AccessMaskFlagsHumanized));
}
}
@@ -102,9 +190,21 @@ public AccessMaskFlags DeniedAccessMaskFlags
}
}
+ private AccessControlEntryFlags _InheritanceFlags;
+ public AccessControlEntryFlags AccessControlEntryFlags
+ {
+ get => _InheritanceFlags;
+ set
+ {
+ if (SetProperty(ref _InheritanceFlags, value))
+ OnPropertyChanged(nameof(InheritanceFlagsHumanized));
+ }
+ }
+
public AccessMaskFlags InheritedAllowAccessMaskFlags { get; set; }
public AccessMaskFlags InheritedDenyAccessMaskFlags { get; set; }
+ #endregion
private bool _IsSelected;
public bool IsSelected
@@ -114,10 +214,9 @@ public bool IsSelected
{
if (SetProperty(ref _IsSelected, value))
{
- if (!value)
- AreAdvancedPermissionsShown = false;
+ AreAdvancedPermissionsShown = false;
- OnPropertyChanged(nameof(IsEditEnabled));
+ OnPropertyChanged(nameof(IsEditable));
}
}
}
@@ -130,86 +229,10 @@ public bool AreAdvancedPermissionsShown
{
// Reinitialize list
if (SetProperty(ref _AreAdvancedPermissionsShown, value))
- AccessMaskItemList = SecurityAdvancedAccessControlItemFactory.Initialize(this, AreAdvancedPermissionsShown, IsInherited, IsFolder);
+ AccessMaskItems = SecurityAdvancedAccessControlItemFactory.Initialize(this, value, IsInherited, IsFolder);
}
}
- public bool IsInherited { get; set; }
-
- public bool IsEditEnabled
- => IsSelected && !IsInherited;
-
- public string AccessControlTypeHumanized
- => AccessControlType switch
- {
- AccessControlType.Allow => "Allow",
- _ => "Deny" // AccessControlType.Deny
- };
-
- public string AccessControlTypeGlyph
- => AccessControlType switch
- {
- AccessControlType.Allow => "\xE73E",
- _ => "\xF140" // AccessControlType.Deny
- };
-
- public string AccessMaskFlagsHumanized
- {
- get
- {
- var accessMaskStrings = new List();
-
- if (AccessMaskFlags == AccessMaskFlags.NULL)
- accessMaskStrings.Add("None".GetLocalizedResource());
-
- if (FullControlAccess)
- accessMaskStrings.Add("SecurityFullControlLabel/Text".GetLocalizedResource());
- else if (ModifyAccess)
- accessMaskStrings.Add("SecurityModifyLabel/Text".GetLocalizedResource());
- else if (ReadAndExecuteAccess)
- accessMaskStrings.Add("SecurityReadAndExecuteLabel/Text".GetLocalizedResource());
- else if (ReadAccess)
- accessMaskStrings.Add("SecurityReadLabel/Text".GetLocalizedResource());
-
- if (!FullControlAccess && !ModifyAccess && WriteAccess)
- accessMaskStrings.Add("Write".GetLocalizedResource());
-
- if (SpecialAccess)
- accessMaskStrings.Add("SecuritySpecialLabel/Text".GetLocalizedResource());
-
- return string.Join(",", accessMaskStrings);
- }
- }
-
- public string IsInheritedHumanized
- => IsInherited ? "Yes".GetLocalizedResource() : "No".GetLocalizedResource();
-
- public string InheritanceFlagsHumanized
- {
- get
- {
- var inheritanceStrings = new List();
-
- if (PropagationFlags == PropagationFlags.None ||
- PropagationFlags == PropagationFlags.NoPropagateInherit)
- inheritanceStrings.Add("SecurityAdvancedFlagsFolderLabel".GetLocalizedResource());
-
- if (InheritanceFlags.HasFlag(InheritanceFlags.ContainerInherit))
- inheritanceStrings.Add("SecurityAdvancedFlagsSubfoldersLabel".GetLocalizedResource());
-
- if (InheritanceFlags.HasFlag(InheritanceFlags.ObjectInherit))
- inheritanceStrings.Add("SecurityAdvancedFlagsFilesLabel".GetLocalizedResource());
-
- // Capitalize first letter
- if (inheritanceStrings.Any())
- inheritanceStrings[0] = char.ToUpperInvariant(inheritanceStrings[0].First()) + inheritanceStrings[0][1..];
-
- return string.Join(",", inheritanceStrings);
- }
- }
-
- public ObservableCollection AccessMaskItemList { get; set; }
-
#region Security page
public bool WriteAccess => AccessMaskFlags.HasFlag(AccessMaskFlags.Write);
public bool ReadAccess => AccessMaskFlags.HasFlag(AccessMaskFlags.Read);
@@ -318,40 +341,37 @@ public bool DeniedFullControlAccess
public IRelayCommand ChangeAccessControlTypeCommand { get; set; }
public IRelayCommand ChangeInheritanceFlagsCommand { get; set; }
- public AccessControlEntry(bool isFolder, string ownerSid, AccessControlType type, AccessMaskFlags accessMaskFlags, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
+ public AccessControlEntry(bool isFolder, string ownerSid, AccessControlEntryType type, AccessMaskFlags accessMaskFlags, bool isInherited, AccessControlEntryFlags inheritanceFlags)
{
- AccessMaskItemList = SecurityAdvancedAccessControlItemFactory.Initialize(this, AreAdvancedPermissionsShown, IsInherited, IsFolder);
-
- ChangeAccessControlTypeCommand = new RelayCommand(x =>
- {
- AccessControlType = Enum.Parse(x);
- });
+ AccessMaskItems = SecurityAdvancedAccessControlItemFactory.Initialize(this, AreAdvancedPermissionsShown, IsInherited, IsFolder);
- ChangeInheritanceFlagsCommand = new RelayCommand(x =>
- {
- var parts = x.Split(',');
+ //ChangeAccessControlTypeCommand = new RelayCommand(x =>
+ //{
+ // AccessControlType = Enum.Parse(x);
+ //});
- InheritanceFlags = Enum.Parse(parts[0]);
- PropagationFlags = Enum.Parse(parts[1]);
- });
+ //ChangeInheritanceFlagsCommand = new RelayCommand(x =>
+ //{
+ // var parts = x.Split(',');
+ // InheritanceFlags = Enum.Parse(parts[0]);
+ //});
IsFolder = isFolder;
- Principal = Principal.FromSid(ownerSid);
+ Principal = new(ownerSid);
AccessControlType = type;
AccessMaskFlags = accessMaskFlags;
IsInherited = isInherited;
- InheritanceFlags = inheritanceFlags;
- PropagationFlags = propagationFlags;
+ AccessControlEntryFlags = inheritanceFlags;
switch (AccessControlType)
{
- case AccessControlType.Allow:
+ case AccessControlEntryType.Allow:
if (IsInherited)
InheritedAllowAccessMaskFlags |= AccessMaskFlags;
else
AllowedAccessMaskFlags |= AccessMaskFlags;
break;
- case AccessControlType.Deny:
+ case AccessControlEntryType.Deny:
if (IsInherited)
InheritedDenyAccessMaskFlags |= AccessMaskFlags;
else
diff --git a/src/Files.App/Filesystem/Security/AccessControlEntryFlags.cs b/src/Files.App/Filesystem/Security/AccessControlEntryFlags.cs
new file mode 100644
index 000000000000..da2cc69800df
--- /dev/null
+++ b/src/Files.App/Filesystem/Security/AccessControlEntryFlags.cs
@@ -0,0 +1,63 @@
+// Copyright (c) 2023 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
+namespace Files.App.Filesystem.Security
+{
+ ///
+ /// Represents inheritance flags of an ACE
+ ///
+ [Flags]
+ public enum AccessControlEntryFlags
+ {
+ ///
+ /// No ACE flags are set.
+ ///
+ None = 0x0,
+
+ ///
+ /// The access mask is propagated onto child leaf objects.
+ ///
+ ObjectInherit = 0x1,
+
+ ///
+ /// The access mask is propagated to child container objects.
+ ///
+ ContainerInherit = 0x2,
+
+ ///
+ /// The access checks do not apply to the object; they only apply to its children.
+ ///
+ NoPropagateInherit = 0x4,
+
+ ///
+ /// The access mask is propagated only to child objects. This includes both container and leaf child objects.
+ ///
+ InheritOnly = 0x8,
+
+ ///
+ /// A logical OR of System.Security.AccessControl.AceFlags.ObjectInherit, System.Security.AccessControl.AceFlags.ContainerInherit,
+ /// System.Security.AccessControl.AceFlags.NoPropagateInherit, and System.Security.AccessControl.AceFlags.InheritOnly.
+ ///
+ InheritanceFlags = 0xf,
+
+ ///
+ /// An ACE is inherited from a parent container rather than being explicitly set for an object.
+ ///
+ Inherited = 0x10,
+
+ ///
+ /// Successful access attempts are audited.
+ ///
+ SuccessfulAccess = 0x40,
+
+ ///
+ /// The access mask is propagated onto child leaf objects.
+ ///
+ FailedAccess = 0x80,
+
+ ///
+ /// All access attempts are audited.
+ ///
+ AuditFlags = 0xc0
+ }
+}
diff --git a/src/Files.App/Filesystem/Security/AccessControlType.cs b/src/Files.App/Filesystem/Security/AccessControlEntryType.cs
similarity index 74%
rename from src/Files.App/Filesystem/Security/AccessControlType.cs
rename to src/Files.App/Filesystem/Security/AccessControlEntryType.cs
index 0c903c5a8098..a4e93da8533f 100644
--- a/src/Files.App/Filesystem/Security/AccessControlType.cs
+++ b/src/Files.App/Filesystem/Security/AccessControlEntryType.cs
@@ -6,15 +6,15 @@ namespace Files.App.Filesystem.Security
///
/// Represents ACE type.
///
- public enum AccessControlType
+ public enum AccessControlEntryType
{
///
- /// ACCESS_ALLOWED_ACE
+ /// ACCESS_ALLOWED_ACE type
///
Allow,
///
- /// ACCESS_DENIED_ACE
+ /// ACCESS_DENIED_ACE type
///
Deny
}
diff --git a/src/Files.App/Filesystem/Security/AccessControlList.cs b/src/Files.App/Filesystem/Security/AccessControlList.cs
index f95214c666f3..8d9235d8d2ef 100644
--- a/src/Files.App/Filesystem/Security/AccessControlList.cs
+++ b/src/Files.App/Filesystem/Security/AccessControlList.cs
@@ -1,47 +1,53 @@
-// Copyright (c) 2023 Files Community
+// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.
-using System.Collections.ObjectModel;
-
namespace Files.App.Filesystem.Security
{
///
- /// Represents an ACL.
+ /// Represents an access control list (ACL).
///
- public class AccessControlList
+ public class AccessControlList : ObservableObject
{
///
- /// File owner information
+ /// Object path.
///
- public Principal Owner { get; set; }
+ public string Path { get; private set; }
///
- /// Whether the DACL is protected
+ /// Whether the path indicates folder or not.
///
- public bool IsProtected { get; set; }
+ public bool IsFolder { get; private set; }
///
- /// Whether the DACL is valid one
+ /// The owner in the security descriptor (SD).
+ /// NULL if the security descriptor has no owner SID.
///
- public bool IsValid { get; set; }
+ public Principal Owner { get; private set; }
///
- /// File path which have this access control list
+ /// Validates an access control list (ACL).
///
- public string Path { get; set; }
+ public bool IsValid { get; private set; }
///
- /// Whether the path indicates folder or not
+ /// Access control entry (ACE) list
///
- public bool IsFolder { get; set; }
+ public ObservableCollection AccessControlEntries { get; private set; }
- ///
- /// ACE list
- ///
- public ObservableCollection AccessControlEntries { get; set; }
+ public AccessControlList(string path, bool isFolder, Principal owner, bool isValid)
+ {
+ Path = path;
+ IsFolder = isFolder;
+ Owner = owner;
+ IsValid = isValid;
+ AccessControlEntries = new();
+ }
public AccessControlList()
{
+ Path = string.Empty;
+ Owner = new(string.Empty);
+ AccessControlEntries = new();
}
}
}
diff --git a/src/Files.App/Filesystem/Security/AccessMaskItem.cs b/src/Files.App/Filesystem/Security/AccessMaskItem.cs
index 92f2e3ec312f..da090e64bcc9 100644
--- a/src/Files.App/Filesystem/Security/AccessMaskItem.cs
+++ b/src/Files.App/Filesystem/Security/AccessMaskItem.cs
@@ -48,7 +48,7 @@ private void ToggleAccess(AccessMaskFlags accessMask, bool value)
_ace.AccessMaskFlags &= ~accessMask;
}
- private void AccessControlEntry_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ private void AccessControlEntry_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(AccessControlEntry.AccessMaskFlags))
OnPropertyChanged(nameof(IsEnabled));
diff --git a/src/Files.App/Filesystem/Security/InheritanceFlags.cs b/src/Files.App/Filesystem/Security/InheritanceFlags.cs
deleted file mode 100644
index 9ce98372c390..000000000000
--- a/src/Files.App/Filesystem/Security/InheritanceFlags.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2023 Files Community
-// Licensed under the MIT License. See the LICENSE.
-
-using System;
-
-namespace Files.App.Filesystem.Security
-{
- ///
- /// Represents inheritance flags of an ACE
- ///
- [Flags]
- public enum InheritanceFlags
- {
- ///
- /// No inheritance flags
- ///
- None = 0,
-
- ///
- /// Child objects that are containers, such as directories, inherit the ACE as an effective ACE.
- /// The inherited ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
- ///
- ContainerInherit = 1,
-
- ///
- /// Noncontainer child objects inherit the ACE as an effective ACE.
- /// For child objects that are containers, the ACE is inherited as an inherit-only ACE
- /// unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
- ///
- ObjectInherit = 2
- }
-}
diff --git a/src/Files.App/Filesystem/Security/Principal.cs b/src/Files.App/Filesystem/Security/Principal.cs
index d4f02d30f735..f49d303330c9 100644
--- a/src/Files.App/Filesystem/Security/Principal.cs
+++ b/src/Files.App/Filesystem/Security/Principal.cs
@@ -1,9 +1,8 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.
-using CommunityToolkit.Mvvm.ComponentModel;
-using System.Collections.Generic;
using System.Text;
+using Vanara.PInvoke;
using static Vanara.PInvoke.AdvApi32;
namespace Files.App.Filesystem.Security
@@ -13,6 +12,34 @@ namespace Files.App.Filesystem.Security
///
public class Principal : ObservableObject
{
+ ///
+ /// Account type.
+ ///
+ public PrincipalType PrincipalType { get; private set; }
+
+ ///
+ /// Acount security identifier (SID).
+ ///
+ public string? Sid { get; private set; }
+
+ ///
+ /// A domain the account belongs to
+ ///
+ public string? Domain { get; private set; }
+
+ ///
+ /// Account name
+ ///
+ public string? Name { get; private set; }
+
+ ///
+ /// Indicates whether this instance is valid or not
+ ///
+ public bool IsValid { get; private set; }
+
+ ///
+ /// Account type glyph.
+ ///
public string Glyph
=> PrincipalType switch
{
@@ -21,78 +48,80 @@ public string Glyph
_ => "\xE716",
};
- public string? Sid { get; set; }
-
- public string? Domain { get; set; }
-
- public string? Name { get; set; }
-
+ ///
+ /// Account display name
+ ///
public string? DisplayName
=> string.IsNullOrEmpty(Name) ? Sid : Name;
- public string? FullNameOrSid
- => string.IsNullOrEmpty(Domain) ? Sid : $"{Domain}\\{Name}";
-
+ ///
+ /// Account full name or just name
+ ///
public string? FullNameHumanized
- => string.IsNullOrEmpty(Domain) ? string.Empty : $"({Domain}\\{Name})";
+ => string.IsNullOrEmpty(Domain) ? Name : $"{Domain}\\{Name}";
- public List Groups { get; set; }
-
- private PrincipalType PrincipalType { get; set; }
-
- public Principal()
- {
- Groups = new();
-
- PrincipalType = PrincipalType.Unknown;
- }
+ ///
+ /// Account humanized full name.
+ ///
+ public string FullNameHumanizedWithBrackes
+ => string.IsNullOrEmpty(Domain) ? string.Empty : $"({Domain}\\{Name})";
- public static Principal FromSid(string sid)
+ public Principal(string sid)
{
- var userGroup = new Principal()
- {
- Sid = sid
- };
-
if (string.IsNullOrEmpty(sid))
- return userGroup;
+ return;
- var lpSid = ConvertStringSidToSid(userGroup.Sid);
+ Sid = sid;
+ var lpSid = ConvertStringSidToSid(sid);
- var lpName = new StringBuilder();
- var lpDomain = new StringBuilder();
- int cchName = 0;
- int cchDomainName = 0;
+ StringBuilder lpName = new(), lpDomain = new();
+ int cchName = 0, cchDomainName = 0;
- LookupAccountSid(null, lpSid, lpName, ref cchName, lpDomain, ref cchDomainName, out _);
+ // Get size of account name and domain name
+ bool bResult = LookupAccountSid(null, lpSid, lpName, ref cchName, lpDomain, ref cchDomainName, out _);
+ // Ensure requested capacity
lpName.EnsureCapacity(cchName);
lpDomain.EnsureCapacity(cchDomainName);
- if (LookupAccountSid(null, lpSid, lpName, ref cchName, lpDomain, ref cchDomainName, out var snu))
+ // Get account name and domain
+ bResult = LookupAccountSid(null, lpSid, lpName, ref cchName, lpDomain, ref cchDomainName, out var snu);
+ if(!bResult)
+ return;
+
+ PrincipalType = snu switch
{
- userGroup.PrincipalType = snu switch
- {
- // Group
- var x when
- (x == SID_NAME_USE.SidTypeAlias ||
- x == SID_NAME_USE.SidTypeGroup ||
- x == SID_NAME_USE.SidTypeWellKnownGroup)
- => PrincipalType.Group,
-
- // User
- SID_NAME_USE.SidTypeUser
- => PrincipalType.User,
-
- // Unknown
- _ => PrincipalType.Unknown
- };
-
- userGroup.Name = lpName.ToString();
- userGroup.Domain = lpDomain.ToString();
+ // Group
+ var x when
+ (x == SID_NAME_USE.SidTypeAlias ||
+ x == SID_NAME_USE.SidTypeGroup ||
+ x == SID_NAME_USE.SidTypeWellKnownGroup)
+ => PrincipalType.Group,
+
+ // User
+ SID_NAME_USE.SidTypeUser
+ => PrincipalType.User,
+
+ // Unknown
+ _ => PrincipalType.Unknown
+ };
+
+ lpDomain.Clear();
+
+ // Replace domain name with computer name if the account type is user or alias type
+ if (snu == SID_NAME_USE.SidTypeUser || snu == SID_NAME_USE.SidTypeAlias)
+ {
+ lpDomain = new(256, 256);
+ uint size = (uint)lpDomain.Capacity;
+ bResult = Kernel32.GetComputerName(lpDomain, ref size);
+ if (!bResult)
+ return;
}
- return userGroup;
+ Name = lpName.ToString();
+ Domain = lpDomain.ToString().ToLower();
+
+ IsValid = true;
}
}
}
diff --git a/src/Files.App/Filesystem/Security/PropagationFlags.cs b/src/Files.App/Filesystem/Security/PropagationFlags.cs
deleted file mode 100644
index 40f76059c416..000000000000
--- a/src/Files.App/Filesystem/Security/PropagationFlags.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2023 Files Community
-// Licensed under the MIT License. See the LICENSE.
-
-using System;
-
-namespace Files.App.Filesystem.Security
-{
- ///
- /// Represents propagation flags of an ACE
- ///
- [Flags]
- public enum PropagationFlags
- {
- ///
- /// No propagation flags
- ///
- None = 0,
-
- ///
- /// If the ACE is inherited by a child object, the system clears the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags in the inherited ACE.
- /// This prevents the ACE from being inherited by subsequent generations of objects.
- ///
- NoPropagateInherit = 1,
-
- ///
- /// Indicates an inherit-only ACE, which does not control access to the object to which it is attached.
- /// If this flag is not set, the ACE is an effective ACE which controls access to the object to which it is attached.
- /// Both effective and inherit-only ACEs can be inherited depending on the state of the other inheritance flags.
- ///
- InheritOnly = 2
- }
-}
diff --git a/src/Files.App/Helpers/FileOperationsHelpers.cs b/src/Files.App/Helpers/FileOperationsHelpers.cs
index 6ec7d6dc3cb5..c2ad1c7f2ce9 100644
--- a/src/Files.App/Helpers/FileOperationsHelpers.cs
+++ b/src/Files.App/Helpers/FileOperationsHelpers.cs
@@ -627,15 +627,6 @@ public static bool SetLinkIcon(string filePath, string iconFile, int iconIndex)
return false;
}
- public static AccessControlList GetFilePermissions(string filePath, bool isFolder)
- => FileSecurityHelpers.GetAccessControlList(filePath, isFolder);
-
- public static bool SetFileOwner(string filePath, string ownerSid)
- => FileSecurityHelpers.SetOwner(filePath, ownerSid);
-
- public static bool SetAccessRuleProtection(string filePath, bool isFolder, bool isProtected, bool preserveInheritance)
- => FileSecurityHelpers.SetAccessControlProtection(filePath, isFolder, isProtected, preserveInheritance);
-
public static Task OpenObjectPickerAsync(long hWnd)
{
return Win32API.StartSTATask(() =>
@@ -649,6 +640,7 @@ public static bool SetAccessRuleProtection(string filePath, bool isFolder, bool
MultiSelect = false,
ShowAdvancedView = true
};
+
picker.AttributesToFetch.Add("objectSid");
using (picker)
@@ -659,13 +651,9 @@ public static bool SetAccessRuleProtection(string filePath, bool isFolder, bool
{
var attribs = picker.SelectedObject.FetchedAttributes;
if (attribs.Any() && attribs[0] is byte[] objectSid)
- {
return new SecurityIdentifier(objectSid, 0).Value;
- }
- }
- catch
- {
}
+ catch {}
}
}
diff --git a/src/Files.App/Helpers/FileSecurityHelpers.cs b/src/Files.App/Helpers/FileSecurityHelpers.cs
index d317cc0b076c..cfce2fd78cf2 100644
--- a/src/Files.App/Helpers/FileSecurityHelpers.cs
+++ b/src/Files.App/Helpers/FileSecurityHelpers.cs
@@ -3,15 +3,9 @@
using Files.App.Filesystem.Security;
using Files.App.Shell;
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-using System.Security.AccessControl;
-using System.Security.Principal;
-using System.Text;
using Vanara.PInvoke;
using static Vanara.PInvoke.AdvApi32;
-using FilesSecurity = Files.App.Filesystem.Security;
+using SystemSecurity = System.Security.AccessControl;
namespace Files.App.Helpers
{
@@ -20,29 +14,11 @@ namespace Files.App.Helpers
///
public static class FileSecurityHelpers
{
- public static bool SetOwner(string path, string sid)
- {
- SECURITY_INFORMATION secInfo = SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION;
-
- // Get PSID object from string sid
- var pSid = ConvertStringSidToSid(sid);
-
- // Change owner
- var result = SetNamedSecurityInfo(path, SE_OBJECT_TYPE.SE_FILE_OBJECT, secInfo, pSid);
-
- pSid.Dispose();
-
- // Run PowerShell as Admin
- if (result.Failed)
- {
- return Win32API.RunPowershellCommand(
- $"-command \"try {{ $path = '{path}'; $ID = new-object System.Security.Principal.SecurityIdentifier('{sid}'); $acl = get-acl $path; $acl.SetOwner($ID); set-acl -path $path -aclObject $acl }} catch {{ exit 1; }}\"",
- true);
- }
-
- return true;
- }
-
+ ///
+ /// Get the owner of the object specified by the path.
+ ///
+ /// The file full path
+ ///
public static string GetOwner(string path)
{
GetNamedSecurityInfo(
@@ -60,56 +36,47 @@ public static string GetOwner(string path)
return szSid;
}
- public static bool GetAccessControlProtection(string path, bool isFolder)
+ ///
+ /// Set the owner of the object specified by the path.
+ ///
+ /// The file full path
+ /// The owner security identifier (SID)
+ ///
+ public static bool SetOwner(string path, string sid)
{
- FileSystemSecurity fileSystemSecurity;
-
- if (isFolder && Directory.Exists(path))
- {
- fileSystemSecurity = FileSystemAclExtensions.GetAccessControl(new DirectoryInfo(path));
- return fileSystemSecurity.AreAccessRulesProtected;
- }
- else if (File.Exists(path))
- {
- fileSystemSecurity = FileSystemAclExtensions.GetAccessControl(new FileInfo(path));
- return fileSystemSecurity.AreAccessRulesProtected;
- }
- else
- {
- return false;
- }
- }
+ SECURITY_INFORMATION secInfo = SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION;
- public static bool SetAccessControlProtection(string path, bool isFolder, bool isProtected, bool preserveInheritance)
- {
- FileSystemSecurity fileSystemSecurity;
+ // Get PSID object from string sid
+ var pSid = ConvertStringSidToSid(sid);
- if (isFolder && Directory.Exists(path))
- {
- fileSystemSecurity = FileSystemAclExtensions.GetAccessControl(new DirectoryInfo(path));
- fileSystemSecurity.SetAccessRuleProtection(isProtected, preserveInheritance);
- FileSystemAclExtensions.SetAccessControl(new DirectoryInfo(path), (DirectorySecurity)fileSystemSecurity);
+ // Change owner
+ var result = SetNamedSecurityInfo(path, SE_OBJECT_TYPE.SE_FILE_OBJECT, secInfo, pSid);
- return true;
- }
- else if (File.Exists(path))
- {
- fileSystemSecurity = FileSystemAclExtensions.GetAccessControl(new FileInfo(path));
- fileSystemSecurity.SetAccessRuleProtection(isProtected, preserveInheritance);
- FileSystemAclExtensions.SetAccessControl(new FileInfo(path), (FileSecurity)fileSystemSecurity);
+ pSid.Dispose();
- return true;
- }
- else
+ // Run PowerShell as Admin
+ if (result.Failed)
{
- return false;
+ return Win32API.RunPowershellCommand(
+ $"-command \"try {{ $path = '{path}'; $ID = new-object System.Security.Principal.SecurityIdentifier('{sid}'); $acl = get-acl $path; $acl.SetOwner($ID); set-acl -path $path -aclObject $acl }} catch {{ exit 1; }}\"",
+ true);
}
+
+ return true;
}
- public static AccessControlList GetAccessControlList(string path, bool isFolder)
+ ///
+ /// Get information about an access control list (ACL).
+ ///
+ ///
+ ///
+ /// If the function succeeds, an instance of AccessControlList; otherwise, null. To get extended error information, call GetLastError.
+ public static Win32Error GetAccessControlList(string path, bool isFolder, out AccessControlList acl)
{
+ acl = new();
+
// Get DACL
- GetNamedSecurityInfo(
+ var win32Error = GetNamedSecurityInfo(
path,
SE_OBJECT_TYPE.SE_FILE_OBJECT,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION,
@@ -119,107 +86,175 @@ public static AccessControlList GetAccessControlList(string path, bool isFolder)
out _,
out _);
+ if (win32Error.Failed || pDacl == PACL.NULL)
+ return win32Error;
+
// Get ACL size info
- GetAclInformation(pDacl, out ACL_SIZE_INFORMATION aclSize);
+ bool bResult = GetAclInformation(pDacl, out ACL_SIZE_INFORMATION aclSize);
+ if (!bResult)
+ return Kernel32.GetLastError();
// Get owner
var szOwnerSid = GetOwner(path);
+ var principal = new Principal(szOwnerSid);
- // Initialize
- var acl = new AccessControlList()
- {
- Owner = Principal.FromSid(szOwnerSid),
- IsProtected = GetAccessControlProtection(path, isFolder),
- IsValid = true,
- AccessControlEntries = new(),
- Path = path,
- IsFolder = isFolder
- };
+ var isValidAcl = IsValidAcl(pDacl);
+
+ List aces = new();
// Get ACEs
for (uint i = 0; i < aclSize.AceCount; i++)
{
- GetAce(pDacl, i, out var pAce);
+ bResult = GetAce(pDacl, i, out var pAce);
+ if (!bResult)
+ return Kernel32.GetLastError();
var szSid = ConvertSidToStringSid(pAce.GetSid());
- var header = pAce.GetHeader();
-
- FilesSecurity.AccessControlType type;
- FilesSecurity.InheritanceFlags inheritanceFlags = FilesSecurity.InheritanceFlags.None;
- FilesSecurity.PropagationFlags propagationFlags = FilesSecurity.PropagationFlags.None;
+ AccessControlEntryType type;
+ AccessControlEntryFlags inheritanceFlags = AccessControlEntryFlags.None;
AccessMaskFlags accessMaskFlags = (AccessMaskFlags)pAce.GetMask();
+ var header = pAce.GetHeader();
type = header.AceType switch
{
- AceType.AccessAllowed => FilesSecurity.AccessControlType.Allow,
- _ => FilesSecurity.AccessControlType.Deny
+ SystemSecurity.AceType.AccessAllowed => AccessControlEntryType.Allow,
+ _ => AccessControlEntryType.Deny
};
- bool isInherited = header.AceFlags.HasFlag(AceFlags.InheritanceFlags);
+ bool isInherited = header.AceFlags.HasFlag(SystemSecurity.AceFlags.InheritanceFlags);
- if (header.AceFlags.HasFlag(AceFlags.ContainerInherit))
- inheritanceFlags |= FilesSecurity.InheritanceFlags.ContainerInherit;
- if (header.AceFlags.HasFlag(AceFlags.ObjectInherit))
- inheritanceFlags |= FilesSecurity.InheritanceFlags.ObjectInherit;
- if (header.AceFlags.HasFlag(AceFlags.NoPropagateInherit))
- propagationFlags |= FilesSecurity.PropagationFlags.NoPropagateInherit;
- if (header.AceFlags.HasFlag(AceFlags.InheritOnly))
- propagationFlags |= FilesSecurity.PropagationFlags.InheritOnly;
+ if (header.AceFlags.HasFlag(SystemSecurity.AceFlags.ContainerInherit))
+ inheritanceFlags |= AccessControlEntryFlags.ContainerInherit;
+ if (header.AceFlags.HasFlag(SystemSecurity.AceFlags.ObjectInherit))
+ inheritanceFlags |= AccessControlEntryFlags.ObjectInherit;
+ if (header.AceFlags.HasFlag(SystemSecurity.AceFlags.NoPropagateInherit))
+ inheritanceFlags |= AccessControlEntryFlags.NoPropagateInherit;
+ if (header.AceFlags.HasFlag(SystemSecurity.AceFlags.InheritOnly))
+ inheritanceFlags |= AccessControlEntryFlags.InheritOnly;
// Initialize an ACE
- acl.AccessControlEntries.Add(new(isFolder, szSid, type, accessMaskFlags, isInherited, inheritanceFlags, propagationFlags));
+ aces.Add(new(isFolder, szSid, type, accessMaskFlags, isInherited, inheritanceFlags));
}
- return acl;
- }
+ // Initialize with proper data
+ acl = new AccessControlList(path, isFolder, principal, isValidAcl);
- public static bool SetAccessControlList(AccessControlList acl)
- {
- return false;
+ // Set access control entries
+ foreach (var ace in aces)
+ acl.AccessControlEntries.Add(ace);
+
+ return Kernel32.GetLastError();
}
+ ///
+ /// Get access control list (ACL) initialized with default data.
+ ///
+ ///
+ ///
+ /// If the function succeeds, an instance of AccessControlList; otherwise, null.
public static AccessControlEntry InitializeDefaultAccessControlEntry(bool isFolder, string ownerSid)
{
return new(
isFolder,
ownerSid,
- FilesSecurity.AccessControlType.Allow,
- FilesSecurity.AccessMaskFlags.ReadAndExecute,
+ AccessControlEntryType.Allow,
+ AccessMaskFlags.ReadAndExecute,
false,
isFolder
- ? FilesSecurity.InheritanceFlags.ContainerInherit | FilesSecurity.InheritanceFlags.ObjectInherit
- : FilesSecurity.InheritanceFlags.None,
- FilesSecurity.PropagationFlags.None);
+ ? AccessControlEntryFlags.ContainerInherit | AccessControlEntryFlags.ObjectInherit
+ : AccessControlEntryFlags.None);
}
- public static bool AddAccessControlEntry(string path, AccessControlEntry entry)
+ ///
+ /// Add an default Access Control Entry (ACE) to the specified object's DACL
+ ///
+ /// The object's path to add an new ACE to its DACL
+ /// Principal's SID
+ /// If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is a nonzero error code defined in WinError.h.
+ public static Win32Error AddAccessControlEntry(string szPath, string szSid)
{
- // Get DACL
- GetNamedSecurityInfo(
- path,
+ // Get DACL for the specified object
+ var result = GetNamedSecurityInfo(
+ szPath,
SE_OBJECT_TYPE.SE_FILE_OBJECT,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION,
out _,
out _,
- out var pDacl,
+ out var pDACL,
out _,
out _);
- // Get ACL size info
- GetAclInformation(pDacl, out ACL_SIZE_INFORMATION aclSize);
-
- uint revision = GetAclInformation(pDacl, out ACL_REVISION_INFORMATION aclRevision) ? aclRevision.AclRevision : 0U;
+ if (result.Failed)
+ return result;
- // Get ACEs
- for (uint i = 0; i < aclSize.AceCount; i++)
+ // Initialize default trustee
+ var explicitAccess = new EXPLICIT_ACCESS
{
- //GetAce(pDacl, i, out var pAce);
+ grfAccessMode = ACCESS_MODE.GRANT_ACCESS,
+ grfAccessPermissions = ACCESS_MASK.GENERIC_READ | ACCESS_MASK.GENERIC_EXECUTE,
+ grfInheritance = INHERIT_FLAGS.NO_INHERITANCE,
+ Trustee = new TRUSTEE(new SafePSID(szSid)),
+ };
- //AddAce(pDacl, revision, 0, (IntPtr)pAce, ((PACL)pDacl).Length() - (uint)Marshal.SizeOf(typeof(ACL)));
- }
+ // Add an new ACE and get a new ACL
+ result = SetEntriesInAcl(1, new[] { explicitAccess }, pDACL, out var pNewDACL);
+
+ if (result.Failed)
+ return result;
+
+ // Set the new ACL
+ result = SetNamedSecurityInfo(
+ szPath,
+ SE_OBJECT_TYPE.SE_FILE_OBJECT,
+ SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION,
+ ppDacl: pNewDACL);
+
+ if (result.Failed)
+ return result;
+
+ return result;
+ }
+
+ ///
+ /// Add an Access Control Entry (ACE) from the specified object's DACL
+ ///
+ /// The object's path to remove an ACE from its DACL
+ ///
+ ///
+ public static Win32Error RemoveAccessControlEntry(string szPath, uint dwAceIndex)
+ {
+ // Get DACL for the specified object
+ var result = GetNamedSecurityInfo(
+ szPath,
+ SE_OBJECT_TYPE.SE_FILE_OBJECT,
+ SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION,
+ out _,
+ out _,
+ out var pDACL,
+ out _,
+ out _);
+
+ if (result.Failed)
+ return result;
+
+ // Remove an ACE
+ bool bResult = DeleteAce(pDACL, dwAceIndex);
+
+ if (!bResult)
+ return Kernel32.GetLastError();
+
+ // Set the new ACL
+ result = SetNamedSecurityInfo(
+ szPath,
+ SE_OBJECT_TYPE.SE_FILE_OBJECT,
+ SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION,
+ ppDacl: pDACL);
+
+ if (result.Failed)
+ return result;
- return false;
+ return result;
}
}
}
diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw
index d31cc4ff4ec3..a576b494b6a8 100644
--- a/src/Files.App/Strings/en-US/Resources.resw
+++ b/src/Files.App/Strings/en-US/Resources.resw
@@ -3211,4 +3211,16 @@
Location:
+
+ Click 'Advanced permissions' to continue.
+
+
+ You must have Read permissions to view the properties of this item.
+
+
+ To try taking ownership of the item, which includes permission to view its properties, click Change above.
+
+
+ Unable to display permissions.
+
\ No newline at end of file
diff --git a/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs b/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs
index f9fc0cba2859..f00caca3a1f3 100644
--- a/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs
+++ b/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs
@@ -1,218 +1,257 @@
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using Files.App.Data.Items;
-using Files.App.Extensions;
-using Files.App.Filesystem;
+// Copyright (c) 2023 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
using Files.App.Filesystem.Security;
-using Files.App.Helpers;
using Microsoft.UI.Xaml;
-using System.Threading.Tasks;
+using Vanara.PInvoke;
using Windows.Storage;
namespace Files.App.ViewModels.Properties
{
public class SecurityAdvancedViewModel : ObservableObject
{
- public ListedItem Item { get; }
+ private readonly PropertiesPageNavigationParameter _navigationParameter;
- private readonly Window Window;
+ private readonly Window _window;
- private AccessControlList _accessControlList;
- public AccessControlList AccessControlList
+ private readonly string _path;
+
+ private readonly bool _isFolder;
+
+ public bool IsAddAccessControlEntryButtonEnabled =>
+ AccessControlList is not null &&
+ AccessControlList.IsValid;
+
+ public bool IsDeleteAccessControlEntryButtonEnabled =>
+ AccessControlList is not null &&
+ AccessControlList.IsValid &&
+ SelectedAccessControlEntry is not null &&
+ SelectedAccessControlEntry.IsInherited is false;
+
+ public IconFileInfo ShieldIconFileInfo { get; private set; }
+
+ public bool CurrentInstanceCanReadPermissions { get; private set; }
+
+ public bool CurrentInstanceCanChangePermissions { get; private set; }
+
+ public string DisableInheritanceOption
{
- get => _accessControlList;
- set
+ get
{
- if (SetProperty(ref _accessControlList, value))
- {
- ChangeOwnerCommand.NotifyCanExecuteChanged();
- AddAccessControlEntryCommand.NotifyCanExecuteChanged();
- RemoveAccessControlEntryCommand.NotifyCanExecuteChanged();
- DisableInheritanceCommand.NotifyCanExecuteChanged();
- ReplaceChildPermissionsCommand.NotifyCanExecuteChanged();
- }
+ //if (!_isProtected)
+ // return "SecurityAdvancedInheritedEnable/Text".GetLocalizedResource();
+ //else if (_preserveInheritance)
+ // return "SecurityAdvancedInheritedConvert/Text".GetLocalizedResource();
+ //else
+ // return "SecurityAdvancedInheritedRemove/Text".GetLocalizedResource();
+
+ return string.Empty;
}
}
- private AccessControlEntry _selectedAccessControlEntry;
- public AccessControlEntry SelectedAccessControlEntry
+ private AccessControlList _AccessControlList;
+ public AccessControlList AccessControlList
{
- get => _selectedAccessControlEntry;
+ get => _AccessControlList;
+ set => SetProperty(ref _AccessControlList, value);
+ }
+
+ private AccessControlEntry? _SelectedAccessControlEntry;
+ public AccessControlEntry? SelectedAccessControlEntry
+ {
+ get => _SelectedAccessControlEntry;
set
{
- if (_selectedAccessControlEntry is not null)
- _selectedAccessControlEntry.IsSelected = false;
+ // Previous selection
+ if (_SelectedAccessControlEntry is not null)
+ _SelectedAccessControlEntry.IsSelected = false;
- if (SetProperty(ref _selectedAccessControlEntry, value))
+ if (SetProperty(ref _SelectedAccessControlEntry, value))
{
- value.IsSelected = true;
- RemoveAccessControlEntryCommand.NotifyCanExecuteChanged();
+ if(value is not null)
+ value.IsSelected = true;
+
+ OnPropertyChanged(nameof(IsDeleteAccessControlEntryButtonEnabled));
}
}
}
- private bool _isFolder;
- public bool IsFolder
+ private bool _DisplayElements;
+ public bool DisplayElements
{
- get => _isFolder;
- set => SetProperty(ref _isFolder, value);
+ get => _DisplayElements;
+ set => SetProperty(ref _DisplayElements, value);
}
- public string DisableInheritanceOption
+ private string _ErrorMessage = string.Empty;
+ public string ErrorMessage
{
- get
- {
- if (!_isProtected)
- return "SecurityAdvancedInheritedEnable/Text".GetLocalizedResource();
- else if (_preserveInheritance)
- return "SecurityAdvancedInheritedConvert/Text".GetLocalizedResource();
- else
- return "SecurityAdvancedInheritedRemove/Text".GetLocalizedResource();
- }
+ get => _ErrorMessage;
+ set => SetProperty(ref _ErrorMessage, value);
}
- private bool _isProtected;
-
- private bool _preserveInheritance;
-
- private GridLength _columnType = new(64d);
- public GridLength ColumnType
+ private GridLength _ColumnTypeGridLength = new(64d);
+ public GridLength ColumnTypeGridLength
{
- get => _columnType;
- set => SetProperty(ref _columnType, value);
+ get => _ColumnTypeGridLength;
+ set => SetProperty(ref _ColumnTypeGridLength, value);
}
- private GridLength _columnPrincipal = new(200d);
- public GridLength ColumnPrincipal
+ private GridLength _ColumnPrincipalGridLength = new(200d);
+ public GridLength ColumnPrincipalGridLength
{
- get => _columnPrincipal;
- set => SetProperty(ref _columnPrincipal, value);
+ get => _ColumnPrincipalGridLength;
+ set => SetProperty(ref _ColumnPrincipalGridLength, value);
}
- private GridLength _columnAccess = new(160d);
- public GridLength ColumnAccess
+ private GridLength _ColumnAccessGridLength = new(160d);
+ public GridLength ColumnAccessGridLength
{
- get => _columnAccess;
- set => SetProperty(ref _columnAccess, value);
+ get => _ColumnAccessGridLength;
+ set => SetProperty(ref _ColumnAccessGridLength, value);
}
- private GridLength _columnInherited = new(70d);
- public GridLength ColumnInherited
+ private GridLength _ColumnInheritedGridLength = new(70d);
+ public GridLength ColumnInheritedGridLength
{
- get => _columnInherited;
- set => SetProperty(ref _columnInherited, value);
+ get => _ColumnInheritedGridLength;
+ set => SetProperty(ref _ColumnInheritedGridLength, value);
}
- public RelayCommand ChangeOwnerCommand { get; set; }
- public RelayCommand AddAccessControlEntryCommand { get; set; }
- public RelayCommand RemoveAccessControlEntryCommand { get; set; }
- public RelayCommand DisableInheritanceCommand { get; set; }
- public RelayCommand SetDisableInheritanceOptionCommand { get; set; }
- public RelayCommand ReplaceChildPermissionsCommand { get; set; }
+ public IAsyncRelayCommand ChangeOwnerCommand { get; set; }
+ public IAsyncRelayCommand AddAccessControlEntryCommand { get; set; }
+ public IAsyncRelayCommand RemoveAccessControlEntryCommand { get; set; }
- public SecurityAdvancedViewModel(ListedItem item, Window window)
+ public SecurityAdvancedViewModel(PropertiesPageNavigationParameter parameter)
{
- IsFolder = item.PrimaryItemAttribute == StorageItemTypes.Folder && !item.IsShortcut;
- Item = item;
- Window = window;
+ _navigationParameter = parameter;
+ _window = parameter.Window;
- InitializeCommands();
- GetAccessControlList();
- }
-
- public SecurityAdvancedViewModel(DriveItem item, Window window)
- {
- IsFolder = true;
- Item = new ListedItem()
+ switch (parameter.Parameter)
{
- ItemNameRaw = item.Text,
- ItemPath = item.Path,
- PrimaryItemAttribute = StorageItemTypes.Folder
+ case ListedItem listedItem:
+ _path = listedItem.ItemPath;
+ _isFolder = listedItem.PrimaryItemAttribute == StorageItemTypes.Folder && !listedItem.IsShortcut;
+ break;
+ case DriveItem driveItem:
+ _path = driveItem.Path;
+ _isFolder = true;
+ break;
+ default:
+ var defaultlistedItem = (ListedItem)parameter.Parameter;
+ _path = defaultlistedItem.ItemPath;
+ _isFolder = defaultlistedItem.PrimaryItemAttribute == StorageItemTypes.Folder && !defaultlistedItem.IsShortcut;
+ break;
};
- Window = window;
- InitializeCommands();
- GetAccessControlList();
+ LoadShieldIconResource();
+
+ LoadAccessControlEntry();
+
+ ChangeOwnerCommand = new AsyncRelayCommand(ExecuteChangeOwnerCommand);
+ AddAccessControlEntryCommand = new AsyncRelayCommand(ExecuteAddAccessControlEntryCommand);
+ RemoveAccessControlEntryCommand = new AsyncRelayCommand(ExecuteRemoveAccessControlEntryCommand);
}
- private void InitializeCommands()
+ private void LoadShieldIconResource()
{
- ChangeOwnerCommand = new RelayCommand(ChangeOwner, () => AccessControlList is not null);
- AddAccessControlEntryCommand = new RelayCommand(AddAccessControlEntry, () => AccessControlList is not null && AccessControlList.IsValid);
- RemoveAccessControlEntryCommand = new RelayCommand(RemoveAccessControlEntry, () => AccessControlList is not null && AccessControlList.IsValid && SelectedAccessControlEntry is not null);
- DisableInheritanceCommand = new RelayCommand(DisableInheritance, () => AccessControlList is not null && AccessControlList.IsValid && (AccessControlList.IsProtected != _isProtected));
- SetDisableInheritanceOptionCommand = new RelayCommand(SetDisableInheritanceOption);
- ReplaceChildPermissionsCommand = new RelayCommand(ReplaceChildPermissions, () => AccessControlList is not null && AccessControlList.IsValid);
+ string imageres = System.IO.Path.Combine(Constants.UserEnvironmentPaths.SystemRootPath, "System32", "imageres.dll");
+
+ var imageResList = Shell.Win32API.ExtractSelectedIconsFromDLL(
+ imageres,
+ new List() { Constants.ImageRes.ShieldIcon },
+ 16);
+
+ ShieldIconFileInfo = imageResList.First();
}
- private async void ChangeOwner()
+ private void LoadAccessControlEntry()
{
- var pickedObject = await OpenObjectPicker();
- if (pickedObject is not null)
+ var error = FileSecurityHelpers.GetAccessControlList(_path, _isFolder, out _AccessControlList);
+ SelectedAccessControlEntry = AccessControlList.AccessControlEntries.FirstOrDefault();
+
+ if (!AccessControlList.IsValid)
{
- bool isFolder = Item.PrimaryItemAttribute == StorageItemTypes.Folder && !Item.IsShortcut;
+ DisplayElements = false;
- // Set owner and refresh file permissions
- if (FileOperationsHelpers.SetFileOwner(Item.ItemPath, pickedObject))
- GetAccessControlList();
+ if (error == Win32Error.ERROR_ACCESS_DENIED)
+ {
+ ErrorMessage =
+ "SecurityRequireReadPermissions".GetLocalizedResource() +
+ "\r\n\r\n" +
+ "SecuritySuggestToTakeOwnership".GetLocalizedResource();
+ }
+ else
+ {
+ ErrorMessage =
+ "SecurityUnableToDisplayPermissions".GetLocalizedResource() +
+ "\r\n\r\n" +
+ error.FormatMessage();
+ }
+ }
+ else
+ {
+ DisplayElements = true;
+ ErrorMessage = string.Empty;
}
}
- private async void AddAccessControlEntry()
+ private async Task ExecuteChangeOwnerCommand()
{
- // Pick an user/group
- var sid = await OpenObjectPicker();
+ var sid = await FileOperationsHelpers.OpenObjectPickerAsync(FilePropertiesHelpers.GetWindowHandle(_window).ToInt64());
if (string.IsNullOrEmpty(sid))
return;
- // Initialize
- var ace = FileSecurityHelpers.InitializeDefaultAccessControlEntry(IsFolder, sid);
- AccessControlList.AccessControlEntries.Add(ace);
+ await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() =>
+ {
+ // Set owner
+ FileSecurityHelpers.SetOwner(_path, sid);
- // Save
- FileSecurityHelpers.SetAccessControlList(AccessControlList);
+ // Reload
+ LoadAccessControlEntry();
+ });
}
- private void RemoveAccessControlEntry()
+ private async Task ExecuteAddAccessControlEntryCommand()
{
- // Remove
- AccessControlList.AccessControlEntries.Remove(SelectedAccessControlEntry);
+ // Pick an user or a group with Object Picker UI
+ var sid = await FileOperationsHelpers.OpenObjectPickerAsync(FilePropertiesHelpers.GetWindowHandle(_window).ToInt64());
+ if (string.IsNullOrEmpty(sid))
+ return;
- // Save
- FileSecurityHelpers.SetAccessControlList(AccessControlList);
- }
+ await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() =>
+ {
+ // Run Win32API
+ var win32Result = FileSecurityHelpers.AddAccessControlEntry(_path, sid);
- private void DisableInheritance()
- {
- // Update protection status and refresh access control
- if (FileOperationsHelpers.SetAccessRuleProtection(Item.ItemPath, IsFolder, _isProtected, _preserveInheritance))
- GetAccessControlList();
+ // Add a new ACE to the ACL
+ var ace = FileSecurityHelpers.InitializeDefaultAccessControlEntry(_isFolder, sid);
+ AccessControlList.AccessControlEntries.Insert(0, ace);
+ });
}
- private void SetDisableInheritanceOption(string options)
+ private async Task ExecuteRemoveAccessControlEntryCommand()
{
- _isProtected = bool.Parse(options.Split(',')[0]);
- _preserveInheritance = bool.Parse(options.Split(',')[1]);
+ if (SelectedAccessControlEntry is null)
+ return;
- OnPropertyChanged(nameof(DisableInheritanceOption));
- DisableInheritanceCommand.NotifyCanExecuteChanged();
- }
+ await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() =>
+ {
+ // Get index of the ACE
+ var index = AccessControlList.AccessControlEntries.IndexOf(SelectedAccessControlEntry);
- private void ReplaceChildPermissions()
- {
- }
+ // Run Win32API
+ var win32Result = FileSecurityHelpers.RemoveAccessControlEntry(_path, (uint)index);
- public void GetAccessControlList()
- {
- AccessControlList = FileOperationsHelpers.GetFilePermissions(Item.ItemPath, IsFolder);
- }
+ // Remove the ACE
+ AccessControlList.AccessControlEntries.Remove(SelectedAccessControlEntry);
- public bool SaveChangedAccessControlList()
- {
- return false;
- }
+ if (AccessControlList.AccessControlEntries.Count == 0)
+ return;
- public Task OpenObjectPicker()
- => FileOperationsHelpers.OpenObjectPickerAsync(FilePropertiesHelpers.GetWindowHandle(Window).ToInt64());
+ // Re-select item
+ SelectedAccessControlEntry = AccessControlList.AccessControlEntries.First();
+ });
+ }
}
}
diff --git a/src/Files.App/ViewModels/Properties/SecurityViewModel.cs b/src/Files.App/ViewModels/Properties/SecurityViewModel.cs
index 601b95e03e50..87d04e15aa51 100644
--- a/src/Files.App/ViewModels/Properties/SecurityViewModel.cs
+++ b/src/Files.App/ViewModels/Properties/SecurityViewModel.cs
@@ -1,29 +1,42 @@
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using Files.App.Data.Items;
-using Files.App.Extensions;
-using Files.App.Filesystem;
+// Copyright (c) 2023 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
+using CommunityToolkit.WinUI;
using Files.App.Filesystem.Security;
-using Files.App.Helpers;
using Microsoft.UI.Xaml;
-using System.Linq;
-using System.Threading.Tasks;
+using Vanara.PInvoke;
using Windows.Storage;
namespace Files.App.ViewModels.Properties
{
public class SecurityViewModel : ObservableObject
{
- public string Path { get; set; }
+ private readonly PropertiesPageNavigationParameter _navigationParameter;
- private readonly Window Window;
+ private readonly Window _window;
- private bool _IsFolder;
- public bool IsFolder
- {
- get => _IsFolder;
- set => SetProperty(ref _IsFolder, value);
- }
+ private readonly string _path;
+
+ private readonly bool _isFolder;
+
+ public bool DisplayElements { get; private set; }
+
+ public string ErrorMessage { get; private set; }
+
+ public bool IsAddAccessControlEntryButtonEnabled =>
+ AccessControlList is not null &&
+ AccessControlList.IsValid;
+
+ public bool IsDeleteAccessControlEntryButtonEnabled =>
+ AccessControlList is not null &&
+ AccessControlList.IsValid &&
+ SelectedAccessControlEntry is not null &&
+ SelectedAccessControlEntry.IsInherited is false;
+
+ public string SelectedItemHeaderText =>
+ SelectedAccessControlEntry is null
+ ? "Permissions".GetLocalizedResource()
+ : string.Format("SecurityPermissionsHeaderText".GetLocalizedResource(), SelectedAccessControlEntry?.Principal.DisplayName);
private AccessControlList _AccessControlList;
public AccessControlList AccessControlList
@@ -32,88 +45,108 @@ public AccessControlList AccessControlList
set => SetProperty(ref _AccessControlList, value);
}
- private AccessControlEntry _SelectedAccessControlEntry;
- public AccessControlEntry SelectedAccessControlEntry
+ private AccessControlEntry? _SelectedAccessControlEntry;
+ public AccessControlEntry? SelectedAccessControlEntry
{
get => _SelectedAccessControlEntry;
set
{
+ // Previous selection
if (_SelectedAccessControlEntry is not null)
_SelectedAccessControlEntry.IsSelected = false;
- if (SetProperty(ref _SelectedAccessControlEntry, value))
+ if (value is not null && SetProperty(ref _SelectedAccessControlEntry, value))
{
value.IsSelected = true;
- RemoveAccessControlEntryCommand?.NotifyCanExecuteChanged();
+
+ OnPropertyChanged(nameof(IsDeleteAccessControlEntryButtonEnabled));
OnPropertyChanged(nameof(SelectedItemHeaderText));
}
}
}
- public string SelectedItemHeaderText
- => string.Format("SecurityPermissionsHeaderText".GetLocalizedResource(), SelectedAccessControlEntry.Principal.DisplayName);
-
- public IRelayCommand AddAccessControlEntryCommand { get; set; }
- public IRelayCommand RemoveAccessControlEntryCommand { get; set; }
-
- public SecurityViewModel(ListedItem item, Window window)
- {
- IsFolder = item.PrimaryItemAttribute == StorageItemTypes.Folder && !item.IsShortcut;
- Path = item.ItemPath;
- AccessControlList = FileSecurityHelpers.GetAccessControlList(Path, IsFolder);
- SelectedAccessControlEntry = AccessControlList.AccessControlEntries.FirstOrDefault();
- Window = window;
-
- InitializeCommands();
- }
+ public IAsyncRelayCommand AddAccessControlEntryCommand { get; set; }
+ public IAsyncRelayCommand RemoveAccessControlEntryCommand { get; set; }
- public SecurityViewModel(DriveItem item, Window window)
+ public SecurityViewModel(PropertiesPageNavigationParameter parameter)
{
- IsFolder = true;
- Path = item.Path;
- AccessControlList = FileSecurityHelpers.GetAccessControlList(Path, IsFolder);
- SelectedAccessControlEntry = AccessControlList.AccessControlEntries.FirstOrDefault();
- Window = window;
+ _navigationParameter = parameter;
+ _window = parameter.Window;
- InitializeCommands();
- }
+ switch (parameter.Parameter)
+ {
+ case ListedItem listedItem:
+ _path = listedItem.ItemPath;
+ _isFolder = listedItem.PrimaryItemAttribute == StorageItemTypes.Folder && !listedItem.IsShortcut;
+ break;
+ case DriveItem driveItem:
+ _path = driveItem.Path;
+ _isFolder = true;
+ break;
+ default:
+ var defaultlistedItem = (ListedItem)parameter.Parameter;
+ _path = defaultlistedItem.ItemPath;
+ _isFolder = defaultlistedItem.PrimaryItemAttribute == StorageItemTypes.Folder && !defaultlistedItem.IsShortcut;
+ break;
+ };
+
+ var error = FileSecurityHelpers.GetAccessControlList(_path, _isFolder, out _AccessControlList);
+ _SelectedAccessControlEntry = AccessControlList.AccessControlEntries.FirstOrDefault();
+
+ if (!AccessControlList.IsValid)
+ {
+ DisplayElements = false;
+ ErrorMessage = error == Win32Error.ERROR_ACCESS_DENIED
+ ? "SecurityRequireReadPermissions".GetLocalizedResource() + "\r\n" + "SecurityClickAdvancedPermissions".GetLocalizedResource()
+ : "SecurityUnableToDisplayPermissions".GetLocalizedResource();
+ }
+ else
+ {
+ DisplayElements = true;
+ ErrorMessage = string.Empty;
+ }
- private void InitializeCommands()
- {
- AddAccessControlEntryCommand = new AsyncRelayCommand(AddAccessControlEntry, () => AccessControlList is not null && AccessControlList.IsValid);
- RemoveAccessControlEntryCommand = new RelayCommand(RemoveAccessControlEntry, () => AccessControlList is not null && AccessControlList.IsValid && SelectedAccessControlEntry is not null);
+ AddAccessControlEntryCommand = new AsyncRelayCommand(ExecuteAddAccessControlEntryCommand);
+ RemoveAccessControlEntryCommand = new AsyncRelayCommand(ExecuteRemoveAccessControlEntryCommand);
}
- private async Task AddAccessControlEntry()
+ private async Task ExecuteAddAccessControlEntryCommand()
{
- // Pick an user/group
- var sid = await OpenObjectPicker();
+ // Pick an user or a group with Object Picker UI
+ var sid = await FileOperationsHelpers.OpenObjectPickerAsync(FilePropertiesHelpers.GetWindowHandle(_window).ToInt64());
if (string.IsNullOrEmpty(sid))
return;
- // Initialize
- var ace = FileSecurityHelpers.InitializeDefaultAccessControlEntry(IsFolder, sid);
- AccessControlList.AccessControlEntries.Add(ace);
+ await App.Window.DispatcherQueue.EnqueueAsync(() =>
+ {
+ // Run Win32API
+ var win32Result = FileSecurityHelpers.AddAccessControlEntry(_path, sid);
- // Save
- FileSecurityHelpers.SetAccessControlList(AccessControlList);
+ // Add a new ACE to the ACL
+ var ace = FileSecurityHelpers.InitializeDefaultAccessControlEntry(_isFolder, sid);
+ AccessControlList.AccessControlEntries.Insert(0, ace);
+ });
}
- private void RemoveAccessControlEntry()
+ private async Task ExecuteRemoveAccessControlEntryCommand()
{
- // Remove
- AccessControlList.AccessControlEntries.Remove(SelectedAccessControlEntry);
+ await App.Window.DispatcherQueue.EnqueueAsync(() =>
+ {
+ // Get index of the ACE
+ var index = AccessControlList.AccessControlEntries.IndexOf(SelectedAccessControlEntry);
- // Save
- FileSecurityHelpers.SetAccessControlList(AccessControlList);
- }
+ // Run Win32API
+ var win32Result = FileSecurityHelpers.RemoveAccessControlEntry(_path, (uint)index);
- public bool SaveChangedAccessControlList()
- {
- return false;
- }
+ // Remove the ACE
+ AccessControlList.AccessControlEntries.Remove(SelectedAccessControlEntry);
+
+ if (AccessControlList.AccessControlEntries.Count == 0)
+ return;
- private Task OpenObjectPicker()
- => FileOperationsHelpers.OpenObjectPickerAsync(FilePropertiesHelpers.GetWindowHandle(Window).ToInt64());
+ // Re-select item
+ SelectedAccessControlEntry = AccessControlList.AccessControlEntries.First();
+ });
+ }
}
}
diff --git a/src/Files.App/Views/Properties/SecurityAdvancedPage.xaml b/src/Files.App/Views/Properties/SecurityAdvancedPage.xaml
index 10cf4c0035ab..535189479db8 100644
--- a/src/Files.App/Views/Properties/SecurityAdvancedPage.xaml
+++ b/src/Files.App/Views/Properties/SecurityAdvancedPage.xaml
@@ -10,8 +10,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:security="using:Files.App.Filesystem.Security"
xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls"
- xmlns:toolkitconverters="using:CommunityToolkit.WinUI.UI.Converters"
+ xmlns:uc="using:Files.App.UserControls"
xmlns:vm="using:Files.App.ViewModels.Properties"
+ xmlns:wctconverters="using:CommunityToolkit.WinUI.UI.Converters"
DataContext="{x:Bind SecurityAdvancedViewModel, Mode=OneWay}"
Tag="Security"
mc:Ignorable="d">
@@ -40,15 +41,23 @@
-
-
+
-
+
+
@@ -64,8 +73,9 @@
+
@@ -86,33 +96,41 @@
Orientation="Horizontal"
Spacing="8">
+
+
+ ToolTipService.ToolTip="{x:Bind SecurityAdvancedViewModel.AccessControlList.Owner.FullNameHumanized, Mode=OneWay}">
-
+
+
+ x:Load="{x:Bind SecurityAdvancedViewModel.AccessControlList.Owner.IsValid, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
+ IsTextSelectionEnabled="True"
+ Style="{StaticResource BodyTextBlockStyle}"
+ Text="Unable to display current owner." />
-
+
+
-
+
@@ -121,11 +139,25 @@
+
+
+
@@ -137,6 +169,7 @@
+
@@ -158,7 +191,8 @@
Padding="8"
Background="Transparent"
BorderBrush="Transparent"
- Command="{x:Bind SecurityAdvancedViewModel.AddAccessControlEntryCommand, Mode=OneWay}">
+ Command="{x:Bind SecurityAdvancedViewModel.AddAccessControlEntryCommand, Mode=OneWay}"
+ IsEnabled="{x:Bind SecurityAdvancedViewModel.IsAddAccessControlEntryButtonEnabled, Mode=OneWay}">
@@ -166,7 +200,8 @@
Padding="8"
Background="Transparent"
BorderBrush="Transparent"
- Command="{x:Bind SecurityAdvancedViewModel.RemoveAccessControlEntryCommand, Mode=OneWay}">
+ Command="{x:Bind SecurityAdvancedViewModel.RemoveAccessControlEntryCommand, Mode=OneWay}"
+ IsEnabled="{x:Bind SecurityAdvancedViewModel.IsDeleteAccessControlEntryButtonEnabled, Mode=OneWay}">
@@ -174,19 +209,21 @@
+
+
-
+
-
-
-
-
+
+
+
+
+
+ Glyph="{x:Bind Principal.Glyph, Mode=OneWay}" />
+
+
@@ -395,6 +435,7 @@
+
-
+
+
+
@@ -456,7 +499,7 @@
-
+