We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
util.RemoteUserAuth raises builtins.TypeError
RemoteUserAuth (nginx http basic auth) fails with Type error, because the result of the regex match has values of type `bytes`, e.g.: `{'username': b'foobar'}`
Full stacktrace
```python
        Traceback (most recent call last):
          File "/.../python3.5/site-packages/twisted/internet/defer.py", line 1418, in _inlineCallbacks
            result = g.send(result)
          File "/.../python3.5/site-packages/buildbot/www/auth.py", line 131, in maybeAutoLogin
            yield self.updateUserInfo(request)
          File "/.../python3.5/site-packages/twisted/internet/defer.py", line 1613, in unwindGenerator 
            return _cancellableInlineCallbacks(gen)
          File "/.../python3.5/site-packages/twisted/internet/defer.py", line 1529, in _cancellableInlineCallbacks
            _inlineCallbacks(None, g, status)
        --- <exception caught here> ---
          File "/.../python3.5/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
            current.result = callback(current.result, *args, **kw)
          File "/.../python3.5/site-packages/buildbot/www/resource.py", line 92, in failHttpError
            f.trap(Error)
          File "/.../python3.5/site-packages/twisted/python/failure.py", line 439, in trap
            self.raiseException()
          File "/.../python3.5/site-packages/twisted/python/failure.py", line 467, in raiseException
            raise self.value.with_traceback(self.tb)
          File "/.../python3.5/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
            current.result = callback(current.result, *args, **kw)
          File "/.../python3.5/site-packages/buildbot/www/resource.py", line 85, in failHttpRedirect
            f.trap(Redirect)
          File "/.../python3.5/site-packages/twisted/python/failure.py", line 439, in trap
            self.raiseException()
          File "/.../python3.5/site-packages/twisted/python/failure.py", line 467, in raiseException
            raise self.value.with_traceback(self.tb)
          File "/.../python3.5/site-packages/buildbot/www/config.py", line 126, in renderIndex
            yield self.config['auth'].maybeAutoLogin(request)
          File "/.../python3.5/site-packages/buildbot/www/auth.py", line 131, in maybeAutoLogin
            yield self.updateUserInfo(request)
          File "/.../python3.5/site-packages/twisted/internet/defer.py", line 1418, in _inlineCallbacks
            result = g.send(result)
          File "/.../python3.5/site-packages/buildbot/www/auth.py", line 78, in updateUserInfo
            session.updateSession(request)
          File "/.../python3.5/site-packages/buildbot/www/service.py", line 110, in updateSession
            request.addCookie(cookiename, self.uid, path=b"/",
          File "/.../python3.5/site-packages/buildbot/www/service.py", line 137, in uid
            return jwt.encode(claims, self.site.session_secret, algorithm=SESSION_SECRET_ALGORITHM)
          File "/.../python3.5/site-packages/jwt/api_jwt.py", line 62, in encode
            cls=json_encoder
          File "/usr/lib/python3.5/json/__init__.py", line 237, in dumps
            **kw).encode(obj)
          File "/usr/lib/python3.5/json/encoder.py", line 198, in encode
            chunks = self.iterencode(o, _one_shot=True)
          File "/usr/lib/python3.5/json/encoder.py", line 256, in iterencode
            return _iterencode(o, 0)
          File "/usr/lib/python3.5/json/encoder.py", line 179, in default
            raise TypeError(repr(o) + " is not JSON serializable")
        builtins.TypeError: b'foobar' is not JSON serializable
```
following line [here](https://github.com/buildbot/buildbot/blob/master/master/buildbot/www/auth.py#L127) resolves the problem:
```python
user_info = {k: buildbot.util.bytes2unicode(v) for k, v in res.groupdict().items()}
```
Sorry if this is a duplicate, but I failed to find a ticket for this issue. 
I will create a PR as soon as I have spare time.

--- 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/auth.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 
17 import re
18 from abc import ABCMeta
19 from abc import abstractmethod
20 
21 from twisted.cred.checkers import FilePasswordDB
22 from twisted.cred.checkers import ICredentialsChecker
23 from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
24 from twisted.cred.credentials import IUsernamePassword
25 from twisted.cred.error import UnauthorizedLogin
26 from twisted.cred.portal import IRealm
27 from twisted.cred.portal import Portal
28 from twisted.internet import defer
29 from twisted.web.error import Error
30 from twisted.web.guard import BasicCredentialFactory
31 from twisted.web.guard import DigestCredentialFactory
32 from twisted.web.guard import HTTPAuthSessionWrapper
33 from twisted.web.resource import IResource
34 from zope.interface import implementer
35 
36 from buildbot.util import bytes2unicode
37 from buildbot.util import config
38 from buildbot.util import unicode2bytes
39 from buildbot.www import resource
40 
41 
42 class AuthRootResource(resource.Resource):
43 
44     def getChild(self, path, request):
45         # return dynamically generated resources
46         if path == b'login':
47             return self.master.www.auth.getLoginResource()
48         elif path == b'logout':
49             return self.master.www.auth.getLogoutResource()
50         return resource.Resource.getChild(self, path, request)
51 
52 
53 class AuthBase(config.ConfiguredMixin):
54 
55     def __init__(self, userInfoProvider=None):
56         self.userInfoProvider = userInfoProvider
57 
58     def reconfigAuth(self, master, new_config):
59         self.master = master
60 
61     def maybeAutoLogin(self, request):
62         return defer.succeed(None)
63 
64     def getLoginResource(self):
65         raise Error(501, b"not implemented")
66 
67     def getLogoutResource(self):
68         return LogoutResource(self.master)
69 
70     @defer.inlineCallbacks
71     def updateUserInfo(self, request):
72         session = request.getSession()
73         if self.userInfoProvider is not None:
74             infos = yield self.userInfoProvider.getUserInfo(session.user_info['username'])
75             session.user_info.update(infos)
76             session.updateSession(request)
77 
78     def getConfigDict(self):
79         return {'name': type(self).__name__}
80 
81 
82 class UserInfoProviderBase(config.ConfiguredMixin):
83     name = "noinfo"
84 
85     def getUserInfo(self, username):
86         return defer.succeed({'email': username})
87 
88 
89 class LoginResource(resource.Resource):
90 
91     def render_GET(self, request):
92         return self.asyncRenderHelper(request, self.renderLogin)
93 
94     @defer.inlineCallbacks
95     def renderLogin(self, request):
96         raise NotImplementedError
97 
98 
99 class NoAuth(AuthBase):
100     pass
101 
102 
103 class RemoteUserAuth(AuthBase):
104     header = b"REMOTE_USER"
105     headerRegex = re.compile(br"(?P<username>[^ @]+)@(?P<realm>[^ @]+)")
106 
107     def __init__(self, header=None, headerRegex=None, **kwargs):
108         AuthBase.__init__(self, **kwargs)
109         if self.userInfoProvider is None:
110             self.userInfoProvider = UserInfoProviderBase()
111         if header is not None:
112             self.header = unicode2bytes(header)
113         if headerRegex is not None:
114             self.headerRegex = re.compile(unicode2bytes(headerRegex))
115 
116     @defer.inlineCallbacks
117     def maybeAutoLogin(self, request):
118         header = request.getHeader(self.header)
119         if header is None:
120             raise Error(403, b"missing http header " + self.header + b". Check your reverse proxy config!")
121         res = self.headerRegex.match(header)
122         if res is None:
123             raise Error(
124                 403, b'http header does not match regex! "' + header + b'" not matching ' + self.headerRegex.pattern)
125         session = request.getSession()
126         if session.user_info != dict(res.groupdict()):
127             session.user_info = dict(res.groupdict())
128             yield self.updateUserInfo(request)
129 
130 
131 @implementer(IRealm)
132 class AuthRealm(object):
133 
134     def __init__(self, master, auth):
135         self.auth = auth
136         self.master = master
137 
138     def requestAvatar(self, avatarId, mind, *interfaces):
139         if IResource in interfaces:
140             return (IResource,
141                     PreAuthenticatedLoginResource(self.master, avatarId),
142                     lambda: None)
143         raise NotImplementedError()
144 
145 
146 class TwistedICredAuthBase(AuthBase):
147 
148     def __init__(self, credentialFactories, checkers, **kwargs):
149         AuthBase.__init__(self, **kwargs)
150         if self.userInfoProvider is None:
151             self.userInfoProvider = UserInfoProviderBase()
152         self.credentialFactories = credentialFactories
153         self.checkers = checkers
154 
155     def getLoginResource(self):
156         return HTTPAuthSessionWrapper(
157             Portal(AuthRealm(self.master, self), self.checkers),
158             self.credentialFactories)
159 
160 
161 class HTPasswdAuth(TwistedICredAuthBase):
162 
163     def __init__(self, passwdFile, **kwargs):
164         TwistedICredAuthBase.__init__(
165             self,
166             [DigestCredentialFactory(b"md5", b"buildbot"),
167              BasicCredentialFactory(b"buildbot")],
168             [FilePasswordDB(passwdFile)],
169             **kwargs)
170 
171 
172 class UserPasswordAuth(TwistedICredAuthBase):
173 
174     def __init__(self, users, **kwargs):
175         if isinstance(users, dict):
176             users = {user: unicode2bytes(pw) for user, pw in users.items()}
177         elif isinstance(users, list):
178             users = [(user, unicode2bytes(pw)) for user, pw in users]
179         TwistedICredAuthBase.__init__(
180             self,
181             [DigestCredentialFactory(b"md5", b"buildbot"),
182              BasicCredentialFactory(b"buildbot")],
183             [InMemoryUsernamePasswordDatabaseDontUse(**dict(users))],
184             **kwargs)
185 
186 
187 @implementer(ICredentialsChecker)
188 class CustomAuth(TwistedICredAuthBase):
189     __metaclass__ = ABCMeta
190     credentialInterfaces = [IUsernamePassword]
191 
192     def __init__(self, **kwargs):
193         TwistedICredAuthBase.__init__(
194             self,
195             [BasicCredentialFactory(b"buildbot")],
196             [self],
197             **kwargs)
198 
199     def requestAvatarId(self, cred):
200         if self.check_credentials(cred.username, cred.password):
201             return defer.succeed(cred.username)
202         return defer.fail(UnauthorizedLogin())
203 
204     @abstractmethod
205     def check_credentials(username, password):
206         return False
207 
208 
209 def _redirect(master, request):
210     url = request.args.get(b"redirect", [b"/"])[0]
211     url = bytes2unicode(url)
212     return resource.Redirect(master.config.buildbotURL + "#" + url)
213 
214 
215 class PreAuthenticatedLoginResource(LoginResource):
216     # a LoginResource which is already authenticated via a
217     # HTTPAuthSessionWrapper
218 
219     def __init__(self, master, username):
220         LoginResource.__init__(self, master)
221         self.username = username
222 
223     @defer.inlineCallbacks
224     def renderLogin(self, request):
225         session = request.getSession()
226         session.user_info = dict(username=bytes2unicode(self.username))
227         yield self.master.www.auth.updateUserInfo(request)
228         raise _redirect(self.master, request)
229 
230 
231 class LogoutResource(resource.Resource):
232 
233     def render_GET(self, request):
234         session = request.getSession()
235         session.expire()
236         session.updateSession(request)
237         request.redirect(_redirect(self.master, request).url)
238         return b''
239
```
--- 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/auth.py b/master/buildbot/www/auth.py
--- a/master/buildbot/www/auth.py
+++ b/master/buildbot/www/auth.py
@@ -123,8 +123,9 @@
             raise Error(
                 403, b'http header does not match regex! "' + header + b'" not matching ' + self.headerRegex.pattern)
         session = request.getSession()
-        if session.user_info != dict(res.groupdict()):
-            session.user_info = dict(res.groupdict())
+        user_info = {k: bytes2unicode(v) for k, v in res.groupdict().items()}
+        if session.user_info != user_info:
+            session.user_info = user_info
             yield self.updateUserInfo(request)

{"golden_diff": "diff --git a/master/buildbot/www/auth.py b/master/buildbot/www/auth.py\n--- a/master/buildbot/www/auth.py\n+++ b/master/buildbot/www/auth.py\n@@ -123,8 +123,9 @@\n             raise Error(\n                 403, b'http header does not match regex! \"' + header + b'\" not matching ' + self.headerRegex.pattern)\n         session = request.getSession()\n-        if session.user_info != dict(res.groupdict()):\n-            session.user_info = dict(res.groupdict())\n+        user_info = {k: bytes2unicode(v) for k, v in res.groupdict().items()}\n+        if session.user_info != user_info:\n+            session.user_info = user_info\n             yield self.updateUserInfo(request)\n", "issue": "util.RemoteUserAuth raises builtins.TypeError\nRemoteUserAuth (nginx http basic auth) fails with Type error, because the result of the regex match has values of type `bytes`, e.g.: `{'username': b'foobar'}`\r\nFull stacktrace\r\n```python\r\n        Traceback (most recent call last):\r\n          File \"/.../python3.5/site-packages/twisted/internet/defer.py\", line 1418, in _inlineCallbacks\r\n            result = g.send(result)\r\n          File \"/.../python3.5/site-packages/buildbot/www/auth.py\", line 131, in maybeAutoLogin\r\n            yield self.updateUserInfo(request)\r\n          File \"/.../python3.5/site-packages/twisted/internet/defer.py\", line 1613, in unwindGenerator \r\n            return _cancellableInlineCallbacks(gen)\r\n          File \"/.../python3.5/site-packages/twisted/internet/defer.py\", line 1529, in _cancellableInlineCallbacks\r\n            _inlineCallbacks(None, g, status)\r\n        --- <exception caught here> ---\r\n          File \"/.../python3.5/site-packages/twisted/internet/defer.py\", line 654, in _runCallbacks\r\n            current.result = callback(current.result, *args, **kw)\r\n          File \"/.../python3.5/site-packages/buildbot/www/resource.py\", line 92, in failHttpError\r\n            f.trap(Error)\r\n          File \"/.../python3.5/site-packages/twisted/python/failure.py\", line 439, in trap\r\n            self.raiseException()\r\n          File \"/.../python3.5/site-packages/twisted/python/failure.py\", line 467, in raiseException\r\n            raise self.value.with_traceback(self.tb)\r\n          File \"/.../python3.5/site-packages/twisted/internet/defer.py\", line 654, in _runCallbacks\r\n            current.result = callback(current.result, *args, **kw)\r\n          File \"/.../python3.5/site-packages/buildbot/www/resource.py\", line 85, in failHttpRedirect\r\n            f.trap(Redirect)\r\n          File \"/.../python3.5/site-packages/twisted/python/failure.py\", line 439, in trap\r\n            self.raiseException()\r\n          File \"/.../python3.5/site-packages/twisted/python/failure.py\", line 467, in raiseException\r\n            raise self.value.with_traceback(self.tb)\r\n          File \"/.../python3.5/site-packages/buildbot/www/config.py\", line 126, in renderIndex\r\n            yield self.config['auth'].maybeAutoLogin(request)\r\n          File \"/.../python3.5/site-packages/buildbot/www/auth.py\", line 131, in maybeAutoLogin\r\n            yield self.updateUserInfo(request)\r\n          File \"/.../python3.5/site-packages/twisted/internet/defer.py\", line 1418, in _inlineCallbacks\r\n            result = g.send(result)\r\n          File \"/.../python3.5/site-packages/buildbot/www/auth.py\", line 78, in updateUserInfo\r\n            session.updateSession(request)\r\n          File \"/.../python3.5/site-packages/buildbot/www/service.py\", line 110, in updateSession\r\n            request.addCookie(cookiename, self.uid, path=b\"/\",\r\n          File \"/.../python3.5/site-packages/buildbot/www/service.py\", line 137, in uid\r\n            return jwt.encode(claims, self.site.session_secret, algorithm=SESSION_SECRET_ALGORITHM)\r\n          File \"/.../python3.5/site-packages/jwt/api_jwt.py\", line 62, in encode\r\n            cls=json_encoder\r\n          File \"/usr/lib/python3.5/json/__init__.py\", line 237, in dumps\r\n            **kw).encode(obj)\r\n          File \"/usr/lib/python3.5/json/encoder.py\", line 198, in encode\r\n            chunks = self.iterencode(o, _one_shot=True)\r\n          File \"/usr/lib/python3.5/json/encoder.py\", line 256, in iterencode\r\n            return _iterencode(o, 0)\r\n          File \"/usr/lib/python3.5/json/encoder.py\", line 179, in default\r\n            raise TypeError(repr(o) + \" is not JSON serializable\")\r\n        builtins.TypeError: b'foobar' is not JSON serializable\r\n```\r\nfollowing line [here](https://github.com/buildbot/buildbot/blob/master/master/buildbot/www/auth.py#L127) resolves the problem:\r\n```python\r\nuser_info = {k: buildbot.util.bytes2unicode(v) for k, v in res.groupdict().items()}\r\n```\r\nSorry if this is a duplicate, but I failed to find a ticket for this issue. \r\nI will create a PR as soon as I have spare time.\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\n\nimport re\nfrom abc import ABCMeta\nfrom abc import abstractmethod\n\nfrom twisted.cred.checkers import FilePasswordDB\nfrom twisted.cred.checkers import ICredentialsChecker\nfrom twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse\nfrom twisted.cred.credentials import IUsernamePassword\nfrom twisted.cred.error import UnauthorizedLogin\nfrom twisted.cred.portal import IRealm\nfrom twisted.cred.portal import Portal\nfrom twisted.internet import defer\nfrom twisted.web.error import Error\nfrom twisted.web.guard import BasicCredentialFactory\nfrom twisted.web.guard import DigestCredentialFactory\nfrom twisted.web.guard import HTTPAuthSessionWrapper\nfrom twisted.web.resource import IResource\nfrom zope.interface import implementer\n\nfrom buildbot.util import bytes2unicode\nfrom buildbot.util import config\nfrom buildbot.util import unicode2bytes\nfrom buildbot.www import resource\n\n\nclass AuthRootResource(resource.Resource):\n\n    def getChild(self, path, request):\n        # return dynamically generated resources\n        if path == b'login':\n            return self.master.www.auth.getLoginResource()\n        elif path == b'logout':\n            return self.master.www.auth.getLogoutResource()\n        return resource.Resource.getChild(self, path, request)\n\n\nclass AuthBase(config.ConfiguredMixin):\n\n    def __init__(self, userInfoProvider=None):\n        self.userInfoProvider = userInfoProvider\n\n    def reconfigAuth(self, master, new_config):\n        self.master = master\n\n    def maybeAutoLogin(self, request):\n        return defer.succeed(None)\n\n    def getLoginResource(self):\n        raise Error(501, b\"not implemented\")\n\n    def getLogoutResource(self):\n        return LogoutResource(self.master)\n\n    @defer.inlineCallbacks\n    def updateUserInfo(self, request):\n        session = request.getSession()\n        if self.userInfoProvider is not None:\n            infos = yield self.userInfoProvider.getUserInfo(session.user_info['username'])\n            session.user_info.update(infos)\n            session.updateSession(request)\n\n    def getConfigDict(self):\n        return {'name': type(self).__name__}\n\n\nclass UserInfoProviderBase(config.ConfiguredMixin):\n    name = \"noinfo\"\n\n    def getUserInfo(self, username):\n        return defer.succeed({'email': username})\n\n\nclass LoginResource(resource.Resource):\n\n    def render_GET(self, request):\n        return self.asyncRenderHelper(request, self.renderLogin)\n\n    @defer.inlineCallbacks\n    def renderLogin(self, request):\n        raise NotImplementedError\n\n\nclass NoAuth(AuthBase):\n    pass\n\n\nclass RemoteUserAuth(AuthBase):\n    header = b\"REMOTE_USER\"\n    headerRegex = re.compile(br\"(?P<username>[^ @]+)@(?P<realm>[^ @]+)\")\n\n    def __init__(self, header=None, headerRegex=None, **kwargs):\n        AuthBase.__init__(self, **kwargs)\n        if self.userInfoProvider is None:\n            self.userInfoProvider = UserInfoProviderBase()\n        if header is not None:\n            self.header = unicode2bytes(header)\n        if headerRegex is not None:\n            self.headerRegex = re.compile(unicode2bytes(headerRegex))\n\n    @defer.inlineCallbacks\n    def maybeAutoLogin(self, request):\n        header = request.getHeader(self.header)\n        if header is None:\n            raise Error(403, b\"missing http header \" + self.header + b\". Check your reverse proxy config!\")\n        res = self.headerRegex.match(header)\n        if res is None:\n            raise Error(\n                403, b'http header does not match regex! \"' + header + b'\" not matching ' + self.headerRegex.pattern)\n        session = request.getSession()\n        if session.user_info != dict(res.groupdict()):\n            session.user_info = dict(res.groupdict())\n            yield self.updateUserInfo(request)\n\n\n@implementer(IRealm)\nclass AuthRealm(object):\n\n    def __init__(self, master, auth):\n        self.auth = auth\n        self.master = master\n\n    def requestAvatar(self, avatarId, mind, *interfaces):\n        if IResource in interfaces:\n            return (IResource,\n                    PreAuthenticatedLoginResource(self.master, avatarId),\n                    lambda: None)\n        raise NotImplementedError()\n\n\nclass TwistedICredAuthBase(AuthBase):\n\n    def __init__(self, credentialFactories, checkers, **kwargs):\n        AuthBase.__init__(self, **kwargs)\n        if self.userInfoProvider is None:\n            self.userInfoProvider = UserInfoProviderBase()\n        self.credentialFactories = credentialFactories\n        self.checkers = checkers\n\n    def getLoginResource(self):\n        return HTTPAuthSessionWrapper(\n            Portal(AuthRealm(self.master, self), self.checkers),\n            self.credentialFactories)\n\n\nclass HTPasswdAuth(TwistedICredAuthBase):\n\n    def __init__(self, passwdFile, **kwargs):\n        TwistedICredAuthBase.__init__(\n            self,\n            [DigestCredentialFactory(b\"md5\", b\"buildbot\"),\n             BasicCredentialFactory(b\"buildbot\")],\n            [FilePasswordDB(passwdFile)],\n            **kwargs)\n\n\nclass UserPasswordAuth(TwistedICredAuthBase):\n\n    def __init__(self, users, **kwargs):\n        if isinstance(users, dict):\n            users = {user: unicode2bytes(pw) for user, pw in users.items()}\n        elif isinstance(users, list):\n            users = [(user, unicode2bytes(pw)) for user, pw in users]\n        TwistedICredAuthBase.__init__(\n            self,\n            [DigestCredentialFactory(b\"md5\", b\"buildbot\"),\n             BasicCredentialFactory(b\"buildbot\")],\n            [InMemoryUsernamePasswordDatabaseDontUse(**dict(users))],\n            **kwargs)\n\n\n@implementer(ICredentialsChecker)\nclass CustomAuth(TwistedICredAuthBase):\n    __metaclass__ = ABCMeta\n    credentialInterfaces = [IUsernamePassword]\n\n    def __init__(self, **kwargs):\n        TwistedICredAuthBase.__init__(\n            self,\n            [BasicCredentialFactory(b\"buildbot\")],\n            [self],\n            **kwargs)\n\n    def requestAvatarId(self, cred):\n        if self.check_credentials(cred.username, cred.password):\n            return defer.succeed(cred.username)\n        return defer.fail(UnauthorizedLogin())\n\n    @abstractmethod\n    def check_credentials(username, password):\n        return False\n\n\ndef _redirect(master, request):\n    url = request.args.get(b\"redirect\", [b\"/\"])[0]\n    url = bytes2unicode(url)\n    return resource.Redirect(master.config.buildbotURL + \"#\" + url)\n\n\nclass PreAuthenticatedLoginResource(LoginResource):\n    # a LoginResource which is already authenticated via a\n    # HTTPAuthSessionWrapper\n\n    def __init__(self, master, username):\n        LoginResource.__init__(self, master)\n        self.username = username\n\n    @defer.inlineCallbacks\n    def renderLogin(self, request):\n        session = request.getSession()\n        session.user_info = dict(username=bytes2unicode(self.username))\n        yield self.master.www.auth.updateUserInfo(request)\n        raise _redirect(self.master, request)\n\n\nclass LogoutResource(resource.Resource):\n\n    def render_GET(self, request):\n        session = request.getSession()\n        session.expire()\n        session.updateSession(request)\n        request.redirect(_redirect(self.master, request).url)\n        return b''\n", "path": "master/buildbot/www/auth.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\n\nimport re\nfrom abc import ABCMeta\nfrom abc import abstractmethod\n\nfrom twisted.cred.checkers import FilePasswordDB\nfrom twisted.cred.checkers import ICredentialsChecker\nfrom twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse\nfrom twisted.cred.credentials import IUsernamePassword\nfrom twisted.cred.error import UnauthorizedLogin\nfrom twisted.cred.portal import IRealm\nfrom twisted.cred.portal import Portal\nfrom twisted.internet import defer\nfrom twisted.web.error import Error\nfrom twisted.web.guard import BasicCredentialFactory\nfrom twisted.web.guard import DigestCredentialFactory\nfrom twisted.web.guard import HTTPAuthSessionWrapper\nfrom twisted.web.resource import IResource\nfrom zope.interface import implementer\n\nfrom buildbot.util import bytes2unicode\nfrom buildbot.util import config\nfrom buildbot.util import unicode2bytes\nfrom buildbot.www import resource\n\n\nclass AuthRootResource(resource.Resource):\n\n    def getChild(self, path, request):\n        # return dynamically generated resources\n        if path == b'login':\n            return self.master.www.auth.getLoginResource()\n        elif path == b'logout':\n            return self.master.www.auth.getLogoutResource()\n        return resource.Resource.getChild(self, path, request)\n\n\nclass AuthBase(config.ConfiguredMixin):\n\n    def __init__(self, userInfoProvider=None):\n        self.userInfoProvider = userInfoProvider\n\n    def reconfigAuth(self, master, new_config):\n        self.master = master\n\n    def maybeAutoLogin(self, request):\n        return defer.succeed(None)\n\n    def getLoginResource(self):\n        raise Error(501, b\"not implemented\")\n\n    def getLogoutResource(self):\n        return LogoutResource(self.master)\n\n    @defer.inlineCallbacks\n    def updateUserInfo(self, request):\n        session = request.getSession()\n        if self.userInfoProvider is not None:\n            infos = yield self.userInfoProvider.getUserInfo(session.user_info['username'])\n            session.user_info.update(infos)\n            session.updateSession(request)\n\n    def getConfigDict(self):\n        return {'name': type(self).__name__}\n\n\nclass UserInfoProviderBase(config.ConfiguredMixin):\n    name = \"noinfo\"\n\n    def getUserInfo(self, username):\n        return defer.succeed({'email': username})\n\n\nclass LoginResource(resource.Resource):\n\n    def render_GET(self, request):\n        return self.asyncRenderHelper(request, self.renderLogin)\n\n    @defer.inlineCallbacks\n    def renderLogin(self, request):\n        raise NotImplementedError\n\n\nclass NoAuth(AuthBase):\n    pass\n\n\nclass RemoteUserAuth(AuthBase):\n    header = b\"REMOTE_USER\"\n    headerRegex = re.compile(br\"(?P<username>[^ @]+)@(?P<realm>[^ @]+)\")\n\n    def __init__(self, header=None, headerRegex=None, **kwargs):\n        AuthBase.__init__(self, **kwargs)\n        if self.userInfoProvider is None:\n            self.userInfoProvider = UserInfoProviderBase()\n        if header is not None:\n            self.header = unicode2bytes(header)\n        if headerRegex is not None:\n            self.headerRegex = re.compile(unicode2bytes(headerRegex))\n\n    @defer.inlineCallbacks\n    def maybeAutoLogin(self, request):\n        header = request.getHeader(self.header)\n        if header is None:\n            raise Error(403, b\"missing http header \" + self.header + b\". Check your reverse proxy config!\")\n        res = self.headerRegex.match(header)\n        if res is None:\n            raise Error(\n                403, b'http header does not match regex! \"' + header + b'\" not matching ' + self.headerRegex.pattern)\n        session = request.getSession()\n        user_info = {k: bytes2unicode(v) for k, v in res.groupdict().items()}\n        if session.user_info != user_info:\n            session.user_info = user_info\n            yield self.updateUserInfo(request)\n\n\n@implementer(IRealm)\nclass AuthRealm(object):\n\n    def __init__(self, master, auth):\n        self.auth = auth\n        self.master = master\n\n    def requestAvatar(self, avatarId, mind, *interfaces):\n        if IResource in interfaces:\n            return (IResource,\n                    PreAuthenticatedLoginResource(self.master, avatarId),\n                    lambda: None)\n        raise NotImplementedError()\n\n\nclass TwistedICredAuthBase(AuthBase):\n\n    def __init__(self, credentialFactories, checkers, **kwargs):\n        AuthBase.__init__(self, **kwargs)\n        if self.userInfoProvider is None:\n            self.userInfoProvider = UserInfoProviderBase()\n        self.credentialFactories = credentialFactories\n        self.checkers = checkers\n\n    def getLoginResource(self):\n        return HTTPAuthSessionWrapper(\n            Portal(AuthRealm(self.master, self), self.checkers),\n            self.credentialFactories)\n\n\nclass HTPasswdAuth(TwistedICredAuthBase):\n\n    def __init__(self, passwdFile, **kwargs):\n        TwistedICredAuthBase.__init__(\n            self,\n            [DigestCredentialFactory(b\"md5\", b\"buildbot\"),\n             BasicCredentialFactory(b\"buildbot\")],\n            [FilePasswordDB(passwdFile)],\n            **kwargs)\n\n\nclass UserPasswordAuth(TwistedICredAuthBase):\n\n    def __init__(self, users, **kwargs):\n        if isinstance(users, dict):\n            users = {user: unicode2bytes(pw) for user, pw in users.items()}\n        elif isinstance(users, list):\n            users = [(user, unicode2bytes(pw)) for user, pw in users]\n        TwistedICredAuthBase.__init__(\n            self,\n            [DigestCredentialFactory(b\"md5\", b\"buildbot\"),\n             BasicCredentialFactory(b\"buildbot\")],\n            [InMemoryUsernamePasswordDatabaseDontUse(**dict(users))],\n            **kwargs)\n\n\n@implementer(ICredentialsChecker)\nclass CustomAuth(TwistedICredAuthBase):\n    __metaclass__ = ABCMeta\n    credentialInterfaces = [IUsernamePassword]\n\n    def __init__(self, **kwargs):\n        TwistedICredAuthBase.__init__(\n            self,\n            [BasicCredentialFactory(b\"buildbot\")],\n            [self],\n            **kwargs)\n\n    def requestAvatarId(self, cred):\n        if self.check_credentials(cred.username, cred.password):\n            return defer.succeed(cred.username)\n        return defer.fail(UnauthorizedLogin())\n\n    @abstractmethod\n    def check_credentials(username, password):\n        return False\n\n\ndef _redirect(master, request):\n    url = request.args.get(b\"redirect\", [b\"/\"])[0]\n    url = bytes2unicode(url)\n    return resource.Redirect(master.config.buildbotURL + \"#\" + url)\n\n\nclass PreAuthenticatedLoginResource(LoginResource):\n    # a LoginResource which is already authenticated via a\n    # HTTPAuthSessionWrapper\n\n    def __init__(self, master, username):\n        LoginResource.__init__(self, master)\n        self.username = username\n\n    @defer.inlineCallbacks\n    def renderLogin(self, request):\n        session = request.getSession()\n        session.user_info = dict(username=bytes2unicode(self.username))\n        yield self.master.www.auth.updateUserInfo(request)\n        raise _redirect(self.master, request)\n\n\nclass LogoutResource(resource.Resource):\n\n    def render_GET(self, request):\n        session = request.getSession()\n        session.expire()\n        session.updateSession(request)\n        request.redirect(_redirect(self.master, request).url)\n        return b''\n", "path": "master/buildbot/www/auth.py"}]}