[Checkins] SVN: zc.winauth/trunk/src/zc/winauth/ Initial import. A
pluggable authentication plugin for interacting with Windows
authentication.
Gary Poster
gary at zope.com
Tue Aug 15 17:27:30 EDT 2006
Log message for revision 69546:
Initial import. A pluggable authentication plugin for interacting with Windows authentication.
Changed:
A zc.winauth/trunk/src/zc/winauth/README.txt
A zc.winauth/trunk/src/zc/winauth/__init__.py
A zc.winauth/trunk/src/zc/winauth/configure.zcml
A zc.winauth/trunk/src/zc/winauth/integration.txt
A zc.winauth/trunk/src/zc/winauth/interfaces.py
A zc.winauth/trunk/src/zc/winauth/tests.py
A zc.winauth/trunk/src/zc/winauth/winauth.py
-=-
Added: zc.winauth/trunk/src/zc/winauth/README.txt
===================================================================
--- zc.winauth/trunk/src/zc/winauth/README.txt 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/README.txt 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1,169 @@
+======================
+Windows Authentication
+======================
+
+This package provides for authenticating windows users with their domain
+user names and passwords.
+
+---------------------------
+WindowsAuthenticationPlugin
+---------------------------
+
+The authentication is done via a WindowsAuthenticationPlugin:
+
+ >>> from zc.winauth.winauth import WindowsAuthenticationPlugin
+ >>> wap = WindowsAuthenticationPlugin()
+
+When a user needs to be authenticated there credentials are passed to the
+plugin:
+
+ >>> credentials = {'login': 'jdoe', 'password': 'pass'}
+
+The result of authentication is information about the principal:
+
+ >>> info = wap.authenticateCredentials(credentials)
+ >>> info is not None
+ True
+
+The IDs are constructed using Windows' SIDs:
+
+ >>> info.id
+ 'zc.winauth.S-...'
+
+They also have a title:
+
+ >>> info.title
+ u'John Doe'
+
+If an unknown user name or incorrect password are given, None is returned:
+
+ >>> badCredentials = {'login': 'jdoe', 'password': 'wrong'}
+ >>> wap.authenticateCredentials(badCredentials) is None
+ True
+
+If the credentials are None, then None is returned:
+
+ >>> print wap.authenticateCredentials(None)
+ None
+
+
+------
+Prefix
+------
+
+The plugin uses a prefix for all principal IDs:
+
+ >>> wap.prefix
+ 'zc.winauth'
+
+If an ID is passed to principalInfo that doesn't have the right prefix, None is
+returned:
+
+ >>> print wap.principalInfo('different.prefix')
+ None
+
+-------------
+Server Errors
+-------------
+
+If an error occurrs while checking a user's password, the credentials are
+denied:
+
+ >>> bool(wap.checkPassword('error', 'pass'))
+ False
+
+Something similar happens if an error occurrs while retrieving principal info:
+
+ >>> print wap.principalInfo('zc.winauth.error')
+ None
+
+If the error is that the domain controller we're using is no longer available,
+the result will be as if the user doesn't exist (dc_is_dead is just a knob for
+testing).
+
+ >>> win32net.dc_is_dead = True
+ >>> print wap.authenticateCredentials(credentials)
+ None
+ >>> print wap.principalInfo('zc.winauth.jsmith')
+ None
+ >>> win32net.dc_is_dead = False
+
+-------------
+Missing Title
+-------------
+
+If a user has an empty "full_name", their user name will be used instead:
+
+ >>> info = wap.principalInfo('zc.winauth.S-1-5-2')
+ >>> info.title
+ 'jsmith'
+
+-------------------------
+Repeating bad credentials
+-------------------------
+
+In the face of bad credentials, authentication will only be attempted once
+during a timeout period. This is to resolve problems when a policy of locking
+out accounts after a number of failed login attempts is in effect.
+
+ >>> credentials = {'login': 'jdoe', 'password': 'pass'}
+ >>> wap.authenticateCredentials(credentials) is not None
+ True
+
+If the credentials suddenly start failing...
+
+ >>> win32security.all_logins_are_bad = True
+ >>> wap.authenticateCredentials(credentials) is not None
+ False
+
+...they will continue to fail.
+
+ >>> win32security.all_logins_are_bad = False
+ >>> wap.authenticateCredentials(credentials) is not None
+ False
+ >>> wap.authenticateCredentials(credentials) is not None
+ False
+
+After a timeout, the credentials will work again.
+
+ >>> import zc.winauth.winauth
+ >>> original_timeout = zc.winauth.winauth.BAD_CREDENTIALS_TIMEOUT
+ >>> zc.winauth.winauth.BAD_CREDENTIALS_TIMEOUT = 0
+ >>> wap.authenticateCredentials(credentials) is not None
+ True
+ >>> zc.winauth.winauth.BAD_CREDENTIALS_TIMEOUT = original_timeout
+
+It is common for credentials == None at some point *between* authentication of
+the real credentials. So, if again the credentials initially work...
+
+ >>> credentials = {'login': 'jdoe', 'password': 'pass'}
+ >>> wap.authenticateCredentials(credentials) is not None
+ True
+
+...and then suddenly start failing...
+
+ >>> win32security.all_logins_are_bad = True
+ >>> wap.authenticateCredentials(credentials) is not None
+ False
+
+...they will continue to fail, even if None credentials are tried between.
+
+ >>> win32security.all_logins_are_bad = False
+ >>> wap.authenticateCredentials(credentials) is not None
+ False
+ >>> wap.authenticateCredentials(None) is not None
+ False
+ >>> wap.authenticateCredentials(credentials) is not None
+ False
+
+After a timeout, the credentials will work again.
+
+ >>> import zc.winauth.winauth
+ >>> original_timeout = zc.winauth.winauth.BAD_CREDENTIALS_TIMEOUT
+ >>> zc.winauth.winauth.BAD_CREDENTIALS_TIMEOUT = 0
+ >>> wap.authenticateCredentials(credentials) is not None
+ True
+
+We need to set the timeout back.
+
+ >>> zc.winauth.winauth.BAD_CREDENTIALS_TIMEOUT = original_timeout
Property changes on: zc.winauth/trunk/src/zc/winauth/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.winauth/trunk/src/zc/winauth/__init__.py
===================================================================
--- zc.winauth/trunk/src/zc/winauth/__init__.py 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/__init__.py 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1 @@
+#
Added: zc.winauth/trunk/src/zc/winauth/configure.zcml
===================================================================
--- zc.winauth/trunk/src/zc/winauth/configure.zcml 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/configure.zcml 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1,20 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain="zc.winauth">
+
+ <localUtility class=".winauth.WindowsAuthenticationPlugin">
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.authentication.interfaces.IAuthenticatorPlugin"
+ />
+ </localUtility>
+
+ <browser:addMenuItem
+ title="Windows Authentication Plugin"
+ description="A Windows PAU Authentication Plugin"
+ class=".winauth.WindowsAuthenticationPlugin"
+ permission="zope.ManageContent"
+ />
+
+</configure>
Added: zc.winauth/trunk/src/zc/winauth/integration.txt
===================================================================
--- zc.winauth/trunk/src/zc/winauth/integration.txt 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/integration.txt 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1,44 @@
+======================
+Windows Authentication
+======================
+
+This package provides for authenticating windows users with their domain
+user names and passwords.
+
+WindowsAuthenticationPlugin
+===========================
+
+The authentication is done via a WindowsAuthenticationPlugin.
+
+ >>> from zc.winauth.winauth import WindowsAuthenticationPlugin
+ >>> wap = WindowsAuthenticationPlugin()
+
+When a user needs to be authenticated there credentials are passed to the
+plugin.
+
+ >>> credentials = {'login': 'testy', 'password': 'test'}
+
+The result of authentication is the principal's name with a prefix, and a
+little information about the principal.
+
+ >>> info = wap.authenticateCredentials(credentials)
+ >>> info is not None
+ True
+
+Winauth uses the Windows SID as part of the ID.
+
+ >>> info.id
+ u'...S-...-...-...'
+ >>> info.title
+ u'Testy Testerson'
+
+If an unknown user name or incorrect password are given, None is returned.
+
+ >>> badCredentials = {'login': 'testy', 'password': 'wrong'}
+ >>> wap.authenticateCredentials(badCredentials) is None
+ True
+
+If a bad ID is used to get principal info, no info is returned:
+
+ >>> print wap.principalInfo('zc.winauth.bad')
+ None
Property changes on: zc.winauth/trunk/src/zc/winauth/integration.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.winauth/trunk/src/zc/winauth/interfaces.py
===================================================================
--- zc.winauth/trunk/src/zc/winauth/interfaces.py 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/interfaces.py 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Additional interfaces for zc.winauth.
+
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+
+
+class IUserInfo(zope.interface.Interface):
+
+ """Additional user information provided by the zc.winauth plugin."""
+
+ name = zope.schema.TextLine(
+ title=u"Short name for the login account",
+ description=u"This is usually the login name for the user.",
+ )
Added: zc.winauth/trunk/src/zc/winauth/tests.py
===================================================================
--- zc.winauth/trunk/src/zc/winauth/tests.py 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/tests.py 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1,179 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+import sys, unittest, warnings, os
+import doctest
+from zope.testing.doctest import DocFileSuite
+import zc.winauth.winauth
+
+
+class FakeHandle:
+ def __init__(self, sid):
+ self.sid = sid
+
+ def Close(self):
+ pass
+
+
+class FakeError:
+ def __init__(self, *args):
+ self.args = args
+
+ def __getitem__(self, index):
+ return self.args[index]
+
+
+class FakeWin32netcon:
+ FILTER_NORMAL_ACCOUNT = 0
+
+
+class FakeWin32security:
+ LOGON32_LOGON_NETWORK = 1
+ LOGON32_PROVIDER_DEFAULT = 2
+ TokenUser = 3
+ all_logins_are_bad = False
+
+ def LogonUser(self, userName, domain, password, logonType, logonProvider):
+ # if we are currently dis-allowing all logins...
+ if self.all_logins_are_bad:
+ raise pywintypes.error(1326)
+
+ if userName == 'jdoe' and password == 'pass':
+ return FakeHandle(sid='S-1-5-1')
+ elif userName == 'error':
+ raise pywintypes.error(999999999)
+ else:
+ raise pywintypes.error(1326)
+
+ def GetTokenInformation(self, handle, token_type):
+ return [handle.sid]
+
+ def ConvertSidToStringSid(self, sid):
+ return sid
+
+ def GetBinarySid(self, sid):
+ if sid.startswith('S-'):
+ return sid
+ else:
+ raise ValueError
+
+ def LookupAccountSid(self, domain, sid):
+ assert domain is None
+ if sid == 'S-1-5-1':
+ return ['jdoe']
+ if sid == 'S-1-5-2':
+ return ['jsmith']
+ raise RuntimeError('uknown SID "%s"' % sid)
+
+ def LookupAccountName(self, domain, name):
+ assert domain is None
+ if name == 'jdoe':
+ return ['S-1-5-1']
+ if name == 'jsmith':
+ return ['S-1-5-2']
+ if name == 'extra':
+ return ['S-1-5-3']
+ raise RuntimeError('uknown name "%s"' % name)
+
+
+class FakePywintypes:
+ error = FakeError
+
+
+class FakeWin32net:
+ dc_is_dead = False
+
+ def NetUserGetInfo(self, server, username, *args):
+ if username == 'jdoe':
+ return {'name': username, 'full_name': u'John Doe'}
+ elif username == 'jsmith':
+ return {'name': username, 'full_name': u''}
+ else:
+ raise pywintypes.error(1326)
+
+ def NetGetDCName(self, *args):
+ if self.dc_is_dead:
+ raise FakeError(999999999)
+ return 'DomainControllerName'
+
+ def NetServerGetInfo(self, *args):
+ if self.dc_is_dead:
+ raise FakeError(999999999)
+
+ def NetUserEnum(self, server, level, filter, handle, prefLen=None):
+ if handle == 0:
+ data = [self.NetUserGetInfo(server, username)
+ for username in ['jdoe', 'jsmith']]
+ return data, len(data)+1, 1
+ else:
+ data = [{'name': 'extra', 'full_name': 'Extra Guy'}]
+ return data, len(data)+1, 0
+
+
+def setUp(test):
+ # set up some fake modules
+ global pywintypes
+ global win32net
+ pywintypes = zc.winauth.winauth.pywintypes = FakePywintypes()
+ win32net = zc.winauth.winauth.win32net = FakeWin32net()
+ win32security = zc.winauth.winauth.win32security = FakeWin32security()
+ zc.winauth.winauth.win32netcon = FakeWin32netcon()
+ test.globs['win32net'] = win32net
+ test.globs['win32security'] = win32security
+
+def tearDown(test):
+ global pywintypes
+ global win32net
+ # remove the fake modules
+ if sys.platform.startswith('win'):
+ import pywintypes, win32security, win32net, win32netcon
+ zc.winauth.winauth.pywintypes = pywintypes
+ zc.winauth.winauth.win32security = win32security
+ zc.winauth.winauth.win32net = win32net
+ zc.winauth.winauth.win32netcon = win32netcon
+ else:
+ del pywintypes
+ del win32net
+ del zc.winauth.winauth.pywintypes
+ del zc.winauth.winauth.win32security
+ del zc.winauth.winauth.win32net
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(DocFileSuite('README.txt', setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS,
+ ))
+
+ if sys.platform.startswith('win') and 'FIPS_DEVEL_MODE' not in os.environ:
+ wap = zc.winauth.winauth.WindowsAuthenticationPlugin()
+
+ # if it appears that the test user has been configured, do the
+ # integration tests
+ import pywintypes, win32net
+ try:
+ win32net.NetGetDCName(None, None)
+ except pywintypes.error, e:
+ warnings.warn('this machine is apparently not part of a domain;'
+ ' Windows authentication tests are being skipped.')
+ else:
+ if wap.searchUsers('Testy Testerson', 0, 10):
+ suite.addTest(DocFileSuite('integration.txt',
+ optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS))
+ else:
+ warnings.warn('The test user is not set up; Windows'
+ ' authentication tests are being skipped.')
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Added: zc.winauth/trunk/src/zc/winauth/winauth.py
===================================================================
--- zc.winauth/trunk/src/zc/winauth/winauth.py 2006-08-15 21:25:55 UTC (rev 69545)
+++ zc.winauth/trunk/src/zc/winauth/winauth.py 2006-08-15 21:27:29 UTC (rev 69546)
@@ -0,0 +1,248 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Windows Authentication Plugin."""
+import sys, logging, time, re
+
+import persistent
+
+from zope import interface
+from zope.location.interfaces import ILocation
+import zope.app.authentication
+import zope.app.authentication.interfaces
+import zope.app.error.interfaces
+
+import zc.security.interfaces
+import zc.winauth.interfaces
+
+BAD_CREDENTIALS_TIMEOUT = 30
+
+if sys.platform.startswith('win'):
+ import pywintypes, win32security, win32net, win32netcon
+ import pythoncom, win32api
+ from win32com.client import GetObject
+ from win32com.client.gencache import EnsureDispatch
+
+
+class PrincipalInfo(object):
+ interface.implements(
+ zope.app.authentication.interfaces.IPrincipalInfo,
+ zc.winauth.interfaces.IUserInfo)
+
+ def __init__(self, id, title, description='', name=''):
+ self.id = id
+ self.title = title
+ self.description = description
+ self.name = name
+
+
+class WindowsAuthenticationPlugin(persistent.Persistent):
+ interface.implements(
+ zope.app.authentication.interfaces.IAuthenticatorPlugin,
+ zc.security.interfaces.ISimpleUserSearch,
+ ILocation) # TODO remove this
+
+ __name__ = __parent__ = None
+ _server = None
+ prefix = 'zc.winauth'
+ last_bad_credentials = None
+ last_bad_time = 0
+
+ def __init__(self):
+ assert win32security
+
+ def authenticateCredentials(self, credentials):
+ if credentials is None:
+ return
+
+ if (time.time() - self.last_bad_time < BAD_CREDENTIALS_TIMEOUT and
+ credentials == self.last_bad_credentials):
+ return
+
+ # this if is to prevent a write-on-read
+ if self.last_bad_credentials is not None:
+ self.last_bad_credentials = None
+ self.last_bad_time = 0
+
+ username = credentials['login']
+ password = credentials['password']
+
+ logging.info('Begining authentication for "%s".' % username)
+ info = None
+ uid = self.checkPassword(username, password)
+ if uid is None:
+ self.last_bad_credentials = credentials
+ self.last_bad_time = time.time()
+ else:
+ info = self.principalInfo(self.prefix + '.' + uid)
+
+ logging.info('Done with authentication for "%s".' % username)
+ return info
+
+ # For this method to work, the user running the code requires the
+ # privilege to "Act as part of the Operating System" (SE_TCB_NAME)
+ # on Windows 2000 (but not XP or 2003). To add it go to Control Panel/
+ # Administrative Tools/Local Security Policy. Expand Local Policies
+ # and choose User Rights Assignment. Double-click on "Act as part of
+ # the operating system" and add the desired user. The user must log
+ # out and back in for the new privilege to take effect.
+ def checkPassword(self, username, password):
+ logging.info('Authenticating credentials for "%s".' % username)
+ try:
+ handle=win32security.LogonUser(username, None, password,
+ # We use LOGON32_LONGON_NETWORK because it's faster.
+ win32security.LOGON32_LOGON_NETWORK,
+ win32security.LOGON32_PROVIDER_DEFAULT)
+
+ sid = win32security.GetTokenInformation(handle,
+ win32security.TokenUser)[0]
+ # we got back a binary SID, we want a string representation
+ sid = win32security.ConvertSidToStringSid(sid)
+ # We're not going to use the handle, just seeing if we can get it.
+ handle.Close()
+ except pywintypes.error, e:
+ # Because of the sheer number of windows-specific errors that can
+ # occur here, we have to assume any of them mean that the
+ # credentials were not valid.
+
+ # log the exception
+ logging.warning('An exception occurred while attempting to '
+ 'authenticate the user "%s".' % username,
+ exc_info=sys.exc_info())
+ # something bad happened, so the user can't be authenticated
+ result = None
+ else:
+ result = sid
+
+
+ logging.info('Done authenticating credentials for "%s".' % username)
+ return result
+
+ def principalInfo(self, id):
+ if not id.startswith(self.prefix+'.'):
+ return
+
+ string_sid = id[len(self.prefix)+1:]
+
+ try:
+ sid = win32security.GetBinarySid(string_sid)
+ except ValueError:
+ return None
+
+ username = win32security.LookupAccountSid(None, sid)[0]
+
+ while True:
+ try:
+ # try to use the current server to get user info
+ info = win32net.NetUserGetInfo(self.server, username, 10)
+ except pywintypes.error, e:
+ # Because of the sheer number of windows-specific errors
+ # that can occur here, we have to assume any of them mean
+ # that the user's info couldn't be retrieved.
+
+ # log the exception
+ logging.warning('An exception occurred while attempting '
+ 'to get information about the user "%s".'
+ % username, exc_info=sys.exc_info())
+ # something bad happened, so the user's info can't be retrieved
+ return
+ break
+
+ title = info['full_name']
+ if not title:
+ title = username
+ description = info.get('comment') or ''
+ name = info.get('name') or ''
+
+ return PrincipalInfo(id, title, description, name)
+
+ @property
+ def server(self):
+ while True:
+ if self._server is None:
+ try:
+ self._server = win32net.NetGetDCName(None, None)
+ except pywintypes.error:
+ logging.warn('An exception occurred while attempting '
+ 'to contact a domain controller.',
+ exc_info=sys.exc_info())
+ raise
+
+ try:
+ # make sure the server is still alive, if it is, this won't
+ # raise an exception
+ win32net.NetServerGetInfo(self._server, 100)
+ except pywintypes.error:
+ # the server appears to be down, forget it's name and try again
+ self._server = None
+ logging.warn('The domain controller went away, will try to '
+ 'find another one.', exc_info=sys.exc_info())
+ continue
+ break
+ return self._server
+
+ def searchUsers(self, filter, start, size):
+ def getSid(username):
+ sid = win32security.LookupAccountName(None, username)[0]
+ sid = win32security.ConvertSidToStringSid(sid)
+ return sid
+
+ def getConnection():
+ connection = EnsureDispatch('ADODB.Connection')
+ connection.Provider = 'ADsDSOObject'
+ connection.Open('Active Directory Provider')
+ return connection
+
+ def query(query_string):
+ command = EnsureDispatch('ADODB.Command')
+ command.ActiveConnection = getConnection()
+ command.CommandText = query_string
+
+ recordset, result = command.Execute()
+ while not recordset.EOF:
+ yield GetObject(str(recordset.Fields.Item(0)))
+ recordset.MoveNext()
+
+ def buildWhere(filter):
+ chunks = filter.strip().split(' ')
+ chunks = ["displayName='*%s*'" % chunk for chunk in chunks]
+ return ' and '.join(chunks)
+
+ def filterIsSafe(filter):
+ return re.match(r'^(\w|\s)*$', filter)
+
+ if not filterIsSafe(filter):
+ return []
+
+ pythoncom.CoInitialize()
+ results = []
+ domain_name = win32api.GetDomainName()
+ root = GetObject('LDAP://%s/rootDSE' % domain_name)
+ dnc = root.Get('defaultNamingContext')
+ ADsPath = GetObject('LDAP://%s/%s' % (domain_name, dnc)).ADsPath
+
+ where = "objectCategory = 'Person'"
+
+ if filter.strip():
+ where = where + 'and ' + buildWhere(filter)
+
+ for user in query("select * from '%s' where %s" % (ADsPath, where)):
+ if user.sAMAccountName:
+ results.append(self.prefix + '.' + getSid(user.sAMAccountName))
+
+ # see if we've found enough to stop
+ if len(results) >= start + size:
+ break
+
+ return results[start:start+size]
More information about the Checkins
mailing list