C # Kullanıcının bir klasöre yazma erişimi olup olmadığını test edin


187

Bir kullanıcının aslında yapmaya başlamadan önce bir klasöre yazıp yazamayacağını test etmek gerekir.

Directory.GetAccessControl () yöntemini kullanarak klasörün güvenlik izinlerini almayı deneyen aşağıdaki yöntemi (C # 2.0) uyguladım .

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

Yazma erişimi için nasıl test edileceğimi googling yaparken böyle bir şey ortaya çıkmadı ve aslında Windows'ta izinleri test etmek çok karmaşık görünüyordu. İşleri aşırı basitleştirdiğimden ve bu yöntemin işe yaramış görünse de sağlam olmadığından endişeliyim.

Mevcut kullanıcının yazma erişimine sahip olup olmadığını test etme yöntemim doğru çalışır mı?


13
İzinleri görüntüleme erişimine sahip olmak, ona izin verilmesine izin vermiyor mu?
deed02392

Yanıtlar:


61

Bu, C # 'da klasör erişimini kontrol etmek için mükemmel geçerli bir yoldur. Düşebileceği tek yer, bunu bir istisnanın ek yükünün sorun olabileceği sıkı bir döngüde çağırmanız gerektiğidir .

Daha önce benzer başka sorular da sorulmuştur.


1
Funnily yeterince başka bir sekmede açık bu diğer sorular vardı ama DirectorySecurity hakkında cevap görmedim, bana sadece kabul edilen değil tüm cevapları okumayı öğretmek ;-)
Chris B

Windows'ta uzun yollar kullandığınızda da düşmeyecek mi?
Alexandru

11
Bu size yazma izniniz olup olmadığını söylemez, yalnızca o klasördeki izinlere bakıp bakamayacağınızı gösterir. Ayrıca yazma izniniz olabilir ancak izinleri arayamayabilirsiniz.
RandomEngy

65

Bu yazı için bu gün biraz geç olduğunu takdir, ama bu kod biraz yararlı bulabilirsiniz.

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

Bunu bir Konsol uygulamasına bırakın ve ihtiyacınız olanı yapıp yapmadığını görün.


Doğru hedef! Bana çok yardımcı oluyor!
11:27, smwikipedia

Ben çağrı bir istisna GetAccessControlalıyorum ama benim yazılım aslında baktığım dizine yazma yeteneğine sahiptir ..?
Jon Cage

@JonCage - ne istisna alıyorsunuz? Akla ilk gelen şey, ironik olarak, bir güvenlik problemidir. Uygulamanızın çalıştığı hesabın EKL bilgilerini alma izni var mı?
Duncan Howe

1
FileSystemAccessRule türü için bir kontrol eklemeniz gerekir. Bu bir Reddetme kuralıysa, yanlış yazılabilir olarak bildirirsiniz.
tdemay

2
Bunu kullanmaya çalışıyorum. Başka bir sorun buldum. Haklar yalnızca gruplara atanmışsa ve belirli kullanıcılara atanmamışsa, bu, yazma erişimine sahip olmadıklarını yanlış bildirir. Örneğin, "Kimliği doğrulanmış kullanıcılar" a yazma erişimi verildi
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
Bu yanıt, yalnızca izin ihlallerini değil, bir dosya yazmaya çalışırken oluşabilecek tüm istisnaları yakalayacaktır.
Matt Ellen

7
@GY,, string tempFileName = Path.GetRandomFileName();açıkça
Alexey Khoroshikh

3
@Matt, bu tam olarak sorulan soruya "dizin yazılabilir" sorusuna cevap verir, ancak hata nedenine bakılmaksızın. Bunun yerine " neden dizine yazamıyorum " yanıtını veriyorsunuz . :)
Alexey Khoroshikh

1
Bu kod ile yanlış pozitif olsun. Yürütme kullanıcısının bu klasöre yazma izni olmasa da, File.Create () işlevi OK (ve son seçeneği değiştirirseniz geçici dosya bırakır). Gerçekten tuhaf - nedenini anlamaya çalışmak için bir saat harcadım ama güldüm.
NickG

4
Aşağıda denediğim tüm alternatiflerden (ve referans verilen bağlantılardan) - bu güvenilir bir şekilde çalışan tek kişi.
TarmoPikaro

24

Bunların çoğunu denedim, ancak hepsi aynı nedenden dolayı yanlış pozitifler veriyorlar ... Dizini mevcut bir izin için test etmek yeterli değil, giriş yapmış kullanıcının bu grubun üyesi olup olmadığını kontrol etmeniz gerekiyor izni. Bunu yapmak için kullanıcıların kimliğini alır ve FileSystemAccessRule IdentityReference içeren bir grubun üyesi olup olmadığını denetlersiniz. Bunu test ettim, kusursuz çalışıyor ..

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

Teşekkürler John, kullanıcı grubunu tekrar Tanımla Kuralını kontrol etmek için kodunuzu kullanana kadar yanlış pozitif aldım!
Paul L

1
Ben kimlik için ek bir kontrol eklemek zorunda kaldı.Owner == rule.IdentityReference Ben erişim veren bir kullanıcı vardı ama hizmetleri için adanmış bir yerel hesap gibi herhangi bir grupta değil
grinder22

2
AccessControlType reddetme izin vermeden önceliklidir, bu nedenle erişim hakkını reddeden tamamen kapsamlı kurallar da kontrol edilmelidir ve reddetme türlerini kontrol ederken , tam olarak sahip olmadığınız araçların bir (AccessRight & rule.FileSystemRights) > 0parçası olan herhangi bir alt erişim türü reddedilmiş olmalıdır. erişimiAccessRightAccessRight
TJ Rockefeller

Yukarıda bahsedilen öğütücü22 olarak değişmem gerekiyordu; if (identity.Groups.Contains (rule.IdentityReference)) için if (identity.Groups.Contains (rule.IdentityReference) || identity.Owner.Equals (rule.IdentityReference)) olarak erişime sahip olan ancak olmayan bir kullanıcı vardı t herhangi bir grupta.
ehambright

13

Bir dizine yazıp yazamayacağınızı test etmenin tek% 100 güvenilir yolu IMHO, aslında dizine yazmak ve sonunda istisnaları yakalamaktır.


13

Örneğin, tüm kullanıcılar (Builtin \ Users) için bu yöntem iyi çalışır.

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

8

Bunu dene:

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

Yanılmıyorsam, bu yakın ama tam olarak orada değil - fsAccessRule.AccessControlTypeolabilecek gerçeği görmezden geliyor AccessControlType.Deny.
Jonathan Gilbert

Bu benim için Win7 dev makinemde çalışıyordu ama Win10'da başarısız oldu (hem test cihazı hem de kendi test makinem için). Düzeltmek için SSDS'nin değişikliği (aşağıya bakın) görünür.
winwaed

6

Kodunuz DirectorySecuritybelirli bir dizini için alır ve bir özel durumu (güvenlik bilgilerine erişememeniz nedeniyle) doğru şekilde işler. Ancak, örneğinizde, hangi erişime izin verildiğini görmek için döndürülen nesneyi sorgulamıyorsunuz ve bence bunu eklemeniz gerekiyor.


+1 - Ben sadece GetAccessControl çağrılırken bir istisna atılmadı bu sorunla karşılaştım ama aynı dizine yazmaya çalışırken yetkisiz bir istisna alıyorum.
Mayo

6

CsabaS'ın cevabının , açık reddetme erişim kurallarını açıklayan değiştirilmiş bir versiyonu . İşlev, bir dizin için tüm FileSystemAccessRules öğelerinden geçer ve geçerli kullanıcının bir dizine erişimi olan bir rolde olup olmadığını denetler. Böyle bir rol bulunamazsa veya kullanıcı erişimi reddedilmiş bir roldeyse, işlev false değerini döndürür. Okuma haklarını kontrol etmek için FileSystemRights.Read işlevini iletin; yazma hakları için FileSystemRights.Write komutunu iletin. Geçerli kullanıcının haklarını değil, rasgele bir kullanıcının haklarını denetlemek istiyorsanız, currentUser WindowsIdentity'yi istediğiniz WindowsIdentity yerine koyun. Ayrıca, kullanıcının dizini güvenli bir şekilde kullanıp kullanamayacağını belirlemek için bu tür işlevlere güvenmemenizi tavsiye ederim. Bu cevap nedenini mükemmel bir şekilde açıklıyor.

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

Csaba'nın kodu Windows 10'da benim için başarısız oldu (ancak Win7 geliştirici makinemde iyi). Sorunu çözmek için yukarıdakiler görünür.
winwaed

4

Yukarıdaki çözümler iyi ama benim için bu kodu basit ve uygulanabilir buluyorum. Sadece geçici bir dosya oluşturun. Dosya oluşturulursa, ortalama kullanıcı yazma erişimine sahiptir.

        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
Güzel! Bir şey olsa kullanıcı olabileceğini Createizni ancak Deletekullanıcı gerçi bu durumda bu yanlış bile dönecekti yapar yazma iznine sahip.
Chris B

Kodlama için en uygun cevap :) Bunu da sadece kullanıyorum, ancak büyük eşzamanlı istekler olduğunda, çok fazla okuma / yazma performansı yavaşlatabilir, bu durumda diğer cevaplarda verilen erişim kontrolü yöntemini kullanabilirsiniz.
vibs2006

1
Path.CombineBunun yerine kullanın Path.Combine(tempfilepath, "temp.txt").
MmegaMan

3

Dizinde Yazma Erişimi olup olmadığını kontrol etmek için aşağıdaki kod bloğunu deneyebilirsiniz. FileSystemAccessRule öğesini kontrol eder.

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

Kodunuzda potansiyel bir yarış durumunuz var - kullanıcının kontrol ederken klasöre yazma izinleri varsa, ancak kullanıcı aslında klasöre yazmadan önce bu izin geri çekilirse ne olur? Yazma, yakalamanız ve işlemeniz gereken bir istisna atar. Yani ilk kontrol anlamsız. İsterseniz sadece yazma ve herhangi bir istisna işleyebilir. Bu, durumunuz için standart modeldir.



1

Sadece söz konusu dosyaya erişmeye çalışmak yeterli değildir. Test, programı çalıştıran kullanıcının izinleriyle çalışacaktır. Bu, mutlaka test etmek istediğiniz kullanıcı izinleri değildir.


0

Ash'e katılıyorum, bu iyi olmalı. Alternatif olarak, bildirici CAS kullanabilir ve erişim yoksa, programın ilk başta çalışmasını engelleyebilirsiniz.

CAS özelliklerinden bazılarının duyduğumdan C # 4.0'da bulunmayabileceğine inanıyorum, bunun bir sorun olup olmadığından emin değilim.


0

Kabul edilen yanıtta önerildiği gibi Windows 7'de bir istisna atmak için GetAccessControl () alınamadı.

SDD'lerin cevabının bir varyasyonunu kullanarak sona erdi :

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

Bu yardımcı olur umarım.


0

Aynı sorunla karşılaştım: belirli bir dizinde okuma / yazma olup olmadığını nasıl doğrularım. Sonunda ... gerçekten test etmek için kolay bir çözüm buldum. İşte benim basit ama etkili çözümüm.

 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

}

Umarım yardımcı olur !


0

Buradaki cevapların çoğu yazma erişimini kontrol etmez. Sadece kullanıcı / grup 'Okuma İzni' olup olmadığını kontrol eder (dosya / dizinin ACE listesini okuyun).

Ayrıca ACE üzerinden yineleme yapmak ve Güvenlik Tanımlayıcısı ile eşleşip eşleşmediğini kontrol etmek işe yaramaz, çünkü kullanıcı ayrıcalık alabileceği / kaybedebileceği bir grubun üyesi olabilir. Daha da kötüsü iç içe gruplar.

Bu eski bir iplik olduğunu biliyorum ama şimdi arayan herhangi biri için daha iyi bir yolu var.

Kullanıcının Okuma İzni ayrıcalığı olması şartıyla, Etkili erişimi kontrol etmek için Authz API'sini kullanabilirsiniz.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.