30

I'm trying to change the permissions of a file in .NET Core. However, it seems that FileInfo doesn't have any SetAccessControl anymore.

// Create a new FileInfo object.
FileInfo fInfo = new FileInfo(FileName);

// Get a FileSecurity object that represents the 
// current security settings.
FileSecurity fSecurity = fInfo.GetAccessControl();

// Add the FileSystemAccessRule to the security settings. 
fSecurity.AddAccessRule(new FileSystemAccessRule(Account,
                                                Rights,
                                                ControlType));

// Set the new access settings.
fInfo.SetAccessControl(fSecurity);

The goal is just to add execution right to the current owner of a file (which is not Windows or Unix specific feature).

Any clues on how to do that on .NET Core ?

Fab
  • 14,327
  • 5
  • 49
  • 68
  • What would you expect to happen on Unix, which has a different access control system? Or is this a Windows-only .Net Core application? – svick Nov 06 '16 at 15:19
  • 2
    @svick I would expect something like a common subset. I will check what has been done (if it has been done) on Mono. – Fab Nov 07 '16 at 02:20
  • With new versions of .NET (6+) this code works as-is. There is no System.IO.FileSystem.AccessControl assembly to add. – Skrymsli Jul 11 '23 at 21:55

7 Answers7

32

The FileSecurity class is now part of the System.IO.FileSystem.AccessControl package for .NET Core. There is no longer a File.GetAccessControl method so you will need to instantiate the FileSecurity instance yourself.

Bacon Bits
  • 30,782
  • 5
  • 59
  • 66
Patrik Svensson
  • 13,536
  • 8
  • 56
  • 77
  • 8
    As an aside, creating a new .NET Core 2.0 Library will not include this assembly. You will have to add the `System.IO.FileSystem.AccessControl` as a NuGet package. – Scyssion Nov 13 '17 at 21:49
  • @Scyssion Yes, my comment says that. – Patrik Svensson Dec 28 '17 at 11:44
  • 13
    An example would help massively. I can't find a working example for .net core anywhere. – Learner Feb 15 '19 at 00:29
  • @Learner Check the [unit tests project in the Github repository](https://github.com/dotnet/corefx/tree/master/src/System.IO.FileSystem.AccessControl/tests) for examples – Jérôme MEVEL Mar 27 '19 at 06:52
  • Note that unlike .NET Core API, that package does NOT support long file name. Check my additional answer below for details. – Luke Vo Apr 18 '20 at 09:21
28

At this time there are two extension methods: GetAccessControl and SetAccessControl, for FileInfo, DirectoryInfo and etc.

So you can use var ac = new FileInfo(path).GetAccessControl(), this expression is valid both in .NET Framework and .Net Core. But you still need dotnet add package System.IO.FileSystem.AccessControl.

File.GetAccessControl isn't available in .NET Core.

ref: https://learn.microsoft.com/dotnet/api/system.io.filesystemaclextensions.getaccesscontrol

imba-tjd
  • 491
  • 4
  • 11
13

How to Get and modify User Group Other Rights on Windows

I finally implement the Windows file permission access:

1. Get the file security:

      var security = new FileSecurity(fileSystemInfoFullName, 
                AccessControlSections.Owner | 
                AccessControlSections.Group |
                AccessControlSections.Access);

2. Get the authorization rules:

var authorizationRules = security.GetAccessRules(true, true, typeof(NTAccount));

3. Get the authorization rules for the owner:

var owner = security.GetOwner(typeof(NTAccount));
foreach (AuthorizationRule rule in authorizationRules)
{
    FileSystemAccessRule fileRule = rule as FileSystemAccessRule;
    if (fileRule != null)
    {
        if (owner != null && fileRule.IdentityReference == owner)
        {
             if (fileRule.FileSystemRights.HasFlag(FileSystemRights.ExecuteFile) ||
                fileRule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute) ||
                fileRule.FileSystemRights.HasFlag(FileSystemRights.FullControl))
            {
                ownerRights.IsExecutable = true;
            }
        }
        else if (group != null && fileRule.IdentityReference == group)
        {
            // TO BE CONTINUED...
        }
    }
}

4. Add a rule for owner:

security.ModifyAccessRule(AccessControlModification.Add,
    new FileSystemAccessRule(owner, FileSystemRights.Modify, AccessControlType.Allow),
    out bool modified);

5. Bonus

How to get the group and others, or ... my definition of something equivalent ?

var group = security.GetGroup(typeof(NTAccount));

var others = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null)
                 .Translate(typeof(NTAccount));

Note: This code comes from my open source project Lx.Shell

Fab
  • 14,327
  • 5
  • 49
  • 68
5

The documentation says this IS supported and it works (for me). https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemaclextensions?view=dotnet-plat-ext-3.1 DOES have a SetAccessControl method

Be sure to add the System.IO.FileSystem.AccessControl NuGet package.

Here's what I had in .NET Framework:

var ds = new DirectorySecurity();
ds.AddAccessRule(new FileSystemAccessRule(adminSI, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
ds.SetAccessRuleProtection(true, false); // disable inheritance and clear any inherited permissions

Directory.SetAccessControl(<path to directory>, ds);

And here's what it is working in .NET Core 3.1. Only the last line is different:

var ds = new DirectorySecurity();
ds.AddAccessRule(new FileSystemAccessRule(adminSI, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
ds.SetAccessRuleProtection(true, false); // disable inheritance and clear any inherited permissions

System.IO.FileSystemAclExtensions.SetAccessControl(new DirectoryInfo(<path to directory>), ds);
Jon R
  • 836
  • 11
  • 9
2

This is to add to other answers. Note that the GetAccessControl and SetAccessControl in the System.IO.FileSystem.AccessControl does NOT support long file name (255 chars) like other .NET Core System.IO APIs.

The exception you receive is ArgumentException thrown by internal calls, the parameter is name.

If you are using that package, you need to add this if you may find long file name:

if (usingFile.FullName.Length > 255)
{
    usingFile = new FileInfo(@"\\?\" + file.FullName);
}

or

if (folder.FullName.Length > 255)
{
    folder = new DirectoryInfo(@"\\?\" + folder.FullName);
}
Luke Vo
  • 17,859
  • 21
  • 105
  • 181
0

Another way to handle acls for directory or file:

       // Adds an ACL entry on the specified directory for the specified account.
    public static void AddDirectorySecurity(string FileName, string Account, FileSystemRights Rights, AccessControlType ControlType)
    {
        // Create a new DirectoryInfo object.
        DirectoryInfo dInfo = new DirectoryInfo(FileName);

        // Get a DirectorySecurity object that represents the 
        // current security settings.
        DirectorySecurity dSecurity = dInfo.GetAccessControl();

        // Add the FileSystemAccessRule to the security settings. 
        dSecurity.AddAccessRule(new FileSystemAccessRule(Account,
                                                        Rights,
                                                        ControlType));

        // Set the new access settings.
        dInfo.SetAccessControl(dSecurity);
    }

    // Removes an ACL entry on the specified directory for the specified account.
    public static void RemoveDirectorySecurity(string FileName, string Account, FileSystemRights Rights, AccessControlType ControlType)
    {
        // Create a new DirectoryInfo object.
        DirectoryInfo dInfo = new DirectoryInfo(FileName);

        // Get a DirectorySecurity object that represents the 
        // current security settings.
        DirectorySecurity dSecurity = dInfo.GetAccessControl();

        // Add the FileSystemAccessRule to the security settings. 
        dSecurity.RemoveAccessRule(new FileSystemAccessRule(Account,
                                                        Rights,
                                                        ControlType));

        // Set the new access settings.
        dInfo.SetAccessControl(dSecurity);
    }

    // Adds an ACL entry on the specified file for the specified account.
    public static void AddFileSecurity(string fileName, string account,
        FileSystemRights rights, AccessControlType controlType)
    {

        // Create a new FileInfo object.
        FileInfo fInfo = new FileInfo(fileName);

        // Get a FileSecurity object that represents the 
        // current security settings.
        FileSecurity fSecurity = fInfo.GetAccessControl();

        // Add the FileSystemAccessRule to the security settings.
        fSecurity.AddAccessRule(new FileSystemAccessRule(account,
            rights, controlType));

        // Set the new access settings.
        fInfo.SetAccessControl(fSecurity);
    }

    // Removes an ACL entry on the specified file for the specified account.
    public static void RemoveFileSecurity(string fileName, string account,
        FileSystemRights rights, AccessControlType controlType)
    {

        // Create a new FileInfo object.
        FileInfo fInfo = new FileInfo(fileName);

        // Get a FileSecurity object that represents the 
        // current security settings.
        FileSecurity fSecurity = fInfo.GetAccessControl();

        // Remove the FileSystemAccessRule from the security settings.
        fSecurity.RemoveAccessRule(new FileSystemAccessRule(account,
            rights, controlType));

        // Set the new access settings.
        fInfo.SetAccessControl(fSecurity);
    }

    //example for open onClick folderdialog and get owner by NTACCOUNT of folder from acl
    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        var folderPicker = new Windows.Storage.Pickers.FolderPicker();
        folderPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop;
        folderPicker.FileTypeFilter.Add("*");

        Windows.Storage.StorageFolder folder = await folderPicker.PickSingleFolderAsync();
        if (folder != null)
        {
            // Application now has read/write access to all contents in the picked folder
            // (including other sub-folder contents)
            Windows.Storage.AccessCache.StorageApplicationPermissions.
            FutureAccessList.AddOrReplace("PickedFolderToken", folder);

            // Create a new FileInfo object.
            FileInfo fInfo = new FileInfo(folder.ToString());

            // Get a FileSecurity object that represents the 
            // current security settings.
            FileSecurity fSecurity = fInfo.GetAccessControl();

            IdentityReference identityReference = fSecurity.GetOwner(typeof(SecurityIdentifier));
            NTAccount ntAccount = identityReference.Translate(typeof(NTAccount)) as NTAccount;
            var fileOwner = ntAccount.Value;


            //do something with file Owner
            //this.tb1.Text = "folder: " + folder.Name + " in Pfad: " + folder.Path + "owned by: " + fileOwner;
        }
        else
        {
            //error Handler
        }

    }
  • As the original question says ... Net Core no longer has GetAccessControl or SetAccessControl methods on DirectoryInfo – Richard Petheram Feb 06 '20 at 08:38
  • The GetAccessControl and SetAccessControl have been completely withdrawn from Net Core 3.0 and above, it will work in 2.2 though. If you tried to run it you'd get a Method Not Found exception (or something-like). I have a multi-targettting project with exactly this problem, it works on Framework 4.7.2 and Core 2.2, but fails on Core 3.0 and 3.1. – Richard Petheram Feb 10 '20 at 11:15
  • Then it was from a 2.2. project. Thank you –  Feb 10 '20 at 11:47
0

Following what @jon-r said, I was able to get mine working. This is still relevant in .NET 7. This is using System.IO.FileSystem.AccessControl and System.IO NuGet packages.

My .NET Framework 4.8 code:

var ds = Directory.GetAccessControl(dir);
ds.AddAccessRule(new FileSystemAccessRule(user, FileSystemRights.Modify, InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
ds.AddAccessRule(new FileSystemAccessRule(user, FileSystemRights.Modify, InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
Directory.SetAccessControl(dir, ds);

My .NET 7 code:

var ds = new DirectorySecurity();
ds.AddAccessRule(new FileSystemAccessRule(user, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
System.IO.FileSystemAclExtensions.SetAccessControl(new DirectoryInfo(dir), ds);

My initial code looked a little different than Jon's so I wanted to include it for others.