diff --git a/jupyterhub/apihandlers/auth.py b/jupyterhub/apihandlers/auth.py index 193e4ce9..73419e56 100644 --- a/jupyterhub/apihandlers/auth.py +++ b/jupyterhub/apihandlers/auth.py @@ -12,9 +12,24 @@ from .base import APIHandler -class AuthorizationsAPIHandler(APIHandler): +class TokenAPIHandler(APIHandler): @token_authenticated def get(self, token): + orm_token = self.db.query(orm.APIToken).filter(orm.APIToken.token == token).first() + if orm_token is None: + raise web.HTTPError(404) + self.write(json.dumps({ + 'user' : orm_token.user.name, + })) + +class CookieAPIHandler(APIHandler): + @token_authenticated + def get(self, cookie_name): + cookie_value = self.request.body + btoken = self.get_secure_cookie(cookie_name, cookie_value) + if not btoken: + raise web.HTTPError(404) + token = btoken.decode('utf8', 'replace') orm_token = self.db.query(orm.CookieToken).filter(orm.CookieToken.token == token).first() if orm_token is None: raise web.HTTPError(404) @@ -23,5 +38,6 @@ class AuthorizationsAPIHandler(APIHandler): })) default_handlers = [ - (r"/api/authorizations/([^/]+)", AuthorizationsAPIHandler), + (r"/api/authorizations/cookie/([^/]+)", CookieAPIHandler), + (r"/api/authorizations/token/([^/]+)", TokenAPIHandler), ] diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py index b021d490..669388c2 100644 --- a/jupyterhub/handlers/base.py +++ b/jupyterhub/handlers/base.py @@ -83,12 +83,15 @@ class BaseHandler(RequestHandler): def get_current_user_cookie(self): """get_current_user from a cookie token""" - token = self.get_cookie(self.hub.server.cookie_name, None) - if token: + btoken = self.get_secure_cookie(self.hub.server.cookie_name) + if btoken: + token = btoken.decode('utf8', 'replace') cookie_token = orm.CookieToken.find(self.db, token) if cookie_token: return cookie_token.user else: + # don't log the token itself + self.log.warn("Invalid cookie token") # have cookie, but it's not valid. Clear it and start over. self.clear_cookie(self.hub.server.cookie_name, path=self.hub.server.base_url) @@ -128,7 +131,7 @@ class BaseHandler(RequestHandler): cookie_token = user.new_cookie_token() self.db.add(cookie_token) self.db.commit() - self.set_cookie( + self.set_secure_cookie( user.server.cookie_name, cookie_token.token, path=user.server.base_url, @@ -139,7 +142,7 @@ class BaseHandler(RequestHandler): cookie_token = user.new_cookie_token() self.db.add(cookie_token) self.db.commit() - self.set_cookie( + self.set_secure_cookie( self.hub.server.cookie_name, cookie_token.token, path=self.hub.server.base_url) diff --git a/jupyterhub/orm.py b/jupyterhub/orm.py index 8d3ae39f..5abbbfd8 100644 --- a/jupyterhub/orm.py +++ b/jupyterhub/orm.py @@ -299,7 +299,6 @@ class User(Base): hub = db.query(Hub).first() self.server = Server( cookie_name='%s-%s' % (hub.server.cookie_name, self.name), - cookie_secret=hub.server.cookie_secret, base_url=url_path_join(base_url, 'user', self.name), ) db.add(self.server) diff --git a/jupyterhub/singleuser.py b/jupyterhub/singleuser.py index fe04236a..1ac4c58b 100644 --- a/jupyterhub/singleuser.py +++ b/jupyterhub/singleuser.py @@ -28,19 +28,20 @@ if V(IPython.__version__) < V('2.2'): # which authenticate via the central auth server. -def verify_token(self, token): +def verify_token(self, cookie_name, encrypted_cookie): """monkeypatch method for token verification""" - token_cache = self.settings['token_cache'] - if token in token_cache: + cookie_cache = self.settings['cookie_cache'] + if encrypted_cookie in cookie_cache: # we've seen this token before, don't ask upstream again - return token_cache[token] + return cookie_cache[encrypted_cookie] hub_api_url = self.settings['hub_api_url'] hub_api_key = self.settings['hub_api_key'] r = requests.get(url_path_join( - hub_api_url, "authorizations", token, + hub_api_url, "authorizations/cookie", cookie_name, ), - headers = {'Authorization' : 'token %s' % hub_api_key} + headers = {'Authorization' : 'token %s' % hub_api_key}, + data=encrypted_cookie, ) if r.status_code == 404: data = {'user' : ''} @@ -49,16 +50,16 @@ def verify_token(self, token): data = None else: data = r.json() - token_cache[token] = data + cookie_cache[encrypted_cookie] = data return data def get_current_user(self): """alternative get_current_user to query the central server""" my_user = self.settings['user'] - token = self.get_cookie(self.cookie_name, '') - if token: - auth_data = self.verify_token(token) + encrypted_cookie = self.get_cookie(self.cookie_name) + if encrypted_cookie: + auth_data = self.verify_token(self.cookie_name, encrypted_cookie) if not auth_data: # treat invalid token the same as no token return None @@ -123,10 +124,9 @@ class SingleUserNotebookApp(NotebookApp): s = getattr(self, 'tornado_settings', getattr(self, 'webapp_settings') ) - s['token_cache'] = {} + s['cookie_cache'] = {} s['user'] = self.user s['hub_api_key'] = env.pop('JPY_API_TOKEN') - s['cookie_secret'] = env.pop('JPY_COOKIE_SECRET') s['cookie_name'] = self.cookie_name s['login_url'] = url_path_join(self.hub_prefix, 'login') s['hub_api_url'] = self.hub_api_url diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 8c0d9349..f79bf4e1 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -65,7 +65,6 @@ class Spawner(LoggingConfigurable): for key in self.env_keep: if key in os.environ: env[key] = os.environ[key] - env['JPY_COOKIE_SECRET'] = self.user.server.cookie_secret env['JPY_API_TOKEN'] = self.api_token return env diff --git a/jupyterhub/tests/test_api.py b/jupyterhub/tests/test_api.py index 90b0ab9b..ce914c6f 100644 --- a/jupyterhub/tests/test_api.py +++ b/jupyterhub/tests/test_api.py @@ -48,23 +48,25 @@ def test_auth_api(app): # make a new cookie token user = db.query(orm.User).first() + api_token = user.new_api_token() + db.add(api_token) cookie_token = user.new_cookie_token() db.add(cookie_token) db.commit() # check success: - r = api_request(app, 'authorizations', cookie_token.token) + r = api_request(app, 'authorizations/token', api_token.token) assert r.status_code == 200 reply = r.json() assert reply['user'] == user.name # check fail - r = api_request(app, 'authorizations', cookie_token.token, + r = api_request(app, 'authorizations/token', api_token.token, headers={'Authorization': 'no sir'}, ) assert r.status_code == 403 - r = api_request(app, 'authorizations', cookie_token.token, + r = api_request(app, 'authorizations/token', api_token.token, headers={'Authorization': 'token: %s' % cookie_token.token}, ) assert r.status_code == 403