more hooks for authenticators

Allow authenticators to:

- register custom handlers
- change login and logout URLs
- replace the entire login form

This appears to be enough to get oauth working.
This commit is contained in:
MinRK
2014-09-22 22:08:01 -07:00
parent ee5ad66ba7
commit 0577e10276
6 changed files with 41 additions and 7 deletions

View File

@@ -210,7 +210,8 @@ class JupyterHubApp(Application):
def _cookie_secret_default(self):
return os.environ.get('JPY_COOKIE_SECRET', random_hex(64))
authenticator_class = Type("jupyterhub.auth.PAMAuthenticator", config=True,
authenticator_class = Type(PAMAuthenticator, Authenticator,
config=True,
help="""Class for authenticating users.
This should be a class with the following form:
@@ -224,12 +225,14 @@ class JupyterHubApp(Application):
and `data` is the POST form data from the login page.
"""
)
authenticator = Instance(Authenticator)
def _authenticator_default(self):
return self.authenticator_class(config=self.config)
# class for spawning single-user servers
spawner_class = Type("jupyterhub.spawner.LocalProcessSpawner", config=True,
spawner_class = Type(LocalProcessSpawner, Spawner,
config=True,
help="""The class to use for spawning single-user servers.
Should be a subclass of Spawner.
@@ -323,6 +326,8 @@ class JupyterHubApp(Application):
h = []
h.extend(handlers.default_handlers)
h.extend(apihandlers.default_handlers)
# load handlers from the authenticator
h.extend(self.authenticator.get_handlers(self))
self.handlers = self.add_url_prefix(self.hub_prefix, h)
@@ -534,13 +539,16 @@ class JupyterHubApp(Application):
def init_tornado_settings(self):
"""Set up the tornado settings dict."""
base_url = self.base_url
base_url = self.hub.server.base_url
template_path = os.path.join(self.data_files_path, 'templates'),
jinja_env = Environment(
loader=FileSystemLoader(template_path),
**self.jinja_environment_options
)
login_url = self.authenticator.login_url(base_url)
logout_url = self.authenticator.logout_url(base_url)
settings = dict(
config=self.config,
log=self.log,
@@ -550,9 +558,10 @@ class JupyterHubApp(Application):
admin_users=self.admin_users,
authenticator=self.authenticator,
spawner_class=self.spawner_class,
base_url=base_url,
base_url=self.base_url,
cookie_secret=self.hub.server.cookie_secret,
login_url=url_path_join(self.hub.server.base_url, 'login'),
login_url=login_url,
logout_url=logout_url,
static_path=os.path.join(self.data_files_path, 'static'),
static_url_prefix=url_path_join(self.hub.server.base_url, 'static/'),
template_path=template_path,

View File

@@ -9,6 +9,8 @@ import simplepam
from IPython.config import LoggingConfigurable
from IPython.utils.traitlets import Unicode, Set
from .utils import url_path_join
class Authenticator(LoggingConfigurable):
"""A class for authentication.
@@ -22,6 +24,7 @@ class Authenticator(LoggingConfigurable):
If empty, allow any user to attempt login.
"""
)
custom_html = Unicode('')
@gen.coroutine
def authenticate(self, handler, data):
@@ -32,6 +35,22 @@ class Authenticator(LoggingConfigurable):
and return None on failed authentication.
"""
def login_url(self, base_url):
"""Override to register a custom login handler"""
return url_path_join(base_url, 'login')
def logout_url(self, base_url):
"""Override to register a custom logout handler"""
return url_path_join(base_url, 'logout')
def get_handlers(self, app):
"""Return any custom handlers the authenticator needs to register
(e.g. for OAuth)
"""
return []
class PAMAuthenticator(Authenticator):
encoding = Unicode('utf8', config=True,
help="""The encoding to use for PAM """

View File

@@ -206,6 +206,7 @@ class BaseHandler(RequestHandler):
base_url=self.hub.server.base_url,
user=user,
login_url=self.settings['login_url'],
logout_url=self.settings['logout_url'],
static_url=self.static_url,
)

View File

@@ -25,6 +25,7 @@ class LoginHandler(BaseHandler):
next=url_escape(self.get_argument('next', default='')),
username=username,
message=message,
custom_html=self.authenticator.custom_html,
)
def get(self):

View File

@@ -6,6 +6,9 @@
{% block main %}
<div id="login-main" class="container">
{% if custom_html %}
{{custom_html}}
{% else %}
<form action="{{login_url}}?next={{next}}" method="post" role="form">
<div class="input-group">
<span class="input-group-addon">Username:</span>
@@ -24,6 +27,7 @@
</div>
</div>
{% endif %}
{% endif %}
<div/>
{% endblock %}

View File

@@ -83,9 +83,9 @@
<span id="login_widget">
{% if user %}
<a id="logout" class="btn navbar-btn btn-default pull-right" href="{{base_url}}logout">Logout</a>
<a id="logout" class="btn navbar-btn btn-default pull-right" href="{{logout_url}}">Logout</a>
{% else %}
<a id="login" class="btn navbar-btn btn-default pull-right" href="{{base_url}}login">Login</a>
<a id="login" class="btn navbar-btn btn-default pull-right" href="{{login_url}}">Login</a>
{% endif %}
</span>