mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-10 03:23:04 +00:00
token expiry fixes
typos in token expiry: - omitted from token model (it's in the spec in docs, but wasn't in the model) - wrong type when sorting oauth tokens on token page could cause token page to not render
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
import datetime
|
||||
|
||||
from http.client import responses
|
||||
|
||||
@@ -14,7 +14,17 @@ from .. import orm
|
||||
from ..handlers import BaseHandler
|
||||
from ..utils import isoformat, url_path_join
|
||||
|
||||
|
||||
class APIHandler(BaseHandler):
|
||||
"""Base class for API endpoints
|
||||
|
||||
Differences from page handlers:
|
||||
|
||||
- JSON responses and errors
|
||||
- strict referer checking for Cookie-authenticated requests
|
||||
- strict content-security-policy
|
||||
- methods for REST API models
|
||||
"""
|
||||
|
||||
@property
|
||||
def content_security_policy(self):
|
||||
@@ -137,7 +147,7 @@ class APIHandler(BaseHandler):
|
||||
'oauth_client': token.client.description or token.client.client_id,
|
||||
}
|
||||
if token.expires_at:
|
||||
expires_at = datetime.datetime.fromtimestamp(token.expires_at)
|
||||
expires_at = datetime.fromtimestamp(token.expires_at)
|
||||
else:
|
||||
raise TypeError(
|
||||
"token must be an APIToken or OAuthAccessToken, not %s"
|
||||
@@ -157,6 +167,7 @@ class APIHandler(BaseHandler):
|
||||
'kind': kind,
|
||||
'created': isoformat(token.created),
|
||||
'last_activity': isoformat(token.last_activity),
|
||||
'expires_at': isoformat(expires_at),
|
||||
}
|
||||
model.update(extra)
|
||||
return model
|
||||
|
@@ -247,9 +247,11 @@ class TokenPageHandler(BaseHandler):
|
||||
api_tokens.append(token)
|
||||
|
||||
# group oauth client tokens by client id
|
||||
# AccessTokens have expires_at as an integer timestamp
|
||||
now_timestamp = now.timestamp()
|
||||
oauth_tokens = defaultdict(list)
|
||||
for token in user.oauth_tokens:
|
||||
if token.expires_at and token.expires_at < now:
|
||||
if token.expires_at and token.expires_at < now_timestamp:
|
||||
self.log.warning("Deleting expired token")
|
||||
self.db.delete(token)
|
||||
self.db.commit()
|
||||
|
@@ -1214,14 +1214,19 @@ def test_token_as_user_deprecated(app, as_user, for_user, status):
|
||||
|
||||
|
||||
@mark.gen_test
|
||||
@mark.parametrize("headers, status, note", [
|
||||
({}, 200, 'test note'),
|
||||
({}, 200, ''),
|
||||
({'Authorization': 'token bad'}, 403, ''),
|
||||
@mark.parametrize("headers, status, note, expires_in", [
|
||||
({}, 200, 'test note', None),
|
||||
({}, 200, '', 100),
|
||||
({'Authorization': 'token bad'}, 403, '', None),
|
||||
])
|
||||
def test_get_new_token(app, headers, status, note):
|
||||
def test_get_new_token(app, headers, status, note, expires_in):
|
||||
options = {}
|
||||
if note:
|
||||
body = json.dumps({'note': note})
|
||||
options['note'] = note
|
||||
if expires_in:
|
||||
options['expires_in'] = expires_in
|
||||
if options:
|
||||
body = json.dumps(options)
|
||||
else:
|
||||
body = ''
|
||||
# request a new token
|
||||
@@ -1239,6 +1244,10 @@ def test_get_new_token(app, headers, status, note):
|
||||
assert reply['user'] == 'admin'
|
||||
assert reply['created']
|
||||
assert 'last_activity' in reply
|
||||
if expires_in:
|
||||
assert isinstance(reply['expires_at'], str)
|
||||
else:
|
||||
assert reply['expires_at'] is None
|
||||
if note:
|
||||
assert reply['note'] == note
|
||||
else:
|
||||
|
@@ -598,6 +598,29 @@ def test_announcements(app, announcements):
|
||||
assert_announcement("logout", r.text)
|
||||
|
||||
|
||||
@pytest.mark.gen_test
|
||||
def test_token_page(app):
|
||||
name = "cake"
|
||||
cookies = yield app.login_user(name)
|
||||
r = yield get_page("token", app, cookies=cookies)
|
||||
r.raise_for_status()
|
||||
assert urlparse(r.url).path.endswith('/hub/token')
|
||||
assert "Request new API token" in r.text
|
||||
assert "API Tokens" in r.text
|
||||
assert "Server at %s" % app.users[name].url in r.text
|
||||
# no oauth tokens yet, shouldn't have that section
|
||||
assert "Authorized Applications" not in r.text
|
||||
|
||||
# spawn the user to trigger oauth, etc.
|
||||
r = yield get_page("spawn", app, cookies=cookies)
|
||||
r.raise_fo_status()
|
||||
|
||||
r = yield get_page("token", app, cookies=cookies)
|
||||
r.raise_for_status()
|
||||
assert "API Tokens" in r.text
|
||||
assert "Authorized Applications" not in r.text
|
||||
|
||||
|
||||
@pytest.mark.gen_test
|
||||
def test_server_not_running_api_request(app):
|
||||
cookies = yield app.login_user("bees")
|
||||
|
Reference in New Issue
Block a user