Files
jupyterhub/jupyterhub/handlers/login.py
Daisuke Taniwaki 0b085a91b6 Fix format issues
2020-12-23 13:50:27 +09:00

169 lines
5.8 KiB
Python

"""HTTP Handlers for the hub server"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import asyncio
from jinja2 import Template
from tornado import web
from tornado.escape import url_escape
from tornado.httputil import url_concat
from ..utils import maybe_future
from .base import BaseHandler
class LogoutHandler(BaseHandler):
"""Log a user out by clearing their login cookie."""
@property
def shutdown_on_logout(self):
return self.settings.get('shutdown_on_logout', False)
async def _shutdown_servers(self, user):
"""Shutdown servers for logout
Get all active servers for the provided user, stop them.
"""
active_servers = [
name
for (name, spawner) in user.spawners.items()
if spawner.active and not spawner.pending
]
if active_servers:
self.log.info("Shutting down %s's servers", user.name)
futures = []
for server_name in active_servers:
futures.append(maybe_future(self.stop_single_user(user, server_name)))
await asyncio.gather(*futures)
def _backend_logout_cleanup(self, name):
"""Default backend logout actions
Send a log message, clear some cookies, increment the logout counter.
"""
self.log.info("User logged out: %s", name)
self.clear_login_cookie()
self.statsd.incr('logout')
async def default_handle_logout(self):
"""The default logout action
Optionally cleans up servers, clears cookies, increments logout counter
Cleaning up servers can be prevented by setting shutdown_on_logout to
False.
"""
user = self.current_user
if user:
if self.shutdown_on_logout:
await self._shutdown_servers(user)
self._backend_logout_cleanup(user.name)
async def handle_logout(self):
"""Custom user action during logout
By default a no-op, this function should be overridden in subclasses
to have JupyterHub take a custom action on logout.
"""
return
async def render_logout_page(self):
"""Render the logout page, if any
Override this function to set a custom logout page.
"""
if self.authenticator.auto_login:
html = await self.render_template('logout.html')
self.finish(html)
else:
self.redirect(self.settings['login_url'], permanent=False)
async def get(self):
"""Log the user out, call the custom action, forward the user
to the logout page
"""
await self.default_handle_logout()
await self.handle_logout()
await self.render_logout_page()
class LoginHandler(BaseHandler):
"""Render the login page."""
def _render(self, login_error=None, username=None):
context = {
"next": url_escape(self.get_argument('next', default='')),
"username": username,
"login_error": login_error,
"login_url": self.settings['login_url'],
"authenticator_login_url": url_concat(
self.authenticator.login_url(self.hub.base_url),
{'next': self.get_argument('next', '')},
),
}
custom_html = Template(
self.authenticator.get_custom_html(self.hub.base_url)
).render(**context)
return self.render_template(
'login.html',
**context,
custom_html=custom_html,
)
async def get(self):
self.statsd.incr('login.request')
user = self.current_user
if user:
# set new login cookie
# because single-user cookie may have been cleared or incorrect
self.set_login_cookie(user)
self.redirect(self.get_next_url(user), permanent=False)
else:
if self.authenticator.auto_login:
auto_login_url = self.authenticator.login_url(self.hub.base_url)
if auto_login_url == self.settings['login_url']:
# auto_login without a custom login handler
# means that auth info is already in the request
# (e.g. REMOTE_USER header)
user = await self.login_user()
if user is None:
# auto_login failed, just 403
raise web.HTTPError(403)
else:
self.redirect(self.get_next_url(user))
else:
if self.get_argument('next', default=False):
auto_login_url = url_concat(
auto_login_url, {'next': self.get_next_url()}
)
self.redirect(auto_login_url)
return
username = self.get_argument('username', default='')
self.finish(await self._render(username=username))
async def post(self):
# parse the arguments dict
data = {}
for arg in self.request.arguments:
data[arg] = self.get_argument(arg, strip=False)
auth_timer = self.statsd.timer('login.authenticate').start()
user = await self.login_user(data)
auth_timer.stop(send=False)
if user:
# register current user for subsequent requests to user (e.g. logging the request)
self._jupyterhub_user = user
self.redirect(self.get_next_url(user))
else:
html = await self._render(
login_error='Invalid username or password', username=data['username']
)
self.finish(html)
# /login renders the login page or the "Login with..." link,
# so it should always be registered.
# /logout clears cookies.
default_handlers = [(r"/login", LoginHandler), (r"/logout", LogoutHandler)]