We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Uninitialized ASN.1 value when using LdapUserInfo
After updating some Python packages, I got the following exception on login:

```
        Traceback (most recent call last):
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1442, in gotResult
            _inlineCallbacks(r, g, deferred)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1432, in _inlineCallbacks
            deferred.errback()
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 500, in errback
            self._startRunCallbacks(fail)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 567, in _startRunCallbacks
            self._runCallbacks()
        --- <exception caught here> ---
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 653, in _runCallbacks
            current.result = callback(current.result, *args, **kw)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/resource.py", line 91, in failHttpError
            f.trap(Error)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/failure.py", line 346, in trap
            self.raiseException()
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 653, in _runCallbacks
            current.result = callback(current.result, *args, **kw)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/resource.py", line 84, in failHttpRedirect
            f.trap(Redirect)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/failure.py", line 346, in trap
            self.raiseException()
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1384, in _inlineCallbacks
            result = result.throwExceptionIntoGenerator(g)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/failure.py", line 393, in throwExceptionIntoGenerator
            return g.throw(self.type, self.value, self.tb)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/oauth2.py", line 67, in renderLogin
            infos = yield self.auth.userInfoProvider.getUserInfo(details['username'])
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/threadpool.py", line 250, in inContext
            result = inContext.theWork()
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/threadpool.py", line 266, in <lambda>
            inContext.theWork = lambda: context.call(ctx, func, *args, **kw)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/context.py", line 122, in callWithContext
            return self.currentContext().callWithContext(ctx, func, *args, **kw)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/context.py", line 85, in callWithContext
            return func(*args,**kw)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/ldapuserinfo.py", line 92, in thd
            c = self.connectLdap()
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/ldapuserinfo.py", line 82, in connectLdap
            authentication=auth)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/core/connection.py", line 278, in __init__
            self.bind(read_server_info=True)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/core/connection.py", line 427, in bind
            response = self.post_send_single_response(self.send('bindRequest', request, controls))
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/sync.py", line 122, in post_send_single_response
            responses, result = self.get_response(message_id)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/base.py", line 298, in get_response
            responses = self._get_response(message_id)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/sync.py", line 168, in _get_response
            dict_response = self.decode_response(ldap_resp)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/base.py", line 403, in decode_response
            result = bind_response_to_dict(component)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/operation/bind.py", line 119, in bind_response_to_dict
            'saslCreds': bytes(response['serverSaslCreds']) if response['serverSaslCreds'] is not None else None}
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/pyasn1/type/univ.py", line 939, in __str__
            return str(self._value)
          File "/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/pyasn1/type/base.py", line 186, in plug
            raise error.PyAsn1Error('Uninitialized ASN.1 value ("%s" attribute looked up)' % name)
        pyasn1.error.PyAsn1Error: Uninitialized ASN.1 value ("__str__" attribute looked up)
```

The problem disappeared after downgrading pyasn1 from 0.3.2 to the version 0.1.9, which I used before.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `master/buildbot/www/ldapuserinfo.py`
Content:
```
1 # This file is part of Buildbot.  Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8 # details.
9 #
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 #
14 # Copyright Buildbot Team Members
15 
16 from __future__ import absolute_import
17 from __future__ import print_function
18 from future.moves.urllib.parse import urlparse
19 
20 import ldap3
21 
22 from twisted.internet import threads
23 
24 from buildbot.util import flatten
25 from buildbot.www import auth
26 from buildbot.www import avatar
27 
28 
29 class LdapUserInfo(avatar.AvatarBase, auth.UserInfoProviderBase):
30     name = 'ldap'
31 
32     def __init__(self, uri, bindUser, bindPw,
33                  accountBase,
34                  accountPattern,
35                  accountFullName,
36                  accountEmail,
37                  groupBase=None,
38                  groupMemberPattern=None,
39                  groupName=None,
40                  avatarPattern=None,
41                  avatarData=None,
42                  accountExtraFields=None):
43         avatar.AvatarBase.__init__(self)
44         auth.UserInfoProviderBase.__init__(self)
45         self.uri = uri
46         self.bindUser = bindUser
47         self.bindPw = bindPw
48         self.accountBase = accountBase
49         self.accountEmail = accountEmail
50         self.accountPattern = accountPattern
51         self.accountFullName = accountFullName
52         group_params = [p for p in (groupName, groupMemberPattern, groupBase)
53                         if p is not None]
54         if len(group_params) not in (0, 3):
55             raise ValueError(
56                 "Incomplete LDAP groups configuration. "
57                 "To use Ldap groups, you need to specify the three "
58                 "parameters (groupName, groupMemberPattern and groupBase). ")
59 
60         self.groupName = groupName
61         self.groupMemberPattern = groupMemberPattern
62         self.groupBase = groupBase
63         self.avatarPattern = avatarPattern
64         self.avatarData = avatarData
65         if accountExtraFields is None:
66             accountExtraFields = []
67         self.accountExtraFields = accountExtraFields
68 
69     def connectLdap(self):
70         server = urlparse(self.uri)
71         netloc = server.netloc.split(":")
72         # define the server and the connection
73         s = ldap3.Server(netloc[0], port=int(netloc[1]), use_ssl=server.scheme == 'ldaps',
74                          get_info=ldap3.GET_ALL_INFO)
75 
76         auth = ldap3.AUTH_SIMPLE
77         if self.bindUser is None and self.bindPw is None:
78             auth = ldap3.AUTH_ANONYMOUS
79 
80         c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.STRATEGY_SYNC,
81                              user=self.bindUser, password=self.bindPw,
82                              authentication=auth)
83         return c
84 
85     def search(self, c, base, filterstr='f', attributes=None):
86         c.search(
87             base, filterstr, ldap3.SEARCH_SCOPE_WHOLE_SUBTREE, attributes=attributes)
88         return c.response
89 
90     def getUserInfo(self, username):
91         def thd():
92             c = self.connectLdap()
93             infos = {'username': username}
94             pattern = self.accountPattern % dict(username=username)
95             res = self.search(c, self.accountBase, pattern,
96                               attributes=[
97                                   self.accountEmail, self.accountFullName] +
98                               self.accountExtraFields)
99             if len(res) != 1:
100                 raise KeyError(
101                     "ldap search \"%s\" returned %d results" % (pattern, len(res)))
102             dn, ldap_infos = res[0]['dn'], res[0]['raw_attributes']
103             if isinstance(dn, bytes):
104                 dn = dn.decode('utf-8')
105 
106             def getLdapInfo(x):
107                 if isinstance(x, list):
108                     return x[0]
109                 return x
110             infos['full_name'] = getLdapInfo(ldap_infos[self.accountFullName])
111             infos['email'] = getLdapInfo(ldap_infos[self.accountEmail])
112             for f in self.accountExtraFields:
113                 if f in ldap_infos:
114                     infos[f] = getLdapInfo(ldap_infos[f])
115 
116             if self.groupMemberPattern is None:
117                 infos['groups'] = []
118                 return infos
119 
120             # needs double quoting of backslashing
121             pattern = self.groupMemberPattern % dict(dn=dn)
122             res = self.search(c, self.groupBase, pattern,
123                               attributes=[self.groupName])
124             infos['groups'] = flatten(
125                 [group_infos['raw_attributes'][self.groupName] for group_infos in res])
126             return infos
127         return threads.deferToThread(thd)
128 
129     def findAvatarMime(self, data):
130         # http://en.wikipedia.org/wiki/List_of_file_signatures
131         if data.startswith("\xff\xd8\xff"):
132             return ("image/jpeg", data)
133         if data.startswith("\x89PNG"):
134             return ("image/png", data)
135         if data.startswith("GIF8"):
136             return ("image/gif", data)
137         # ignore unknown image format
138         return None
139 
140     def getUserAvatar(self, user_email, size, defaultAvatarUrl):
141         def thd():
142             c = self.connectLdap()
143             pattern = self.avatarPattern % dict(email=user_email)
144             res = self.search(c, self.accountBase, pattern,
145                               attributes=[self.avatarData])
146             if not res:
147                 return None
148             ldap_infos = res[0]['raw_attributes']
149             if self.avatarData in ldap_infos and ldap_infos[self.avatarData]:
150                 data = ldap_infos[self.avatarData][0]
151                 return self.findAvatarMime(data)
152             return None
153         return threads.deferToThread(thd)
154
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/master/buildbot/www/ldapuserinfo.py b/master/buildbot/www/ldapuserinfo.py
--- a/master/buildbot/www/ldapuserinfo.py
+++ b/master/buildbot/www/ldapuserinfo.py
@@ -71,20 +71,19 @@
         netloc = server.netloc.split(":")
         # define the server and the connection
         s = ldap3.Server(netloc[0], port=int(netloc[1]), use_ssl=server.scheme == 'ldaps',
-                         get_info=ldap3.GET_ALL_INFO)
+                         get_info=ldap3.ALL)
 
-        auth = ldap3.AUTH_SIMPLE
+        auth = ldap3.SIMPLE
         if self.bindUser is None and self.bindPw is None:
-            auth = ldap3.AUTH_ANONYMOUS
+            auth = ldap3.ANONYMOUS
 
-        c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.STRATEGY_SYNC,
+        c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.SYNC,
                              user=self.bindUser, password=self.bindPw,
                              authentication=auth)
         return c
 
     def search(self, c, base, filterstr='f', attributes=None):
-        c.search(
-            base, filterstr, ldap3.SEARCH_SCOPE_WHOLE_SUBTREE, attributes=attributes)
+        c.search(base, filterstr, ldap3.SUBTREE, attributes=attributes)
         return c.response
 
     def getUserInfo(self, username):

{"golden_diff": "diff --git a/master/buildbot/www/ldapuserinfo.py b/master/buildbot/www/ldapuserinfo.py\n--- a/master/buildbot/www/ldapuserinfo.py\n+++ b/master/buildbot/www/ldapuserinfo.py\n@@ -71,20 +71,19 @@\n         netloc = server.netloc.split(\":\")\n         # define the server and the connection\n         s = ldap3.Server(netloc[0], port=int(netloc[1]), use_ssl=server.scheme == 'ldaps',\n-                         get_info=ldap3.GET_ALL_INFO)\n+                         get_info=ldap3.ALL)\n \n-        auth = ldap3.AUTH_SIMPLE\n+        auth = ldap3.SIMPLE\n         if self.bindUser is None and self.bindPw is None:\n-            auth = ldap3.AUTH_ANONYMOUS\n+            auth = ldap3.ANONYMOUS\n \n-        c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.STRATEGY_SYNC,\n+        c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.SYNC,\n                              user=self.bindUser, password=self.bindPw,\n                              authentication=auth)\n         return c\n \n     def search(self, c, base, filterstr='f', attributes=None):\n-        c.search(\n-            base, filterstr, ldap3.SEARCH_SCOPE_WHOLE_SUBTREE, attributes=attributes)\n+        c.search(base, filterstr, ldap3.SUBTREE, attributes=attributes)\n         return c.response\n \n     def getUserInfo(self, username):\n", "issue": "Uninitialized ASN.1 value when using LdapUserInfo\nAfter updating some Python packages, I got the following exception on login:\r\n\r\n```\r\n        Traceback (most recent call last):\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 1442, in gotResult\r\n            _inlineCallbacks(r, g, deferred)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 1432, in _inlineCallbacks\r\n            deferred.errback()\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 500, in errback\r\n            self._startRunCallbacks(fail)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 567, in _startRunCallbacks\r\n            self._runCallbacks()\r\n        --- <exception caught here> ---\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 653, in _runCallbacks\r\n            current.result = callback(current.result, *args, **kw)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/resource.py\", line 91, in failHttpError\r\n            f.trap(Error)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/failure.py\", line 346, in trap\r\n            self.raiseException()\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 653, in _runCallbacks\r\n            current.result = callback(current.result, *args, **kw)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/resource.py\", line 84, in failHttpRedirect\r\n            f.trap(Redirect)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/failure.py\", line 346, in trap\r\n            self.raiseException()\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/internet/defer.py\", line 1384, in _inlineCallbacks\r\n            result = result.throwExceptionIntoGenerator(g)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/failure.py\", line 393, in throwExceptionIntoGenerator\r\n            return g.throw(self.type, self.value, self.tb)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/oauth2.py\", line 67, in renderLogin\r\n            infos = yield self.auth.userInfoProvider.getUserInfo(details['username'])\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/threadpool.py\", line 250, in inContext\r\n            result = inContext.theWork()\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/threadpool.py\", line 266, in <lambda>\r\n            inContext.theWork = lambda: context.call(ctx, func, *args, **kw)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/context.py\", line 122, in callWithContext\r\n            return self.currentContext().callWithContext(ctx, func, *args, **kw)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/twisted/python/context.py\", line 85, in callWithContext\r\n            return func(*args,**kw)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/ldapuserinfo.py\", line 92, in thd\r\n            c = self.connectLdap()\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/buildbot/www/ldapuserinfo.py\", line 82, in connectLdap\r\n            authentication=auth)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/core/connection.py\", line 278, in __init__\r\n            self.bind(read_server_info=True)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/core/connection.py\", line 427, in bind\r\n            response = self.post_send_single_response(self.send('bindRequest', request, controls))\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/sync.py\", line 122, in post_send_single_response\r\n            responses, result = self.get_response(message_id)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/base.py\", line 298, in get_response\r\n            responses = self._get_response(message_id)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/sync.py\", line 168, in _get_response\r\n            dict_response = self.decode_response(ldap_resp)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/strategy/base.py\", line 403, in decode_response\r\n            result = bind_response_to_dict(component)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/ldap3/operation/bind.py\", line 119, in bind_response_to_dict\r\n            'saslCreds': bytes(response['serverSaslCreds']) if response['serverSaslCreds'] is not None else None}\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/pyasn1/type/univ.py\", line 939, in __str__\r\n            return str(self._value)\r\n          File \"/home/buibot00/venv/bb_twisted_test/local/lib/python2.7/site-packages/pyasn1/type/base.py\", line 186, in plug\r\n            raise error.PyAsn1Error('Uninitialized ASN.1 value (\"%s\" attribute looked up)' % name)\r\n        pyasn1.error.PyAsn1Error: Uninitialized ASN.1 value (\"__str__\" attribute looked up)\r\n```\r\n\r\nThe problem disappeared after downgrading pyasn1 from 0.3.2 to the version 0.1.9, which I used before.\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom future.moves.urllib.parse import urlparse\n\nimport ldap3\n\nfrom twisted.internet import threads\n\nfrom buildbot.util import flatten\nfrom buildbot.www import auth\nfrom buildbot.www import avatar\n\n\nclass LdapUserInfo(avatar.AvatarBase, auth.UserInfoProviderBase):\n    name = 'ldap'\n\n    def __init__(self, uri, bindUser, bindPw,\n                 accountBase,\n                 accountPattern,\n                 accountFullName,\n                 accountEmail,\n                 groupBase=None,\n                 groupMemberPattern=None,\n                 groupName=None,\n                 avatarPattern=None,\n                 avatarData=None,\n                 accountExtraFields=None):\n        avatar.AvatarBase.__init__(self)\n        auth.UserInfoProviderBase.__init__(self)\n        self.uri = uri\n        self.bindUser = bindUser\n        self.bindPw = bindPw\n        self.accountBase = accountBase\n        self.accountEmail = accountEmail\n        self.accountPattern = accountPattern\n        self.accountFullName = accountFullName\n        group_params = [p for p in (groupName, groupMemberPattern, groupBase)\n                        if p is not None]\n        if len(group_params) not in (0, 3):\n            raise ValueError(\n                \"Incomplete LDAP groups configuration. \"\n                \"To use Ldap groups, you need to specify the three \"\n                \"parameters (groupName, groupMemberPattern and groupBase). \")\n\n        self.groupName = groupName\n        self.groupMemberPattern = groupMemberPattern\n        self.groupBase = groupBase\n        self.avatarPattern = avatarPattern\n        self.avatarData = avatarData\n        if accountExtraFields is None:\n            accountExtraFields = []\n        self.accountExtraFields = accountExtraFields\n\n    def connectLdap(self):\n        server = urlparse(self.uri)\n        netloc = server.netloc.split(\":\")\n        # define the server and the connection\n        s = ldap3.Server(netloc[0], port=int(netloc[1]), use_ssl=server.scheme == 'ldaps',\n                         get_info=ldap3.GET_ALL_INFO)\n\n        auth = ldap3.AUTH_SIMPLE\n        if self.bindUser is None and self.bindPw is None:\n            auth = ldap3.AUTH_ANONYMOUS\n\n        c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.STRATEGY_SYNC,\n                             user=self.bindUser, password=self.bindPw,\n                             authentication=auth)\n        return c\n\n    def search(self, c, base, filterstr='f', attributes=None):\n        c.search(\n            base, filterstr, ldap3.SEARCH_SCOPE_WHOLE_SUBTREE, attributes=attributes)\n        return c.response\n\n    def getUserInfo(self, username):\n        def thd():\n            c = self.connectLdap()\n            infos = {'username': username}\n            pattern = self.accountPattern % dict(username=username)\n            res = self.search(c, self.accountBase, pattern,\n                              attributes=[\n                                  self.accountEmail, self.accountFullName] +\n                              self.accountExtraFields)\n            if len(res) != 1:\n                raise KeyError(\n                    \"ldap search \\\"%s\\\" returned %d results\" % (pattern, len(res)))\n            dn, ldap_infos = res[0]['dn'], res[0]['raw_attributes']\n            if isinstance(dn, bytes):\n                dn = dn.decode('utf-8')\n\n            def getLdapInfo(x):\n                if isinstance(x, list):\n                    return x[0]\n                return x\n            infos['full_name'] = getLdapInfo(ldap_infos[self.accountFullName])\n            infos['email'] = getLdapInfo(ldap_infos[self.accountEmail])\n            for f in self.accountExtraFields:\n                if f in ldap_infos:\n                    infos[f] = getLdapInfo(ldap_infos[f])\n\n            if self.groupMemberPattern is None:\n                infos['groups'] = []\n                return infos\n\n            # needs double quoting of backslashing\n            pattern = self.groupMemberPattern % dict(dn=dn)\n            res = self.search(c, self.groupBase, pattern,\n                              attributes=[self.groupName])\n            infos['groups'] = flatten(\n                [group_infos['raw_attributes'][self.groupName] for group_infos in res])\n            return infos\n        return threads.deferToThread(thd)\n\n    def findAvatarMime(self, data):\n        # http://en.wikipedia.org/wiki/List_of_file_signatures\n        if data.startswith(\"\\xff\\xd8\\xff\"):\n            return (\"image/jpeg\", data)\n        if data.startswith(\"\\x89PNG\"):\n            return (\"image/png\", data)\n        if data.startswith(\"GIF8\"):\n            return (\"image/gif\", data)\n        # ignore unknown image format\n        return None\n\n    def getUserAvatar(self, user_email, size, defaultAvatarUrl):\n        def thd():\n            c = self.connectLdap()\n            pattern = self.avatarPattern % dict(email=user_email)\n            res = self.search(c, self.accountBase, pattern,\n                              attributes=[self.avatarData])\n            if not res:\n                return None\n            ldap_infos = res[0]['raw_attributes']\n            if self.avatarData in ldap_infos and ldap_infos[self.avatarData]:\n                data = ldap_infos[self.avatarData][0]\n                return self.findAvatarMime(data)\n            return None\n        return threads.deferToThread(thd)\n", "path": "master/buildbot/www/ldapuserinfo.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom future.moves.urllib.parse import urlparse\n\nimport ldap3\n\nfrom twisted.internet import threads\n\nfrom buildbot.util import flatten\nfrom buildbot.www import auth\nfrom buildbot.www import avatar\n\n\nclass LdapUserInfo(avatar.AvatarBase, auth.UserInfoProviderBase):\n    name = 'ldap'\n\n    def __init__(self, uri, bindUser, bindPw,\n                 accountBase,\n                 accountPattern,\n                 accountFullName,\n                 accountEmail,\n                 groupBase=None,\n                 groupMemberPattern=None,\n                 groupName=None,\n                 avatarPattern=None,\n                 avatarData=None,\n                 accountExtraFields=None):\n        avatar.AvatarBase.__init__(self)\n        auth.UserInfoProviderBase.__init__(self)\n        self.uri = uri\n        self.bindUser = bindUser\n        self.bindPw = bindPw\n        self.accountBase = accountBase\n        self.accountEmail = accountEmail\n        self.accountPattern = accountPattern\n        self.accountFullName = accountFullName\n        group_params = [p for p in (groupName, groupMemberPattern, groupBase)\n                        if p is not None]\n        if len(group_params) not in (0, 3):\n            raise ValueError(\n                \"Incomplete LDAP groups configuration. \"\n                \"To use Ldap groups, you need to specify the three \"\n                \"parameters (groupName, groupMemberPattern and groupBase). \")\n\n        self.groupName = groupName\n        self.groupMemberPattern = groupMemberPattern\n        self.groupBase = groupBase\n        self.avatarPattern = avatarPattern\n        self.avatarData = avatarData\n        if accountExtraFields is None:\n            accountExtraFields = []\n        self.accountExtraFields = accountExtraFields\n\n    def connectLdap(self):\n        server = urlparse(self.uri)\n        netloc = server.netloc.split(\":\")\n        # define the server and the connection\n        s = ldap3.Server(netloc[0], port=int(netloc[1]), use_ssl=server.scheme == 'ldaps',\n                         get_info=ldap3.ALL)\n\n        auth = ldap3.SIMPLE\n        if self.bindUser is None and self.bindPw is None:\n            auth = ldap3.ANONYMOUS\n\n        c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.SYNC,\n                             user=self.bindUser, password=self.bindPw,\n                             authentication=auth)\n        return c\n\n    def search(self, c, base, filterstr='f', attributes=None):\n        c.search(base, filterstr, ldap3.SUBTREE, attributes=attributes)\n        return c.response\n\n    def getUserInfo(self, username):\n        def thd():\n            c = self.connectLdap()\n            infos = {'username': username}\n            pattern = self.accountPattern % dict(username=username)\n            res = self.search(c, self.accountBase, pattern,\n                              attributes=[\n                                  self.accountEmail, self.accountFullName] +\n                              self.accountExtraFields)\n            if len(res) != 1:\n                raise KeyError(\n                    \"ldap search \\\"%s\\\" returned %d results\" % (pattern, len(res)))\n            dn, ldap_infos = res[0]['dn'], res[0]['raw_attributes']\n            if isinstance(dn, bytes):\n                dn = dn.decode('utf-8')\n\n            def getLdapInfo(x):\n                if isinstance(x, list):\n                    return x[0]\n                return x\n            infos['full_name'] = getLdapInfo(ldap_infos[self.accountFullName])\n            infos['email'] = getLdapInfo(ldap_infos[self.accountEmail])\n            for f in self.accountExtraFields:\n                if f in ldap_infos:\n                    infos[f] = getLdapInfo(ldap_infos[f])\n\n            if self.groupMemberPattern is None:\n                infos['groups'] = []\n                return infos\n\n            # needs double quoting of backslashing\n            pattern = self.groupMemberPattern % dict(dn=dn)\n            res = self.search(c, self.groupBase, pattern,\n                              attributes=[self.groupName])\n            infos['groups'] = flatten(\n                [group_infos['raw_attributes'][self.groupName] for group_infos in res])\n            return infos\n        return threads.deferToThread(thd)\n\n    def findAvatarMime(self, data):\n        # http://en.wikipedia.org/wiki/List_of_file_signatures\n        if data.startswith(\"\\xff\\xd8\\xff\"):\n            return (\"image/jpeg\", data)\n        if data.startswith(\"\\x89PNG\"):\n            return (\"image/png\", data)\n        if data.startswith(\"GIF8\"):\n            return (\"image/gif\", data)\n        # ignore unknown image format\n        return None\n\n    def getUserAvatar(self, user_email, size, defaultAvatarUrl):\n        def thd():\n            c = self.connectLdap()\n            pattern = self.avatarPattern % dict(email=user_email)\n            res = self.search(c, self.accountBase, pattern,\n                              attributes=[self.avatarData])\n            if not res:\n                return None\n            ldap_infos = res[0]['raw_attributes']\n            if self.avatarData in ldap_infos and ldap_infos[self.avatarData]:\n                data = ldap_infos[self.avatarData][0]\n                return self.findAvatarMime(data)\n            return None\n        return threads.deferToThread(thd)\n", "path": "master/buildbot/www/ldapuserinfo.py"}]}