web-dev-qa-db-ja.com

Pythonを使用してWindowsでフォルダの権限を設定する

Pythonを使用して、ユーザーのADアカウントが作成されたときに新しい個人用フォルダーを作成しています。フォルダーは作成されていますが、権限が正しくありません。CanPython =新しく作成したフォルダーにユーザーを追加し、そのアクセス許可を変更しますか?これをどこからコーディングし始めるのかわかりません。

20
ren

win32securityモジュール pywin32 の一部です。以下は、あなたがやりたいことを行う です。

この例では、ファイルの新しいDACLを作成し、古いDACLを置き換えますが、既存のDACLを変更するのは簡単です。次のように、空のDACLを作成する代わりに、セキュリティ記述子から既存のDACLを取得するだけです。

import win32security
import ntsecuritycon as con

FILENAME = "whatever"

userx, domain, type = win32security.LookupAccountName ("", "User X")
usery, domain, type = win32security.LookupAccountName ("", "User Y")

sd = win32security.GetFileSecurity(FILENAME, win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()   # instead of dacl = win32security.ACL()

dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.FILE_GENERIC_READ | con.FILE_GENERIC_WRITE, userx)
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.FILE_ALL_ACCESS, usery)

sd.SetSecurityDescriptorDacl(1, dacl, 0)   # may not be necessary
win32security.SetFileSecurity(FILENAME, win32security.DACL_SECURITY_INFORMATION, sd)
30
kindall

ACEのセキュリティ記述子の「リスト」に関心がある人のために、what-have-yaは次のデータ構造を使用します。私はしばらくの間、これを手伝って、それ以来これを使ってきました。

typical_aces={
    2032127L:"Full Control(All)",
    1179817L:"Read(RX)",
    1180086L:"Add",
    1180095L:"Add&Read",
    1245631L:"Change"
}

binary_aces={
    1:"ACCESS_READ",            #0x00000001
    2:"ACCESS_WRITE",           #0x00000002
    4:"ACCESS_CREATE",          #0x00000004
    8:"ACCESS_EXEC",            #0x00000008
    16:"ACCESS_DELETE",         #0x00000010
    32:"ACCESS_ATRIB",          #0x00000020
    64:"ACCESS_PERM",           #0x00000040
    32768:"ACCESS_GROUP",       #0x00008000
    65536:"DELETE",             #0x00010000
    131072:"READ_CONTROL",      #0x00020000
    262144:"WRITE_DAC",         #0x00040000
    524288:"WRITE_OWNER",       #0x00080000
    1048576:"SYNCHRONIZE",      #0x00100000
    16777216:"ACCESS_SYSTEM_SECURITY",#0x01000000
    33554432:"MAXIMUM_ALLOWED", #0x02000000
    268435456:"GENERIC_ALL",    #0x10000000
    536870912:"GENERIC_EXECUTE",#0x20000000
    1073741824:"GENERIC_WRITE", #0x40000000
    65535:"SPECIFIC_RIGHTS_ALL",#0x0000ffff
    983040:"STANDARD_RIGHTS_REQUIRED",#0x000f0000
    2031616:"STANDARD_RIGHTS_ALL",#0x001f0000
    }

マスクを特定のDACL /パスから以下に渡します。

def calculate_plaintext_mask(mask):
    a=2147483648L
    if typical_aces.has_key(mask):
        return typical_aces[mask]
    else:
        result='NONE'
        while a>>1:
            a=a>>1
            masked=mask&a
            if masked:
                if binary_aces.has_key(masked):
                    result=binary_aces[masked]+':'+result
    return result
5
Michael Flyger

これが kindall's answer のバージョンで、EXPLICIT_ACCESSエントリに SetEntriesInAcl を使用して正規の順序でACEを含む適切なACLを作成します(例:アクセス-denied ACEが最初にリストされます)。また、このバージョンでは、廃止された関数SetNamedSecurityInfoとは異なり、継承可能なACEの伝播をサポートする SetFileSecurity を使用してDACLを設定します。

import ntsecuritycon
import win32security

FILENAME = "whatever"
USERX = "UserX"
USERY = "UserY"

entries = [{'AccessMode': win32security.GRANT_ACCESS,
            'AccessPermissions': 0,
            'Inheritance': win32security.CONTAINER_INHERIT_ACE |
                           win32security.OBJECT_INHERIT_ACE,
            'Trustee': {'TrusteeType': win32security.TRUSTEE_IS_USER,
                        'TrusteeForm': win32security.TRUSTEE_IS_NAME,
                        'Identifier': ''}}
            for i in range(2)]

entries[0]['AccessPermissions'] = (ntsecuritycon.GENERIC_READ |
                                   ntsecuritycon.GENERIC_WRITE)
entries[0]['Trustee']['Identifier'] = USERX
entries[1]['AccessPermissions'] = ntsecuritycon.GENERIC_ALL
entries[1]['Trustee']['Identifier'] = USERY

sd = win32security.GetNamedSecurityInfo(FILENAME, win32security.SE_FILE_OBJECT,
        win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()
dacl.SetEntriesInAcl(entries)
win32security.SetNamedSecurityInfo(FILENAME, win32security.SE_FILE_OBJECT,
    win32security.DACL_SECURITY_INFORMATION |
    win32security.UNPROTECTED_DACL_SECURITY_INFORMATION,
    None, None, dacl, None)
5
Eryk Sun

以下は、ACL継承の有無にかかわらず、WindowsのNTFSファイルでファイルの所有権/ ACLを再帰的に設定する、またはPythonで完全に設定する方法です。

最初に、ファイルの所有権を取得できる関数(set_file_owner())が必要です。次に、WindowsでACLを処理できる関数(set_acls())が必要です。

アクセス許可を簡単にするために、R、RX、M、Fアクセス許可をアクセス許可ビットマスクに変換する関数(easy_permissions())を用意します。

それを取得したら、os.listdirを再帰的にディレクトリに入れ(get_files_recursive()を使用)、PermissionErrorで関数を実行して権限を処理します。これが完了したら、すべてのファイルをもう一度ループして、それに応じてアクセス許可を設定します。

基礎となるコード:

import os
from fnmatch import fnmatch
from itertools import chain
import win32api
import win32security
import ntsecuritycon
import pywintypes


def glob_path_match(path, pattern_list):
    """
    Checks if path is in a list of glob style wildcard paths
    :param path: path of file / directory
    :param pattern_list: list of wildcard patterns to check for
    :return: Boolean
    """
    return any(fnmatch(path, pattern) for pattern in pattern_list)


def get_files_recursive(root, include_dirs=False, d_exclude_list=None, f_exclude_list=None,
                        ext_exclude_list=None, ext_include_list=None,
                        depth=0, primary_root=None, fn_on_perm_error=None):
    """
    Walk a path to recursively find files
    Modified version of https://stackoverflow.com/a/24771959/2635443 that includes exclusion lists
    and accepts glob style wildcards on files and directories
    :param root: (str) path to explore
    :param include_dirs: (bool) should output list include directories
    :param d_exclude_list: (list) list of root relative directories paths to exclude
    :param f_exclude_list: (list) list of filenames without paths to exclude
    :param ext_exclude_list: list() list of file extensions to exclude, ex: ['.log', '.bak'],
           takes precedence over ext_include_list
    :param ext_include_lsit: (list) only include list of file extensions, ex: ['.py']
    :param depth: (int) depth of recursion to acheieve, 0 means unlimited, 1 is just the current dir...
    :param primary_root: (str) Only used for internal recursive exclusion lookup, don't pass an argument here
    :param fn_on_perm_error: (function) Optional function to pass, which argument will be the file / directory that has permission errors
    :return: list of files found in path
    """

    # Make sure we don't get paths with antislashes on Windows
    if os.path.isdir(root):
        root = os.path.normpath(root)
    else:
        return root

    # Check if we are allowed to read directory, if not, try to fix permissions if fn_on_perm_error is passed
    try:
        os.listdir(root)
    except PermissionError:
        if fn_on_perm_error is not None:
            fn_on_perm_error(root)

    if d_exclude_list is not None and primary_root is None:
        # Make sure we use a valid os separator for exclusion lists
        # Also make sure we do this only once
        d_exclude_list = [os.path.normpath(d) for d in d_exclude_list]
    else:
        d_exclude_list = []
    if f_exclude_list is None:
        f_exclude_list = []
    if ext_exclude_list is None:
        ext_exclude_list = []

    def _find_files():
        try:
            if include_dirs:
                yield root
            for f in os.listdir(root):
                file_ext = os.path.splitext(f)[1]
                if os.path.isfile(os.path.join(root, f)) and not glob_path_match(f, f_exclude_list) \
                    and file_ext not in ext_exclude_list \
                    and (file_ext in ext_include_list if ext_include_list is not None else True):
                    yield os.path.join(root, f)

        except PermissionError:
            pass

    def _find_files_in_dirs(depth):
        if depth == 0 or depth > 1:
            depth = depth - 1 if depth > 1 else 0
            try:
                for d in os.listdir(root):
                    d = os.path.join(root, d)
                    if os.path.isdir(d):
                        p_root = os.path.join(primary_root, d) if primary_root is not None else d
                        if not glob_path_match(p_root, d_exclude_list):
                            files_in_d = get_files_recursive(os.path.join(root, d), d_exclude_list=d_exclude_list,
                                                             f_exclude_list=f_exclude_list,
                                                             ext_exclude_list=ext_exclude_list,
                                                             ext_include_list=ext_include_list,
                                                             depth=depth, primary_root=p_root,
                                                             fn_on_perm_error=fn_on_perm_error)
                            if include_dirs:
                                yield d
                            if files_in_d:
                                for f in files_in_d:
                                    yield f

            except PermissionError:
                pass

    # Chain both generators
    return chain(_find_files(), _find_files_in_dirs(depth))


def get_binary_sid(string=None):
    """
    Wrapper function that returns PySID object from SID identifier or username
    If none given, we'll get current user

    :param string: (str) SID identifier or username
    :return: (PySID) object
    """
    if string is None:
        string = win32api.GetUserName()
    if string.startswith('S-1-'):
        # Consider we deal with a sid string
        return win32security.GetBinarySid(string)
    else:
        # Try to resolve username
        # LookupAccountName returns Tuple (user, domain, type)

        try:
            user, _, _ = win32security.LookupAccountName('', string)
            print(user)
            return user
        except pywintypes.error as e:
            raise OSError('Cannot map security ID: {0} with name. {1}'.format(string, e))


def set_file_owner(path, owner=None, force=False):
    """
    Set owner on NTFS files / directories
    https://stackoverflow.com/a/61009508/2635443

    :param path: (str) path
    :param owner: (PySID) object that represents the security identifier. If not set, current security identifier will be used
    :param force: (bool) Shall we force take ownership
    :return:
    """
    try:
        hToken = win32security.OpenThreadToken(win32api.GetCurrentThread(),
                                               win32security.TOKEN_ALL_ACCESS, True)

    except win32security.error:
        hToken = win32security.OpenProcessToken(win32api.GetCurrentProcess(),
                                                win32security.TOKEN_ALL_ACCESS)
    if owner is None:
        owner = win32security.GetTokenInformation(hToken, win32security.TokenOwner)
    prev_state = ()
    if force:
        new_state = [(win32security.LookupPrivilegeValue(None, name),
                      win32security.SE_PRIVILEGE_ENABLED)
                     for name in (win32security.SE_TAKE_OWNERSHIP_NAME,
                                  win32security.SE_RESTORE_NAME)]
        prev_state = win32security.AdjustTokenPrivileges(hToken, False,
                                                         new_state)
    try:
        sd = win32security.SECURITY_DESCRIPTOR()
        sd.SetSecurityDescriptorOwner(owner, False)
        win32security.SetFileSecurity(path, win32security.OWNER_SECURITY_INFORMATION, sd)
    except pywintypes.error as e:
        # Let's raise OSError so we don't need to import pywintypes in parent module to catch the exception
        raise OSError('Cannot take ownership of file: {0}. {1}.'.format(path, e))
    finally:
        if prev_state:
            win32security.AdjustTokenPrivileges(hToken, False, prev_state)


def easy_permissions(permission):
    """
    Creates ntsecuritycon permission bitmask from simple rights

    :param permission: (str) Simple R, RX, RWX, F  rights
    :return: (int) ntsecuritycon permission bitmask
    """
    permission = permission.upper()
    if permission == 'R':
        return ntsecuritycon.GENERIC_READ
    if permission == 'RX':
        return ntsecuritycon.GENERIC_READ | ntsecuritycon.GENERIC_EXECUTE
    if permission in ['RWX', 'M']:
        return ntsecuritycon.GENERIC_READ | ntsecuritycon.GENERIC_WRITE | ntsecuritycon.GENERIC_EXECUTE
    if permission == 'F':
        return ntsecuritycon.GENERIC_ALL
    raise ValueError('Bogus easy permission')


def set_acls(path, user_list=None, group_list=None, owner=None, permission=None, inherit=False, inheritance=False):
    """
    Set Windows DACL list

    :param path: (str) path to directory/file
    :param user_sid_list: (list) str usernames or PySID objects
    :param group_sid_list: (list) str groupnames or PySID objects
    :param owner: (str) owner name or PySID obect
    :param permission: (int) permission bitmask
    :param inherit: (bool) inherit parent permissions
    :param inheritance: (bool) apply ACL to sub folders and files
    """
    if inheritance:
        inheritance_flags = win32security.CONTAINER_INHERIT_ACE | win32security.OBJECT_INHERIT_ACE
    else:
        inheritance_flags = win32security.NO_INHERITANCE

    security_descriptor = {'AccessMode': win32security.GRANT_ACCESS,
                           'AccessPermissions': 0,
                           'Inheritance': inheritance_flags,
                           'Trustee': {'TrusteeType': '',
                                       'TrusteeForm': win32security.TRUSTEE_IS_SID,
                                       'Identifier': ''}
                           }

    # Now create a security descriptor for each user in the ACL list
    security_descriptors = []

    # If no user / group is defined, let's take current user
    if user_list is None and group_list is None:
        user_list = [get_binary_sid()]

    if user_list is not None:
        for sid in user_list:
            sid = get_binary_sid(sid)
            s = security_descriptor
            s['AccessPermissions'] = permission
            s['Trustee']['TrusteeType'] = win32security.TRUSTEE_IS_USER
            s['Trustee']['Identifier'] = sid
            security_descriptors.append(s)

    if group_list is not None:
        for sid in group_list:
            sid = get_binary_sid(sid)
            s = security_descriptor
            s['AccessPermissions'] = permission
            s['Trustee']['TrusteeType'] = win32security.TRUSTEE_IS_GROUP
            s['Trustee']['Identifier'] = sid
            security_descriptors.append(s)

    try:
        sd = win32security.GetNamedSecurityInfo(path, win32security.SE_FILE_OBJECT,
                                                win32security.DACL_SECURITY_INFORMATION | win32security.UNPROTECTED_DACL_SECURITY_INFORMATION)
    except pywintypes.error as e:
        raise OSError('Failed to read security for file: {0}. {1}'.format(path, e))
    dacl = sd.GetSecurityDescriptorDacl()
    dacl.SetEntriesInAcl(security_descriptors)

    security_information_flags = win32security.DACL_SECURITY_INFORMATION

    if not inherit:
        # PROTECTED_DACL_SECURITY_INFORMATION disables inheritance from parent
        security_information_flags = security_information_flags | win32security.PROTECTED_DACL_SECURITY_INFORMATION
    else:
        security_information_flags = security_information_flags | win32security.UNPROTECTED_DACL_SECURITY_INFORMATION

    # If we want to change owner, SetNamedSecurityInfo will need win32security.OWNER_SECURITY_INFORMATION in SECURITY_INFORMATION
    if owner is not None:
        security_information_flags = security_information_flags | win32security.OWNER_SECURITY_INFORMATION
        if isinstance(owner, str):
            owner = get_binary_sid(owner)

    try:
        # SetNamedSecurityInfo(path, object_type, security_information, owner, group, dacl, sacl)
        win32security.SetNamedSecurityInfo(path, win32security.SE_FILE_OBJECT,
                                           security_information_flags,
                                           owner, None, dacl, None)
    except pywintypes.error as e:
        raise OSError


def take_ownership_recursive(path, owner=None):
    def take_own(path):
        nonlocal owner
        try:
            set_file_owner(path, owner=owner, force=True)
        except OSError:
            print('Permission error on: {0}.'.format(path))

    files = get_files_recursive(path, include_dirs=True, fn_on_perm_error=take_own)
    for file in files:
        set_file_owner(file, force=True)


def get_files_recursive_and_set_permissions(path, owner=None, permissions=None, user_list=None):
    def fix_perms(path):
        nonlocal permissions
        nonlocal owner
        nonlocal user_list
        if permissions == None:
            permissions = easy_permissions('F')
        print('Permission error on: {0}.'.format(path))
        try:
            set_acls(path, user_list=user_list, owner=owner, permission=permissions, inheritance=False)
        except OSError:
            # Lets force ownership change
            try:
                set_file_owner(path, force=True)
                # Now try again
                set_acls(path, user_list=user_list, owner=owner, permission=permissions, inheritance=False)
            except OSError as e:
                print('Cannot fix permission on {0}. {1}'.format(path, e))

    files = get_files_recursive(path, include_dirs=True, fn_on_perm_error=fix_perms)
    for file in files:
        set_acls(file, user_list=user_list, owner=owner, permission=easy_permissions('F'), inheritance=False)

以下に、コードの使用例をいくつか示します。

# Recursively set owner
take_ownership_recursive(r'C:\MYPATH', owner=get_binary_sid('MyUser'))

# Recursively set permissions
get_files_recursive_and_set_permissions(r'C;\MYPATH', permissions=easy_permissions('F'), user_list=['MyUser', 'MyOtherUser'])

# Recursively set permissions with inheritance
get_files_recursive_and_set_permissions(r'C:\MYPATH', permissions=easy_permissions('RX'), user_list=['S-1-5-18'], inheritance=True)

# Set permissions
set_acls(r'C:\MYPATH', permissions=easy_permissions('F'), user_list['MyUser'])

# Take ownership
set_file_owner(r'C:\MYPATH', owner=get_binary_sid('MyUser'), Force=True)

Pythonでのwin32securityファイル処理に関するすべての投稿を提供してくれたEryk Sunに感謝します。適切なコードを書くことができました。 https://stackoverflow.com/a/43244697/263544 および https://stackoverflow.com/a/61009508/263544 を参照してください

0
Orsiris de Jong