expose cookie options and pass them down to spawners

enables forcing all-session cookies with:

```python
c.JupyterHub.tornado_settings['cookie_options'] = {
    'expires_days': None,
}
```
This commit is contained in:
Min RK
2018-03-23 10:38:50 +01:00
parent 82cab39e1c
commit e374e93cfb
4 changed files with 37 additions and 5 deletions

View File

@@ -30,7 +30,10 @@ from tornado.httputil import url_concat
from tornado.web import HTTPError, RequestHandler
from traitlets.config import SingletonConfigurable
from traitlets import Unicode, Integer, Instance, default, observe, validate
from traitlets import (
Unicode, Integer, Instance, Dict,
default, observe, validate,
)
from ..utils import url_path_join
@@ -197,6 +200,24 @@ class HubAuth(SingletonConfigurable):
help="""The name of the cookie I should be looking for"""
).tag(config=True)
cookie_options = Dict(
help="""Additional options to pass when setting cookies.
Can include things like `expires_days=None` for session-expiry
or `secure=True` if served on HTTPS and default HTTPS discovery fails
(e.g. behind some proxies).
"""
).tag(config=True)
@default('cookie_options')
def _default_cookie_options(self):
# load default from env
options_env = os.environ.get('JUPYTERHUB_COOKIE_OPTIONS')
if options_env:
return json.loads(options_env)
else:
return {}
cookie_cache_max_age = Integer(help="DEPRECATED. Use cache_max_age")
@observe('cookie_cache_max_age')
def _deprecated_cookie_cache(self, change):
@@ -580,6 +601,8 @@ class HubOAuth(HubAuth):
}
if handler.request.protocol == 'https':
kwargs['secure'] = True
# load user cookie overrides
kwargs.update(self.cookie_options)
handler.set_secure_cookie(
cookie_name,
b64_state,
@@ -627,6 +650,8 @@ class HubOAuth(HubAuth):
}
if handler.request.protocol == 'https':
kwargs['secure'] = True
# load user cookie overrides
kwargs.update(self.cookie_options)
app_log.debug("Setting oauth cookie for %s: %s, %s",
handler.request.remote_ip, self.cookie_name, kwargs)
handler.set_secure_cookie(

View File

@@ -218,6 +218,7 @@ class Service(LoggingConfigurable):
base_url = Unicode()
db = Any()
orm = Any()
cookie_options = Dict()
oauth_provider = Any()
@@ -299,6 +300,7 @@ class Service(LoggingConfigurable):
environment=env,
api_token=self.api_token,
oauth_client_id=self.oauth_client_id,
cookie_options=self.cookie_options,
cwd=self.cwd,
hub=self.hub,
user=_MockUser(

View File

@@ -6,6 +6,7 @@ Contains base Spawner class & default implementation
# Distributed under the terms of the Modified BSD License.
import errno
import json
import os
import pipes
import shutil
@@ -99,11 +100,12 @@ class Spawner(LoggingConfigurable):
"""
return bool(self.pending or self.ready)
# options passed by constructor
authenticator = Any()
hub = Any()
orm_spawner = Any()
db = Any()
cookie_options = Dict()
@observe('orm_spawner')
def _orm_spawner_changed(self, change):
@@ -587,6 +589,8 @@ class Spawner(LoggingConfigurable):
env['JUPYTERHUB_ADMIN_ACCESS'] = '1'
# OAuth settings
env['JUPYTERHUB_CLIENT_ID'] = self.oauth_client_id
if self.cookie_options:
env['JUPYTERHUB_COOKIE_OPTIONS'] = json.dumps(self.cookie_options)
env['JUPYTERHUB_HOST'] = self.hub.public_host
env['JUPYTERHUB_OAUTH_CALLBACK_URL'] = \
url_path_join(self.user.url, self.name, 'oauth_callback')

View File

@@ -215,6 +215,7 @@ class User:
proxy_spec=url_path_join(self.proxy_spec, name, '/'),
db=self.db,
oauth_client_id=client_id,
cookie_options = self.settings.get('cookie_options', {}),
)
# update with kwargs. Mainly for testing.
spawn_kwargs.update(kwargs)