diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py index c8c0146a..ba569edb 100644 --- a/jupyterhub/apihandlers/users.py +++ b/jupyterhub/apihandlers/users.py @@ -214,18 +214,39 @@ class UserTokenListAPIHandler(APIHandler): 'oauth_tokens': oauth_tokens, })) - @admin_or_self - def post(self, name): + async def post(self, name): + body = self.get_json_body() or {} + if not isinstance(body, dict): + raise web.HTTPError(400, "Body must be a JSON dict or empty") + requester = self.get_current_user() + if requester is None: + # defer to Authenticator for identifying the user + # can be username+password or an upstream auth token + try: + name = await self.authenticator.authenticate(self, body.get('auth')) + except web.HTTPError as e: + # turn any authentication error into 403 + raise web.HTTPError(403) + except Exception as e: + # suppress and log error here in case Authenticator + # isn't prepared to handle auth via this data + self.log.error("Error authenticating request for %s: %s", + self.request.uri, e) + raise web.HTTPError(403) + requester = self.find_user(name) + if requester is None: + # couldn't identify requester + raise web.HTTPError(403) user = self.find_user(name) if requester is not user and not requester.admin: raise web.HTTPError(403, "Only admins can request tokens for other users") if not user: raise web.HTTPError(404, "No such user: %s" % name) - body = self.get_json_body() if requester is not user: kind = 'user' if isinstance(requester, User) else 'service' - note = (body or {}).get('note') + + note = body.get('note') if not note: note = "Requested via api" if requester is not user: diff --git a/jupyterhub/tests/test_api.py b/jupyterhub/tests/test_api.py index 93258fa9..6a736023 100644 --- a/jupyterhub/tests/test_api.py +++ b/jupyterhub/tests/test_api.py @@ -1289,6 +1289,31 @@ def test_token_for_user(app, as_user, for_user, status): ) assert r.status_code == 404 + +@mark.gen_test +def test_token_authenticator_noauth(app): + """Create a token for a user relying on Authenticator.authenticate and no auth header""" + name = 'user' + data = { + 'auth': { + 'username': name, + 'password': name, + }, + } + r = yield api_request(app, 'users', name, 'tokens', + method='post', + data=json.dumps(data) if data else None, + noauth=True, + ) + assert r.status_code == 200 + reply = r.json() + assert 'token' in reply + r = yield api_request(app, 'authorizations', 'token', reply['token']) + r.raise_for_status() + reply = r.json() + assert reply['name'] == name + + @mark.gen_test @mark.parametrize("as_user, for_user, status", [ ('admin', 'other', 200),