C # Uji apakah pengguna memiliki akses tulis ke folder

187

Saya perlu menguji apakah pengguna dapat menulis ke folder sebelum benar-benar mencoba melakukannya.

Saya telah menerapkan metode berikut (dalam C # 2.0) yang mencoba untuk mengambil izin keamanan untuk folder menggunakan metode Directory.GetAccessControl () .

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

Ketika saya googling cara menguji akses tulis, tidak ada yang seperti ini muncul dan tampaknya sangat rumit untuk benar-benar menguji izin di Windows. Saya khawatir bahwa saya terlalu menyederhanakan hal-hal dan bahwa metode ini tidak kuat, meskipun tampaknya berhasil.

Apakah metode saya untuk menguji apakah pengguna saat ini memiliki akses tulis berfungsi dengan benar?

Chris B
sumber
13
Apakah tidak memiliki akses untuk melihat izin benar-benar sama dengan tidak diizinkan menulis surat kepadanya?
deed02392

Jawaban:

61

Itu cara yang sangat valid untuk memeriksa akses folder di C #. Satu-satunya tempat yang mungkin jatuh adalah jika Anda perlu menyebutnya dalam loop ketat di mana overhead pengecualian mungkin menjadi masalah.

Ada beberapa pertanyaan serupa yang diajukan sebelumnya.

Abu
sumber
1
Lucunya, salah satu dari pertanyaan-pertanyaan lain terbuka di tab lain tetapi belum melihat jawaban tentang DirectorySecurity, mengajari saya untuk membaca semua jawaban, bukan hanya yang diterima ;-)
Chris B
Bukankah itu juga jatuh ketika Anda menggunakan jalur panjang di Windows?
Alexandru
11
Itu tidak akan memberi tahu Anda jika Anda memiliki izin menulis, itu hanya akan memberi tahu Anda jika Anda dapat mencari izin pada folder itu atau tidak. Anda juga mungkin bisa menulis tetapi tidak bisa mencari izin.
RandomEngy
65

Saya menghargai bahwa ini sedikit terlambat untuk posting ini, tetapi Anda mungkin menemukan sedikit kode ini berguna.

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

Masukkan itu ke dalam aplikasi Konsol dan lihat apakah itu sesuai dengan yang Anda butuhkan.

Duncan Howe
sumber
Tepat sasaran! Banyak membantu saya!
smwikipedia
Saya mendapatkan pengecualian pada panggilan ke GetAccessControltetapi perangkat lunak saya sebenarnya mampu menulis ke direktori yang saya cari ..?
Jon Cage
@ JonCage - pengecualian apa yang Anda dapatkan? Ironisnya, hal pertama yang terlintas dalam pikiran adalah masalah keamanan. Apakah akun yang dijalankan aplikasi Anda memiliki izin untuk mendapatkan informasi ACL?
Duncan Howe
1
Anda perlu menambahkan tanda centang untuk jenis FileSystemAccessRule. Jika aturan Tolak, Anda akan salah melaporkannya sebagai dapat ditulisi.
tdemay
2
Saya mencoba menggunakan ini. Menemukan masalah lain. Jika hak hanya diberikan kepada grup dan bukan pengguna tertentu, ini akan salah melaporkan mereka tidak memiliki akses tulis. Misalnya, akses tulis yang diberikan kepada "Pengguna
terotentikasi
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;
    }
}
harga diri
sumber
7
Jawaban ini akan menangkap semua pengecualian yang bisa terjadi ketika mencoba menulis file, bukan hanya pelanggaran izin.
Matt Ellen
7
@ GY,, string tempFileName = Path.GetRandomFileName();jelas
Alexey Khoroshikh
3
@ Matt, jawaban ini persis pertanyaan yang diajukan "apakah direktori dapat ditulis" terlepas dari alasan kegagalan, namun. Anda lebih suka menjawab " mengapa saya tidak bisa menulis ke direktori". :)
Alexey Khoroshikh
1
Saya mendapatkan false positive dengan kode ini. File.Create () berjalan OK (dan meninggalkan file temp jika Anda mengubah opsi terakhir) meskipun pengguna yang mengeksekusi tidak memiliki izin untuk menulis ke folder itu. Benar-benar aneh - menghabiskan satu jam mencoba mencari tahu mengapa tetapi saya bingung.
NickG
4
Dari semua alternatif yang saya coba di bawah ini (dan tautan yang direferensikan) - ini adalah satu-satunya yang bekerja dengan andal.
TarmoPikaro
24

Saya mencoba sebagian besar dari ini, tetapi mereka memberikan positif palsu, semua untuk alasan yang sama .. Tidak cukup untuk menguji direktori untuk izin yang tersedia, Anda harus memeriksa bahwa pengguna yang masuk adalah anggota grup yang memiliki izin. Untuk melakukan ini, Anda mendapatkan identitas pengguna, dan memeriksa apakah itu adalah anggota grup yang berisi FileSystemAccessRule IdentityReference. Saya telah menguji ini, bekerja dengan sempurna ..

    /// <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;
    }
JGU
sumber
Terima kasih John, saya mendapatkan false positive juga sampai saya menggunakan kode Anda untuk memeriksa grup pengguna lagi aturan IdentifyReference!
Paul L
1
saya harus menambahkan cek tambahan untuk identity.Owner == rule.IdentityReference karena saya memiliki pengguna yang memberikan akses tetapi tidak dalam grup apa pun, seperti akun lokal khusus untuk layanan
grinder22
2
AccessControlType deny didahulukan daripada allow, jadi untuk menjadi aturan yang sepenuhnya menyeluruh yang menolak hak akses harus diperiksa juga, dan ketika memeriksa untuk jenis (AccessRight & rule.FileSystemRights) > 0penolakan haruslah karena setiap jenis akses sub ditolak yang merupakan bagian dari AccessRightcara Anda tidak memiliki penuh akses keAccessRight
TJ Rockefeller
Seperti penggiling 22 yang disebutkan di atas, saya perlu berubah; if (identity.Groups.Contains (rule.IdentityReference)) hingga if (identity.Groups.Contains (rule.IdentityReference) || identity.Owner.Equals (rule.IdentityReference)) ketika saya memiliki pengguna yang memiliki akses tetapi tidak t di salah satu grup.
ehambright
13

IMHO satu-satunya cara yang dapat diandalkan 100% untuk menguji apakah Anda dapat menulis ke direktori adalah dengan benar-benar menulis dan akhirnya menangkap pengecualian.

Darin Dimitrov
sumber
13

Sebagai contoh untuk semua pengguna (Pengguna Builtin \), metode ini berfungsi dengan baik - selamat menikmati.

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;
    }
}
UGEEN
sumber
8

Coba ini:

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");
}
CsabaS
sumber
Jika saya tidak salah, ini dekat tapi tidak cukup di sana - itu mengabaikan fakta yang fsAccessRule.AccessControlTypebisa terjadi AccessControlType.Deny.
Jonathan Gilbert
Ini bekerja untuk saya di mesin dev Win7 saya tetapi gagal pada Win10 (baik untuk tester dan mesin uji saya sendiri). modifikasi ssds (lihat di bawah) muncul untuk memperbaikinya.
Menang
6

Kode Anda mendapatkan DirectorySecurityuntuk direktori yang diberikan, dan menangani pengecualian (karena Anda tidak memiliki akses ke informasi keamanan) dengan benar. Namun, dalam sampel Anda, Anda tidak benar-benar menginterogasi objek yang dikembalikan untuk melihat akses apa yang diizinkan - dan saya pikir Anda perlu menambahkan ini.

Vinay Sajip
sumber
+1 - Saya baru saja mengalami masalah ini di mana pengecualian tidak dilontarkan saat memanggil GetAccessControl namun saya mendapatkan pengecualian yang tidak sah ketika mencoba menulis ke direktori yang sama.
Mayo
6

Berikut adalah versi modifikasi dari jawaban CsabaS , yang menjelaskan aturan akses penolakan eksplisit. Fungsi melewati semua FileSystemAccessRules untuk direktori, dan memeriksa apakah pengguna saat ini dalam peran yang memiliki akses ke direktori. Jika tidak ada peran yang ditemukan atau pengguna dalam peran dengan akses ditolak, fungsi mengembalikan false. Untuk memeriksa hak baca, operasikan FileSystemRights.Baca ke fungsi; untuk hak menulis, lewati FileSystemRights.Write. Jika Anda ingin memeriksa hak pengguna yang arbitrer dan bukan hak yang sekarang, gantilah WindowsIdentity Pengguna saat ini dengan WindowsIdentity yang diinginkan. Saya juga menyarankan agar tidak mengandalkan fungsi seperti ini untuk menentukan apakah pengguna dapat menggunakan direktori dengan aman. Jawaban ini dengan sempurna menjelaskan alasannya.

    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;
    }
sdds
sumber
Kode Csaba gagal untuk saya di Windows 10 (tapi baik-baik saja di mesin Win7 dev saya). Di atas muncul untuk memperbaiki masalah.
Menang
4

Solusi di atas baik tetapi bagi saya, saya menemukan kode ini sederhana dan bisa diterapkan. Cukup buat file sementara. Jika file dibuat, berarti pengguna memiliki akses tulis.

        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;
        }
Ali Asad
sumber
3
Bagus! Satu hal adalah bahwa pengguna mungkin memiliki Createizin tetapi tidak Deletedalam hal ini akan kembali salah meskipun pengguna memang memiliki izin menulis.
Chris B
Jawaban yang paling mudah untuk pengkodean :) Saya juga menggunakan yang satu ini saja, namun, ketika ada permintaan bersamaan besar maka begitu banyak membaca / menulis mungkin memperlambat kinerja sehingga dalam kasus-kasus Anda dapat menggunakan metodologi accesscontrol seperti yang diberikan dalam jawaban lain.
vibs2006
1
Gunakan Path.Combinesebagai gantinya Path.Combine(tempfilepath, "temp.txt").
ΩmegaMan
3

Anda dapat mencoba blok kode berikut untuk memeriksa apakah direktori tersebut memiliki akses tulis. Itu memeriksa 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 
}
RockWorld
sumber
2

Anda memiliki potensi kondisi ras dalam kode Anda - apa yang terjadi jika pengguna memiliki izin untuk menulis ke folder ketika Anda memeriksa, tetapi sebelum pengguna benar-benar menulis ke folder izin ini ditarik? Tulisan akan melempar pengecualian yang perlu Anda tangkap dan tangani. Jadi pemeriksaan awal tidak ada gunanya. Anda sebaiknya menulis dan menangani segala pengecualian. Ini adalah pola standar untuk situasi Anda.


sumber
1

Hanya mencoba mengakses file yang dipermasalahkan belum cukup. Tes akan berjalan dengan izin pengguna yang menjalankan program - Yang belum tentu izin pengguna yang ingin Anda uji terhadap.

Mort
sumber
0

Saya setuju dengan Ash, itu seharusnya baik-baik saja. Atau Anda dapat menggunakan CAS deklaratif dan benar-benar mencegah program berjalan di tempat pertama jika mereka tidak memiliki akses.

Saya percaya beberapa fitur CAS mungkin tidak hadir di C # 4.0 dari apa yang saya dengar, tidak yakin apakah itu mungkin masalah atau tidak.

Ian
sumber
0

Saya tidak bisa mendapatkan GetAccessControl () untuk melempar pengecualian pada Windows 7 seperti yang direkomendasikan dalam jawaban yang diterima.

Saya akhirnya menggunakan variasi jawaban 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;
        }

Semoga ini membantu.

Patrick
sumber
0

Saya menghadapi masalah yang sama: cara memverifikasi jika saya bisa membaca / menulis di direktori tertentu. Saya berakhir dengan solusi mudah untuk ... benar-benar mengujinya. Inilah solusi sederhana namun efektif saya.

 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

}

Semoga ini bisa membantu!

l.raimondi
sumber
0

Sebagian besar jawaban di sini tidak memeriksa akses tulis. Itu hanya memeriksa apakah pengguna / grup dapat 'Baca Izin' (Baca daftar ACE dari file / direktori).

Juga melakukan iterasi melalui ACE dan memeriksa apakah cocok dengan Security Identifier tidak berfungsi karena pengguna dapat menjadi anggota grup yang darinya ia dapat / kehilangan hak istimewa. Lebih buruk dari itu adalah kelompok bersarang.

Saya tahu ini adalah utas lama tetapi ada cara yang lebih baik bagi siapa pun yang melihatnya sekarang.

Asalkan pengguna memiliki hak Baca Izin adalah, seseorang dapat menggunakan API Authz untuk memeriksa akses Efektif.

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

Kamaal
sumber