How do you check for permissions to write to a directory or file?
Solution 1:
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 ;)
Solution 2:
When your code does the following:
- Checks the current user has permission to do something.
- 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.
Solution 3:
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;
}
Solution 4:
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!
Solution 5:
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;
}