可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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.
回答3:
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;
}
}
回答4:
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;
}
回答5:
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;
}
}
回答6:
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.
回答7:
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");
}
回答8:
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.
回答9:
I used the same function for check if file hasWriteAccess:
private static bool HasWriteAccessToFile(string filePath)
{
try
{
// Attempt to get a list of security permissions from the file.
// This will raise an exception if the path is read only or do not have access to view the permissions.
File.GetAccessControl(filePath);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}
回答10:
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;
}
回答11:
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
}
回答12:
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.
回答13:
http://www.codeproject.com/KB/files/UserFileAccessRights.aspx
Very usefull Class, check for improved version in messages bellow.
回答14:
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;
}
回答15:
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.
回答16:
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.
回答17:
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.
回答18:
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 !