C#ユーザーがフォルダーへの書き込みアクセス権を持っているかどうかをテストする


187

ユーザーがフォルダーに書き込むことができるかどうかを実際に試す前にテストする必要があります。

Directory.GetAccessControl()メソッドを使用してフォルダーのセキュリティ権限を取得しようとする次のメソッド(C#2.0)を実装しました。

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でアクセス許可をテストすることは非常に複雑に見えました。私は物事を単純化しすぎており、この方法は機能しているように見えますが、堅牢ではないことを懸念しています。

現在のユーザーが書き込みアクセス権を持っているかどうかをテストする方法は正しく機能しますか?


13
アクセス許可を表示するアクセス権がないのは、書き込みを許可されていないのと同じですか?
deed02392 2013

回答:


60

これは、C#でフォルダーアクセスを確認するための完全に有効な方法です。例外発生する可能性がある唯一の場所は、例外のオーバーヘッドが問題となる可能性があるタイトなループでこれを呼び出す必要がある場合です。

以前に尋ねられた他の同様の 質問がありました。


1
おかしなことに、他の質問の1つを別のタブで開いていましたが、DirectorySecurityについての回答がまだありませんでした。承認された回答だけでなく、すべての回答を読むように教えてください;-)
Chris B

Windowsで長いパスを使用する場合も落ちませんか?
Alexandru

11
書き込み権限があるかどうかはわかりませんが、そのフォルダの権限を検索できるかどうかだけがわかります。また、書き込みはできるが、権限を検索できない場合もあります。
RandomEngy

65

これがこの投稿の1日の終わりに少し遅くなったことを感謝しますが、このコードのコードが役立つ場合があります。

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

それをコンソールアプリにドロップして、必要な機能を果たしているかどうかを確認します。


目標どおりに!とても助かります!
smwikipedia

の呼び出しで例外が発生しましたGetAccessControlが、私のソフトウェアは実際に、私が見ているディレクトリに書き込むことができます。
ジョンケージ

@JonCage-どのような例外が発生していますか?皮肉にも、最初に頭に浮かぶのはセキュリティの問題です。アプリが実行されているアカウントに、ACL情報を取得する権限がありますか?
ダンカンハウ

1
FileSystemAccessRuleタイプのチェックを追加する必要があります。拒否ルールの場合、書き込み可能であると誤って報告されます。
tdemay

2
これを使おうとしています。別の問題が見つかりました。権限が特定のユーザーではなくグループにのみ割り当てられている場合、書き込みアクセス権がないと誤って報告されます。例えば、書き込みアクセスは、「認証済みユーザー」に付与された
tdemay

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

7
この回答は、権限違反だけでなく、ファイルを書き込もうとしたときに発生する可能性のあるすべての例外をキャッチします。
Matt Ellen

7
@GY、、string tempFileName = Path.GetRandomFileName();明らかに
Alexey Khoroshikh

3
@マット、これは、失敗の理由に関係なく、「ディレクトリは書き込み可能ですか」という質問に正確に答えます。「ディレクトリに書き込めない理由」に答えてください。:)
アレクセイホロシフ2014

1
このコードで誤検知が発生します。実行中のユーザーにそのフォルダーへの書き込み権限がない場合でも、File.Create()は正常に実行されます(最後のオプションを変更すると一時ファイルが残ります)。本当に奇妙です-なぜか理解するために1時間を費やしましたが、私は困惑しています。
NickG 2015

4
以下で試したすべての代替案(および参照リンク)から-これは確実に機能する唯一のものです。
TarmoPikaro 2015

23

私はこれらのほとんどを試しましたが、すべて同じ理由で誤検知を与えます。利用可能な権限についてディレクトリをテストするだけでは不十分です。ログインしたユーザーがそれを持っているグループのメンバーであることを確認する必要があります許可。これを行うには、ユーザーIDを取得し、それが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;
    }

Johnに感謝します。コードを使用して、ユーザーグループを再度確認するまで、誤検出が発生しました。ルールLocateReference!
ポールL

1
Identity.Owner == rule.IdentityReferenceに追加のチェックを追加する必要がありました。サービスを提供するローカルアカウントのように、アクセス権を与えたがグループには入れなかったユーザーがいるため
grinder22

1
AccessControlTypeの拒否は許可よりも優先されるため、アクセス権を拒否する完全なルールも確認する必要があります。拒否タイプを確認する場合は(AccessRight & rule.FileSystemRights) > 0、サブアクセスタイプが拒否されているため、完全でAccessRightはないことを意味します。アクセスAccessRight
TJロックフェラー

上記のgrinder22のように、変更する必要がありました。if(identity.Groups.Contains(rule.IdentityReference))からif(identity.Groups.Contains(rule.IdentityReference)|| identity.Owner.Equals(rule.IdentityReference))へのアクセス権を持つユーザーがいるため、 tのいずれかのグループ。
ehambright

13

たとえば、すべてのユーザー(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;
    }
}

12

ディレクトリに書き込むことができるかどうかをテストするための唯一の100%信頼できる方法は、実際にディレクトリに書き込み、最終的に例外をキャッチすることです。


8

これを試して:

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

私が間違っていなければ、これは近いですが、そこにはまったくありません-それはありfsAccessRule.AccessControlType得る事実を見落としていますAccessControlType.Deny
ジョナサンギルバート

これは私のWin7開発マシンでは機能しましたが、Win10では失敗しました(テスターと自分のテストマシンの両方)。ssdsの変更(以下を参照)はそれを修正するようです。
2017年

6

コードはDirectorySecurity指定されたディレクトリのを取得し、(セキュリティ情報にアクセスできないため)例外を正しく処理します。ただし、サンプルでは、​​返されたオブジェクトに実際に問い合わせて、どのアクセスが許可されているかを確認することはありません。これを追加する必要があると思います。


+1-GetAccessControlを呼び出すときに例外がスローされないというこの問題に遭遇しましたが、同じディレクトリに書き込もうとすると不正な例外が発生します。
Mayo

6

これはCsabaSの回答の修正版であり、明示的な拒否アクセスルールを考慮しています。この関数は、ディレクトリのすべてのFileSystemAccessRulesを調べ、現在のユーザーがディレクトリにアクセスできるロールに属しているかどうかを確認します。そのようなロールが見つからない場合、またはユーザーがアクセスを拒否されたロールにある場合、関数はfalseを返します。読み取り権限を確認するには、FileSystemRights.Readを関数に渡します。書き込み権については、FileSystemRights.Writeを渡します。現在の権限ではなく任意のユーザーの権限を確認する場合は、目的のWindowsIdentityをcurrentUser 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;
    }

Windows 10ではCsabaのコードが失敗しました(ただし、Win7開発マシンでは問題ありません)。上記は問題を修正するようです。
2017年

4

上記の解決策は良いですが、私にとっては、このコードはシンプルで実用的です。一時ファイルを作成するだけです。ファイルが作成された場合、その平均ユーザーは書き込みアクセス権を持っています。

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

3
いいね!ただし、ユーザーにはCreateアクセス許可があるかもしれませんがDelete、ユーザーに書き込みアクセス許可がある場合でもfalseが返されます。
クリスB

コーディングのための最も便利な答え:)私もこれを使用しますが、同時要求が大きい場合、読み取り/書き込みが多すぎるとパフォーマンスが低下する可能性があるため、他の回答で示されているようにアクセス制御方法を使用できます。
vibs2006

1
Path.Combineなどの代わりに使用しPath.Combine(tempfilepath, "temp.txt")ます。
ΩmegaMan

3

次のコードブロックを試して、ディレクトリに書き込みアクセス権があるかどうかを確認できます。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 
}

2

コードに潜在的な競合状態があります。チェック時にユーザーがフォルダーへの書き込み権限を持っている場合、ユーザーが実際にフォルダーに書き込む前に、この権限は取り消されますか?書き込みは、キャッチして処理する必要がある例外をスローします。したがって、最初のチェックは無意味です。書き込みを行って例外を処理することもできます。これは、状況の標準的なパターンです。



1

問題のファイルにアクセスしようとするだけでは必ずしも十分ではありません。テストは、プログラムを実行しているユーザーのアクセス許可で実行されます。これは、テストするユーザーのアクセス許可とは限りません。


0

私はアッシュに同意します、それは問題ないはずです。あるいは、宣言型CASを使用して、アクセス権がない場合、プログラムが最初から実行されないようにすることもできます。

私が聞いたところによると、CAS機能の一部がC#4.0に存在しない可能性があります。それが問題であるかどうかはわかりません。


0

受け入れられた回答で推奨されているように、Windows 7でGetAccessControl()に例外をスローさせることができませんでした。

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

お役に立てれば。


0

私は同じ問題に直面しました:特定のディレクトリで読み書きできるかどうかを確認する方法。私は簡単な解決策になってしまいました...実際にそれをテストします。これが私のシンプルですが効果的な解決策です。

 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

}

それが役に立てば幸い !


0

ここでの回答のほとんどは、書き込みアクセスをチェックしません。それは、ユーザー/グループが「読み取り許可」(ファイル/ディレクトリのACEリストを読み取る)ができるかどうかを確認するだけです。

また、ユーザーは特権を取得または失うグループのメンバーになる可能性があるため、ACEを反復してセキュリティ識別子と一致するかどうかを確認することはできません。さらに悪いのは、ネストされたグループです。

私はこれが古いスレッドであることを知っていますが、今見る人にはもっと良い方法があります。

ユーザーに読み取り許可特権がある場合、Authz APIを使用して有効なアクセスを確認できます。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.