mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-17 15:03:02 +00:00
Merge branch 'master' into end-to-end-ssl
This commit is contained in:
@@ -6,7 +6,6 @@ from datetime import datetime, timedelta
|
||||
from urllib.parse import quote, urlparse
|
||||
import warnings
|
||||
|
||||
from oauth2.error import ClientNotFoundError
|
||||
from sqlalchemy import inspect
|
||||
from tornado import gen
|
||||
from tornado.log import app_log
|
||||
@@ -183,19 +182,41 @@ class User:
|
||||
await self.save_auth_state(auth_state)
|
||||
return auth_state
|
||||
|
||||
def _new_spawner(self, name, spawner_class=None, **kwargs):
|
||||
|
||||
def all_spawners(self, include_default=True):
|
||||
"""Generator yielding all my spawners
|
||||
|
||||
including those that are not running.
|
||||
|
||||
Spawners that aren't running will be low-level orm.Spawner objects,
|
||||
while those that are will be higher-level Spawner wrapper objects.
|
||||
"""
|
||||
|
||||
for name, orm_spawner in sorted(self.orm_user.orm_spawners.items()):
|
||||
if name == '' and not include_default:
|
||||
continue
|
||||
if name and not self.allow_named_servers:
|
||||
continue
|
||||
if name in self.spawners:
|
||||
# yield wrapper if it exists (server may be active)
|
||||
yield self.spawners[name]
|
||||
else:
|
||||
# otherwise, yield low-level ORM object (server is not active)
|
||||
yield orm_spawner
|
||||
|
||||
def _new_spawner(self, server_name, spawner_class=None, **kwargs):
|
||||
"""Create a new spawner"""
|
||||
if spawner_class is None:
|
||||
spawner_class = self.spawner_class
|
||||
self.log.debug("Creating %s for %s:%s", spawner_class, self.name, name)
|
||||
self.log.debug("Creating %s for %s:%s", spawner_class, self.name, server_name)
|
||||
|
||||
orm_spawner = self.orm_spawners.get(name)
|
||||
orm_spawner = self.orm_spawners.get(server_name)
|
||||
if orm_spawner is None:
|
||||
orm_spawner = orm.Spawner(user=self.orm_user, name=name)
|
||||
orm_spawner = orm.Spawner(user=self.orm_user, name=server_name)
|
||||
self.db.add(orm_spawner)
|
||||
self.db.commit()
|
||||
assert name in self.orm_spawners
|
||||
if name == '' and self.state:
|
||||
assert server_name in self.orm_spawners
|
||||
if server_name == '' and self.state:
|
||||
# migrate user.state to spawner.state
|
||||
orm_spawner.state = self.state
|
||||
self.state = None
|
||||
@@ -203,15 +224,15 @@ class User:
|
||||
# use fully quoted name for client_id because it will be used in cookie-name
|
||||
# self.escaped_name may contain @ which is legal in URLs but not cookie keys
|
||||
client_id = 'jupyterhub-user-%s' % quote(self.name)
|
||||
if name:
|
||||
client_id = '%s-%s' % (client_id, quote(name))
|
||||
if server_name:
|
||||
client_id = '%s-%s' % (client_id, quote(server_name))
|
||||
spawn_kwargs = dict(
|
||||
user=self,
|
||||
orm_spawner=orm_spawner,
|
||||
hub=self.settings.get('hub'),
|
||||
authenticator=self.authenticator,
|
||||
config=self.settings.get('config'),
|
||||
proxy_spec=url_path_join(self.proxy_spec, name, '/'),
|
||||
proxy_spec=url_path_join(self.proxy_spec, server_name, '/'),
|
||||
db=self.db,
|
||||
oauth_client_id=client_id,
|
||||
cookie_options = self.settings.get('cookie_options', {}),
|
||||
@@ -334,6 +355,13 @@ class User:
|
||||
else:
|
||||
return self.base_url
|
||||
|
||||
def server_url(self, server_name=''):
|
||||
"""Get the url for a server with a given name"""
|
||||
if not server_name:
|
||||
return self.url
|
||||
else:
|
||||
return url_path_join(self.url, server_name)
|
||||
|
||||
def progress_url(self, server_name=''):
|
||||
"""API URL for progress endpoint for a server with a given name"""
|
||||
url_parts = [self.settings['hub'].base_url, 'api/users', self.escaped_name]
|
||||
@@ -343,7 +371,7 @@ class User:
|
||||
url_parts.extend(['server/progress'])
|
||||
return url_path_join(*url_parts)
|
||||
|
||||
async def spawn(self, server_name='', options=None):
|
||||
async def spawn(self, server_name='', options=None, handler=None):
|
||||
"""Start the user's spawner
|
||||
|
||||
depending from the value of JupyterHub.allow_named_servers
|
||||
@@ -373,6 +401,9 @@ class User:
|
||||
spawner.server = server = Server(orm_server=orm_server)
|
||||
assert spawner.orm_spawner.server is orm_server
|
||||
|
||||
# pass requesting handler to the spawner
|
||||
# e.g. for processing GET params
|
||||
spawner.handler = handler
|
||||
# Passing user_options to the spawner
|
||||
spawner.user_options = options or {}
|
||||
# we are starting a new server, make sure it doesn't restore state
|
||||
@@ -384,17 +415,14 @@ class User:
|
||||
client_id = spawner.oauth_client_id
|
||||
oauth_provider = self.settings.get('oauth_provider')
|
||||
if oauth_provider:
|
||||
client_store = oauth_provider.client_authenticator.client_store
|
||||
try:
|
||||
oauth_client = client_store.fetch_by_client_id(client_id)
|
||||
except ClientNotFoundError:
|
||||
oauth_client = None
|
||||
oauth_client = oauth_provider.fetch_by_client_id(client_id)
|
||||
# create a new OAuth client + secret on every launch
|
||||
# containers that resume will be updated below
|
||||
client_store.add_client(client_id, api_token,
|
||||
url_path_join(self.url, server_name, 'oauth_callback'),
|
||||
description="Server at %s" % (url_path_join(self.base_url, server_name) + '/'),
|
||||
)
|
||||
oauth_provider.add_client(
|
||||
client_id, api_token,
|
||||
url_path_join(self.url, server_name, 'oauth_callback'),
|
||||
description="Server at %s" % (url_path_join(self.base_url, server_name) + '/'),
|
||||
)
|
||||
db.commit()
|
||||
|
||||
# trigger pre-spawn hook on authenticator
|
||||
@@ -469,10 +497,10 @@ class User:
|
||||
)
|
||||
# update OAuth client secret with updated API token
|
||||
if oauth_provider:
|
||||
client_store = oauth_provider.client_authenticator.client_store
|
||||
client_store.add_client(client_id, spawner.api_token,
|
||||
url_path_join(self.url, server_name, 'oauth_callback'),
|
||||
)
|
||||
oauth_provider.add_client(
|
||||
client_id, spawner.api_token,
|
||||
url_path_join(self.url, server_name, 'oauth_callback'),
|
||||
)
|
||||
db.commit()
|
||||
|
||||
except Exception as e:
|
||||
@@ -497,6 +525,9 @@ class User:
|
||||
# raise original exception
|
||||
spawner._start_pending = False
|
||||
raise e
|
||||
finally:
|
||||
# clear reference to handler after start finishes
|
||||
spawner.handler = None
|
||||
spawner.start_polling()
|
||||
|
||||
# store state
|
||||
@@ -572,11 +603,25 @@ class User:
|
||||
# remove server entry from db
|
||||
spawner.server = None
|
||||
if not spawner.will_resume:
|
||||
# find and remove the API token if the spawner isn't
|
||||
# find and remove the API token and oauth client if the spawner isn't
|
||||
# going to re-use it next time
|
||||
orm_token = orm.APIToken.find(self.db, api_token)
|
||||
if orm_token:
|
||||
self.db.delete(orm_token)
|
||||
# remove oauth client as well
|
||||
# handle upgrades from 0.8, where client id will be `user-USERNAME`,
|
||||
# not just `jupyterhub-user-USERNAME`
|
||||
client_ids = (
|
||||
spawner.oauth_client_id,
|
||||
spawner.oauth_client_id.split('-', 1)[1],
|
||||
)
|
||||
for oauth_client in (
|
||||
self.db
|
||||
.query(orm.OAuthClient)
|
||||
.filter(orm.OAuthClient.identifier.in_(client_ids))
|
||||
):
|
||||
self.log.debug("Deleting oauth client %s", oauth_client.identifier)
|
||||
self.db.delete(oauth_client)
|
||||
self.db.commit()
|
||||
finally:
|
||||
spawner.orm_spawner.started = None
|
||||
|
Reference in New Issue
Block a user