56

I got a program that writes some data to a file using a method like the one below.


public void ExportToFile(string filename)
{
     using(FileStream fstream = new FileStream(filename,FileMode.Create))
     using (TextWriter writer = new StreamWriter(fstream))
     {
         // try catch block for write permissions 
         writer.WriteLine(text);


     }
}

When running the program I get an error:

Unhandled Exception: System.UnauthorizedAccessException: Access to the path 'mypath' is denied. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolea bFromProxy)

Question: What code do I need to catch this and how do I grant the access?

8 답변


63

UPDATE:

Modified the code based on this answer to get rid of obsolete methods.

You can use the Security namespace to check this:

public void ExportToFile(string filename)
{
    var permissionSet = new PermissionSet(PermissionState.None);    
    var writePermission = new FileIOPermission(FileIOPermissionAccess.Write, filename);
    permissionSet.AddPermission(writePermission);

    if (permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet))
    {
        using (FileStream fstream = new FileStream(filename, FileMode.Create))
        using (TextWriter writer = new StreamWriter(fstream))
        {
            // try catch block for write permissions 
            writer.WriteLine("sometext");


        }
    }
    else
    {
        //perform some recovery action here
    }

}

As far as getting those permission, you are going to have to ask the user to do that for you somehow. If you could programatically do this, then we would all be in trouble ;)


  • This solution doesn't work, SecurityManager.IsGranted returns true even though I don't have write permission on the file (Win XP SP3) - Thomas Levesque
  • Are you running as local Admin, or as a less privileged user who doesn't have access? - Josh
  • SecurityManager.IsGranted is attributed as Obsolete. This answer might not work forever. - MarkPflug
  • @Jetnor - You are looking for SecurityManager.IsGranted method. It was deprecated in 4.0 in favor of using PermissionSets. The code above shouldn't be much different, but instead of checking a PermissionSet you only check single permissions at a time. - Josh
  • @Josh try this on your pc... create a new folder, (make sure it has Administrators, Full Control on it, and that you are part of the Administrators group), then add yourself/Windows Account to the security list, then edit your rights and put Deny on the Write... your method returns True for Write because Administrators has Write access, while Deny takes precedence from your account entry. Therefore, your method does not work for me... see my answer below. - MaxOvrdrv

30

When your code does the following:

  1. Checks the current user has permission to do something.
  2. Carries out the action that needs the entitlements checked in 1.

You run the risk that the permissions change between 1 and 2 because you can't predict what else will be happening on the system at runtime. Therefore, your code should handle the situation where an UnauthorisedAccessException is thrown even if you have previously checked permissions.

Note that the SecurityManager class is used to check CAS permissions and doesn't actually check with the OS whether the current user has write access to the specified location (through ACLs and ACEs). As such, IsGranted will always return true for locally running applications.

Example (derived from Josh's example):

//1. Provide early notification that the user does not have permission to write.
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, filename);
if(!SecurityManager.IsGranted(writePermission))
{
    //No permission. 
    //Either throw an exception so this can be handled by a calling function
    //or inform the user that they do not have permission to write to the folder and return.
}

//2. Attempt the action but handle permission changes.
try
{
    using (FileStream fstream = new FileStream(filename, FileMode.Create))
    using (TextWriter writer = new StreamWriter(fstream))
    {
        writer.WriteLine("sometext");
    }
}
catch (UnauthorizedAccessException ex)
{
    //No permission. 
    //Either throw an exception so this can be handled by a calling function
    //or inform the user that they do not have permission to write to the folder and return.
}

It's tricky and not recommended to try to programatically calculate the effective permissions from the folder based on the raw ACLs (which are all that are available through the System.Security.AccessControl classes). Other answers on Stack Overflow and the wider web recommend trying to carry out the action to know whether permission is allowed. This post sums up what's required to implement the permission calculation and should be enough to put you off from doing this.


  • This philosophy is all well and good, but doesn't universally work. For example, I need to show a dialog where the user selects a path to a folder that I want to write to, and I want to populate the dialog with a sensible initial value as a suggestion, with the target folder being created if it doesn't exist. For backwards compatibility reasons, that default path is "C:\<SomeName>". If the user can't write there, I want to fall back to a default under AppData. There's no way I want to try and create a folder, then delete it again, just to determine the initial value for a folder browse dialog. - Roger Sanders
  • @RogerSanders - good point. - Iain

8

Its a fixed version of MaxOvrdrv's Code.

public static bool IsReadable(this DirectoryInfo di)
{
    AuthorizationRuleCollection rules;
    WindowsIdentity identity;
    try
    {
        rules = di.GetAccessControl().GetAccessRules(true, true, typeof(SecurityIdentifier));
        identity = WindowsIdentity.GetCurrent();
    }
    catch (UnauthorizedAccessException uae)
    {
        Debug.WriteLine(uae.ToString());
        return false;
    }

    bool isAllow = false;
    string userSID = identity.User.Value;

    foreach (FileSystemAccessRule rule in rules)
    {
        if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference))
        {
            if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) ||
                rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) ||
                rule.FileSystemRights.HasFlag(FileSystemRights.ReadData)) && rule.AccessControlType == AccessControlType.Deny)
                return false;
            else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) &&
                rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) &&
                rule.FileSystemRights.HasFlag(FileSystemRights.ReadData)) && rule.AccessControlType == AccessControlType.Allow)
                isAllow = true;

        }
    }
    return isAllow;
}

public static bool IsWriteable(this DirectoryInfo me)
{
    AuthorizationRuleCollection rules;
    WindowsIdentity identity;
    try
    {
        rules = me.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
        identity = WindowsIdentity.GetCurrent();
    }
    catch (UnauthorizedAccessException uae)
    {
        Debug.WriteLine(uae.ToString());
        return false;
    }

    bool isAllow = false;
    string userSID = identity.User.Value;

    foreach (FileSystemAccessRule rule in rules)
    {
        if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference))
        {
            if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) ||
                rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) ||
                rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) ||
                rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) ||
                rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Deny)
                return false;
            else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) &&
                rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) &&
                rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) &&
                rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) &&
                rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Allow)
                isAllow = true;

        }
    }
    return isAllow;
}


  • This really worked, for me! Thanks! - Smartis
  • This really worked! I guess selected answer checks permissions based on loged in user, not application. - ibocon
  • This works! I checked C:\Program Files\... folder without running my application as admin. IsWriteable() returned false in that case. - arnobpl

6

Sorry, but none of the previous solutions helped me. I need to check both sides: SecurityManager and SO permissions. I have learned a lot with Josh code and with iain answer, but I'm afraid I need to use Rakesh code (also thanks to him). Only one bug: I found that he only checks for Allow and not for Deny permissions. So my proposal is:

        string folder;
        AuthorizationRuleCollection rules;
        try {
            rules = Directory.GetAccessControl(folder)
                .GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
        } catch(Exception ex) { //Posible UnauthorizedAccessException
            throw new Exception("No permission", ex);
        }

        var rulesCast = rules.Cast<FileSystemAccessRule>();
        if(rulesCast.Any(rule => rule.AccessControlType == AccessControlType.Deny)
            || !rulesCast.Any(rule => rule.AccessControlType == AccessControlType.Allow))
            throw new Exception("No permission");

        //Here I have permission, ole!



4

Since this isn't closed, i would like to submit a new entry for anyone looking to have something working properly for them... using an amalgamation of what i found here, as well as using DirectoryServices to debug the code itself and find the proper code to use, here's what i found that works for me in every situation... note that my solution extends DirectoryInfo object... :

    public static bool IsReadable(this DirectoryInfo me)
    {

        AuthorizationRuleCollection rules;
        WindowsIdentity identity;
        try
        {
            rules = me.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
            identity = WindowsIdentity.GetCurrent();
        }
        catch (Exception ex)
        { //Posible UnauthorizedAccessException
            return false;
        }

        bool isAllow=false;
        string userSID = identity.User.Value;

        foreach (FileSystemAccessRule rule in rules)
        {
            if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference))
            {
                if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadData) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadExtendedAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadPermissions)) && rule.AccessControlType == AccessControlType.Deny)
                    return false;
                else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadData) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadExtendedAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.ReadPermissions)) && rule.AccessControlType == AccessControlType.Allow)
                    isAllow = true;
            }
        }

        return isAllow;
    }

    public static bool IsWriteable(this DirectoryInfo me)
    {
        AuthorizationRuleCollection rules;
        WindowsIdentity identity;
        try
        {
            rules = me.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
            identity = WindowsIdentity.GetCurrent();
        }
        catch (Exception ex)
        { //Posible UnauthorizedAccessException
            return false;
        }

        bool isAllow = false;
        string userSID = identity.User.Value;

        foreach (FileSystemAccessRule rule in rules)
        {
            if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference))
            {
                if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.WriteExtendedAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Deny)
                    return false;
                else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.WriteExtendedAttributes) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) ||
                    rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Allow)
                    isAllow = true;
            }
        }

        return me.IsReadable() && isAllow;
    }


  • I noticed two (IMO) possible improvements. 1st, check for the existence of the directory (me.Exists) at the start of IsReadable. 2nd, inside IsWritable, move the IsReadable check to the top. Also note that some situations allow for writing files but not reading them so some people would want to keep the two methods "unlinked". - K Kimble
  • Also, I believe it will work to combine the enums outside the method call and make them static: public static FileSystemRights _ReadRights = FileSystemRights.Read | FileSystemRights.ReadAndExecute | FileSystemRights.ReadAttributes | FileSystemRights.ReadData | FileSystemRights.ReadExtendedAttributes | FileSystemRights.ReadPermissions; Then simplify your test calls: if (rule.FileSystemRights.HasFlag(_ReadRights) && rule.AccessControlType == AccessControlType.Deny) - K Kimble
  • Also does not work. No solution? - Pedro77
  • Hi @Pedro77 -- Curious... what OS are you running on? I've been using this and have been testing it on XP, Vista and Win7, and all 3 work flawlessly. Are you on Windows 8? - MaxOvrdrv
  • Win7 x64. Logged as admin, app not running was adm. - Pedro77

3

None of these worked for me.. they return as true, even when they aren't. The problem is, you have to test the available permission against the current process user rights, this tests for file creation rights, just change the FileSystemRights clause to 'Write' to test write access..

/// <summary>
/// Test a directory for create file access permissions
/// </summary>
/// <param name="DirectoryPath">Full directory path</param>
/// <returns>State [bool]</returns>
public static bool DirectoryCanCreate(string DirectoryPath)
{
    if (string.IsNullOrEmpty(DirectoryPath)) return false;

    try
    {
        AuthorizationRuleCollection rules = Directory.GetAccessControl(DirectoryPath).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
        WindowsIdentity identity = WindowsIdentity.GetCurrent();

        foreach (FileSystemAccessRule rule in rules)
        {
            if (identity.Groups.Contains(rule.IdentityReference))
            {
                if ((FileSystemRights.CreateFiles & rule.FileSystemRights) == FileSystemRights.CreateFiles)
                {
                    if (rule.AccessControlType == AccessControlType.Allow)
                        return true;
                }
            }
        }
    }
    catch {}
    return false;
}


  • this is by far the best answer... however, you should modify your code to take into account that Deny takes precedence over allow by using 2 booleans... - MaxOvrdrv
  • It returns true for C:\\ but I get an exception when I try to create any file in C:\\ - Pedro77

2

You can try following code block to check if the directory is having Write Access.

It checks the FileSystemAccessRule.

           string directoryPath = "C:\\XYZ"; //folderBrowserDialog.SelectedPath;
           bool isWriteAccess = false;
           try
           {
              AuthorizationRuleCollection collection = Directory.GetAccessControl(directoryPath).GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
              foreach (FileSystemAccessRule rule in collection)
              {
                 if (rule.AccessControlType == AccessControlType.Allow)
                 {
                    isWriteAccess = true;
                    break;
                 }
              }
           }
           catch (UnauthorizedAccessException ex)
           {
              isWriteAccess = false;
           }
           catch (Exception ex)
           {
              isWriteAccess = false;
           }
           if (!isWriteAccess)
           {
             //handle notifications                 
           }


  • It does not work. It returns true for C:\\ but I get an exception when I try to create any file in C:\\. - Pedro77

0

Wow...there is a lot of low-level security code in this thread -- most of which did not work for me, either -- although I learned a lot in the process. One thing that I learned is that most of this code is not geared to applications seeking per user access rights -- it is for Administrators wanting to alter rights programmatically, which -- as has been pointed out -- is not a good thing. As a developer, I cannot use the "easy way out" -- by running as Administrator -- which -- I am not one on the machine that runs the code, nor are my users -- so, as clever as these solutions are -- they are not for my situation, and probably not for most rank and file developers, either.

Like most posters of this type of question -- I initially felt it was "hackey", too -- I have since decided that it is perfectly alright to try it and let the possible exception tell you exactly what the user's rights are -- because the information I got did not tell me what the rights actually were. The code below -- did.

  Private Function CheckUserAccessLevel(folder As String) As Boolean
Try
  Dim newDir As String = String.Format("{0}{1}{2}",
                                       folder,
                                       If(folder.EndsWith("\"),
                                          "",
                                          "\"),
                                       "LookWhatICanDo")
  Dim lookWhatICanDo = Directory.CreateDirectory(newDir)

  Directory.Delete(newDir)
  Return True

Catch ex As Exception
  Return False
End Try

End Function

Linked


Related

Latest