We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
RolesFromEmails doesn't work with GitHub auth (and maybe others)
I've got a setup like this:

```
auth = util.GitHubAuth(CLIENT_ID, CLIENT_SECRET)
authz = util.Authz(
    allowRules = [ util.AnyControlEndpointMatcher(role = "admins") ],
    roleMatchers = [
        util.RolesFromEmails(
            admins = [ "sam@humbug.net" ]
        )
    ]
)
```

`sam@humbug.net` is my primary email address. This doesn't work; I have to use my username, `samizzo`, as the email address in the `RolesFromEmails`.

Looking through the code, I can't see how this has ever worked. The authentication mechanism ends up calling [`UserInfoProviderBase.getUserInfo`](https://github.com/buildbot/buildbot/blob/master/master/buildbot/www/auth.py#L83) which returns the username as the email address in the user info.

I'm not sure what the right fix for this is; I don't know the buildbot code very well. I've switched over to using `RolesFromUsername` which is more convenient anyway.

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

{"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@@ -49,8 +49,6 @@\n class AuthBase(config.ConfiguredMixin):\n \n     def __init__(self, userInfoProvider=None):\n-        if userInfoProvider is None:\n-            userInfoProvider = UserInfoProviderBase()\n         self.userInfoProvider = userInfoProvider\n \n     def reconfigAuth(self, master, new_config):\n@@ -104,6 +102,8 @@\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 = header\n         if headerRegex is not None:\n@@ -145,6 +145,8 @@\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", "issue": "RolesFromEmails doesn't work with GitHub auth (and maybe others)\nI've got a setup like this:\r\n\r\n```\r\nauth = util.GitHubAuth(CLIENT_ID, CLIENT_SECRET)\r\nauthz = util.Authz(\r\n    allowRules = [ util.AnyControlEndpointMatcher(role = \"admins\") ],\r\n    roleMatchers = [\r\n        util.RolesFromEmails(\r\n            admins = [ \"sam@humbug.net\" ]\r\n        )\r\n    ]\r\n)\r\n```\r\n\r\n`sam@humbug.net` is my primary email address. This doesn't work; I have to use my username, `samizzo`, as the email address in the `RolesFromEmails`.\r\n\r\nLooking through the code, I can't see how this has ever worked. The authentication mechanism ends up calling [`UserInfoProviderBase.getUserInfo`](https://github.com/buildbot/buildbot/blob/master/master/buildbot/www/auth.py#L83) which returns the username as the email address in the user info.\r\n\r\nI'm not sure what the right fix for this is; I don't know the buildbot code very well. I've switched over to using `RolesFromUsername` which is more convenient anyway.\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\n\nimport re\n\nfrom twisted.cred.checkers import FilePasswordDB\nfrom twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse\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 bytes2NativeString\nfrom buildbot.util import config\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        if userInfoProvider is None:\n            userInfoProvider = UserInfoProviderBase()\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, \"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 = \"REMOTE_USER\"\n    headerRegex = re.compile(r\"(?P<username>[^ @]+)@(?P<realm>[^ @]+)\")\n\n    def __init__(self, header=None, headerRegex=None, **kwargs):\n        AuthBase.__init__(self, **kwargs)\n        if header is not None:\n            self.header = header\n        if headerRegex is not None:\n            self.headerRegex = re.compile(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, \"missing http header %s. Check your reverse proxy config!\" % (\n                             self.header))\n        res = self.headerRegex.match(header)\n        if res is None:\n            raise Error(\n                403, 'http header does not match regex! \"%s\" not matching %s' %\n                (header, 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        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        TwistedICredAuthBase.__init__(\n            self,\n            [DigestCredentialFactory(b\"md5\", b\"buildbot\"),\n             BasicCredentialFactory(b\"buildbot\")],\n            [InMemoryUsernamePasswordDatabaseDontUse(**dict(users))],\n            **kwargs)\n\n\ndef _redirect(master, request):\n    url = request.args.get(\"redirect\", [\"/\"])[0]\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=bytes2NativeString(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\nfrom __future__ import absolute_import\nfrom __future__ import print_function\n\nimport re\n\nfrom twisted.cred.checkers import FilePasswordDB\nfrom twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse\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 bytes2NativeString\nfrom buildbot.util import config\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, \"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 = \"REMOTE_USER\"\n    headerRegex = re.compile(r\"(?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 = header\n        if headerRegex is not None:\n            self.headerRegex = re.compile(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, \"missing http header %s. Check your reverse proxy config!\" % (\n                             self.header))\n        res = self.headerRegex.match(header)\n        if res is None:\n            raise Error(\n                403, 'http header does not match regex! \"%s\" not matching %s' %\n                (header, 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        TwistedICredAuthBase.__init__(\n            self,\n            [DigestCredentialFactory(b\"md5\", b\"buildbot\"),\n             BasicCredentialFactory(b\"buildbot\")],\n            [InMemoryUsernamePasswordDatabaseDontUse(**dict(users))],\n            **kwargs)\n\n\ndef _redirect(master, request):\n    url = request.args.get(\"redirect\", [\"/\"])[0]\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=bytes2NativeString(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"}]}