C# Проверка, имеет ли пользователь право на запись в папку
Мне нужно проверить, может ли пользователь писать в папку, прежде чем пытаться это сделать.
Я реализовал следующий метод (в C# 2.0), который пытается получить разрешения безопасности для папки с помощью метода Directory.GetAccessControl().
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;
}
}
Когда я гуглял, как проверить доступ на запись, ничего подобного не возникало, и на самом деле было очень сложно проверить права доступа в Windows. Я обеспокоен тем, что я чрезмерно упрощаю вещи и что этот метод не является надежным, хотя, похоже, он работает.
Будет ли мой метод проверки правильности работы текущего пользователя с правами на запись?
21 ответ
Это совершенно правильный способ проверить доступ к папке в C#. Единственное место, где он может упасть, - это если вам нужно вызвать это в узком цикле, где могут возникнуть проблемы с издержками исключения.
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;
}
}
Я ценю, что для этого поста немного поздно, но этот фрагмент кода может оказаться полезным.
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();
Перетащите это в консольное приложение и посмотрите, делает ли оно то, что вам нужно.
Я пробовал большинство из них, но они дают ложные срабатывания, все по той же причине. Недостаточно проверить каталог на доступное разрешение, вы должны проверить, что вошедший в систему пользователь является членом группы, которая имеет это разрешение. Для этого вы получаете удостоверение пользователя и проверяете, является ли он членом группы, которая содержит FileSystemAccessRule IdentityReference. Я проверил это, работает без нареканий..
/// <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;
}
Например, для всех пользователей (Builtin\Users) этот метод работает отлично - наслаждайтесь.
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;
}
}
ИМХО, единственный надежный способ проверить, можете ли вы записать в каталог, - это написать в нем и в конечном итоге перехватить исключения.
Вышеуказанные решения хороши, но для меня этот код прост и выполним. Просто создайте временный файл. Если файл создан, его средний пользователь имеет право на запись.
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;
}
Попробуй это:
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");
}
Ваш код получает DirectorySecurity
для данного каталога и обрабатывает исключение (из-за отсутствия доступа к информации о безопасности) правильно. Однако в вашем примере вы на самом деле не запрашиваете возвращенный объект, чтобы увидеть, какой доступ разрешен - и я думаю, что вам нужно добавить это в.
Вот модифицированная версия ответа CsabaS, которая учитывает явные правила запрета доступа. Функция просматривает все FileSystemAccessRules для каталога и проверяет, находится ли текущий пользователь в роли, которая имеет доступ к каталогу. Если такие роли не найдены или пользователь находится в роли с запрещенным доступом, функция возвращает false. Чтобы проверить права на чтение, передайте FileSystemRights.Read в функцию; для прав записи передайте FileSystemRights.Write. Если вы хотите проверить произвольные права пользователя, а не права текущего, замените currentUser WindowsIdentity на желаемый WindowsIdentity. Я также не рекомендую полагаться на такие функции, чтобы определить, может ли пользователь безопасно использовать каталог. Этот ответ прекрасно объясняет почему.
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;
}
Я использовал ту же функцию для проверки, если файл имеет WriteAccess:
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;
}
}
Вы можете попробовать следующий блок кода, чтобы проверить, имеет ли каталог доступ для записи. Он проверяет 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
}
В вашем коде есть потенциальное состояние гонки - что произойдет, если у пользователя будут права на запись в папку при проверке, но до того, как пользователь действительно запишет в папку, это разрешение будет отозвано? При записи выдается исключение, которое вам нужно будет перехватить и обработать. Так что первоначальная проверка бессмысленна. Вы могли бы также просто написать и обработать любые исключения. Это стандартный шаблон для вашей ситуации.
http://www.codeproject.com/KB/files/UserFileAccessRights.aspx
Очень полезный класс, проверьте улучшенную версию в сообщениях ниже.
Простая попытка доступа к рассматриваемому файлу не обязательно достаточна. Тест будет выполняться с разрешениями пользователя, запустившего программу. Это не обязательно те права пользователя, с которыми вы хотите протестировать.
Это должно быть все, что вам нужно, насколько я могу судить, вам нужно поймать только одно исключение.
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;
}
}
Я столкнулся с той же проблемой: как проверить, могу ли я читать / писать в определенном каталоге. Я получил простое решение... на самом деле проверить его. Вот мое простое, но эффективное решение.
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
}
Надеюсь, поможет!
Бла Бла Бла, просто скопируйте и вставьте этот метод пустоты:
используя Систему;
используя System.IO;
static bool HasWriteAccess(string Path,bool MsgBox=true){string ExMSG="This Program Doesn't Have Permissions To Access/Change The Following ";if(Directory.Exists(Path)){ExMSG+="Folder: "+Path;try{File.WriteAllText(Path+"\\TempFile",null);File.Delete(Path+"\\TempFile");return true;}catch{if(MsgBox){System.Windows.MessageBox.Show(ExMSG);}return false;}}if(File.Exists(Path)){ExMSG+="File: "+Path;try{File.OpenRead(Path);return true;}catch{if(MsgBox){System.Windows.MessageBox.Show(ExMSG);}return false;}}if(MsgBox){System.Windows.MessageBox.Show("The File/Folder: "+Path+" - Doesn't Exist!");}return false;}
Я согласен с Эшем, это должно быть хорошо. В качестве альтернативы вы можете использовать декларативный CAS и фактически запретить запуск программы, если у них нет доступа.
Я считаю, что некоторые функции CAS могут отсутствовать в C# 4.0 из того, что я слышал, не уверен, что это может быть проблемой или нет.
Я не мог заставить GetAccessControl() выдать исключение в Windows 7, как рекомендовано в принятом ответе.
В итоге я использовал вариант ответа sdds:
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;
}
Надеюсь это поможет.
Большинство ответов здесь не проверяет доступ для записи. Он просто проверяет, может ли пользователь / группа "читать разрешение" (читать список ACE файла / каталога).
Также итерация через ACE и проверка, соответствует ли он идентификатору безопасности, не работает, потому что пользователь может быть членом группы, из которой он может получить / потерять привилегии. Хуже того - вложенные группы.
Я знаю, что это старая ветка, но сейчас есть способ получше.
При условии, что у пользователя есть право на чтение, можно использовать API Authz для проверки эффективного доступа.
https://docs.microsoft.com/en-us/windows/win32/secauthz/using-authz-api
https://docs.microsoft.com/en-us/windows/win32/secauthz/checking-access-with-authz-api