From d1ec06691310d456c9b2c8f79ec9d948b186aa76 Mon Sep 17 00:00:00 2001 From: Sebastian Kral Date: Sun, 8 Nov 2015 17:07:35 -0500 Subject: [PATCH] Added function to get the file owner on Windows Using ctypes to not depend on anything else. Finding files feels a little slower now but probably understandable since it is doing more. --- python/tk_multi_workfiles/user_cache.py | 7 +- python/tk_multi_workfiles/win_32_api.py | 94 +++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 python/tk_multi_workfiles/win_32_api.py diff --git a/python/tk_multi_workfiles/user_cache.py b/python/tk_multi_workfiles/user_cache.py index 112bb5e9..93581f9f 100644 --- a/python/tk_multi_workfiles/user_cache.py +++ b/python/tk_multi_workfiles/user_cache.py @@ -113,8 +113,11 @@ def get_file_last_modified_user(self, path): login_name = None if sys.platform == "win32": - # TODO: add windows support.. - pass + try: + from .win_32_api import get_file_owner + login_name = get_file_owner(path) + except: + pass else: try: from pwd import getpwuid diff --git a/python/tk_multi_workfiles/win_32_api.py b/python/tk_multi_workfiles/win_32_api.py new file mode 100644 index 00000000..eb4b4f99 --- /dev/null +++ b/python/tk_multi_workfiles/win_32_api.py @@ -0,0 +1,94 @@ +import ctypes +from ctypes import wintypes + +_PSECURITY_DESCRIPTOR = ctypes.POINTER(wintypes.BYTE) +_PSID = ctypes.POINTER(wintypes.BYTE) +_LPDWORD = ctypes.POINTER(wintypes.DWORD) +_LPBOOL = ctypes.POINTER(wintypes.BOOL) + +_OWNER_SECURITY_INFORMATION = 0X00000001 +_SID_TYPES = dict(enumerate( + "User Group Domain Alias WellKnownGroup DeletedAccount " + "Invalid Unknown Computer Label".split(), 1)) + +_advapi32 = ctypes.windll.advapi32 + +# MSDN windows/desktop/aa446639 +_GetFileSecurity = _advapi32.GetFileSecurityW +_GetFileSecurity.restype = wintypes.BOOL +_GetFileSecurity.argtypes = [ + wintypes.LPCWSTR, # File Name (in) + wintypes.DWORD, # Requested Information (in) + _PSECURITY_DESCRIPTOR, # Security Descriptor (out_opt) + wintypes.DWORD, # Length (in) + _LPDWORD, # Length Needed (out) +] + +# MSDN windows/desktop/aa446651 +_GetSecurityDescriptorOwner = _advapi32.GetSecurityDescriptorOwner +_GetSecurityDescriptorOwner.restype = wintypes.BOOL +_GetSecurityDescriptorOwner.argtypes = [ + _PSECURITY_DESCRIPTOR, # Security Descriptor (in) + ctypes.POINTER(_PSID), # Owner (out) + _LPBOOL, # Owner Exists (out) +] + +# MSDN windows/desktop/aa379166 +_LookupAccountSid = _advapi32.LookupAccountSidW +_LookupAccountSid.restype = wintypes.BOOL +_LookupAccountSid.argtypes = [ + wintypes.LPCWSTR, # System Name (in) + _PSID, # SID (in) + wintypes.LPCWSTR, # Name (out) + _LPDWORD, # Name Size (inout) + wintypes.LPCWSTR, # Domain(out_opt) + _LPDWORD, # Domain Size (inout) + _LPDWORD, # SID Type (out) +] + + +def get_file_security(filename, request): + length = wintypes.DWORD() + _GetFileSecurity(filename, request, None, 0, ctypes.byref(length)) + + if length.value: + sd = (wintypes.BYTE * length.value)() + if _GetFileSecurity(filename, request, sd, length, + ctypes.byref(length)): + return sd + + +def get_security_descriptor_owner(sd): + if sd is not None: + sid = _PSID() + sid_defaulted = wintypes.BOOL() + + if _GetSecurityDescriptorOwner(sd, ctypes.byref(sid), + ctypes.byref(sid_defaulted)): + return sid + + +def look_up_account_sid(sid): + if sid is not None: + SIZE = 256 + name = ctypes.create_unicode_buffer(SIZE) + domain = ctypes.create_unicode_buffer(SIZE) + cch_name = wintypes.DWORD(SIZE) + cch_domain = wintypes.DWORD(SIZE) + sid_type = wintypes.DWORD() + + if _LookupAccountSid(None, sid, name, ctypes.byref(cch_name), + domain, ctypes.byref(cch_domain), + ctypes.byref(sid_type)): + return name.value, domain.value, sid_type.value + + return None, None, None + + +def get_file_owner(path): + request = _OWNER_SECURITY_INFORMATION + + sd = get_file_security(path, request) + sid = get_security_descriptor_owner(sd) + name, domain, sid_type = look_up_account_sid(sid) + return name