Checking for directory and file write permissions in .NET

In my .NET 2.0 application, I need to check if sufficient permissions exist to create and write to files to a directory. To this end, I have the following function that attempts to create a file and write a single byte to it, deleting itself afterwards to test that permissions do exist.

I figured the best way to check was to actually try and do it, catching any exceptions that occur. I'm not particularly happy about the general Exception catch though, so is there a better or perhaps a more accepted way of doing this?

private const string TEMP_FILE = "\\tempFile.tmp";

/// <summary>
/// Checks the ability to create and write to a file in the supplied directory.
/// </summary>
/// <param name="directory">String representing the directory path to check.</param>
/// <returns>True if successful; otherwise false.</returns>
private static bool CheckDirectoryAccess(string directory)
{
    bool success = false;
    string fullPath = directory + TEMP_FILE;

    if (Directory.Exists(directory))
    {
        try
        {
            using (FileStream fs = new FileStream(fullPath, FileMode.CreateNew, 
                                                            FileAccess.Write))
            {
                fs.WriteByte(0xff);
            }

            if (File.Exists(fullPath))
            {
                File.Delete(fullPath);
                success = true;
            }
        }
        catch (Exception)
        {
            success = false;
        }
    }

Solution 1:

Directory.GetAccessControl(path) does what you are asking for.

public static bool HasWritePermissionOnDir(string path)
{
    var writeAllow = false;
    var writeDeny = false;
    var accessControlList = Directory.GetAccessControl(path);
    if (accessControlList == null)
        return false;
    var accessRules = accessControlList.GetAccessRules(true, true, 
                                typeof(System.Security.Principal.SecurityIdentifier));
    if (accessRules ==null)
        return false;

    foreach (FileSystemAccessRule rule in accessRules)
    {
        if ((FileSystemRights.Write & rule.FileSystemRights) != FileSystemRights.Write) 
            continue;

        if (rule.AccessControlType == AccessControlType.Allow)
            writeAllow = true;
        else if (rule.AccessControlType == AccessControlType.Deny)
            writeDeny = true;
    }

    return writeAllow && !writeDeny;
}

(FileSystemRights.Write & rights) == FileSystemRights.Write is using something called "Flags" btw which if you don't know what it is you should really read up on :)

Solution 2:

Deny takes precedence over Allow. Local rules take precedence over inherited rules. I have seen many solutions (including some answers shown here), but none of them takes into account whether rules are inherited or not. Therefore I suggest the following approach that considers rule inheritance (neatly wrapped into a class):

public class CurrentUserSecurity
{
    WindowsIdentity _currentUser;
    WindowsPrincipal _currentPrincipal;

    public CurrentUserSecurity()
    {
        _currentUser = WindowsIdentity.GetCurrent();
        _currentPrincipal = new WindowsPrincipal(_currentUser);
    }

    public bool HasAccess(DirectoryInfo directory, FileSystemRights right)
    {
        // Get the collection of authorization rules that apply to the directory.
        AuthorizationRuleCollection acl = directory.GetAccessControl()
            .GetAccessRules(true, true, typeof(SecurityIdentifier));
        return HasFileOrDirectoryAccess(right, acl);
    }

    public bool HasAccess(FileInfo file, FileSystemRights right)
    {
        // Get the collection of authorization rules that apply to the file.
        AuthorizationRuleCollection acl = file.GetAccessControl()
            .GetAccessRules(true, true, typeof(SecurityIdentifier));
        return HasFileOrDirectoryAccess(right, acl);
    }

    private bool HasFileOrDirectoryAccess(FileSystemRights right,
                                          AuthorizationRuleCollection acl)
    {
        bool allow = false;
        bool inheritedAllow = false;
        bool inheritedDeny = false;

        for (int i = 0; i < acl.Count; i++) {
            var currentRule = (FileSystemAccessRule)acl[i];
            // If the current rule applies to the current user.
            if (_currentUser.User.Equals(currentRule.IdentityReference) ||
                _currentPrincipal.IsInRole(
                                (SecurityIdentifier)currentRule.IdentityReference)) {

                if (currentRule.AccessControlType.Equals(AccessControlType.Deny)) {
                    if ((currentRule.FileSystemRights & right) == right) {
                        if (currentRule.IsInherited) {
                            inheritedDeny = true;
                        } else { // Non inherited "deny" takes overall precedence.
                            return false;
                        }
                    }
                } else if (currentRule.AccessControlType
                                                  .Equals(AccessControlType.Allow)) {
                    if ((currentRule.FileSystemRights & right) == right) {
                        if (currentRule.IsInherited) {
                            inheritedAllow = true;
                        } else {
                            allow = true;
                        }
                    }
                }
            }
        }

        if (allow) { // Non inherited "allow" takes precedence over inherited rules.
            return true;
        }
        return inheritedAllow && !inheritedDeny;
    }
}

However, I made the experience that this does not always work on remote computers as you will not always have the right to query the file access rights there. The solution in that case is to try; possibly even by just trying to create a temporary file, if you need to know the access right before working with the "real" files.