Skip to content

Commit 798a9d0

Browse files
authored
Fix support for None username in credentials (#119)
* Update testing to check for bug #99 * Fix cffi credential backend to correctly manage None as UserName * Augment tests for credentials with none username
1 parent 5b1be6e commit 798a9d0

File tree

2 files changed

+73
-30
lines changed

2 files changed

+73
-30
lines changed

win32ctypes/core/cffi/_authentication.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,24 @@ def fromdict(cls, credential, flag=0):
8888
c_creds = factory()
8989
# values to ref and make sure that they will not go away
9090
values = []
91-
for key in SUPPORTED_CREDKEYS:
92-
if key in credential:
93-
if key == u'CredentialBlob':
94-
blob = make_unicode(credential['CredentialBlob'])
95-
blob_data = ffi.new('wchar_t[]', blob)
96-
# new adds a NULL at the end that we do not want.
97-
c_creds.CredentialBlobSize = \
98-
ffi.sizeof(blob_data) - ffi.sizeof('wchar_t')
99-
c_creds.CredentialBlob = ffi.cast('LPBYTE', blob_data)
100-
values.append(blob_data)
101-
elif key in (u'Type', u'Persist'):
102-
setattr(c_creds, key, credential[key])
103-
else:
104-
blob = make_unicode(credential[key])
105-
value = ffi.new('wchar_t[]', blob)
106-
values.append(value)
107-
setattr(c_creds, key, ffi.cast('LPTSTR', value))
91+
for key, value in credential.items():
92+
if key == u'CredentialBlob':
93+
blob = make_unicode(value)
94+
blob_data = ffi.new('wchar_t[]', blob)
95+
# new adds a NULL at the end that we do not want.
96+
c_creds.CredentialBlobSize = \
97+
ffi.sizeof(blob_data) - ffi.sizeof('wchar_t')
98+
c_creds.CredentialBlob = ffi.cast('LPBYTE', blob_data)
99+
values.append(blob_data)
100+
elif key in (u'Type', u'Persist'):
101+
setattr(c_creds, key, value)
102+
elif value is None:
103+
setattr(c_creds, key, ffi.NULL)
104+
else:
105+
blob = make_unicode(value)
106+
pblob = ffi.new('wchar_t[]', blob)
107+
values.append(pblob)
108+
setattr(c_creds, key, ffi.cast('LPTSTR', pblob))
108109
# keep values alive until c_creds goes away.
109110
_keep_alive[c_creds] = tuple(values)
110111
return c_creds
@@ -136,7 +137,7 @@ def credential2dict(pc_creds):
136137
else:
137138
string_pointer = getattr(pc_creds, key)
138139
if string_pointer == ffi.NULL:
139-
data = u''
140+
data = None
140141
else:
141142
data = ffi.string(string_pointer)
142143
credentials[key] = data

win32ctypes/tests/test_win32cred.py

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ def setUp(self):
3838
except error:
3939
pass
4040

41-
def _demo_credentials(self):
41+
def _demo_credentials(self, UserName=u'jone'):
4242
return {
4343
"Type": CRED_TYPE_GENERIC,
4444
"TargetName": u'jone@doe',
45-
"UserName": u'jone',
45+
"UserName": UserName,
4646
"CredentialBlob": u"doefsajfsakfj",
4747
"Comment": u"Created by MiniPyWin32Cred test suite",
4848
"Persist": CRED_PERSIST_ENTERPRISE}
@@ -66,6 +66,9 @@ def test_write_to_pywin32(self):
6666
self.assertEqual(credentials["TargetName"], u'jone@doe')
6767
self.assertEqual(
6868
credentials["Comment"], u"Created by MiniPyWin32Cred test suite")
69+
# XXX: the fact that we have to decode the password when reading, but
70+
# not encode when writing is a bit strange, but that's what pywin32
71+
# seems to do and we try to be backward compatible here.
6972
self.assertEqual(
7073
credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj")
7174

@@ -79,16 +82,45 @@ def test_read_from_pywin32(self):
7982
credentials = CredRead(target, CRED_TYPE_GENERIC)
8083

8184
# then
82-
# XXX: the fact that we have to decode the password when reading, but
83-
# not encode when writing is a bit strange, but that's what pywin32
84-
# seems to do as well, and we try to be backward compatible here.
8585
self.assertEqual(credentials["UserName"], u"jone")
8686
self.assertEqual(credentials["TargetName"], u'jone@doe')
8787
self.assertEqual(
8888
credentials["Comment"], u"Created by MiniPyWin32Cred test suite")
8989
self.assertEqual(
9090
credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj")
9191

92+
def test_read_from_pywin32_with_none_usename(self):
93+
# given
94+
target = u'jone@doe'
95+
r_credentials = self._demo_credentials(None)
96+
win32cred.CredWrite(r_credentials)
97+
98+
# when
99+
credentials = CredRead(target, CRED_TYPE_GENERIC)
100+
101+
self.assertEqual(credentials["UserName"], None)
102+
self.assertEqual(credentials["TargetName"], u'jone@doe')
103+
self.assertEqual(
104+
credentials["Comment"], u"Created by MiniPyWin32Cred test suite")
105+
self.assertEqual(
106+
credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj")
107+
108+
def test_write_to_pywin32_with_none_usename(self):
109+
# given
110+
target = u'jone@doe'
111+
r_credentials = self._demo_credentials(None)
112+
CredWrite(r_credentials)
113+
114+
# when
115+
credentials = win32cred.CredRead(target, CRED_TYPE_GENERIC)
116+
117+
self.assertEqual(credentials["UserName"], None)
118+
self.assertEqual(credentials["TargetName"], u'jone@doe')
119+
self.assertEqual(
120+
credentials["Comment"], u"Created by MiniPyWin32Cred test suite")
121+
self.assertEqual(
122+
credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj")
123+
92124
def test_read_write(self):
93125
# given
94126
target = u'jone@doe'
@@ -98,17 +130,30 @@ def test_read_write(self):
98130
CredWrite(r_credentials)
99131
credentials = CredRead(target, CRED_TYPE_GENERIC)
100132

101-
# then
102-
# XXX: the fact that we have to decode the password when reading, but
103-
# not encode when writing is a bit strange, but that's what pywin32
104-
# seems to do as well, and we try to be backward compatible here.
105133
self.assertEqual(credentials["UserName"], u"jone")
106134
self.assertEqual(credentials["TargetName"], u'jone@doe')
107135
self.assertEqual(
108136
credentials["Comment"], u"Created by MiniPyWin32Cred test suite")
109137
self.assertEqual(
110138
credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj")
111139

140+
def test_read_write_with_none_username(self):
141+
# given
142+
target = u'jone@doe'
143+
r_credentials = self._demo_credentials(None)
144+
145+
# when
146+
CredWrite(r_credentials)
147+
credentials = CredRead(target, CRED_TYPE_GENERIC)
148+
149+
# then
150+
self.assertEqual(credentials["UserName"], None)
151+
self.assertEqual(credentials["TargetName"], u'jone@doe')
152+
self.assertEqual(
153+
credentials["Comment"], u"Created by MiniPyWin32Cred test suite")
154+
self.assertEqual(
155+
credentials["CredentialBlob"].decode('utf-16'), u"doefsajfsakfj")
156+
112157
def test_enumerate_filter(self):
113158
# given
114159
r_credentials = self._demo_credentials()
@@ -118,9 +163,6 @@ def test_enumerate_filter(self):
118163
credentials = CredEnumerate('jone*')[0]
119164

120165
# then
121-
# XXX: the fact that we have to decode the password when reading, but
122-
# not encode when writing is a bit strange, but that's what pywin32
123-
# seems to do as well, and we try to be backward compatible here.
124166
self.assertEqual(credentials["UserName"], u"jone")
125167
self.assertEqual(credentials["TargetName"], u'jone@doe')
126168
self.assertEqual(

0 commit comments

Comments
 (0)