私の.NET 2.0アプリケーションでは、ファイルを作成してディレクトリに書き込むための十分な権限があるかどうかを確認する必要があります。この目的のために、私はファイルを作成しそれにシングルバイトを書き込もうとする次の関数を持っています。その後、自分自身を削除してパーミッションが存在することをテストします。
私がチェックする最良の方法は、実際に試してみて、発生した例外をすべて検出することでした。私は一般的な例外のキャッチについては特にうんざりしていません、それでこれを行うより良いまたはおそらくより受け入れられた方法がありますか?
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;
}
}
による答えリチャードそしてジェイソン正しい方向にソートされています。しかしあなたがすべきことは有効な権限を計算するコードを実行しているユーザーIDのため。上の例のどれも、たとえばグループメンバーシップを正しく説明していません。
私はかなり確信していますキースブラウン彼の中にこれを行うためのいくつかのコードがありましたウィキ版(現時点ではオフライン)Windowsセキュリティに関する.NET開発者ガイド。これはまた彼の中で合理的な詳細で議論されますWindowsセキュリティのプログラミング本。
効果的なパーミッションを計算することは気が遠くなるためのものではなく、ファイルを作成してスローされたセキュリティ例外をキャッチしようとするあなたのコードはおそらく最も抵抗の少ないパスです。
Directory.GetAcessControl(path)
あなたが求めていることをします。
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
"Flags"という名前のものを使用しています。それが何であるかわからない場合は、実際にお読みください:)
Deny
優先するAllow
。ローカルルールは継承ルールよりも優先されます。私は多くの解決策を見てきました(ここに示されたいくつかの答えを含む)、しかしそれらのどれもがルールがそうであるかどうかを考慮に入れません継承されましたか否か。そのため、ルールの継承を考慮した(クラスにきちんとラップされた)次のようなアプローチを提案します。
public class CurrentUserSecurity
{
WindowsIdentity _currentUser;
WindowsPrincipal _currentPrincipal;
public CurrentUserSecurity()
{
_currentUser = WindowsIdentity.GetCurrent();
_currentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
}
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;
}
}
ただし、リモートコンピュータではファイルアクセス権を常に照会する権利があるとは限らないため、これは必ずしもリモートコンピュータでは機能しないという経験をしました。その場合の解決策は試してみることです。 「本当の」ファイルを扱う前にアクセス権を知る必要がある場合は、一時ファイルを作成しようとしただけでも可能です。
この質問に対してKevが受け入れた答えは、実際にはコードを何も与えていない、それは私がアクセスできない他のリソースを指し示しているだけです。それで、これが私の機能に対する私の最善の試みです。実際には、探している権限が「書き込み」権限であり、現在のユーザーが適切なグループに属していることを確認します。
ネットワークパスなどに関しては完全ではないかもしれませんが、私の目的には十分です。「Program Files」の下のローカル設定ファイルの書き込み可能性をチェックします。
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;
}
}
BUILTIN\Administrators
。私が地元の駅の管理者になっていると、スニペットが誤って戻ってきましたtrue
。 - Ilia Barahovski
IMOでは、いつものようにこのようなディレクトリを操作する必要がありますが、使用前に権限を確認する代わりに、UnauthorizedAccessExceptionを処理して適切に対応する正しい方法を提供してください。この方法は簡単で、エラーが少なくなります。
作成したばかりのこのC#スニペットを使ってみてください。
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);
}
}
}
}
そしてここにあなたがまた見ることができる参照。私のコードでは、ディレクトリに書き込もうとする前に、どのようにして許可を確認することができるかについてのアイデアを得ることができます。
typeof
この場合、オブジェクトの型を返しますNTAccount
。docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/…への呼び出しGetAccessRules()
呼び出されたときにアカウントの種類が必要です。msdn.microsoft.com/en-us/library/… - Jason EvansNTAccount
?いつも使うNTAアカウント? - KiquenetNTAccount
はWindows PC上のユーザーアカウントを表します。そのため、上記のコードではそれが必要です。 - Jason Evans
このリンクによると:http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/
既存のクラスSecurityManagerを使用する方が簡単です
string FileLocation = @"C:\test.txt";
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, FileLocation);
if (SecurityManager.IsGranted(writePermission))
{
// you have permission
}
else
{
// permission is required!
}
しかし、時代遅れになっているようです。代わりにPermissionSetを使用することをお勧めします。
[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.")]
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);
}
string fullPath = directory + TEMP_FILE;
fullPathを取得するために文字列を連結する代わりにPath.Combineメソッドを使用してください。Path.Combine(directory, TEMP_FILE)
- nomail