Skip to content

Commit

Permalink
Merge pull request #366 from GeorgianaElena/words-matter
Browse files Browse the repository at this point in the history
Rename OAuthenticator.whitelist to allow
  • Loading branch information
minrk committed Sep 8, 2020
2 parents 944d1b7 + 8e93252 commit 397a3ec
Show file tree
Hide file tree
Showing 15 changed files with 267 additions and 97 deletions.
2 changes: 1 addition & 1 deletion docs/source/example-oauthenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ async def authenticate(self, handler, data=None):
# and login has failed
return None

# here we can add additional checks such as against team whitelists
# here we can add additional checks such as against team allowed lists
# if the OAuth provider has such a concept

# 'name' is the JupyterHub username
Expand Down
6 changes: 3 additions & 3 deletions docs/source/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The general steps to take when using OAuthenticator:
2. Register with the provider
3. Choose an authenticator class, or use :class:`~.oauthenticator.generic.GenericOAuthenticator`
and configure JupyterHub to use it
4. Configure the authenticator class (client_id, client_secret, callback_url, whitelist, etc.)
4. Configure the authenticator class (client_id, client_secret, callback_url, allowed_users, etc.)
5. Specific configuration for your identity provider

OAuthenticator currently supports the following **identity providers**:
Expand Down Expand Up @@ -209,13 +209,13 @@ groups by setting

::

c.GitLabOAuthenticator.gitlab_project_id_whitelist = [ ... ]
c.GitLabOAuthenticator.allowed_gitlab_project_ids = [ ... ]

and

::

c.GitLabOAuthenticator.gitlab_group_whitelist = [ ... ]
c.GitLabOAuthenticator.allowed_gitlab_groups = [ ... ]

but be aware that each entry incurs a separate API call, increasing the
risk of rate limiting and timeouts.
Expand Down
8 changes: 4 additions & 4 deletions docs/source/google.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,21 @@ and give it read only access to users and groups.

**Note:** if you remove a member from a google group you will have to force this user to login again in order for the change to take effect

#### if you want to manage admin users and whitelisted via google groups
#### if you want to manage admin users and allowed users via google groups

```python
c.GoogleOAuthenticator.gsuite_administrator = {'example.com': 'someuser'}
c.GoogleOAuthenticator.google_service_account_keys = {'example.com': '/path/to/service_account.json'}
c.GoogleOAuthenticator.admin_google_groups = {'example.com': ['someadmingroup']}
c.GoogleOAuthenticator.google_group_whitelist = {'example.com': ['somegroupwithaccess', 'othergroupwithaccess'] }
c.GoogleOAuthenticator.allowed_google_groups = {'example.com': ['somegroupwithaccess', 'othergroupwithaccess'] }
```

#### if you only want to whitelist users via google groups
#### if you only want to allow users via google groups

```python
c.GoogleOAuthenticator.gsuite_administrator = {'example.com': 'someuser'}
c.GoogleOAuthenticator.google_service_account_keys = {'example.com': '/path/to/service_account.json'}
c.GoogleOAuthenticator.google_group_whitelist = {'example.com': ['somegroupwithaccess', 'othergroupwithaccess'] }
c.GoogleOAuthenticator.allowed_google_groups = {'example.com': ['somegroupwithaccess', 'othergroupwithaccess'] }
```
### You are done!

Expand Down
4 changes: 2 additions & 2 deletions examples/full/jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

c.LocalGitHubOAuthenticator.create_system_users = True

c.Authenticator.whitelist = whitelist = set()
c.Authenticator.allowed_users = allowed_users = set()
c.JupyterHub.admin_users = admin = set()

import os
Expand All @@ -26,7 +26,7 @@
continue
parts = line.split()
name = parts[0]
whitelist.add(name)
allowed_users.add(name)
if len(parts) > 1 and parts[1] == 'admin':
admin.add(name)

Expand Down
31 changes: 20 additions & 11 deletions oauthenticator/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from jupyterhub.auth import LocalAuthenticator

from traitlets import Set, default
from traitlets import Set, default, observe

from .oauth2 import OAuthLoginHandler, OAuthenticator

Expand All @@ -28,6 +28,14 @@ def _api_headers(access_token):

class BitbucketOAuthenticator(OAuthenticator):

_deprecated_aliases = {
"team_whitelist": ("allowed_teams", "0.12.0"),
}

@observe(*list(_deprecated_aliases))
def _deprecated_trait(self, change):
super()._deprecated_trait(change)

login_service = "Bitbucket"
client_id_env = 'BITBUCKET_CLIENT_ID'
client_secret_env = 'BITBUCKET_CLIENT_SECRET'
Expand All @@ -40,11 +48,12 @@ def _authorize_url_default(self):
def _token_url_default(self):
return "https://bitbucket.org/site/oauth2/access_token"

team_whitelist = Set(
config=True, help="Automatically whitelist members of selected teams"
team_whitelist = Set(help="Deprecated, use `BitbucketOAuthenticator.allowed_teams`", config=True,)

allowed_teams = Set(
config=True, help="Automatically allow members of selected teams"
)

bitbucket_team_whitelist = team_whitelist

headers = {
"Accept": "application/json",
Expand Down Expand Up @@ -93,20 +102,20 @@ async def authenticate(self, handler, data=None):

username = resp_json["username"]

# Check if user is a member of any whitelisted teams.
# Check if user is a member of any allowed teams.
# This check is performed here, as the check requires `access_token`.
if self.bitbucket_team_whitelist:
user_in_team = await self._check_team_whitelist(username, access_token)
if self.allowed_teams:
user_in_team = await self._check_membership_allowed_teams(username, access_token)
if not user_in_team:
self.log.warning("%s not in team whitelist", username)
self.log.warning("%s not in team allowed list of users", username)
return None

return {
'name': username,
'auth_state': {'access_token': access_token, 'bitbucket_user': resp_json},
}

async def _check_team_whitelist(self, username, access_token):
async def _check_membership_allowed_teams(self, username, access_token):
http_client = AsyncHTTPClient()

headers = _api_headers(access_token)
Expand All @@ -121,8 +130,8 @@ async def _check_team_whitelist(self, username, access_token):
next_page = resp_json.get('next', None)

user_teams = set([entry["username"] for entry in resp_json["values"]])
# check if any of the organizations seen thus far are in whitelist
if len(self.bitbucket_team_whitelist & user_teams) > 0:
# check if any of the organizations seen thus far are in the allowed list
if len(self.allowed_teams & user_teams) > 0:
return True
return False

Expand Down
27 changes: 18 additions & 9 deletions oauthenticator/cilogon.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Caveats:
- For user whitelist/admin purposes, username will be the ePPN by default.
- For allowed user list /admin purposes, username will be the ePPN by default.
This is typically an email address and may not work as a Unix userid.
Normalization may be required to turn the JupyterHub username into a Unix username.
- Default username_claim of ePPN does not work for all providers,
Expand All @@ -22,7 +22,7 @@
from tornado.httputil import url_concat
from tornado.httpclient import HTTPRequest, AsyncHTTPClient

from traitlets import Unicode, List, Bool, default, validate
from traitlets import Unicode, List, Bool, default, validate, observe

from jupyterhub.auth import LocalAuthenticator

Expand All @@ -44,6 +44,14 @@ def authorize_redirect(self, *args, **kwargs):


class CILogonOAuthenticator(OAuthenticator):
_deprecated_aliases = {
"idp_whitelist": ("allowed_idps", "0.12.0"),
}

@observe(*list(_deprecated_aliases))
def _deprecated_trait(self, change):
super()._deprecated_trait(change)

login_service = "CILogon"

client_id_env = 'CILOGON_CLIENT_ID'
Expand Down Expand Up @@ -78,15 +86,16 @@ def _validate_scope(self, proposal):
return ['openid'] + proposal.value
return proposal.value

idp_whitelist = List(
idp_whitelist = List(help="Deprecated, use `CIlogonOAuthenticator.allowed_idps`", config=True,)
allowed_idps = List(
config=True,
help="""A list of IDP which can be stripped from the username after the @ sign.""",
)
strip_idp_domain = Bool(
False,
config=True,
help="""Remove the IDP domain from the username. Note that only domains which
appear in the `idp_whitelist` will be stripped.""",
appear in the `allowed_idps` will be stripped.""",
)
idp = Unicode(
config=True,
Expand Down Expand Up @@ -187,14 +196,14 @@ async def authenticate(self, handler, data=None):
)
raise web.HTTPError(500, "Failed to get username from CILogon")

if self.idp_whitelist:
if self.allowed_idps:
gotten_name, gotten_idp = username.split('@')
if gotten_idp not in self.idp_whitelist:
if gotten_idp not in self.allowed_idps:
self.log.error(
"Trying to login from not whitelisted domain %s", gotten_idp
"Trying to login from not allowed domain %s", gotten_idp
)
raise web.HTTPError(500, "Trying to login from not whitelisted domain")
if len(self.idp_whitelist) == 1 and self.strip_idp_domain:
raise web.HTTPError(500, "Trying to login from a domain not allowed")
if len(self.allowed_idps) == 1 and self.strip_idp_domain:
username = gotten_name
userdict = {"name": username}
# Now we set up auth_state
Expand Down
28 changes: 19 additions & 9 deletions oauthenticator/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from jupyterhub.auth import LocalAuthenticator

from traitlets import List, Set, Unicode, default
from traitlets import List, Set, Unicode, default, observe

from .common import next_page_from_links
from .oauth2 import OAuthLoginHandler, OAuthenticator
Expand All @@ -37,6 +37,14 @@ class GitHubOAuthenticator(OAuthenticator):
# set scopes via config, e.g.
# c.GitHubOAuthenticator.scope = ['read:org']

_deprecated_aliases = {
"github_organization_whitelist": ("allowed_organizations", "0.12.0"),
}

@observe(*list(_deprecated_aliases))
def _deprecated_trait(self, change):
super()._deprecated_trait(change)

login_service = "GitHub"

github_url = Unicode("https://github.com", config=True)
Expand Down Expand Up @@ -108,8 +116,10 @@ def _github_client_secret_changed(self, name, old, new):
client_id_env = 'GITHUB_CLIENT_ID'
client_secret_env = 'GITHUB_CLIENT_SECRET'

github_organization_whitelist = Set(
config=True, help="Automatically whitelist members of selected organizations"
github_organization_whitelist = Set(help="Deprecated, use `GitHubOAuthenticator.allowed_organizations`", config=True,)

allowed_organizations = Set(
config=True, help="Automatically allow members of selected organizations"
)

async def authenticate(self, handler, data=None):
Expand Down Expand Up @@ -168,17 +178,17 @@ async def authenticate(self, handler, data=None):
# username is now the GitHub userid.
if not username:
return None
# Check if user is a member of any whitelisted organizations.
# Check if user is a member of any allowed organizations.
# This check is performed here, as it requires `access_token`.
if self.github_organization_whitelist:
for org in self.github_organization_whitelist:
user_in_org = await self._check_organization_whitelist(
if self.allowed_organizations:
for org in self.allowed_organizations:
user_in_org = await self._check_membership_allowed_organizations(
org, username, access_token
)
if user_in_org:
break
else: # User not found in member list for any organisation
self.log.warning("User %s is not in org whitelist", username)
self.log.warning("User %s is not in allowed org list", username)
return None
userdict = {"name": username}
# Now we set up auth_state
Expand All @@ -197,7 +207,7 @@ async def authenticate(self, handler, data=None):

return userdict

async def _check_organization_whitelist(self, org, username, access_token):
async def _check_membership_allowed_organizations(self, org, username, access_token):
http_client = AsyncHTTPClient()
headers = _api_headers(access_token)
# Check membership of user `username` for organization `org` via api [check-membership](https://developer.github.com/v3/orgs/members/#check-membership)
Expand Down
Loading

0 comments on commit 397a3ec

Please sign in to comment.