-
Notifications
You must be signed in to change notification settings - Fork 390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] support authentication #596
Conversation
This is a really great start, thank you! I think the main thing missing to get the basics started for the auth to work is setting up the oauth client configuration for the Binder service, as illustrated in this example. We'll also want to make sure the environment variables defined here are available to the binder process |
4ec7b29
to
b5bc39b
Compare
binderhub/launcher.py
Outdated
class Launcher(LoggingConfigurable): | ||
"""Object for encapsulating launching an image for a user""" | ||
|
||
hub_api_token = Unicode(help="The API token for the Hub") | ||
hub_url = Unicode(help="The URL of the Hub") | ||
hub_api_url = Unicode(help="The URL of the Hub API") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the thinking behind introducing this new parameter when we already have hub_url
? Would be good to add a comment to explain it for future readers. (If this is just a temporary thing and part of the WIP ignore this comment :) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, that was unnecessary. i removed it in the next commit. thanks :)
92bfe11
to
197a04a
Compare
@minrk firstly thank you for the feedback and links. I tested the current state with
The extra configuration I used to enable auth with github: jupyterhub:
cull:
users: false
hub:
baseUrl: /jupyter/
extraConfig:
binder: |
from kubespawner import KubeSpawner
class BinderSpawner(KubeSpawner):
def start(self):
if 'image' in self.user_options:
# binder service sets the image spec via user options
self.image_spec = self.user_options['image']
# to disabled pvc creation
# self.storage_pvc_ensure = False
# self.volumes = []
# self.volume_mounts = []
return super().start()
c.JupyterHub.spawner_class = BinderSpawner
c.JupyterHub.allow_named_servers = True
services:
binder:
url: "/base_url/services/service_name"
oauth_client_id: "binder-oauth-client-test"
auth:
type: github
github:
clientId: "client id from GitHub"
clientSecret: "client secret from GitHub"
callbackUrl: "host/jupyter/hub/oauth_callback"
singleuser:
cmd: jupyterhub-singleuser
storage:
type: dynamic
dynamic:
pvcNameTemplate: claim-{userid}{servername}
volumeNameTemplate: volume-{userid}{servername}
homeMountPath: /home/pv
baseUrl: /jupyter/services/binder/ As you see in the conf persistent storage is activated (#377) and mounted to With this configuration user can launch servers under |
24e946a
to
630e55d
Compare
…is causes error and user gets `internal server error`
… token if auth is enabled. add more comments.
I am done for now and it would be nice to have a review :) I think my previous comments were not really explanatory. Here I try to explain the status again. Currently it is possible to have BinderHub running in 3 ways: 1. Without authentication:This is the default configuration. It works in this way if 2. With authentication and without named servers:BinderHub is configured in this way when This way requires:
When authentication is enabled, it is not required to use
from kubespawner import KubeSpawner
class BinderSpawner(KubeSpawner):
def start(self):
if 'image' in self.user_options:
self.image_spec = self.user_options['image']
return super().start() 3. With authentication and with named servers:This is one step further of way 2. Authenticated users can launch multiple servers by using binder service. BinderHub generates a unique server name by using Some further comments:
I really like to hear your comments! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great! I left some suggestions inline, but in general the design looks right to me.
Mainly I think the separate use_oauth
config is unnecessary, since auth_enabled
seems to never be true without it.
I would love to get some tests for the authenticated cases. Do you think you are up for that?
binderhub/base.py
Outdated
If authentication in not enabled, this decorator doesn't do anything. | ||
""" | ||
@functools.wraps(method) | ||
def wrapper(self, *args, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than reimplementing @web.authenticated
, we can define a .get_current_user
that returns 'anonymous'
if auth is not enabled.
binderhub/base.py
Outdated
def initialize(self): | ||
super().initialize() | ||
if self.settings['auth_enabled'] and self.settings['use_oauth']: | ||
self.hub_auth_class = HubOAuth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be self.hub_auth = HubOAuth()
, instantiating the object instead of setting the class?
dev-requirements.txt
Outdated
@@ -7,3 +7,4 @@ pytest-tornado | |||
requests | |||
ruamel.yaml>=0.15 | |||
https://github.com/jupyterhub/chartpress/archive/271c75e.tar.gz | |||
jupyterhub==0.9.2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want to pin jupyterhub in dev-requirements.
start the new server for the logged in user.""", | ||
config=True) | ||
|
||
use_oauth = Bool( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems we don't nee to have separate auth_enabled
and use_oauth
flags. We can have a single auth_enabled
flag that sets up HubOAuth
, perhaps? Or even run with a jupyterhub_service
flag that will load the full service config (URL, etc.)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but don't we need 2 flags to distinguish if an OAuthenticator or another authenticator (e.g. ldapauthenticator
, jhub_shibboleth_auth
) is used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean that when I use jhub_shibboleth_auth
, auth_enabled
is True but user_oauth
is False. And then HubAuth
is used (not HubOAuth
).
binderhub/launcher.py
Outdated
# check if user have a running server ('') | ||
user_data = await self.get_user_data(username) | ||
if server_name in user_data['servers']: | ||
raise web.HTTPError(500, "User %s already has a running server." % username) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error code here ought to be 409
if a server is requested but can't be started because it's running.
define get_current_user rather than reimplementing authenticated. instantiate the object if oauth is used. raise 409 if user already has a running server. dont pin jhub in dev-requirements.
thanks a lot for the review. I made some changes according to your comments. And yes I really like to add tests for authentication. But first I have to learn how to do that. Could you help me about this? I will start with checking the existing tests once again and doing changes if necessary. |
for nbviewer, test for loading page is updated and cases added. nbviewer url generation moved into handler and there it is easier to handle different cases.
sorry, the last commit e1272af actually doesn't do anything for authentication. i was going through existing tests and end up with doing all these changes. if you wish, i can move this commit to another PR. in order to test some cases for nbviwer i used |
@bitnik - thanks for working on this. I'm really excited to try it out so let us know when you think its ready for some beta testing. |
@minrk i started writing tests for auth. could you check b82ec56 if I started correctly? The only problem I have while running auth tests locally is that @default('api_token')
def _default_api_token(self):
return os.getenv('JUPYTERHUB_API_TOKEN', '') |
@minrk when can you review here? I really want to finish this asap :) |
this pr contains many changes unrelated to authentication. i am going to restructure it into smaller ones. sorry that i didn't have time to do it before. |
moved to #666. |
Hi, last days I was working on adding an optional jhub authentication for binder. Firstly, comments on #323 helped a lot, thanks!
It is not completed yet and I won't be available next 2 weeks. So I thought I can make this PR and maybe get some feedback from you if I am going on the right way. It would be great for me, if you can review my changes.
Current situation:
auth.custom.className
set as "nullauthenticator.NullAuthenticator" (default configuration), authentication is not enabled and binderhub works with fake users as before.auth
set to anything different fromNullAuthenticator
, authentication is enabled and when user comes to binder page, it is redirected to jhub login page. But after user logs in there isToo many redirections
error. I think this is becauseHubAuthenticated.get_current_user
can't gets the user and redirects back to login page and log in page redirects back to binder page (because user is logged in).Sorry if I miss something to mention in this PR. I had today less time than planned to work on this.