mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-11 20:13:02 +00:00
raise 403 on disallowed user, rather than redirect to login url
raise UserNotAllowed exception in generic `check_hub_user` when a user or service is identified and not allowed. turn it into `HTTPError(403)` in tornado `get_current_user` wrapper, caching `None` so that subsequent calls don't re-trigger the same error.
This commit is contained in:
@@ -6,7 +6,7 @@ import json
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from tornado.log import LogFormatter, access_log
|
from tornado.log import LogFormatter, access_log
|
||||||
from tornado.web import StaticFileHandler
|
from tornado.web import StaticFileHandler, HTTPError
|
||||||
|
|
||||||
|
|
||||||
def coroutine_traceback(typ, value, tb):
|
def coroutine_traceback(typ, value, tb):
|
||||||
@@ -88,7 +88,7 @@ def log_request(handler):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
user = handler.get_current_user()
|
user = handler.get_current_user()
|
||||||
except web.HTTPError:
|
except HTTPError:
|
||||||
username = ''
|
username = ''
|
||||||
else:
|
else:
|
||||||
if user is None:
|
if user is None:
|
||||||
|
@@ -7,7 +7,6 @@ HubAuth can be used in any application, even outside tornado.
|
|||||||
HubAuthenticated is a mixin class for tornado handlers that should authenticate with the Hub.
|
HubAuthenticated is a mixin class for tornado handlers that should authenticate with the Hub.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
@@ -494,6 +493,18 @@ class HubOAuth(HubAuth):
|
|||||||
handler.clear_cookie(self.cookie_name, path=self.base_url)
|
handler.clear_cookie(self.cookie_name, path=self.base_url)
|
||||||
|
|
||||||
|
|
||||||
|
class UserNotAllowed(Exception):
|
||||||
|
"""Exception raised when a user is identified and not allowed"""
|
||||||
|
def __init__(self, model):
|
||||||
|
self.model = model
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<{cls} {kind}={name}>'.format(
|
||||||
|
cls=self.__class__.__name__,
|
||||||
|
kind=self.model['kind'],
|
||||||
|
name=self.model['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HubAuthenticated(object):
|
class HubAuthenticated(object):
|
||||||
"""Mixin for tornado handlers that are authenticated with JupyterHub
|
"""Mixin for tornado handlers that are authenticated with JupyterHub
|
||||||
@@ -568,7 +579,7 @@ class HubAuthenticated(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
name = model['name']
|
name = model['name']
|
||||||
kind = model.get('kind', 'user')
|
kind = model.setdefault('kind', 'user')
|
||||||
if self.allow_all:
|
if self.allow_all:
|
||||||
app_log.debug("Allowing Hub %s %s (all Hub users and services allowed)", kind, name)
|
app_log.debug("Allowing Hub %s %s (all Hub users and services allowed)", kind, name)
|
||||||
return model
|
return model
|
||||||
@@ -584,7 +595,7 @@ class HubAuthenticated(object):
|
|||||||
return model
|
return model
|
||||||
else:
|
else:
|
||||||
app_log.warning("Not allowing Hub service %s", name)
|
app_log.warning("Not allowing Hub service %s", name)
|
||||||
return None
|
raise UserNotAllowed(model)
|
||||||
|
|
||||||
if self.hub_users and name in self.hub_users:
|
if self.hub_users and name in self.hub_users:
|
||||||
# user in whitelist
|
# user in whitelist
|
||||||
@@ -597,7 +608,7 @@ class HubAuthenticated(object):
|
|||||||
return model
|
return model
|
||||||
else:
|
else:
|
||||||
app_log.warning("Not allowing Hub user %s", name)
|
app_log.warning("Not allowing Hub user %s", name)
|
||||||
return None
|
raise UserNotAllowed(model)
|
||||||
|
|
||||||
def get_current_user(self):
|
def get_current_user(self):
|
||||||
"""Tornado's authentication method
|
"""Tornado's authentication method
|
||||||
@@ -611,7 +622,15 @@ class HubAuthenticated(object):
|
|||||||
if not user_model:
|
if not user_model:
|
||||||
self._hub_auth_user_cache = None
|
self._hub_auth_user_cache = None
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
self._hub_auth_user_cache = self.check_hub_user(user_model)
|
self._hub_auth_user_cache = self.check_hub_user(user_model)
|
||||||
|
except UserNotAllowed as e:
|
||||||
|
# cache None, in case get_user is called again while processing the error
|
||||||
|
self._hub_auth_user_cache = None
|
||||||
|
raise HTTPError(403, "{kind} {name} is not allowed.".format(**e.model))
|
||||||
|
except Exception:
|
||||||
|
self._hub_auth_user_cache = None
|
||||||
|
raise
|
||||||
return self._hub_auth_user_cache
|
return self._hub_auth_user_cache
|
||||||
|
|
||||||
|
|
||||||
|
@@ -37,6 +37,13 @@ def test_singleuser_auth(app, io_loop):
|
|||||||
r = requests.get(url_path_join(url, 'logout'), cookies=cookies)
|
r = requests.get(url_path_join(url, 'logout'), cookies=cookies)
|
||||||
assert len(r.cookies) == 0
|
assert len(r.cookies) == 0
|
||||||
|
|
||||||
|
# another user accessing should get 403, not redirect to login
|
||||||
|
cookies = app.login_user('burgess')
|
||||||
|
r = requests.get(url, cookies=cookies)
|
||||||
|
assert r.status_code == 403
|
||||||
|
print(r.text)
|
||||||
|
assert r.text == ''
|
||||||
|
|
||||||
|
|
||||||
def test_disable_user_config(app, io_loop):
|
def test_disable_user_config(app, io_loop):
|
||||||
# use StubSingleUserSpawner to launch a single-user app in a thread
|
# use StubSingleUserSpawner to launch a single-user app in a thread
|
||||||
|
Reference in New Issue
Block a user