web-dev-qa-db-ja.com

.NETでディレクトリとファイルの書き込み権限を確認する

.NET 2.0アプリケーションでは、ファイルを作成してディレクトリに書き込むための十分なアクセス許可が存在するかどうかを確認する必要があります。この目的のために、ファイルを作成して1バイトを書き込み、アクセス許可が存在することをテストするためにその後削除する、次の関数を使用します。

確認する最良の方法は、実際にそれを試して実行し、発生した例外をキャッチすることであると考えました。ただし、一般的な例外キャッチについてはあまり満足していませんが、これを行うためのより良いまたはおそらく受け入れられた方法はありますか?

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;
        }
    }
75
Andy

RichardJason による答えは、正しい方向に向かっています。ただし、実行する必要があるのは、コードを実行するユーザーIDに対して 有効なアクセス許可を計算する です。上記の例はいずれも、たとえばグループメンバーシップを正しく説明していません。

Keith Brown には、これを行うためのコードがありました wikiバージョン (現時点ではオフライン) Windowsセキュリティの.NET開発者ガイド 。これについては、彼の Programming Windows Security 本でかなり詳しく説明されています。

効果的なアクセス許可を計算するのは気弱な人向けではなく、コードを作成してファイルを作成し、スローされたセキュリティ例外をキャッチすることは、おそらく最も抵抗の少ない方法です。

21
Kev

Directory.GetAccessControl(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は、「フラグ」と呼ばれるものを使用しています。これが何かわからない場合は、実際に読んでください:)

46
richardwiden

DenyAllowよりも優先されます。ローカルルールは、継承されたルールよりも優先されます。私は多くのソリューションを見ました(ここに示すいくつかの回答を含む)が、ルールが継承であるかどうかを考慮していません。したがって、ルールの継承を考慮した次のアプローチをお勧めします(クラスにきちんとラップされます)。

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;
    }
}

ただし、リモートコンピューターでファイルアクセス権を照会する権利が常にあるとは限らないため、これが常にリモートコンピューターで機能するとは限らないという経験をしました。その場合の解決策は試すことです。 「実際の」ファイルを操作する前にアクセス権を知る必要がある場合は、一時ファイルを作成しようとするだけでも可能です。

この質問に対する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;
    }
}
18
Bryce Wagner

IMOでは、このようなディレクトリを通常どおり操作する必要がありますが、使用前に権限を確認する代わりに、UnauthorizedAccessExceptionを処理し、それに応じて対応する正しい方法を提供します。この方法は簡単で、エラーが発生しにくくなります。

5
arbiter

作成したばかりのこの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);
            }
        }
    }
}

そして、 こちら 参照することもできます。私のコードは、ディレクトリへの書き込みを試みる前にアクセス許可を確認する方法についてのアイデアを与えるかもしれません。

3
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.")]
0
Mulder2008