C# Test if user has write access to a folder - c#

I need to test if a user can write to a folder before actually attempting to do so.
I've implemented the following method (in C# 2.0) that attempts to retrieve the security permissions for the folder using Directory.GetAccessControl() method.
private bool hasWriteAccessToFolder(string folderPath)
{
try
{
// Attempt to get a list of security permissions from the folder.
// This will raise an exception if the path is read only or do not have access to view the permissions.
System.Security.AccessControl.DirectorySecurity ds = Directory.GetAccessControl(folderPath);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}
When I was googling how to test for write access nothing like this came up and it appeared very complicated to actually test permissions in Windows. I am concerned that I am over-simplifying things and that this method is not robust, although it does seem to work.
Will my method to test if the current user has write access work correctly?

public bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
{
try
{
using (FileStream fs = File.Create(
Path.Combine(
dirPath,
Path.GetRandomFileName()
),
1,
FileOptions.DeleteOnClose)
)
{ }
return true;
}
catch
{
if (throwIfFails)
throw;
else
return false;
}
}

I appreciate that this is a little late in the day for this post, but you might find this bit of code useful.
string path = #"c:\temp";
string NtAccountName = #"MyDomain\MyUserOrGroup";
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (AuthorizationRule rule in rules)
{
//If we find one that matches the identity we are looking for
if (rule.IdentityReference.Value.Equals(NtAccountName,StringComparison.CurrentCultureIgnoreCase))
{
var filesystemAccessRule = (FileSystemAccessRule)rule;
//Cast to a FileSystemAccessRule to check for access rights
if ((filesystemAccessRule.FileSystemRights & FileSystemRights.WriteData)>0 && filesystemAccessRule.AccessControlType != AccessControlType.Deny)
{
Console.WriteLine(string.Format("{0} has write access to {1}", NtAccountName, path));
}
else
{
Console.WriteLine(string.Format("{0} does not have write access to {1}", NtAccountName, path));
}
}
}
Console.ReadLine();
Drop that into a Console app and see if it does what you need.

That's a perfectly valid way to check for folder access in C#. The only place it might fall down is if you need to call this in a tight loop where the overhead of an exception may be an issue.
There have been other similar questions asked previously.

I tried most of these, but they give false positives, all for the same reason.. It is not enough to test the directory for an available permission, you have to check that the logged in user is a member of a group that has that permission. To do this you get the users identity, and check if it is a member of a group that contains the FileSystemAccessRule IdentityReference. I have tested this, works flawlessly..
/// <summary>
/// Test a directory for create file access permissions
/// </summary>
/// <param name="DirectoryPath">Full path to directory </param>
/// <param name="AccessRight">File System right tested</param>
/// <returns>State [bool]</returns>
public static bool DirectoryHasPermission(string DirectoryPath, FileSystemRights AccessRight)
{
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 ((AccessRight & rule.FileSystemRights) == AccessRight)
{
if (rule.AccessControlType == AccessControlType.Allow)
return true;
}
}
}
}
catch { }
return false;
}

IMHO the only 100% reliable way to test if you can write to a directory is to actually write to it and eventually catch exceptions.

For example for all users (Builtin\Users), this method works fine - enjoy.
public static bool HasFolderWritePermission(string destDir)
{
if(string.IsNullOrEmpty(destDir) || !Directory.Exists(destDir)) return false;
try
{
DirectorySecurity security = Directory.GetAccessControl(destDir);
SecurityIdentifier users = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
foreach(AuthorizationRule rule in security.GetAccessRules(true, true, typeof(SecurityIdentifier)))
{
if(rule.IdentityReference == users)
{
FileSystemAccessRule rights = ((FileSystemAccessRule)rule);
if(rights.AccessControlType == AccessControlType.Allow)
{
if(rights.FileSystemRights == (rights.FileSystemRights | FileSystemRights.Modify)) return true;
}
}
}
return false;
}
catch
{
return false;
}
}

Try this:
try
{
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl();
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(currentUser);
foreach (AuthorizationRule rule in rules)
{
FileSystemAccessRule fsAccessRule = rule as FileSystemAccessRule;
if (fsAccessRule == null)
continue;
if ((fsAccessRule.FileSystemRights & FileSystemRights.WriteData) > 0)
{
NTAccount ntAccount = rule.IdentityReference as NTAccount;
if (ntAccount == null)
{
continue;
}
if (principal.IsInRole(ntAccount.Value))
{
Console.WriteLine("Current user is in role of {0}, has write access", ntAccount.Value);
continue;
}
Console.WriteLine("Current user is not in role of {0}, does not have write access", ntAccount.Value);
}
}
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("does not have write access");
}

Above solutions are good but for me, I find this code simple and workable.
Just create a temporary file. If the file is created, its mean user has the write access.
public static bool HasWritePermission(string tempfilepath)
{
try
{
System.IO.File.Create(tempfilepath + "temp.txt").Close();
System.IO.File.Delete(tempfilepath + "temp.txt");
}
catch (System.UnauthorizedAccessException ex)
{
return false;
}
return true;
}

Your code gets the DirectorySecurity for a given directory, and handles an exception (due to your not having access to the security info) correctly. However, in your sample you don't actually interrogate the returned object to see what access is allowed - and I think you need to add this in.

Here is a modified version of CsabaS's answer, which accounts for explicit deny access rules. The function goes through all FileSystemAccessRules for a directory, and checks if the current user is in a role which has access to a directory. If no such roles are found or the user is in a role with denied access, the function returns false. To check read rights, pass FileSystemRights.Read to the function; for write rights, pass FileSystemRights.Write. If you want to check an arbitrary user's rights and not the current one's, substitute the currentUser WindowsIdentity for the desired WindowsIdentity. I would also advise against relying on functions like this to determine if the user can safely use the directory. This answer perfectly explains why.
public static bool UserHasDirectoryAccessRights(string path, FileSystemRights accessRights)
{
var isInRoleWithAccess = false;
try
{
var di = new DirectoryInfo(path);
var acl = di.GetAccessControl();
var rules = acl.GetAccessRules(true, true, typeof(NTAccount));
var currentUser = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(currentUser);
foreach (AuthorizationRule rule in rules)
{
var fsAccessRule = rule as FileSystemAccessRule;
if (fsAccessRule == null)
continue;
if ((fsAccessRule.FileSystemRights & accessRights) > 0)
{
var ntAccount = rule.IdentityReference as NTAccount;
if (ntAccount == null)
continue;
if (principal.IsInRole(ntAccount.Value))
{
if (fsAccessRule.AccessControlType == AccessControlType.Deny)
return false;
isInRoleWithAccess = true;
}
}
}
}
catch (UnauthorizedAccessException)
{
return false;
}
return isInRoleWithAccess;
}

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
}

You have a potential race condition in your code--what happens if the user has permissions to write to the folder when you check, but before the user actually writes to the folder this permission is withdrawn? The write will throw an exception which you will need to catch and handle. So the initial check is pointless. You might as well just do the write and handle any exceptions. This is the standard pattern for your situation.

http://www.codeproject.com/KB/files/UserFileAccessRights.aspx
Very usefull Class, check for improved version in messages bellow.

Simply trying to access the file in question isn't necessarily enough. The test will run with the permissions of the user running the program - Which isn't necessarily the user permissions you want to test against.

This should be all you need, as far as I can tell you only need to catch the one exception.
private static readonly byte[] TestArray = new byte[]{
69, 70, 71, 72
};
private static bool IsFolderAccessible(string path) {
var temp_path = Path.Combine(path, Path.GetRandomFileName());
try {
using (var file = File.Create(temp_path, TestArray.Length, FileOptions.DeleteOnClose)) {
file.Write(TestArray, 0, TestArray.Length);
}
return true;
} catch (UnauthorizedAccessException ex) {
Log.Warn($"Error accessing file {temp_path}", ex);
return false;
}
}

I agree with Ash, that should be fine. Alternatively you could use declarative CAS and actually prevent the program from running in the first place if they don't have access.
I believe some of the CAS features may not be present in C# 4.0 from what I've heard, not sure if that might be an issue or not.

I couldn't get GetAccessControl() to throw an exception on Windows 7 as recommended in the accepted answer.
I ended up using a variation of sdds's answer:
try
{
bool writeable = false;
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
DirectorySecurity security = Directory.GetAccessControl(pstrPath);
AuthorizationRuleCollection authRules = security.GetAccessRules(true, true, typeof(SecurityIdentifier));
foreach (FileSystemAccessRule accessRule in authRules)
{
if (principal.IsInRole(accessRule.IdentityReference as SecurityIdentifier))
{
if ((FileSystemRights.WriteData & accessRule.FileSystemRights) == FileSystemRights.WriteData)
{
if (accessRule.AccessControlType == AccessControlType.Allow)
{
writeable = true;
}
else if (accessRule.AccessControlType == AccessControlType.Deny)
{
//Deny usually overrides any Allow
return false;
}
}
}
}
return writeable;
}
catch (UnauthorizedAccessException)
{
return false;
}
Hope this helps.

I faced the same problem: how to verify if I can read/write in a particular directory. I ended up with the easy solution to...actually test it.
Here is my simple though effective solution.
class Program
{
/// <summary>
/// Tests if can read files and if any are present
/// </summary>
/// <param name="dirPath"></param>
/// <returns></returns>
private genericResponse check_canRead(string dirPath)
{
try
{
IEnumerable<string> files = Directory.EnumerateFiles(dirPath);
if (files.Count().Equals(0))
return new genericResponse() { status = true, idMsg = genericResponseType.NothingToRead };
return new genericResponse() { status = true, idMsg = genericResponseType.OK };
}
catch (DirectoryNotFoundException ex)
{
return new genericResponse() { status = false, idMsg = genericResponseType.ItemNotFound };
}
catch (UnauthorizedAccessException ex)
{
return new genericResponse() { status = false, idMsg = genericResponseType.CannotRead };
}
}
/// <summary>
/// Tests if can wirte both files or Directory
/// </summary>
/// <param name="dirPath"></param>
/// <returns></returns>
private genericResponse check_canWrite(string dirPath)
{
try
{
string testDir = "__TESTDIR__";
Directory.CreateDirectory(string.Join("/", dirPath, testDir));
Directory.Delete(string.Join("/", dirPath, testDir));
string testFile = "__TESTFILE__.txt";
try
{
TextWriter tw = new StreamWriter(string.Join("/", dirPath, testFile), false);
tw.WriteLine(testFile);
tw.Close();
File.Delete(string.Join("/", dirPath, testFile));
return new genericResponse() { status = true, idMsg = genericResponseType.OK };
}
catch (UnauthorizedAccessException ex)
{
return new genericResponse() { status = false, idMsg = genericResponseType.CannotWriteFile };
}
}
catch (UnauthorizedAccessException ex)
{
return new genericResponse() { status = false, idMsg = genericResponseType.CannotWriteDir };
}
}
}
public class genericResponse
{
public bool status { get; set; }
public genericResponseType idMsg { get; set; }
public string msg { get; set; }
}
public enum genericResponseType
{
NothingToRead = 1,
OK = 0,
CannotRead = -1,
CannotWriteDir = -2,
CannotWriteFile = -3,
ItemNotFound = -4
}
Hope it helps !

Most of the answers here does not check for write access. It just check if the user/group can 'Read Permission' (Read the ACE list of the file/directory).
Also iterating through ACE and checking if it matches the Security Identifier does not work because the user can be a member of a group from which he might get/lose privilege. Worse than that is nested groups.
I know this is an old thread but there is a better way for any one looking now.
Provided the user has Read Permission privilege is, one can use the Authz API to check Effective access.
https://learn.microsoft.com/en-us/windows/win32/secauthz/using-authz-api
https://learn.microsoft.com/en-us/windows/win32/secauthz/checking-access-with-authz-api

Related

Recusively find a file in whole system, return first folder

I'm trying to make a recursive function to return the first folder where the file is found.
I don't use GetFiles because the function stop when there is an exception.
However, it always stops at the 2nd folder and don't return anything cause the file is not found. What am I doing wrong ?
private static bool IsDirFound = false;
private static string SearchFile(string directory)
{
var fileFound = Directory.GetFiles(directory).FirstOrDefault(f => f.EndsWith("file.exe"));
if (fileFound != null)
{
IsDirFound = true;
return directory;
}
else if (!IsDirFound)
{
foreach (string subDir in Directory.GetDirectories(directory))
{
try
{
Console.WriteLine(subDir);
return SearchFile(subDir); // it always stop a the 2nd folder
}
catch
{
// swallow
}
}
}
return null;
}
Be careful with recursive methods, its easy to cause a a stackoverflow exception
Here is your problem
else if (!IsDirFound) // When found
{
...
}
return null; // you return false
However i tweaked this to be a bit more powerful, obviously you'll get better performance without the Func however it is more usable.. I'll leave these details up to you.
Example
private static string SearchFile(string directory, Func<string, bool> condition)
{
// get all files
var files = Directory.GetFiles(directory);
// check the file against what you want
if (files.Any(condition.Invoke))
return directory; // if its found return
// get all dirs
var dirs = Directory.GetDirectories(directory);
// check each one
foreach (var dir in dirs)
{
Console.WriteLine($"Searching {dir}");
try
{
// recurse
var result = SearchFile(dir, condition);
// if its found return
if (result != null)
return result;
}
catch (Exception ex)
{
// log
Console.WriteLine(ex.Message);
}
}
// nothing found here bail
return null;
}
Usage
var result = SearchFile(#"D:\", file => file.EndsWith("test.blah") );

Not all Code Path returns a value

I have a method which is checking for write access of a folder. But its giving me error saying not all code path returns a value?
public bool AccessPackerPlanTemplate(string folderPath)
{
try
{
string path = #"\\Sample";
string NtAccountName = #"Sample";
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (AuthorizationRule rule in rules)
{
//If we find one that matches the identity we are looking for
if (rule.IdentityReference.Value.Equals(NtAccountName, StringComparison.CurrentCultureIgnoreCase))
{
//Cast to a FileSystemAccessRule to check for access rights
if ((((FileSystemAccessRule)rule).FileSystemRights & FileSystemRights.WriteData) > 0)
{
//Show the link
}
}
}
}
catch (UnauthorizedAccessException)
{
return false;
}
}
what am I missing in this method ?
If an error isn't thrown, no boolean gets returned.
You need a return true/false at the end, after the try/catch.
NOT throwing an error is a possible "code path" which the compiler needs a return type for.
You're only ever returning a value when an exception is caught. That's why your compiler is telling you that.
I'll recommend this for your method, because you're not returning a boolean on all ways:
public bool AccessPackerPlanTemplate(string folderPath)
{
bool result = false;
try
{
string path = #"\\Sample";
string NtAccountName = #"Sample";
//... Your code
if(/*Your Condition*/)
{
result = true;
}
}
catch (UnauthorizedAccessException)
{
result = false;
}
return result;
}

Easiest way in C# to find out if an app is running from a network drive?

I want to programmatically find out if my application is running from a network drive. What is the simplest way of doing that? It should support both UNC paths (\\127.0.0.1\d$) and mapped network drives (Z:).
This is for mapped drive case. You can use the DriveInfo class to find out whether drive a is a network drive or not.
DriveInfo info = new DriveInfo("Z");
if (info.DriveType == DriveType.Network)
{
// Running from network
}
Complete method and Sample Code.
public static bool IsRunningFromNetwork(string rootPath)
{
try
{
System.IO.DriveInfo info = new DriveInfo(rootPath);
if (info.DriveType == DriveType.Network)
{
return true;
}
return false;
}
catch
{
try
{
Uri uri = new Uri(rootPath);
return uri.IsUnc;
}
catch
{
return false;
}
}
}
static void Main(string[] args)
{
Console.WriteLine(IsRunningFromNetwork(System.IO.Path.GetPathRoot(AppDomain.CurrentDomain.BaseDirectory))); }
if (new DriveInfo(Application.StartupPath).DriveType == DriveType.Network)
{
// here
}
This is my current method of doing this, but it feels like there should be a better way.
private bool IsRunningFromNetworkDrive()
{
var dir = AppDomain.CurrentDomain.BaseDirectory;
var driveLetter = dir.First();
if (!Char.IsLetter(driveLetter))
return true;
if (new DriveInfo(driveLetter.ToString()).DriveType == DriveType.Network)
return true;
return false;
}
In case using UNC path it is quitely simple - examine host name in UNC and test that it is localhost(127.0.0.1, ::1, hostname, hostname.domain.local, ip-addresses of workstation) or not.
If the path is not UNC - extract the drive letter from path and test the DriveInfo class for its type
I rearranged the solution of dotnetstep, which is in my opinion better because it avoids exceptions when a valid path is passed, and it throws an exception if there is a wrong path passed, which does not allow to make an assumption of true or false.
//----------------------------------------------------------------------------------------------------
/// <summary>Gets a boolean indicating whether the specified path is a local path or a network path.</summary>
/// <param name="path">Path to check</param>
/// <returns>Returns a boolean indicating whether the specified path is a local path or a network path.</returns>
public static Boolean IsNetworkPath(String path) {
Uri uri = new Uri(path);
if (uri.IsUnc) {
return true;
}
DriveInfo info = new DriveInfo(path);
if (info.DriveType == DriveType.Network) {
return true;
}
return false;
}
Test:
//----------------------------------------------------------------------------------------------------
/// <summary>A test for IsNetworkPath</summary>
[TestMethod()]
public void IsNetworkPathTest() {
String s1 = #"\\Test"; // unc
String s2 = #"C:\Program Files"; // local
String s3 = #"S:\"; // mapped
String s4 = "ljöasdf"; // invalid
Assert.IsTrue(RPath.IsNetworkPath(s1));
Assert.IsFalse(RPath.IsNetworkPath(s2));
Assert.IsTrue(RPath.IsNetworkPath(s3));
try {
RPath.IsNetworkPath(s4);
Assert.Fail();
}
catch {}
}
DriveInfo m = DriveInfo.GetDrives().Where(p => p.DriveType == DriveType.Network).FirstOrDefault();
if (m != null)
{
//do stuff
}
else
{
//do stuff
}

GetAccessControl error with NTAccount

private bool HasRights(FileSystemRights fileSystemRights_, string fileName_, bool isFile_)
{
bool hasRights = false;
WindowsIdentity WinIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
WindowsPrincipal WinPrincipal = new WindowsPrincipal(WinIdentity);
AuthorizationRuleCollection arc = null;
if (isFile_)
{
FileInfo fi = new FileInfo(#fileName_);
arc = fi.GetAccessControl().GetAccessRules(true, true, typeof(NTAccount));
}
else
{
DirectoryInfo di = new DirectoryInfo(#fileName_);
arc = di.GetAccessControl().GetAccessRules(true, true, typeof(NTAccount));
}
foreach (FileSystemAccessRule rule in arc)
{
if (WinPrincipal.IsInRole(rule.IdentityReference.Value))
{
if (((int)rule.FileSystemRights & (int)fileSystemRights_) > 0)
{
if (rule.AccessControlType == AccessControlType.Allow)
hasRights = true;
else if (rule.AccessControlType == AccessControlType.Deny)
{
hasRights = false;
break;
}
}
}
}
return hasRights;
}
The above code block is causing me problems. When the WinPrincipal.IsInRole(rule.IdentityReference.Value) is executed the following exception occurs:
"The trust relationship between the primary domain and the trusted domain failed.".
I'm very new to using identities, principles and such so I don't know what's the problem. I'm assuming it's with the use of NTAccount?
Thanks
I could try to address your question by suggesting you use a SecurityIdentifier, but there are many other issues here that would still be show-stoppers even if this were addressed. I'm not talking about inefficiencies such as using FileInfo instead of File, but the basic logic you're trying to use to interpret the DACL.
Take a look at: http://msdn.microsoft.com/en-us/library/cc230290(PROT.10).aspx

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;
}
}
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 :)
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.
The answers by Richard and Jason are sort of in the right direction. However what you should be doing is computing the effective permissions for the user identity running your code. None of the examples above correctly account for group membership for example.
I'm pretty sure Keith Brown had some code to do this in his wiki version (offline at this time) of The .NET Developers Guide to Windows Security. This is also discussed in reasonable detail in his Programming Windows Security book.
Computing effective permissions is not for the faint hearted and your code to attempt creating a file and catching the security exception thrown is probably the path of least resistance.
The accepted answer by Kev to this question doesn't actually give any code, it just points to other resources that I don't have access to. So here's my best attempt at the function. It actually checks that the permission it's looking at is a "Write" permission and that the current user belongs to the appropriate group.
It might not be complete with regard to network paths or whatever, but it's good enough for my purpose, checking local configuration files under "Program Files" for writability:
using System.Security.Principal;
using System.Security.AccessControl;
private static bool HasWritePermission(string FilePath)
{
try
{
FileSystemSecurity security;
if (File.Exists(FilePath))
{
security = File.GetAccessControl(FilePath);
}
else
{
security = Directory.GetAccessControl(Path.GetDirectoryName(FilePath));
}
var rules = security.GetAccessRules(true, true, typeof(NTAccount));
var currentuser = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool result = false;
foreach (FileSystemAccessRule rule in rules)
{
if (0 == (rule.FileSystemRights &
(FileSystemRights.WriteData | FileSystemRights.Write)))
{
continue;
}
if (rule.IdentityReference.Value.StartsWith("S-1-"))
{
var sid = new SecurityIdentifier(rule.IdentityReference.Value);
if (!currentuser.IsInRole(sid))
{
continue;
}
}
else
{
if (!currentuser.IsInRole(rule.IdentityReference.Value))
{
continue;
}
}
if (rule.AccessControlType == AccessControlType.Deny)
return false;
if (rule.AccessControlType == AccessControlType.Allow)
result = true;
}
return result;
}
catch
{
return false;
}
}
IMO, you need to work with such directories as usual, but instead of checking permissions before use, provide the correct way to handle UnauthorizedAccessException and react accordingly. This method is easier and much less error prone.
Try working with this C# snippet I just crafted:
using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string directory = #"C:\downloads";
DirectoryInfo di = new DirectoryInfo(directory);
DirectorySecurity ds = di.GetAccessControl();
foreach (AccessRule rule in ds.GetAccessRules(true, true, typeof(NTAccount)))
{
Console.WriteLine("Identity = {0}; Access = {1}",
rule.IdentityReference.Value, rule.AccessControlType);
}
}
}
}
And here's a reference you could also look at. My code might give you an idea as to how you could check for permissions before attempting to write to a directory.
according to this link:
http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/
it's easier to use existing class SecurityManager
string FileLocation = #"C:\test.txt";
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, FileLocation);
if (SecurityManager.IsGranted(writePermission))
{
// you have permission
}
else
{
// permission is required!
}
but it seems it's been obsoleted, it is suggested to use PermissionSet instead.
[Obsolete("IsGranted is obsolete and will be removed in a future release of the .NET Framework. Please use the PermissionSet property of either AppDomain or Assembly instead.")]
Since the static method 'GetAccessControl' seems to be missing from the present version of .Net core/Standard I had to modify #Bryce Wagner's answer (I went ahead and used more modern syntax):
public static class PermissionHelper
{
public static bool? CurrentUserHasWritePermission(string filePath)
=> new WindowsPrincipal(WindowsIdentity.GetCurrent())
.SelectWritePermissions(filePath)
.FirstOrDefault();
private static IEnumerable<bool?> SelectWritePermissions(this WindowsPrincipal user, string filePath)
=> from rule in filePath
.GetFileSystemSecurity()
.GetAccessRules(true, true, typeof(NTAccount))
.Cast<FileSystemAccessRule>()
let right = user.HasRightSafe(rule)
where right.HasValue
// Deny takes precedence over allow
orderby right.Value == false descending
select right;
private static bool? HasRightSafe(this WindowsPrincipal user, FileSystemAccessRule rule)
{
try
{
return user.HasRight(rule);
}
catch
{
return null;
}
}
private static bool? HasRight(this WindowsPrincipal user,FileSystemAccessRule rule )
=> rule switch
{
{ FileSystemRights: FileSystemRights fileSystemRights } when (fileSystemRights &
(FileSystemRights.WriteData | FileSystemRights.Write)) == 0 => null,
{ IdentityReference: { Value: string value } } when value.StartsWith("S-1-") &&
!user.IsInRole(new SecurityIdentifier(rule.IdentityReference.Value)) => null,
{ IdentityReference: { Value: string value } } when value.StartsWith("S-1-") == false &&
!user.IsInRole(rule.IdentityReference.Value) => null,
{ AccessControlType: AccessControlType.Deny } => false,
{ AccessControlType: AccessControlType.Allow } => true,
_ => null
};
private static FileSystemSecurity GetFileSystemSecurity(this string filePath)
=> new FileInfo(filePath) switch
{
{ Exists: true } fileInfo => fileInfo.GetAccessControl(),
{ Exists: false } fileInfo => (FileSystemSecurity)fileInfo.Directory.GetAccessControl(),
_ => throw new Exception($"Check the file path, {filePath}: something's wrong with it.")
};
}
private static void GrantAccess(string file)
{
bool exists = System.IO.Directory.Exists(file);
if (!exists)
{
DirectoryInfo di = System.IO.Directory.CreateDirectory(file);
Console.WriteLine("The Folder is created Sucessfully");
}
else
{
Console.WriteLine("The Folder already exists");
}
DirectoryInfo dInfo = new DirectoryInfo(file);
DirectorySecurity dSecurity = dInfo.GetAccessControl();
dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
dInfo.SetAccessControl(dSecurity);
}

Categories