diff --git a/.gitignore b/.gitignore index 735aa84..b9d9d98 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ build/ dist/ /docs/build /docs/source/api +win32ctypes/version.py \ No newline at end of file diff --git a/test-requirements.txt b/test-requirements.txt index 94e16e1..55c67e0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,3 +2,4 @@ coverage haas --only-binary pywin32 pywin32 +parameterized diff --git a/win32ctypes/constants.py b/win32ctypes/constants.py new file mode 100644 index 0000000..5c778ab --- /dev/null +++ b/win32ctypes/constants.py @@ -0,0 +1,23 @@ +# +# (C) Copyright 2014 Enthought, Inc., Austin, TX +# All right reserved. +# +# This file is open source software distributed according to the terms in +# LICENSE.txt +# + +# Credential related constants +CRED_TYPE_GENERIC = 0x1 +CRED_PERSIST_SESSION = 0x1 +CRED_PERSIST_LOCAL_MACHINE = 0x2 +CRED_PERSIST_ENTERPRISE = 0x3 +CRED_ENUMERATE_ALL_CREDENTIALS = 0x1 +CRED_PRESERVE_CREDENTIAL_BLOB = 0x1 +CRED_MAX_ATTRIBUTES = 64 +CRED_MAX_STRING_LENGTH = 256 +CRED_MAX_USERNAME_LENGTH = 513 +CRED_MAX_VALUE_SIZE = 256 + +# Library related constants +LOAD_LIBRARY_AS_DATAFILE = 0x2 +LANG_NEUTRAL = 0x00 diff --git a/win32ctypes/core/__init__.py b/win32ctypes/core/__init__.py index 887bb03..b0037b9 100644 --- a/win32ctypes/core/__init__.py +++ b/win32ctypes/core/__init__.py @@ -55,6 +55,6 @@ def find_spec(self, fullname, path, target=None): sys.meta_path.append(BackendFinder([ - '_dll', '_authentication', '_time', + '_dll', '_authentication', '_winbase', '_common', '_resource', '_nl_support', '_system_information'])) diff --git a/win32ctypes/core/cffi/__init__.py b/win32ctypes/core/cffi/__init__.py index 6fac1f4..a47320a 100644 --- a/win32ctypes/core/cffi/__init__.py +++ b/win32ctypes/core/cffi/__init__.py @@ -6,6 +6,8 @@ # LICENSE.txt # import logging +# Initialize the ffi cdef +from . import _cdefinitions # noqa logger = logging.getLogger(__name__) logger.debug('Loaded cffi backend') diff --git a/win32ctypes/core/cffi/_authentication.py b/win32ctypes/core/cffi/_authentication.py index a74b8fb..b2318f2 100644 --- a/win32ctypes/core/cffi/_authentication.py +++ b/win32ctypes/core/cffi/_authentication.py @@ -1,65 +1,16 @@ # -# (C) Copyright 2015 Enthought, Inc., Austin, TX +# (C) Copyright 2015-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # -from weakref import WeakKeyDictionary - from win32ctypes.core.compat import is_text from ._util import ffi, check_false, dlls from ._nl_support import _GetACP from ._common import _PyBytes_FromStringAndSize -ffi.cdef(""" - -typedef struct _FILETIME { - DWORD dwLowDateTime; - DWORD dwHighDateTime; -} FILETIME, *PFILETIME; - -typedef struct _CREDENTIAL_ATTRIBUTE { - LPWSTR Keyword; - DWORD Flags; - DWORD ValueSize; - LPBYTE Value; -} CREDENTIAL_ATTRIBUTE, *PCREDENTIAL_ATTRIBUTE; - -typedef struct _CREDENTIAL { - DWORD Flags; - DWORD Type; - LPWSTR TargetName; - LPWSTR Comment; - FILETIME LastWritten; - DWORD CredentialBlobSize; - LPBYTE CredentialBlob; - DWORD Persist; - DWORD AttributeCount; - PCREDENTIAL_ATTRIBUTE Attributes; - LPWSTR TargetAlias; - LPWSTR UserName; -} CREDENTIAL, *PCREDENTIAL; - - -BOOL WINAPI CredReadW( - LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIAL *Credential); -BOOL WINAPI CredWriteW(PCREDENTIAL Credential, DWORD); -VOID WINAPI CredFree(PVOID Buffer); -BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags); -BOOL WINAPI CredEnumerateW( - LPCWSTR Filter, DWORD Flags, DWORD *Count, PCREDENTIAL **Credential); -""") - -_keep_alive = WeakKeyDictionary() - - -SUPPORTED_CREDKEYS = set(( - u'Type', u'TargetName', u'Persist', - u'UserName', u'Comment', u'CredentialBlob')) - - def make_unicode(password): """ Convert the input string to unicode. @@ -77,41 +28,72 @@ def __call__(self): return ffi.new("PCREDENTIAL")[0] @classmethod - def fromdict(cls, credential, flag=0): - unsupported = set(credential.keys()) - SUPPORTED_CREDKEYS - if len(unsupported): - raise ValueError("Unsupported keys: {0}".format(unsupported)) - if flag != 0: - raise ValueError("flag != 0 not yet supported") - + def fromdict(cls, credential, flags=0): factory = cls() - c_creds = factory() - # values to ref and make sure that they will not go away - values = [] + c_credential = factory() + values = [] # keep values in context during processing for key, value in credential.items(): - if key == u'CredentialBlob': - blob = make_unicode(value) - blob_data = ffi.new('wchar_t[]', blob) - # new adds a NULL at the end that we do not want. - c_creds.CredentialBlobSize = \ - ffi.sizeof(blob_data) - ffi.sizeof('wchar_t') - c_creds.CredentialBlob = ffi.cast('LPBYTE', blob_data) - values.append(blob_data) - elif key in (u'Type', u'Persist'): - setattr(c_creds, key, value) - elif value is None: - setattr(c_creds, key, ffi.NULL) - else: - blob = make_unicode(value) - pblob = ffi.new('wchar_t[]', blob) - values.append(pblob) - setattr(c_creds, key, ffi.cast('LPTSTR', pblob)) - # keep values alive until c_creds goes away. - _keep_alive[c_creds] = tuple(values) - return c_creds + if key == 'CredentialBlob': + blob = ffi.new('wchar_t[]', value) + c_credential.CredentialBlob = ffi.cast('LPBYTE', blob) + c_credential.CredentialBlobSize = ffi.sizeof(blob) - ffi.sizeof('wchar_t') # noqa + values.append(blob) + elif key == 'Attributes': + count = len(value) + if count == 0: + continue + elif count > 1: + raise ValueError('Multiple attributes are not supported') + c_attribute = CREDENTIAL_ATTRIBUTE.fromdict(value[0]) + c_credential.Attributes = PCREDENTIAL_ATTRIBUTE(c_attribute) + c_credential.AttributeCount = count + values.append(c_attribute) + elif key in ('Type', 'Persist', 'Flags'): + setattr(c_credential, key, value) + elif key in ('TargetName', 'Comment', 'TargetAlias', 'UserName'): + if value is None: + setattr(c_credential, key, ffi.NULL) + else: + blob_pointer = ffi.new('wchar_t[]', value) + setattr( + c_credential, key, ffi.cast('LPWSTR', blob_pointer)) + values.append(blob_pointer) + return c_credential + + +class _CREDENTIAL_ATTRIBUTE(object): + + def __call__(self): + return ffi.new("PCREDENTIAL_ATTRIBUTE")[0] + + @classmethod + def fromdict(cls, attribute, flags=0): + factory = cls() + c_attribute = factory() + c_attribute.Flags = attribute.get('Flags', flags) + keyword = attribute.get('Keyword', None) + if keyword is None: + c_attribute.Keyword = ffi.NULL + else: + blob_pointer = ffi.new('wchar_t[]', keyword) + c_attribute.Keyword = ffi.cast('LPWSTR', blob_pointer) + value = attribute['Value'] + if len(value) == 0: + data, size = ffi.NULL, 0 + elif is_text(value): + blob = ffi.new('wchar_t[]', value) + data = ffi.cast('LPBYTE', blob) + size = ffi.sizeof(blob) - ffi.sizeof('wchar_t') # noqa + else: + data = ffi.new('BYTE[]', value) + size = ffi.sizeof(blob_pointer) - ffi.sizeof('BYTE') + c_attribute.Value = data + c_attribute.ValueSize = size + return c_attribute CREDENTIAL = _CREDENTIAL() +CREDENTIAL_ATTRIBUTE = _CREDENTIAL_ATTRIBUTE() def PCREDENTIAL(value=None): @@ -126,47 +108,79 @@ def PPPCREDENTIAL(value=None): return ffi.new("PCREDENTIAL**", ffi.NULL if value is None else value) -def credential2dict(pc_creds): - credentials = {} - for key in SUPPORTED_CREDKEYS: - if key == u'CredentialBlob': +def PCREDENTIAL_ATTRIBUTE(value=None): + return ffi.new( + "PCREDENTIAL_ATTRIBUTE", ffi.NULL if value is None else value) + + +def credential_attribute2dict(c_attribute): + attribute = {} + keyword = c_attribute.Keyword + if keyword == ffi.NULL: + attribute['Keyword'] = None + else: + attribute['Keyword'] = ffi.string(keyword) + attribute['Flags'] = c_attribute.Flags + size = c_attribute.ValueSize + if size > 0: + value = _PyBytes_FromStringAndSize(c_attribute.Value, size) + attribute['Value'] = value + return attribute + + +def credential2dict(c_credential): + credential = {} + for key in dir(c_credential): + if key == 'CredentialBlob': data = _PyBytes_FromStringAndSize( - pc_creds.CredentialBlob, pc_creds.CredentialBlobSize) - elif key in (u'Type', u'Persist'): - data = int(getattr(pc_creds, key)) - else: - string_pointer = getattr(pc_creds, key) + c_credential.CredentialBlob, c_credential.CredentialBlobSize) + elif key == 'Attributes': + attributes = [] + count = c_credential.AttributeCount + c_attributes = c_credential.Attributes + for index in range(count): + attribute = credential_attribute2dict(c_attributes[index]) + attributes.append(attribute) + data = tuple(attributes) + elif key == 'LastWritten': + data = c_credential.LastWritten + elif key in ('Type', 'Persist', 'Flags'): + data = int(getattr(c_credential, key)) + elif key in ('TargetName', 'Comment', 'TargetAlias', 'UserName'): + string_pointer = getattr(c_credential, key) if string_pointer == ffi.NULL: data = None else: data = ffi.string(string_pointer) - credentials[key] = data - return credentials + else: + continue + credential[key] = data + return credential def _CredRead(TargetName, Type, Flags, ppCredential): target = make_unicode(TargetName) return check_false( dlls.advapi32.CredReadW(target, Type, Flags, ppCredential), - u'CredRead') + 'CredRead') def _CredWrite(Credential, Flags): return check_false( - dlls.advapi32.CredWriteW(Credential, Flags), u'CredWrite') + dlls.advapi32.CredWriteW(Credential, Flags), 'CredWrite') def _CredDelete(TargetName, Type, Flags): return check_false( dlls.advapi32.CredDeleteW( - make_unicode(TargetName), Type, Flags), u'CredDelete') + make_unicode(TargetName), Type, Flags), 'CredDelete') def _CredEnumerate(Filter, Flags, Count, pppCredential): filter = make_unicode(Filter) if Filter is not None else ffi.NULL return check_false( dlls.advapi32.CredEnumerateW(filter, Flags, Count, pppCredential), - u'CredEnumerate') + 'CredEnumerate') _CredFree = dlls.advapi32.CredFree diff --git a/win32ctypes/core/cffi/_cdefinitions.py b/win32ctypes/core/cffi/_cdefinitions.py new file mode 100644 index 0000000..682e884 --- /dev/null +++ b/win32ctypes/core/cffi/_cdefinitions.py @@ -0,0 +1,110 @@ +# +# (C) Copyright 2024 Enthought, Inc., Austin, TX +# All right reserved. +# +# This file is open source software distributed according to the terms in +# LICENSE.txt +# +" C definitions for the ffi modules " + +from ._util import ffi + +# _dll module cdefs +ffi.cdef(""" + +HMODULE WINAPI LoadLibraryExW(LPCTSTR lpFileName, HANDLE hFile, DWORD dwFlags); +BOOL WINAPI FreeLibrary(HMODULE hModule); + +""") + +# _winbase module cdefs +ffi.cdef(""" + +typedef struct _FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME, *PFILETIME, *LPFILETIME; + +""") + +# _authentication module cdefs +ffi.cdef(""" +typedef struct _CREDENTIAL_ATTRIBUTE { + LPWSTR Keyword; + DWORD Flags; + DWORD ValueSize; + LPBYTE Value; +} CREDENTIAL_ATTRIBUTE, *PCREDENTIAL_ATTRIBUTE; + +typedef struct _CREDENTIAL { + DWORD Flags; + DWORD Type; + LPWSTR TargetName; + LPWSTR Comment; + FILETIME LastWritten; + DWORD CredentialBlobSize; + LPBYTE CredentialBlob; + DWORD Persist; + DWORD AttributeCount; + PCREDENTIAL_ATTRIBUTE Attributes; + LPWSTR TargetAlias; + LPWSTR UserName; +} CREDENTIAL, *PCREDENTIAL; + + +BOOL WINAPI CredReadW( + LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIAL *Credential); +BOOL WINAPI CredWriteW(PCREDENTIAL Credential, DWORD); +VOID WINAPI CredFree(PVOID Buffer); +BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags); +BOOL WINAPI CredEnumerateW( + LPCWSTR Filter, DWORD Flags, DWORD *Count, PCREDENTIAL **Credential); +""") + + +# _nt_support module cdefs +ffi.cdef(""" + +UINT WINAPI GetACP(void); + +""") + + +# _resource module cdefs +ffi.cdef(""" + +typedef int WINBOOL; +typedef WINBOOL (__stdcall *ENUMRESTYPEPROC) (HANDLE, LPTSTR, LONG_PTR); +typedef WINBOOL (__stdcall *ENUMRESNAMEPROC) (HANDLE, LPCTSTR, LPTSTR, LONG_PTR); +typedef WINBOOL (__stdcall *ENUMRESLANGPROC) (HANDLE, LPCTSTR, LPCTSTR, WORD, LONG_PTR); + +BOOL WINAPI EnumResourceTypesW( + HMODULE hModule, ENUMRESTYPEPROC lpEnumFunc, LONG_PTR lParam); +BOOL WINAPI EnumResourceNamesW( + HMODULE hModule, LPCTSTR lpszType, + ENUMRESNAMEPROC lpEnumFunc, LONG_PTR lParam); +BOOL WINAPI EnumResourceLanguagesW( + HMODULE hModule, LPCTSTR lpType, + LPCTSTR lpName, ENUMRESLANGPROC lpEnumFunc, LONG_PTR lParam); +HRSRC WINAPI FindResourceExW( + HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLanguage); +DWORD WINAPI SizeofResource(HMODULE hModule, HRSRC hResInfo); +HGLOBAL WINAPI LoadResource(HMODULE hModule, HRSRC hResInfo); +LPVOID WINAPI LockResource(HGLOBAL hResData); + +HANDLE WINAPI BeginUpdateResourceW(LPCTSTR pFileName, BOOL bDeleteExistingResources); +BOOL WINAPI EndUpdateResourceW(HANDLE hUpdate, BOOL fDiscard); +BOOL WINAPI UpdateResourceW(HANDLE hUpdate, LPCTSTR lpType, LPCTSTR lpName, WORD wLanguage, LPVOID lpData, DWORD cbData); + +""") # noqa + + +# _system_information module cdefs +ffi.cdef(""" + +BOOL WINAPI Beep(DWORD dwFreq, DWORD dwDuration); +UINT WINAPI GetWindowsDirectoryW(LPTSTR lpBuffer, UINT uSize); +UINT WINAPI GetSystemDirectoryW(LPTSTR lpBuffer, UINT uSize); +DWORD WINAPI GetTickCount(void); + +""") diff --git a/win32ctypes/core/cffi/_common.py b/win32ctypes/core/cffi/_common.py index df7e0ec..2601b55 100644 --- a/win32ctypes/core/cffi/_common.py +++ b/win32ctypes/core/cffi/_common.py @@ -1,16 +1,12 @@ # -# (C) Copyright 2015 Enthought, Inc., Austin, TX +# (C) Copyright 2015-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # -from weakref import WeakKeyDictionary - from ._util import ffi -_keep_alive = WeakKeyDictionary() - def _PyBytes_FromStringAndSize(pointer, size): buffer = ffi.buffer(pointer, size) diff --git a/win32ctypes/core/cffi/_dll.py b/win32ctypes/core/cffi/_dll.py index 038e761..b77602d 100644 --- a/win32ctypes/core/cffi/_dll.py +++ b/win32ctypes/core/cffi/_dll.py @@ -8,14 +8,6 @@ from ._util import ffi, check_null, check_false, dlls, HMODULE, PVOID -ffi.cdef(""" - -HMODULE WINAPI LoadLibraryExW(LPCTSTR lpFileName, HANDLE hFile, DWORD dwFlags); -BOOL WINAPI FreeLibrary(HMODULE hModule); - -""") - - def _LoadLibraryEx(lpFilename, hFile, dwFlags): result = check_null( dlls.kernel32.LoadLibraryExW( diff --git a/win32ctypes/core/cffi/_nl_support.py b/win32ctypes/core/cffi/_nl_support.py index 51f7194..0458bda 100644 --- a/win32ctypes/core/cffi/_nl_support.py +++ b/win32ctypes/core/cffi/_nl_support.py @@ -1,17 +1,11 @@ # -# (C) Copyright 2015-18 Enthought, Inc., Austin, TX +# (C) Copyright 2015-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # -from ._util import ffi, dlls - -ffi.cdef(""" - -UINT WINAPI GetACP(void); - -""") +from ._util import dlls def _GetACP(): diff --git a/win32ctypes/core/cffi/_resource.py b/win32ctypes/core/cffi/_resource.py index 42bcbde..bafe378 100644 --- a/win32ctypes/core/cffi/_resource.py +++ b/win32ctypes/core/cffi/_resource.py @@ -10,34 +10,6 @@ PVOID, RESOURCE, resource, dlls) -ffi.cdef(""" - -typedef int WINBOOL; -typedef WINBOOL (__stdcall *ENUMRESTYPEPROC) (HANDLE, LPTSTR, LONG_PTR); -typedef WINBOOL (__stdcall *ENUMRESNAMEPROC) (HANDLE, LPCTSTR, LPTSTR, LONG_PTR); -typedef WINBOOL (__stdcall *ENUMRESLANGPROC) (HANDLE, LPCTSTR, LPCTSTR, WORD, LONG_PTR); - -BOOL WINAPI EnumResourceTypesW( - HMODULE hModule, ENUMRESTYPEPROC lpEnumFunc, LONG_PTR lParam); -BOOL WINAPI EnumResourceNamesW( - HMODULE hModule, LPCTSTR lpszType, - ENUMRESNAMEPROC lpEnumFunc, LONG_PTR lParam); -BOOL WINAPI EnumResourceLanguagesW( - HMODULE hModule, LPCTSTR lpType, - LPCTSTR lpName, ENUMRESLANGPROC lpEnumFunc, LONG_PTR lParam); -HRSRC WINAPI FindResourceExW( - HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLanguage); -DWORD WINAPI SizeofResource(HMODULE hModule, HRSRC hResInfo); -HGLOBAL WINAPI LoadResource(HMODULE hModule, HRSRC hResInfo); -LPVOID WINAPI LockResource(HGLOBAL hResData); - -HANDLE WINAPI BeginUpdateResourceW(LPCTSTR pFileName, BOOL bDeleteExistingResources); -BOOL WINAPI EndUpdateResourceW(HANDLE hUpdate, BOOL fDiscard); -BOOL WINAPI UpdateResourceW(HANDLE hUpdate, LPCTSTR lpType, LPCTSTR lpName, WORD wLanguage, LPVOID lpData, DWORD cbData); - -""") # noqa - - def ENUMRESTYPEPROC(callback): def wrapped(hModule, lpszType, lParam): return callback(hModule, resource(lpszType), lParam) diff --git a/win32ctypes/core/cffi/_system_information.py b/win32ctypes/core/cffi/_system_information.py index f642da8..4a1f698 100644 --- a/win32ctypes/core/cffi/_system_information.py +++ b/win32ctypes/core/cffi/_system_information.py @@ -1,5 +1,5 @@ # -# (C) Copyright 2018 Enthought, Inc., Austin, TX +# (C) Copyright 2018-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in @@ -9,15 +9,7 @@ # TODO: retrieve this value using ffi MAX_PATH = 260 -MAX_PATH_BUF = u'wchar_t[{0}]'.format(MAX_PATH) - -ffi.cdef(""" - -BOOL WINAPI Beep(DWORD dwFreq, DWORD dwDuration); -UINT WINAPI GetWindowsDirectoryW(LPTSTR lpBuffer, UINT uSize); -UINT WINAPI GetSystemDirectoryW(LPTSTR lpBuffer, UINT uSize); - -""") +MAX_PATH_BUF = f'wchar_t[{MAX_PATH}]' def _GetWindowsDirectory(): @@ -30,3 +22,7 @@ def _GetSystemDirectory(): buffer = ffi.new(MAX_PATH_BUF) directory = dlls.kernel32.GetSystemDirectoryW(buffer, MAX_PATH) return ffi.unpack(buffer, directory) + + +def _GetTickCount(): + return dlls.kernel32.GetTickCount() diff --git a/win32ctypes/core/cffi/_time.py b/win32ctypes/core/cffi/_time.py deleted file mode 100644 index ad33846..0000000 --- a/win32ctypes/core/cffi/_time.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# (C) Copyright 2015-18 Enthought, Inc., Austin, TX -# All right reserved. -# -# This file is open source software distributed according to the terms in -# LICENSE.txt -# -from ._util import ffi, dlls - -ffi.cdef(""" - -DWORD WINAPI GetTickCount(void); - -""") - - -def _GetTickCount(): - return dlls.kernel32.GetTickCount() diff --git a/win32ctypes/core/cffi/_winbase.py b/win32ctypes/core/cffi/_winbase.py new file mode 100644 index 0000000..e31d9a4 --- /dev/null +++ b/win32ctypes/core/cffi/_winbase.py @@ -0,0 +1,30 @@ +# +# (C) Copyright 2024 Enthought, Inc., Austin, TX +# All right reserved. +# +# This file is open source software distributed according to the terms in +# LICENSE.txt +# +from ._util import ffi + + +class _FILETIME(object): + + def __call__(self): + return ffi.new("PFILETIME")[0] + + @classmethod + def fromdict(cls, filetime): + factory = cls() + c_filetime = factory() + c_filetime.dwLowDateTime = filetime['dwLowDateTime'] + c_filetime.dwHighDateTime = filetime['dwHighDateTime'] + return c_filetime + + +def PFILETIME(value=None): + return ffi.new("PFILETIME", ffi.NULL if value is None else value) + + +FILETIME = _FILETIME() +LPFILETIME = PFILETIME diff --git a/win32ctypes/core/ctypes/_authentication.py b/win32ctypes/core/ctypes/_authentication.py index 508f4b1..8b62bad 100644 --- a/win32ctypes/core/ctypes/_authentication.py +++ b/win32ctypes/core/ctypes/_authentication.py @@ -1,69 +1,93 @@ # -# (C) Copyright 2014-18 Enthought, Inc., Austin, TX +# (C) Copyright 2014-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # import ctypes -from ctypes import POINTER, Structure, c_void_p, c_wchar_p, c_char_p, cast +from ctypes import POINTER, Structure, c_char_p, cast from ctypes.wintypes import ( - BOOL, DWORD, FILETIME, LPCWSTR) + BOOL, DWORD, LPCWSTR, LPWSTR) from win32ctypes.core.compat import is_text from ._common import LPBYTE, _PyBytes_FromStringAndSize from ._util import function_factory, check_false_factory, dlls -from ._nl_support import _GetACP +from ._winbase import FILETIME -SUPPORTED_CREDKEYS = set(( - u'Type', u'TargetName', u'Persist', - u'UserName', u'Comment', u'CredentialBlob')) +class CREDENTIAL_ATTRIBUTE(Structure): + _fields_ = [ + ('Keyword', LPWSTR), + ('Flags', DWORD), + ('ValueSize', DWORD), + ('Value', LPBYTE)] + + @classmethod + def fromdict(cls, attribute): + c_attribute = cls() + c_pattribute = PCREDENTIAL_ATTRIBUTE(c_attribute) + ctypes.memset(c_pattribute, 0, ctypes.sizeof(c_attribute)) + c_attribute.populate(attribute) + return c_attribute + + def populate(self, attribute): + # Transfer values + self.Keyword = attribute['Keyword'] + self.Flags = attribute.get('Flags', 0) + value = attribute['Value'] + if is_text(value): + blob, size = _make_blob(value) + else: + blob = c_char_p(value) + blob = cast(blob, LPBYTE) + size = len(value) + self.Value = blob + self.ValueSize = size + + +PCREDENTIAL_ATTRIBUTE = POINTER(CREDENTIAL_ATTRIBUTE) class CREDENTIAL(Structure): _fields_ = [ - ("Flags", DWORD), - ("Type", DWORD), - ("TargetName", c_wchar_p), - ("Comment", c_wchar_p), - ("LastWritten", FILETIME), - ("CredentialBlobSize", DWORD), - ("CredentialBlob", LPBYTE), - ("Persist", DWORD), - ("_DO_NOT_USE_AttributeCount", DWORD), - ("__DO_NOT_USE_Attribute", c_void_p), - ("TargetAlias", c_wchar_p), - ("UserName", c_wchar_p)] + ('Flags', DWORD), + ('Type', DWORD), + ('TargetName', LPWSTR), + ('Comment', LPWSTR), + ('LastWritten', FILETIME), + ('CredentialBlobSize', DWORD), + ('CredentialBlob', LPBYTE), + ('Persist', DWORD), + ('AttributeCount', DWORD), + ('Attributes', PCREDENTIAL_ATTRIBUTE), + ('TargetAlias', LPWSTR), + ('UserName', LPWSTR)] @classmethod - def fromdict(cls, credential, flags=0): - unsupported = set(credential.keys()) - SUPPORTED_CREDKEYS - if len(unsupported): - raise ValueError("Unsupported keys: {0}".format(unsupported)) - if flags != 0: - raise ValueError("flag != 0 not yet supported") - - c_creds = cls() - c_pcreds = PCREDENTIAL(c_creds) - - # zero-out memory - ctypes.memset(c_pcreds, 0, ctypes.sizeof(c_creds)) - - for key in SUPPORTED_CREDKEYS: - if key in credential: - if key != 'CredentialBlob': - setattr(c_creds, key, credential[key]) - else: - blob = make_unicode(credential['CredentialBlob']) - blob_data = ctypes.create_unicode_buffer(blob) - # Create_unicode_buffer adds a NULL at the end of the - # string we do not want that. - c_creds.CredentialBlobSize = \ - ctypes.sizeof(blob_data) - \ - ctypes.sizeof(ctypes.c_wchar) - c_creds.CredentialBlob = ctypes.cast(blob_data, LPBYTE) - return c_creds + def fromdict(cls, credential): + c_credential = cls() + c_pcredential = PCREDENTIAL(c_credential) + ctypes.memset(c_pcredential, 0, ctypes.sizeof(c_credential)) + for key in credential: + if key == 'CredentialBlob': + blob_data, blob_size = _make_blob(credential['CredentialBlob']) + c_credential.CredentialBlob = blob_data + c_credential.CredentialBlobSize = blob_size + elif key == 'Attributes': + attributes = credential.get('Attributes', '') + count = len(attributes) + if count == 0: + continue + elif count > 1: + raise ValueError('Multiple attributes are not supported') + c_attribute = CREDENTIAL_ATTRIBUTE.fromdict(attributes[0]) + c_pattribute = PCREDENTIAL_ATTRIBUTE(c_attribute) + c_credential.Attributes = c_pattribute + c_credential.AttributeCount = count + else: + setattr(c_credential, key, credential[key]) + return c_credential PCREDENTIAL = POINTER(CREDENTIAL) @@ -71,52 +95,76 @@ def fromdict(cls, credential, flags=0): PPPCREDENTIAL = POINTER(PPCREDENTIAL) -def make_unicode(text): - """ Convert the input string to unicode. +def credential_attribute2dict(c_attribute): + attribute = {} + attribute['Keyword'] = c_attribute.Keyword + attribute['Flags'] = c_attribute.Flags + size = c_attribute.ValueSize + if size > 0: + value = _PyBytes_FromStringAndSize( + cast(c_attribute.Value, c_char_p), size) + attribute['Value'] = value + return attribute - """ - if is_text(text): - return text - else: - code_page = _GetACP() - return text.decode(encoding=str(code_page), errors='strict') - -def credential2dict(creds): +def credential2dict(c_credential): credential = {} - for key in SUPPORTED_CREDKEYS: - if key != u'CredentialBlob': - credential[key] = getattr(creds, key) - else: + for key, type_ in CREDENTIAL._fields_: + if key == 'CredentialBlob': blob = _PyBytes_FromStringAndSize( - cast(creds.CredentialBlob, c_char_p), - creds.CredentialBlobSize) - credential[u'CredentialBlob'] = blob + cast(c_credential.CredentialBlob, c_char_p), + c_credential.CredentialBlobSize) + credential['CredentialBlob'] = blob + elif key == 'Attributes': + attributes = [] + count = c_credential.AttributeCount + data = c_credential.Attributes + for index in range(count): + attribute = credential_attribute2dict(data[index]) + attributes.append(attribute) + credential['Attributes'] = tuple(attributes) + elif key in ('AttributeCount', 'CredentialBlobSize'): + continue + else: + credential[key] = getattr(c_credential, key) return credential +def _make_blob(data): + ''' Convert a string to LPBYTE compatible blob value + + ''' + blob_data = ctypes.create_unicode_buffer(data) + # Create_unicode_buffer adds a NULL at the end of the + # string we do not want that. + blob_size = ( + ctypes.sizeof(blob_data) - ctypes.sizeof(ctypes.c_wchar)) + blob_pointer = cast(blob_data, LPBYTE) + return blob_pointer, blob_size + + _CredWrite = function_factory( dlls.advapi32.CredWriteW, [PCREDENTIAL, DWORD], BOOL, - check_false_factory("CredWrite")) + check_false_factory('CredWrite')) _CredRead = function_factory( dlls.advapi32.CredReadW, [LPCWSTR, DWORD, DWORD, PPCREDENTIAL], BOOL, - check_false_factory("CredRead")) + check_false_factory('CredRead')) _CredDelete = function_factory( dlls.advapi32.CredDeleteW, [LPCWSTR, DWORD, DWORD], BOOL, - check_false_factory("CredDelete")) + check_false_factory('CredDelete')) _CredEnumerate = function_factory( dlls.advapi32.CredEnumerateW, [LPCWSTR, DWORD, POINTER(DWORD), PPPCREDENTIAL], BOOL, - check_false_factory("CredEnumerate")) + check_false_factory('CredEnumerate')) _CredFree = function_factory(dlls.advapi32.CredFree, [PCREDENTIAL]) diff --git a/win32ctypes/core/ctypes/_system_information.py b/win32ctypes/core/ctypes/_system_information.py index 1a38757..683c3b4 100644 --- a/win32ctypes/core/ctypes/_system_information.py +++ b/win32ctypes/core/ctypes/_system_information.py @@ -1,12 +1,12 @@ # -# (C) Copyright 2018 Enthought, Inc., Austin, TX +# (C) Copyright 2018-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in # LICENSE.txt # import ctypes -from ctypes.wintypes import LPCWSTR, UINT, LPWSTR, MAX_PATH +from ctypes.wintypes import LPCWSTR, UINT, LPWSTR, MAX_PATH, DWORD from ._util import check_zero, function_factory, dlls @@ -23,6 +23,11 @@ def _GetSystemDirectory(): return ctypes.cast(buffer, LPCWSTR).value +_GetTickCount = function_factory( + dlls.kernel32.GetTickCount, + None, DWORD) + + _BaseGetWindowsDirectory = function_factory( dlls.kernel32.GetWindowsDirectoryW, [LPWSTR, UINT], diff --git a/win32ctypes/core/ctypes/_time.py b/win32ctypes/core/ctypes/_time.py deleted file mode 100644 index da8a936..0000000 --- a/win32ctypes/core/ctypes/_time.py +++ /dev/null @@ -1,15 +0,0 @@ -# -# (C) Copyright 2018 Enthought, Inc., Austin, TX -# All right reserved. -# -# This file is open source software distributed according to the terms in -# LICENSE.txt -# -from ctypes.wintypes import DWORD - -from ._util import function_factory, dlls - - -_GetTickCount = function_factory( - dlls.kernel32.GetTickCount, - None, DWORD) diff --git a/win32ctypes/core/ctypes/_winbase.py b/win32ctypes/core/ctypes/_winbase.py new file mode 100644 index 0000000..44ff8e3 --- /dev/null +++ b/win32ctypes/core/ctypes/_winbase.py @@ -0,0 +1,8 @@ +# +# (C) Copyright 2024 Enthought, Inc., Austin, TX +# All right reserved. +# +# This file is open source software distributed according to the terms in +# LICENSE.txt +# +from ctypes.wintypes import FILETIME # noqa diff --git a/win32ctypes/pywin32/win32api.py b/win32ctypes/pywin32/win32api.py index 18e1934..c2eb5f5 100644 --- a/win32ctypes/pywin32/win32api.py +++ b/win32ctypes/pywin32/win32api.py @@ -7,11 +7,9 @@ # """ A module, encapsulating the Windows Win32 API. """ from win32ctypes.core import ( - _common, _dll, _resource, _system_information, _backend, _time) -from win32ctypes.pywin32.pywintypes import pywin32error as _pywin32error - -LOAD_LIBRARY_AS_DATAFILE = 0x2 -LANG_NEUTRAL = 0x00 + _common, _dll, _resource, _system_information, _backend) +from win32ctypes.constants import LOAD_LIBRARY_AS_DATAFILE, LANG_NEUTRAL # noqa +from .pywintypes import pywin32error as _pywin32error def LoadLibraryEx(fileName, handle, flags): @@ -186,7 +184,7 @@ def GetTickCount(): counts : int The millisecond counts since system startup. """ - return _time._GetTickCount() + return _system_information._GetTickCount() def BeginUpdateResource(filename, delete): diff --git a/win32ctypes/pywin32/win32cred.py b/win32ctypes/pywin32/win32cred.py index 0f37f1d..f3651f9 100644 --- a/win32ctypes/pywin32/win32cred.py +++ b/win32ctypes/pywin32/win32cred.py @@ -7,17 +7,19 @@ # """ Interface to credentials management functions. """ from win32ctypes.core import _authentication, _common, _backend -from win32ctypes.pywin32.pywintypes import pywin32error as _pywin32error +from .pywintypes import pywin32error as _pywin32error -CRED_TYPE_GENERIC = 0x1 -CRED_PERSIST_SESSION = 0x1 -CRED_PERSIST_LOCAL_MACHINE = 0x2 -CRED_PERSIST_ENTERPRISE = 0x3 -CRED_PRESERVE_CREDENTIAL_BLOB = 0 -CRED_ENUMERATE_ALL_CREDENTIALS = 0x1 +# Bring constants into namespace +from win32ctypes.constants import ( # noqa + CRED_TYPE_GENERIC, + CRED_PERSIST_SESSION, + CRED_PERSIST_LOCAL_MACHINE, + CRED_PERSIST_ENTERPRISE, + CRED_ENUMERATE_ALL_CREDENTIALS, + CRED_PERSIST_ENTERPRISE) -def CredWrite(Credential, Flags=CRED_PRESERVE_CREDENTIAL_BLOB): +def CredWrite(Credential, Flags=0): """ Creates or updates a stored credential. Parameters @@ -26,10 +28,15 @@ def CredWrite(Credential, Flags=CRED_PRESERVE_CREDENTIAL_BLOB): A dictionary corresponding to the PyWin32 ``PyCREDENTIAL`` structure. Flags : int - Always pass ``CRED_PRESERVE_CREDENTIAL_BLOB`` (i.e. 0). + ``CRED_PRESERVE_CREDENTIAL_BLOB`` or 0. Default is 0. + + Note + ---- + The max number of the credential attributes supported by + the win32ctypes implementation is 1. """ - c_creds = _authentication.CREDENTIAL.fromdict(Credential, Flags) + c_creds = _authentication.CREDENTIAL.fromdict(Credential) c_pcreds = _authentication.PCREDENTIAL(c_creds) with _pywin32error(): _authentication._CredWrite(c_pcreds, 0) diff --git a/win32ctypes/tests/test_authentication.py b/win32ctypes/tests/test_authentication.py new file mode 100644 index 0000000..33d4fae --- /dev/null +++ b/win32ctypes/tests/test_authentication.py @@ -0,0 +1,149 @@ +# +# (C) Copyright 2022-2024 Enthought, Inc., Austin, TX +# All right reserved. +# +# This file is open source software distributed according to the terms in +# LICENSE.txt +# +import unittest + +from win32ctypes.core import _backend +from win32ctypes.core._authentication import ( + CREDENTIAL, CREDENTIAL_ATTRIBUTE, + credential2dict, credential_attribute2dict) +from win32ctypes.core._winbase import FILETIME +from win32ctypes.constants import ( + CRED_TYPE_GENERIC, CRED_PERSIST_ENTERPRISE) + + +class TestCREDENTIAL(unittest.TestCase): + + maxDiff = None + + def test_fromdict(self): + # given + username = 'john' + password = 'doefsajfsakfj' + comment = 'Created by MiniPyWin32Cred test suite' + target = '{0}@{1}'.format(username, password) + data = { + 'Type': CRED_TYPE_GENERIC, + 'TargetName': target, + 'UserName': username, + 'CredentialBlob': password, + 'Comment': comment, + 'Persist': CRED_PERSIST_ENTERPRISE} + + # when + result = CREDENTIAL.fromdict(data) + + # then + self.assertEqual(result.Type, CRED_TYPE_GENERIC) + self.assertEqual(result.Flags, 0) + self.assertEqual(result.AttributeCount, 0) + self.assertEqual(result.CredentialBlobSize, 26) + self.assertEqual(result.Persist, CRED_PERSIST_ENTERPRISE) + if _backend == 'cffi': + from win32ctypes.core.cffi._util import ffi + self.assertEqual(ffi.string(result.UserName), username) + self.assertEqual(result.TargetAlias, ffi.NULL) + self.assertEqual(ffi.string(result.Comment), comment) + self.assertNotEqual(result.CredentialBlob, ffi.NULL) + else: + self.assertEqual(result.Comment, comment) + self.assertEqual(result.UserName, username) + self.assertIsNone(result.TargetAlias) + + def test_roundtrip(self): + # given + username = 'john' + password = 'doefsajfsakfj' + comment = 'Created by MiniPyWin32Cred test suite' + target = '{0}@{1}'.format(username, password) + keyword = 'mysecret-attribute' + value = 'Created by MiniPyWin32Cred test suite' + attribute1 = {'Keyword': keyword, 'Value': value, 'Flags': 2} + data = { + 'Flags': 2, + 'TargetAlias': 'test', + 'Type': CRED_TYPE_GENERIC, + 'TargetName': target, + 'UserName': username, + 'CredentialBlob': password, + 'Comment': comment, + 'Attributes': (attribute1,), + 'Persist': CRED_PERSIST_ENTERPRISE} + + # when + for _ in range(10): # need to repeat to expose memory issues + credential = CREDENTIAL.fromdict(data) + result = credential2dict(credential) + + # then + if _backend == 'ctypes': + self.assertIsInstance(result['LastWritten'], FILETIME) + del result['LastWritten'] + result['CredentialBlob'] = result['CredentialBlob'].decode('utf-16') # noqa + attribute = result['Attributes'][0] + attribute['Value'] = attribute['Value'].decode('utf-16') + self.assertEqual(result, data) + + +class TestCREDENTIAL_ATTRIBUTE(unittest.TestCase): + + def test_fromdict(self): + # given + keyword = 'mysecret-attribute' + value = 'Created by MiniPyWin32Cred test suite' + data = { + 'Keyword': keyword, + 'Flags': 2, + 'Value': value} + + # when + result = CREDENTIAL_ATTRIBUTE.fromdict(data) + + # then + self.assertEqual(result.Flags, 2) + self.assertIsNotNone(result.Value) + self.assertEqual(result.ValueSize, 74) + if _backend == 'cffi': + from win32ctypes.core.cffi._util import ffi + self.assertEqual( + ffi.string(result.Keyword), 'mysecret-attribute') + else: + self.assertEqual(result.Keyword, 'mysecret-attribute') + + def test_roundtrip_with_string(self): + # given + keyword = 'mysecret-attribute' + value = 'Created by MiniPyWin32Cred test suite' + data = { + 'Keyword': keyword, + 'Flags': 7, + 'Value': value} + + # when + for _ in range(10): # need to repeat to expose memory issues + attribute = CREDENTIAL_ATTRIBUTE.fromdict(data) + result = credential_attribute2dict(attribute) + # then + result['Value'] = result['Value'].decode('utf-16') + self.assertEqual(result, data) + + def test_roundtrip_with_bytes(self): + # given + keyword = 'mysecret-attribute' + value = b'Created by MiniPyWin32Cred test suite' + data = { + 'Keyword': keyword, + 'Flags': 7, + 'Value': value} + + # when + for _ in range(10): # need to repeat to expose memory issues + attribute = CREDENTIAL_ATTRIBUTE.fromdict(data) + result = credential_attribute2dict(attribute) + + # then + self.assertEqual(result, data) diff --git a/win32ctypes/tests/test_backends.py b/win32ctypes/tests/test_backends.py index 97a7de8..4986e0c 100644 --- a/win32ctypes/tests/test_backends.py +++ b/win32ctypes/tests/test_backends.py @@ -1,5 +1,5 @@ # -# (C) Copyright 2023 Enthought, Inc., Austin, TX +# (C) Copyright 2023-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in @@ -11,7 +11,7 @@ from win32ctypes.core import _backend _modules = [ - '_dll', '_authentication', '_time', '_common', + '_dll', '_authentication', '_common', '_resource', '_nl_support', '_system_information'] diff --git a/win32ctypes/tests/test_win32cred.py b/win32ctypes/tests/test_win32cred.py index b34506a..473b8ee 100644 --- a/win32ctypes/tests/test_win32cred.py +++ b/win32ctypes/tests/test_win32cred.py @@ -1,5 +1,5 @@ # -# (C) Copyright 2014 Enthought, Inc., Austin, TX +# (C) Copyright 2014-2024 Enthought, Inc., Austin, TX # All right reserved. # # This file is open source software distributed according to the terms in @@ -10,6 +10,7 @@ import unittest import win32cred +from parameterized import parameterized from win32ctypes.core._winerrors import ERROR_NOT_FOUND from win32ctypes.pywin32.pywintypes import error @@ -29,6 +30,40 @@ pywin32_build = None +def _demo_attributes(multiple=False): + keyword = 'mysecret-attribute' + attribute1 = { + 'Keyword': keyword, + 'Value': b'Created by MiniPyWin32Cred test suite', 'Flags': 0} + attribute2 = { + 'Keyword': keyword + '12', + 'Value': b'Attribute from MiniPyWin32', 'Flags': 0} + if multiple: + return (attribute1, attribute2) + else: + return (attribute1,) + + +def _demo_credentials(UserName=u'jone', multiple=False): + return { + 'Type': CRED_TYPE_GENERIC, + 'TargetName': u'jone@doe', + 'UserName': UserName, + 'CredentialBlob': u'doefsajfsakfj', + 'Attributes': _demo_attributes(multiple), + 'Comment': u'Created by MiniPyWin32Cred test suite', + 'Persist': CRED_PERSIST_ENTERPRISE} + + +TEST_SUPPORTED_CREDENTIALS = [ + (_demo_credentials(), _demo_attributes()), + (_demo_credentials(UserName=None), _demo_attributes())] + +TEST_CREDENTIALS = TEST_SUPPORTED_CREDENTIALS + [ + (_demo_credentials(multiple=True), _demo_attributes(multiple=True)), + (_demo_credentials(UserName=None, multiple=True), _demo_attributes(multiple=True))] # noqa + + class TestCred(unittest.TestCase): def setUp(self): @@ -38,22 +73,13 @@ def setUp(self): except error: pass - def _demo_credentials(self, UserName=u'jone'): - return { - "Type": CRED_TYPE_GENERIC, - "TargetName": u'jone@doe', - "UserName": UserName, - "CredentialBlob": u"doefsajfsakfj", - "Comment": u"Created by MiniPyWin32Cred test suite", - "Persist": CRED_PERSIST_ENTERPRISE} - + @parameterized.expand(TEST_SUPPORTED_CREDENTIALS) @unittest.skipIf( - pywin32_build == "223" and sys.version_info[:2] == (3, 7), - "pywin32 version 223 bug with CredRead (mhammond/pywin32#1232)") - def test_write_to_pywin32(self): + pywin32_build == '223' and sys.version_info[:2] == (3, 7), + 'pywin32 version 223 bug with CredRead (mhammond/pywin32#1232)') + def test_write_to_pywin32(self, r_credentials, r_attributes): # given target = u'jone@doe' - r_credentials = self._demo_credentials() CredWrite(r_credentials) # when @@ -61,118 +87,72 @@ def test_write_to_pywin32(self): TargetName=target, Type=CRED_TYPE_GENERIC) # then - self.assertEqual(credentials["Type"], CRED_TYPE_GENERIC) - self.assertEqual(credentials["UserName"], u"jone") - self.assertEqual(credentials["TargetName"], u'jone@doe') + self.assertEqual(credentials['Type'], CRED_TYPE_GENERIC) + self.assertEqual(credentials['UserName'], r_credentials['UserName']) + self.assertEqual(credentials['TargetName'], 'jone@doe') self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") + credentials['Comment'], 'Created by MiniPyWin32Cred test suite') # XXX: the fact that we have to decode the password when reading, but # not encode when writing is a bit strange, but that's what pywin32 # seems to do and we try to be backward compatible here. self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") + credentials['CredentialBlob'].decode('utf-16'), 'doefsajfsakfj') - def test_read_from_pywin32(self): + @parameterized.expand(TEST_CREDENTIALS) + def test_read_from_pywin32(self, r_credentials, r_attributes): # given - target = u'jone@doe' - r_credentials = self._demo_credentials() + target = 'jone@doe' win32cred.CredWrite(r_credentials) # when credentials = CredRead(target, CRED_TYPE_GENERIC) # then - self.assertEqual(credentials["UserName"], u"jone") - self.assertEqual(credentials["TargetName"], u'jone@doe') + self.assertEqual(credentials['UserName'], r_credentials['UserName']) + self.assertEqual(credentials['TargetName'], 'jone@doe') + self.assertEqual(credentials['Attributes'], r_attributes) self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") + credentials['Comment'], 'Created by MiniPyWin32Cred test suite') self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") + credentials['CredentialBlob'].decode('utf-16'), 'doefsajfsakfj') - def test_read_from_pywin32_with_none_usename(self): + @parameterized.expand(TEST_SUPPORTED_CREDENTIALS) + def test_read_write(self, r_credentials, r_attributes): # given - target = u'jone@doe' - r_credentials = self._demo_credentials(None) - win32cred.CredWrite(r_credentials) - - # when - credentials = CredRead(target, CRED_TYPE_GENERIC) - - self.assertEqual(credentials["UserName"], None) - self.assertEqual(credentials["TargetName"], u'jone@doe') - self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") - self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") - - def test_write_to_pywin32_with_none_usename(self): - # given - target = u'jone@doe' - r_credentials = self._demo_credentials(None) - CredWrite(r_credentials) - - # when - credentials = win32cred.CredRead(target, CRED_TYPE_GENERIC) - - self.assertEqual(credentials["UserName"], None) - self.assertEqual(credentials["TargetName"], u'jone@doe') - self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") - self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") - - def test_read_write(self): - # given - target = u'jone@doe' - r_credentials = self._demo_credentials() - - # when - CredWrite(r_credentials) - credentials = CredRead(target, CRED_TYPE_GENERIC) - - self.assertEqual(credentials["UserName"], u"jone") - self.assertEqual(credentials["TargetName"], u'jone@doe') - self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") - self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") - - def test_read_write_with_none_username(self): - # given - target = u'jone@doe' - r_credentials = self._demo_credentials(None) + target = 'jone@doe' + r_credentials = r_credentials # when CredWrite(r_credentials) credentials = CredRead(target, CRED_TYPE_GENERIC) - # then - self.assertEqual(credentials["UserName"], None) - self.assertEqual(credentials["TargetName"], u'jone@doe') + self.assertEqual(credentials['UserName'], r_credentials['UserName']) + self.assertEqual(credentials['TargetName'], 'jone@doe') + self.assertEqual(credentials['Attributes'], r_attributes) self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") + credentials['Comment'], 'Created by MiniPyWin32Cred test suite') self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") + credentials['CredentialBlob'].decode('utf-16'), 'doefsajfsakfj') def test_enumerate_filter(self): # given - r_credentials = self._demo_credentials() + r_credentials = _demo_credentials() CredWrite(r_credentials) # when credentials = CredEnumerate('jone*')[0] # then - self.assertEqual(credentials["UserName"], u"jone") - self.assertEqual(credentials["TargetName"], u'jone@doe') + self.assertEqual(credentials['UserName'], 'jone') + self.assertEqual(credentials['TargetName'], 'jone@doe') self.assertEqual( - credentials["Comment"], u"Created by MiniPyWin32Cred test suite") + credentials['Comment'], 'Created by MiniPyWin32Cred test suite') self.assertEqual( - credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj") + credentials['CredentialBlob'].decode('utf-16'), 'doefsajfsakfj') def test_enumerate_no_filter(self): # given - r_credentials = self._demo_credentials() + r_credentials = _demo_credentials() CredWrite(r_credentials) # when @@ -191,7 +171,7 @@ def test_enumerate_all(self): def test_read_doesnt_exists(self): # given - target = "Floupi_dont_exists@MiniPyWin" + target = 'Floupi_dont_exists@MiniPyWin' # when/then with self.assertRaises(error) as ctx: @@ -200,11 +180,11 @@ def test_read_doesnt_exists(self): def test_delete_simple(self): # given - target = u'jone@doe' - r_credentials = self._demo_credentials() + target = 'jone@doe' + r_credentials = _demo_credentials() CredWrite(r_credentials, 0) credentials = CredRead(target, CRED_TYPE_GENERIC) - self.assertTrue(credentials is not None) + self.assertIsNotNone(credentials) # when CredDelete(target, CRED_TYPE_GENERIC) @@ -213,17 +193,17 @@ def test_delete_simple(self): with self.assertRaises(error) as ctx: CredRead(target, CRED_TYPE_GENERIC) self.assertEqual(ctx.exception.winerror, ERROR_NOT_FOUND) - self.assertEqual(ctx.exception.funcname, "CredRead") + self.assertEqual(ctx.exception.funcname, 'CredRead') def test_delete_doesnt_exists(self): # given - target = u"Floupi_doesnt_exists@MiniPyWin32" + target = 'Floupi_doesnt_exists@MiniPyWin32' # when/then with self.assertRaises(error) as ctx: CredDelete(target, CRED_TYPE_GENERIC) self.assertEqual(ctx.exception.winerror, ERROR_NOT_FOUND) - self.assertEqual(ctx.exception.funcname, "CredDelete") + self.assertEqual(ctx.exception.funcname, 'CredDelete') if __name__ == '__main__':