Как вы проверяете разрешения на запись в каталог или файл?
Я получил программу, которая записывает некоторые данные в файл с помощью метода, подобного приведенному ниже.
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);
}
}
При запуске программы я получаю сообщение об ошибке:
Необработанное исключение: System.UnauthorizedAccessException: доступ к пути "mypath" запрещен. в System.IO.__Error.WinIOError(Int32 errorCode, String MaybeFullPath) в System.IO.FileStream.Init (Строковый путь, режим FileMode, доступ FileAccess, права nt32, логические значения useRights, общий ресурс FileShare, буфер32Size Int32, параметры FileOptions, SECURITY_ATTRIBUTES sections, String msgPath, Boolean bFromProxy) в System.IO.FileStream..ctor(путь строки, режим FileMode, доступ к FileAccess через FileShare, Int32 bufferSize, параметры FileOptions, строка msgPath, Boolea bFromProxy)
Вопрос: Какой код мне нужен, чтобы поймать это и как мне предоставить доступ?
8 ответов
ОБНОВИТЬ:
Модифицированный код основан на этом ответе, чтобы избавиться от устаревших методов.
Вы можете использовать пространство имен Security для проверки этого:
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
}
}
Что касается получения такого разрешения, вам придется попросить пользователя сделать это для вас. Если бы вы могли программно сделать это, то у всех нас были бы проблемы;)
Когда ваш код делает следующее:
- Проверяет, есть ли у текущего пользователя разрешение на что-либо.
- Выполняет действие, для которого требуются права, отмеченные в 1.
Вы рискуете изменить разрешения между 1 и 2, потому что вы не можете предсказать, что еще произойдет в системе во время выполнения. Следовательно, ваш код должен обрабатывать ситуацию, когда генерируется исключение UnauthorisedAccessException, даже если вы ранее проверили разрешения.
Обратите внимание, что класс SecurityManager используется для проверки разрешений CAS и фактически не проверяет с ОС, имеет ли текущий пользователь доступ на запись в указанное местоположение (через ACL и ACE). Таким образом, IsGranted всегда будет возвращать true для локально работающих приложений.
Пример (полученный из примера Джоша):
//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.
}
Это сложно и не рекомендуется пытаться программно рассчитать эффективные разрешения для папки на основе необработанных ACL-списков (которые доступны через классы System.Security.AccessControl). Другие ответы о переполнении стека и более широкой сети рекомендуют попытаться выполнить действие, чтобы узнать, разрешено ли разрешение. В этом посте подводятся итоги того, что требуется для реализации расчета разрешений, и этого должно быть достаточно, чтобы вы не смогли этого сделать.
Это исправленная версия 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;
}
Извините, но ни одно из предыдущих решений не помогло мне. Мне нужно проверить обе стороны: SecurityManager и SO разрешения. Я многому научился с помощью кода Джоша и с ответом iain, но я боюсь, что мне нужно использовать код Ракеша (также благодаря ему). Только одна ошибка: я обнаружил, что он проверяет только разрешения "Разрешить", а не разрешения "Запретить". Итак, мое предложение:
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!
Поскольку это не закрыто, я хотел бы представить новую запись для всех, кто хочет, чтобы что-то работало для них должным образом... используя объединение того, что я нашел здесь, а также используя DirectoryServices для отладки самого кода и поиска правильный код для использования, вот что я нашел, что работает для меня в любой ситуации... обратите внимание, что мое решение расширяет объект DirectoryInfo...:
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;
}
Ничто из этого не сработало для меня... они возвращаются как правда, даже если это не так. Проблема в том, что вы должны проверить доступное разрешение на соответствие текущим правам пользователя процесса, это тестирует права на создание файла, просто измените предложение FileSystemRights на "Запись", чтобы проверить доступ на запись.
/// <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;
}
Вы можете попробовать следующий блок кода, чтобы проверить, имеет ли каталог доступ для записи.
Он проверяет 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
}
Ух ты... в этой теме много низкоуровневого кода безопасности - большая часть которого у меня тоже не сработала - хотя я многому научился в процессе. Одна вещь, которую я узнал, состоит в том, что большая часть этого кода не предназначена для приложений, ищущих права доступа для каждого пользователя, - для администраторов, желающих программно изменять права, что, как было отмечено, не очень хорошая вещь. Как разработчик, я не могу использовать "легкий выход" - работая от имени администратора, - который - я не один на машине, на которой выполняется код, и не мои пользователи, - настолько же, насколько умны эти решения. - они не для моей ситуации, и, вероятно, не для большинства рядовых разработчиков.
Как и большинство постеров такого типа, - я изначально чувствовал, что это тоже "хаккей", - с тех пор я решил, что это вполне нормально, попробовать и позволить возможному исключению точно сказать, каковы права пользователя - потому что информация, которую я получил, не говорила мне, что на самом деле были права. Код ниже - сделал.
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
Конечная функция