74

私の.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;
        }
    }


  • コードのおかげで、1つだけですが、呼び出し側は、書き込みができても削除ができないと書き込み許可がないという誤った印象を受ける可能性があります。私はこれをFileMode.Createを使うように変更してファイルの削除を取り除きます。明らかにあなたはもうこのコードを必要としないでしょう、しかし私は将来の読者の利益のためにこれを書きます。 - n00b
  • string fullPath = directory + TEMP_FILE;fullPathを取得するために文字列を連結する代わりにPath.Combineメソッドを使用してください。Path.Combine(directory, TEMP_FILE) - nomail
  • 誰かがパンチしてから次の日にパンチアウトするとどうなりますか。 2日後にパンチインしてからパンチアウトするとどうなりますか?私は人々がそれらのことをすることになっていないと確信していますが、行動は定義されるべきです。 - Scott Hannen

8 답변


22

による答えリチャードそしてジェイソン正しい方向にソートされています。しかしあなたがすべきことは有効な権限を計算するコードを実行しているユーザーIDのため。上の例のどれも、たとえばグループメンバーシップを正しく説明していません。

私はかなり確信していますキースブラウン彼の中にこれを行うためのいくつかのコードがありましたウィキ版(現時点ではオフライン)Windowsセキュリティに関する.NET開発者ガイド。これはまた彼の中で合理的な詳細で議論されますWindowsセキュリティのプログラミング本。

効果的なパーミッションを計算することは気が遠くなるためのものではなく、ファイルを作成してスローされたセキュリティ例外をキャッチしようとするあなたのコードはおそらく最も抵抗の少ないパスです。


  • そうでなければ、誰かがチェックをしてから実際に保存を試みるまでの間に許可を変更することができるので、これは唯一の信頼できる方法です(ありそうもないが可能です)。 - Chris Chilvers
  • これをありがとう。だから私のコードにするべき唯一の変更は一般的な&#39;例外&#39;の代わりにセキュリティ例外をキャッチすることです。 - Andy
  • @Andy - はい、有効な権限を計算するためのコードを作成したいのでない限り、これは最も抵抗の少ないパスです。 - Kev
  • なぜ、すべてをとても複雑にしなければならないのですか。 - Vidar
  • @Triynko - 私が引用した記事を読むことをお勧めします。groups.google.com/group/… - コンピューティング効果的許可は、それほど単純ではありません。私のゲストであると私は間違っていることを証明するための答えを提供します。 - Kev

46

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"という名前のものを使用しています。それが何であるかわからない場合は、実際にお読みください:)


  • もちろん、実際にディレクトリにアクセスできない場合は、例外が発生します。 - blowdart
  • 何をチェックしますか?そのディレクトリには書き込み権限がありますが、どのユーザーに対してですか。 :) - Ivan G.
  • 現在のユーザーが書き込みアクセス権を持っているかどうかを確認したいだけの場合に機能します。 - Donny V.
  • @aloneguid:&quot; GetAccessRules&quot; methodsはAuthorizationRuleCollectionを返します。 AthorizationRuleクラスにはIdentityReferenceプロパティがあり、その実行時型は実際にはIdenityReference型(NTAccountまたはSecurity)から派生する2つのうちの1つになります。これは、GetAccessRulesへの呼び出しで指定されます。 IdentityReferenceインスタンス(またはその派生型)を通じて、どのユーザーにルールが適用されるのかを知ることができます。 SIDまたはNTAアカウント名の形式になります。 - Triynko
  • システムディスクのadmin 7以外のアプリケーションでWindows 7を実行してみてください。trueが返されますが、c:\に書き込もうとすると、アクセスできないという例外が表示されます。 - Peter

30

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

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


  • 試してみただけでうまくいきます!ありがとうございました!あなたは私の日を作りました! - GiveEmTheBoot
  • 私はこの答えがそれを達成するための最良の方法だと思います、他の答えも結果を得るために同じ方法を使いますが、この答えだけが継承されたルールとローカルルールを計算するのでそれが私が推測する最も正確なものです。ありがとう、そしておめでとうございます。 - Tolga Evcimen

18

この質問に対して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;
    }
}


  • これはグループではなく、私の場合だけ文字通り追加されたアカウント名に対しては機能しません。 - Random
  • これは「(S-1-5-21-397955417-626881126-188441444-512)」と関係があります。タイプフォーマット?文字列をそのようなSecurityIdentifierに変換して問題は解決しましたか?それがあなたのために今働くかどうかはあなたのコメントからは明らかではありません。 - Bryce Wagner
  • &quot; rule.IdentityReference.Value&quot;を入れると、 currentuser.IsInRole()のパラメータとして、通常の&quot; domain \ user&quot;で一致させようとするIsInRole(string)メソッドを使用します。値。そのため、ユーザー名の文字列ではなくSIDの文字列をプッシュしています。ただし、その前に私の行を使用すると、指定されたSIDのユーザーに一致するSecurityIdentifierオブジェクトが表示されます。その「文字列」引数オーバーロードは、開発者にとっては小さな罠です。これもまた、SID文字列表現ではなく、人間が書き換え可能な形式のアカウント名またはグループ名を受け入れます。 - Random
  • 問題は、「new SecurityIdentifier(SDDLFormat)」ということです。通常のグループ名では機能しません(引数の例外が発生します)。そのため、SDDL形式かどうかの確認を追加しました。 - Bryce Wagner
  • この解決策は私にはうまくいきましたが、ネットワークフォルダに関して1つの問題がありました。フォルダに書き込みを許可するアクセスルールがありますBUILTIN\Administrators。私が地元の駅の管理者になっていると、スニペットが誤って戻ってきましたtrue。 - Ilia Barahovski

5

IMOでは、いつものようにこのようなディレクトリを操作する必要がありますが、使用前に権限を確認する代わりに、UnauthorizedAccessExceptionを処理して適切に対応する正しい方法を提供してください。この方法は簡単で、エラーが少なくなります。


  • あなたはおそらく「&#39;この方法はより簡単で、はるかに言うことを意図していました」もっと少なくエラーが発生しやすい。 - cjbarth

3

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

そしてここにあなたがまた見ることができる参照。私のコードでは、ディレクトリに書き込もうとする前に、どのようにして許可を確認することができるかについてのアイデアを得ることができます。



0

このリンクによると: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.")]


-1

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

        }


  • 何ですかWellKnownSidType.WorldSid? - Kiquenet

リンクされた質問


関連する質問

最近の質問