|
19 | 19 | from jupyterhub.auth import Authenticator, LocalAuthenticator
|
20 | 20 | from jupyterhub.utils import url_path_join
|
21 | 21 |
|
22 |
| -from traitlets import Unicode |
| 22 | +from traitlets import Unicode, Set |
23 | 23 |
|
24 | 24 |
|
25 | 25 | class GitHubMixin(OAuth2Mixin):
|
@@ -158,6 +158,10 @@ class BitbucketOAuthenticator(Authenticator):
|
158 | 158 | config=True)
|
159 | 159 | client_secret = Unicode(os.environ.get('BITBUCKET_CLIENT_SECRET', ''),
|
160 | 160 | config=True)
|
| 161 | + team_whitelist = Set( |
| 162 | + config=True, |
| 163 | + help="Automatically whitelist members of selected teams", |
| 164 | + ) |
161 | 165 |
|
162 | 166 | def login_url(self, base_url):
|
163 | 167 | return url_path_join(base_url, 'oauth_login')
|
@@ -216,9 +220,39 @@ def authenticate(self, handler):
|
216 | 220 | resp_json = json.loads(resp.body.decode('utf8', 'replace'))
|
217 | 221 |
|
218 | 222 | username = resp_json["username"]
|
219 |
| - if self.whitelist and username not in self.whitelist: |
| 223 | + whitelisted = yield self.check_whitelist(username, headers) |
| 224 | + if not whitelisted: |
220 | 225 | username = None
|
221 |
| - raise gen.Return(username) |
| 226 | + return username |
| 227 | + |
| 228 | + def check_whitelist(self, username, headers): |
| 229 | + if self.team_whitelist: |
| 230 | + return self._check_group_whitelist(username, headers) |
| 231 | + else: |
| 232 | + return self._check_user_whitelist(username) |
| 233 | + |
| 234 | + @gen.coroutine |
| 235 | + def _check_user_whitelist(self, user): |
| 236 | + return (not self.whitelist) or (user in self.whitelist) |
| 237 | + |
| 238 | + @gen.coroutine |
| 239 | + def _check_group_whitelist(self, username, headers): |
| 240 | + http_client = AsyncHTTPClient() |
| 241 | + |
| 242 | + # We verify the team membership by calling teams endpoint. |
| 243 | + # Re-use the headers, change the request. |
| 244 | + next_page = url_concat("https://api.bitbucket.org/2.0/teams", |
| 245 | + {'role': 'member'}) |
| 246 | + user_teams = set() |
| 247 | + while next_page: |
| 248 | + req = HTTPRequest(next_page, method="GET", headers=headers) |
| 249 | + resp = yield http_client.fetch(req) |
| 250 | + resp_json = json.loads(resp.body.decode('utf8', 'replace')) |
| 251 | + next_page = resp_json.get('next', None) |
| 252 | + |
| 253 | + user_teams |= \ |
| 254 | + set([entry["username"] for entry in resp_json["values"]]) |
| 255 | + return len(self.team_whitelist & user_teams) > 0 |
222 | 256 |
|
223 | 257 |
|
224 | 258 | class LocalGitHubOAuthenticator(LocalAuthenticator, GitHubOAuthenticator):
|
|
0 commit comments