Kiểm tra C # nếu người dùng có quyền ghi vào thư mục


187

Tôi cần kiểm tra xem người dùng có thể ghi vào thư mục hay không trước khi thực sự cố gắng làm như vậy.

Tôi đã triển khai phương thức sau (trong C # 2.0) để lấy các quyền bảo mật cho thư mục bằng phương thức 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;
    }
}

Khi tôi đang tìm cách kiểm tra quyền truy cập ghi, không có gì giống như thế này xuất hiện và nó thực sự rất phức tạp để kiểm tra quyền trong Windows. Tôi lo ngại rằng tôi đang đơn giản hóa mọi thứ quá mức và phương pháp này không mạnh mẽ, mặc dù nó có vẻ hiệu quả.

Phương pháp của tôi để kiểm tra xem người dùng hiện tại có quyền truy cập ghi hoạt động chính xác không?


13
Không có quyền truy cập để xem các quyền thực sự giống như không được phép ghi vào nó?
deed02392

Câu trả lời:


60

Đó là một cách hoàn toàn hợp lệ để kiểm tra quyền truy cập thư mục trong C #. Nơi duy nhất nó có thể rơi xuống là nếu bạn cần gọi nó trong một vòng lặp chặt chẽ trong đó chi phí ngoại lệ có thể là một vấn đề.

Đã có những câu hỏi tương tự khác được hỏi trước đây.


1
Thật thú vị, tôi đã có một trong những câu hỏi khác được mở trong một tab khác nhưng chưa thấy câu trả lời về DirectorySecurance, hãy dạy tôi đọc tất cả các câu trả lời không chỉ là câu hỏi được chấp nhận ;-)
Chris B

Nó cũng sẽ rơi xuống khi bạn sử dụng các đường dẫn dài trong Windows?
Alexandru

11
Điều đó sẽ không cho bạn biết nếu bạn có quyền ghi, nó sẽ chỉ cho bạn biết bạn có thể tra cứu quyền trên thư mục đó hay không. Ngoài ra, bạn có thể viết nhưng không thể tra cứu quyền.
RandomEngy

65

Tôi đánh giá cao rằng đây là một chút muộn trong ngày cho bài đăng này, nhưng bạn có thể thấy đoạn mã này hữu ích.

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

Thả nó vào một ứng dụng Console và xem nó có làm những gì bạn cần không.


Đúng mục tiêu! Giúp tôi rất nhiều!
smwikipedia

Tôi nhận được một ngoại lệ trong cuộc gọi đến GetAccessControlnhưng phần mềm của tôi thực tế có khả năng ghi vào thư mục tôi đang xem ..?
Jon Lồng

@JonCage - bạn nhận được ngoại lệ gì? Điều đầu tiên nhảy vào tâm trí là, trớ trêu thay, một vấn đề bảo mật. Tài khoản mà ứng dụng của bạn đang chạy có được phép lấy thông tin ACL không?
Duncan Howe

1
Bạn cần thêm một kiểm tra cho loại FileSystemAccessRule. Nếu đó là quy tắc Từ chối, bạn sẽ báo cáo không chính xác là có thể ghi.
tdemay

2
Tôi đang cố gắng sử dụng cái này. Tìm thấy một vấn đề khác. Nếu quyền chỉ được gán cho các nhóm và không phải người dùng cụ thể, điều này sẽ báo cáo không chính xác họ không có quyền truy cập ghi. Ví dụ: quyền truy cập viết được cấp cho "Người dùng được xác thực"
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
Câu trả lời này sẽ nắm bắt tất cả các trường hợp ngoại lệ có thể xảy ra khi cố gắng viết một tệp, không chỉ vi phạm quyền.
Matt Ellen

7
@GY, string tempFileName = Path.GetRandomFileName();hiển nhiên
Alexey Khoroshikh

3
@Matt, câu trả lời này chính xác cho câu hỏi được hỏi "tuy nhiên thư mục có thể ghi được" bất kể lý do thất bại là gì. Bạn trả lời đúng hơn " tại sao tôi không thể ghi vào thư mục". :)
Alexey Khoroshikh

1
Tôi nhận được một dương tính giả với mã này. File.Create () chạy OK (và để lại tệp tạm thời nếu bạn thay đổi tùy chọn cuối cùng) mặc dù người dùng thực thi không có quyền ghi vào thư mục đó. Thực sự rất kỳ quặc - đã dành một giờ cố gắng để tìm hiểu tại sao nhưng tôi lại bối rối.
NickG

4
Từ tất cả các lựa chọn thay thế mà tôi đã thử dưới đây (và các liên kết được tham chiếu) - đây là cách duy nhất hoạt động đáng tin cậy.
TarmoPikaro

23

Tôi đã thử hầu hết những thứ này, nhưng chúng đều cho kết quả dương tính giả, tất cả đều vì cùng một lý do .. Không đủ để kiểm tra thư mục cho phép có sẵn, bạn phải kiểm tra xem người dùng đã đăng nhập có phải là thành viên của nhóm không sự cho phép Để thực hiện việc này, bạn nhận được danh tính người dùng và kiểm tra xem đó có phải là thành viên của nhóm có chứa FileSystemAccessRule IdentityReference hay không. Tôi đã thử nghiệm điều này, hoạt động hoàn hảo ..

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

Cảm ơn John, tôi cũng đã có kết quả dương tính giả cho đến khi tôi sử dụng mã của mình để kiểm tra lại nhóm người dùng quy tắc IdentifyReference!
Paul L

1
tôi đã phải thêm một kiểm tra bổ sung cho danh tính.Owner == rule.IdentityReference vì tôi có một người dùng đã cấp quyền truy cập nhưng không thuộc bất kỳ nhóm nào, như tài khoản địa phương dành riêng cho các dịch vụ
mài22

1
Việc từ chối AccessControlType được ưu tiên hơn mức cho phép, do đó, phải kiểm tra hoàn toàn các quy tắc từ chối quyền truy cập, và khi kiểm tra loại từ chối thì phải (AccessRight & rule.FileSystemRights) > 0bởi vì bất kỳ loại truy cập phụ nào bị từ chối là một phần của AccessRightphương tiện bạn không có đầy đủ truy cập vàoAccessRight
TJ Rockefeller

Như máy xay22 đã đề cập ở trên, tôi cần phải thay đổi; if (nhận dạng. ERIC.Contains (rule.IdentityReference)) thành if (nhận dạng. ERIC.Contains (rule.IdentityReference) || sắc.Owner.Equals (rule.IdentityReference)) vì tôi đã có một người dùng đã truy cập nhưng ' t trong bất kỳ nhóm nào.
ehambright

13

Ví dụ: đối với tất cả người dùng (Được xây dựng \ Người dùng), phương pháp này hoạt động tốt - thưởng thức.

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

IMHO cách duy nhất đáng tin cậy 100% để kiểm tra nếu bạn có thể ghi vào thư mục là thực sự ghi vào thư đó và cuối cùng bắt ngoại lệ.


8

Thử cái này:

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

Nếu tôi không nhầm, điều này gần nhưng không hoàn toàn ở đó - nó bỏ qua thực tế fsAccessRule.AccessControlTypecó thể AccessControlType.Deny.
Jonathan Gilbert

Điều này đã làm việc với tôi trên máy dev Win7 của tôi nhưng không thành công trên Win10 (cả cho người kiểm tra và máy kiểm tra của riêng tôi). sửa đổi của ssds (xem bên dưới) xuất hiện để sửa nó.
thắng

6

Mã của bạn nhận được DirectorySecuritymột thư mục nhất định và xử lý một ngoại lệ (do bạn không có quyền truy cập vào thông tin bảo mật) một cách chính xác. Tuy nhiên, trong mẫu của bạn, bạn không thực sự thẩm vấn đối tượng được trả lại để xem quyền truy cập nào được cho phép - và tôi nghĩ bạn cần thêm điều này vào.


+1 - Tôi vừa gặp phải vấn đề này khi không có ngoại lệ khi gọi GetAccessControl nhưng tôi gặp ngoại lệ trái phép khi cố gắng ghi vào cùng thư mục đó.
Mayo

6

Dưới đây là phiên bản sửa đổi của câu trả lời của CsabaS , tài khoản cho các quy tắc truy cập từ chối rõ ràng. Hàm này đi qua tất cả FileSystemAccessRules cho một thư mục và kiểm tra xem người dùng hiện tại có ở trong vai trò có quyền truy cập vào một thư mục hay không. Nếu không tìm thấy vai trò nào như vậy hoặc người dùng ở vai trò có quyền truy cập bị từ chối, chức năng sẽ trả về sai. Để kiểm tra quyền đọc, hãy chuyển FileSystemRights.Read cho hàm; để có quyền ghi, hãy chuyển FileSystemRights.Write. Nếu bạn muốn kiểm tra quyền của người dùng tùy ý chứ không phải quyền hiện tại, hãy thay thế WindowsIdentity hiện tại cho WindowsIdentity mong muốn. Tôi cũng sẽ khuyên bạn không nên dựa vào các chức năng như thế này để xác định xem người dùng có thể sử dụng thư mục một cách an toàn hay không. Câu trả lời này giải thích hoàn hảo tại sao.

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

Mã của Csaba đã thất bại đối với tôi trên Windows 10 (nhưng tốt trên máy dev Win7 của tôi). Ở trên xuất hiện để khắc phục vấn đề.
winwaed

4

Các giải pháp trên là tốt nhưng đối với tôi, tôi thấy mã này đơn giản và khả thi. Chỉ cần tạo một tập tin tạm thời. Nếu tệp được tạo, người dùng trung bình của nó có quyền truy cập ghi.

        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
Đẹp! Một điều dù là người dùng có thể có Createsự cho phép nhưng không Deletetrong trường hợp này sẽ quay trở lại thậm chí sai sự thật khi người dùng không có quyền ghi.
Chris B

Câu trả lời thuận tiện nhất cho mã hóa :) Tôi cũng chỉ sử dụng câu hỏi này, tuy nhiên, khi có các yêu cầu đồng thời lớn thì việc đọc / ghi quá nhiều có thể làm chậm hiệu suất vì vậy trong những trường hợp đó bạn có thể sử dụng phương pháp accesscontrol như trong các câu trả lời khác.
vibs2006

1
Sử dụng Path.Combinethay thế như Path.Combine(tempfilepath, "temp.txt").
ΩmegaMan

3

Bạn có thể thử khối mã sau để kiểm tra xem thư mục có Truy cập ghi không. Nó kiểm tra 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

Bạn có một điều kiện chủng tộc tiềm năng trong mã của mình - điều gì xảy ra nếu người dùng có quyền ghi vào thư mục khi bạn kiểm tra, nhưng trước khi người dùng thực sự ghi vào thư mục thì quyền này bị rút lại? Việc viết sẽ đưa ra một ngoại lệ mà bạn sẽ cần phải nắm bắt và xử lý. Vì vậy, kiểm tra ban đầu là vô nghĩa. Bạn cũng có thể chỉ cần viết và xử lý bất kỳ trường hợp ngoại lệ. Đây là mô hình chuẩn cho tình huống của bạn.



1

Chỉ cần cố gắng truy cập vào tệp trong câu hỏi không nhất thiết là đủ. Kiểm tra sẽ chạy với quyền của người dùng đang chạy chương trình - Không nhất thiết phải là quyền người dùng bạn muốn kiểm tra.


0

Tôi đồng ý với Ash, điều đó sẽ ổn thôi. Ngoài ra, bạn có thể sử dụng CAS khai báo và thực sự ngăn chương trình chạy ở vị trí đầu tiên nếu chúng không có quyền truy cập.

Tôi tin rằng một số tính năng CAS có thể không có trong C # 4.0 từ những gì tôi đã nghe, không chắc đó có phải là vấn đề hay không.


0

Tôi không thể yêu cầu GetAccessControl () đưa ra một ngoại lệ trên Windows 7 như được đề xuất trong câu trả lời được chấp nhận.

Tôi đã kết thúc bằng cách sử dụng một biến thể của câu trả lời của 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;
        }

Hi vọng điêu nay co ich.


0

Tôi đã đối mặt với cùng một vấn đề: làm thế nào để xác minh xem tôi có thể đọc / ghi trong một thư mục cụ thể không. Tôi đã kết thúc với giải pháp dễ dàng để ... thực sự kiểm tra nó. Đây là giải pháp đơn giản mặc dù hiệu quả của tôi.

 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

}

Hy vọng nó giúp !


0

Hầu hết các câu trả lời ở đây không kiểm tra quyền truy cập ghi. Nó chỉ kiểm tra xem người dùng / nhóm có thể 'Đọc quyền' (Đọc danh sách ACE của tệp / thư mục).

Đồng thời lặp lại thông qua ACE và kiểm tra xem nó có khớp với Mã định danh bảo mật không hoạt động không vì người dùng có thể là thành viên của một nhóm mà từ đó anh ta có thể nhận / mất đặc quyền. Tệ hơn đó là các nhóm lồng nhau.

Tôi biết đây là một chủ đề cũ nhưng có một cách tốt hơn cho bất kỳ ai đang tìm kiếm bây giờ.

Với điều kiện người dùng có đặc quyền Quyền đọc là, người ta có thể sử dụng API Authz để kiểm tra Quyền truy cập hiệu quả.

https://docs.microsoft.com/en-us/windows/win32/secauthz/USE-authz-api

https://docs.microsoft.com/en-us/windows/win32/secauthz/checking-access-with-authz-api

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.