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