Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into required_scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
yuvipanda committed Mar 29, 2024
2 parents c7fc637 + 97b470b commit 00360fa
Show file tree
Hide file tree
Showing 30 changed files with 596 additions and 148 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ jobs:
- name: Downgrade to oldest dependencies
if: matrix.oldest_dependencies != ''
# take any dependencies in requirements.txt such as jupyterhub>=1.2 and
# transform them to jupyterhub==1.2 so we can run tests with the
# take any dependencies in requirements.txt such as jupyterhub>=2.2 and
# transform them to jupyterhub==2.2 so we can run tests with the
# earliest-supported versions
run: |
cat requirements.txt | grep '>=' | sed -e 's@>=@==@g' > oldest-requirements.txt
Expand All @@ -69,4 +69,4 @@ jobs:
pytest
# GitHub action reference: https://github.com/codecov/codecov-action
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v4
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ repos:

# Autoformat: Python code
- repo: https://github.com/psf/black
rev: 23.12.1
rev: 24.1.1
hooks:
- id: black

Expand Down Expand Up @@ -64,7 +64,7 @@ repos:

# Lint: Python code
- repo: https://github.com/pycqa/flake8
rev: "6.1.0"
rev: "7.0.0"
hooks:
- id: flake8

Expand Down
1 change: 1 addition & 0 deletions docs/source/how-to/example-oauthenticator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Example OAuthenticator to use with My Service
"""

from jupyterhub.auth import LocalAuthenticator

from oauthenticator.oauth2 import OAuthenticator, OAuthLoginHandler
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Topic guides go more in-depth on a particular topic.
:maxdepth: 2
:caption: Topic guides
topic/allowing
topic/extending
```

Expand Down
42 changes: 42 additions & 0 deletions docs/source/reference/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,48 @@ command line for details.

## [Unreleased]

### 16.3.0 - 2024-03-20

```{important}
This release includes a security patch for {attr}`.GoogleOAuthenticator.hosted_domain`,
see [GHSA-55m3-44xf-hg4h] for details.
[GHSA-55m3-44xf-hg4h]: https://github.com/jupyterhub/oauthenticator/security/advisories/GHSA-55m3-44xf-hg4h
```

OAuthenticator now requires JupyterHub >=2.2.

([full changelog](https://github.com/jupyterhub/oauthenticator/compare/16.2.1...16.3.0))

#### New features added

- [All] Add `userdata_from_id_token` as alternative to `userdata_url` [#725](https://github.com/jupyterhub/oauthenticator/pull/725) ([@benjimin](https://github.com/benjimin), [@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics))
- [All] Make `username_claim` callable (except for CILogon), like it has been in Generic [#717](https://github.com/jupyterhub/oauthenticator/pull/717) ([@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio), [@GeorgianaElena](https://github.com/GeorgianaElena), [@manics](https://github.com/manics))
- [AzureAD] Support `manage_groups` [#710](https://github.com/jupyterhub/oauthenticator/pull/710) ([@minrk](https://github.com/minrk), [@yuvipanda](https://github.com/yuvipanda), [@GeorgianaElena](https://github.com/GeorgianaElena))
- [Generic] Add support for `manage_groups` [#708](https://github.com/jupyterhub/oauthenticator/pull/708) ([@benjimin](https://github.com/benjimin), [@manics](https://github.com/manics), [@minrk](https://github.com/minrk), [@yuvipanda](https://github.com/yuvipanda))
- [AzureAD] Add an implementation for `Authenticator.manage_groups=True` [#573](https://github.com/jupyterhub/oauthenticator/pull/573) ([@thomafred](https://github.com/thomafred), [@yuvipanda](https://github.com/yuvipanda), [@GeorgianaElena](https://github.com/GeorgianaElena))

#### Maintenance and upkeep improvements

- test: simplify by removing `token_request_style` parameter for mock function [#734](https://github.com/jupyterhub/oauthenticator/pull/734) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics))
- Require jupyterhub>=2.2 [#720](https://github.com/jupyterhub/oauthenticator/pull/720) ([@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio))
- temporary pin for pytest-asyncio [#715](https://github.com/jupyterhub/oauthenticator/pull/715) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio))

#### Documentation improvements

- add example for deploying with mock-oauth2-server [#730](https://github.com/jupyterhub/oauthenticator/pull/730) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio))
- add dedicated doc on details of allowing access [#729](https://github.com/jupyterhub/oauthenticator/pull/729) ([@minrk](https://github.com/minrk), [@GeorgianaElena](https://github.com/GeorgianaElena), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics))
- clarify what claim_groups_key is used for [#707](https://github.com/jupyterhub/oauthenticator/pull/707) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio))

#### Contributors to this release

The following people contributed discussions, new ideas, code and documentation contributions, and review.
See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports).

([GitHub contributors page for this release](https://github.com/jupyterhub/oauthenticator/graphs/contributors?from=2023-11-27&to=2024-03-20&type=c))

@benjimin ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3Abenjimin+updated%3A2023-11-27..2024-03-20&type=Issues)) | @consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3AconsideRatio+updated%3A2023-11-27..2024-03-20&type=Issues)) | @GeorgianaElena ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3AGeorgianaElena+updated%3A2023-11-27..2024-03-20&type=Issues)) | @krassowski ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3Akrassowski+updated%3A2023-11-27..2024-03-20&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3Amanics+updated%3A2023-11-27..2024-03-20&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3Aminrk+updated%3A2023-11-27..2024-03-20&type=Issues)) | @thomafred ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3Athomafred+updated%3A2023-11-27..2024-03-20&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Foauthenticator+involves%3Ayuvipanda+updated%3A2023-11-27..2024-03-20&type=Issues))

## 16.2

### [16.2.1] - 2023-11-27
Expand Down
142 changes: 142 additions & 0 deletions docs/source/topic/allowing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
(allowing)=

# Allowing access to your JupyterHub

OAuthenticator is about deferring **authentication** to an external source,
assuming your users all have accounts _somewhere_.
But many of these sources (e.g. Google, GitHub) have _lots_ of users, and you don't want _all_ of them to be able to use your hub.
This is where **authorization** comes in.

In OAuthenticator, authorization is represented via configuration options that start with `allow` or `block`.

There are also lots of OAuth providers, and as a result, lots of ways to tell OAuthenticator who should be allowed to access your hub.

## Default behavior: nobody is allowed!

The default behavior of OAuthenticator (starting with version 16) is to block all users unless explicitly authorized via _some_ `allow` configuration.
If you want anyone to be able to use your hub, you must specify at least one `allow` configuration.

```{versionchanged} 16
Prior to OAuthenticator 16, `allow_all` was _implied_ if no other `allow` configuration was specified.
Starting from 16, `allow_all` can only be enabled explicitly.
```

## Allowing access

There are several `allow_` configuration options, to grant access to users according to different rules.

When you have only one `allow` configuration, the behavior is generally unambiguous: anyone allowed by the rule can login to the Hub, while anyone not explicitly allowed cannot login.
However, once you start adding additional `allow` configuration, there is some ambiguity in how multiple rules are combined.

```{important}
Additional allow rules **can only grant access**, meaning they only _expand_ who has access to your hub.
Adding an `allow` rule cannot prevent access granted by another `allow` rule.
To block access, use `block` configuration.
```

That is, if a user is granted access by _any_ `allow` configuration, they are allowed.
An allow rule cannot _exclude_ access granted by another `allow` rule.

An example:

```python
c.GitHubOAuthenticator.allowed_users = {"mensah", "art"}
c.GitHubOAuthenticator.allowed_organizations = {"preservation"}
```

means that the users `mensah` and `art` are allowed, _and_ any member of the `preservation` organization are allowed.
Any user that doesn't meet any of the allow rules will not be allowed.

| user | allowed | reason |
| ----- | ------- | ------------------------------------------------------- |
| art | True | in `allowed_users` |
| amena | True | member of `preservation` |
| tlacy | False | not in `allowed_users` and not member of `preservation` |

### `allow_all`

The first and simplest way to allow access is to any user who can successfully authenticate:

```python
c.OAuthenticator.allow_all = True
```

This is appropriate when you use an authentication provider (e.g. an institutional single-sign-on provider), where everyone who has an account in the provider should have access to your Hub.
It may also be appropriate for unadvertised short-lived hubs, e.g. dedicated hubs for workshops that will be shutdown after a day, where you may decide it is acceptable to allow anyone who finds your hub to login.

If `allow_all` is enabled, no other `allow` configuration will have any effect.

```{seealso}
Configuration documentation for {attr}`.OAuthenticator.allow_all`
```

### `allowed_users`

This is top-level JupyterHub configuration, shared by all Authenticators.
This specifies a list of users that are allowed by name.
This is the simplest authorization mechanism when you have a small group of users whose usernames you know:

```python
c.OAuthenticator.allowed_users = {"mensah", "ratthi"}
```

If this is your only configuration, only these users will be allowed, no others.

Note that any additional usernames in the deprecated `admin_users` configuration will also be allowed to login.

```{seealso}
Configuration documentation for {attr}`.OAuthenticator.allowed_users`
```

### `allow_existing_users`

JupyterHub can allow you to add and remove users while the Hub is running via the admin page.
If you add or remove users this way, they will be added to the JupyterHub database, but they will not be able to login unless they are also granted access via an `allow` rule.

To enable managing users via the admin panel, set

```python
c.OAuthenticator.allow_existing_users = True
```

```{warning}
Enabling `allow_existing_users` means that _removing_ users from any explicit allow mechanisms will no longer revoke their access.
Once the user has been added to the database, the only way to revoke their access to the hub is to remove the user from JupyterHub entirely, via the admin page.
```

```{seealso}
Configuration documentation for {attr}`.OAuthenticator.allow_existing_users`
```

### Provider-specific rules

Each OAuthenticator provider may have its own provider-specific rules to allow groups of users access, such as:

- {attr}`.GitHubOAuthenticator.allowed_organizations`
- {attr}`.GitLabOAuthenticator.allowed_gitlab_groups`
- {attr}`.GlobusOAuthenticator.allowed_globus_groups`
- {attr}`.GoogleOAuthenticator.allowed_google_groups`

## Blocking Access

It's possible that you want to limit who has access to your Hub to less than all of the users granted access by your `allow` configuration.
`block` configuration always has higher priority than `allow` configuration, so if a user is both allowed _and_ blocked, they will not be able to login.

The only `block` configuration is the base Authenticators `block_users`,
a set of usernames that will not be allowed to login.

### Revoking previously-allowed access

Any users who have logged in previously will be present in the JupyterHub database.
Removing a user's login permissions (e.g. removing them from a GitLab project when using {attr}`.GitLabOAuthenticator.project_ids`) only prevents future logins;
it does not remove the user from the JupyterHub database.
This means that:

1. any API tokens that the user still has access to will continue to be valid, and can continue to be used
2. any still-valid browser sessions will continue to be logged in.

```{important}
To fully remove a user's access to JupyterHub,
their login permission must be revoked _and_ their user fully deleted from the Hub,
e.g. via the admin page.
```
1 change: 1 addition & 0 deletions examples/auth_state/jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
2. pass select auth_state to Spawner via environment variables
3. enable auth_state via `JUPYTERHUB_CRYPT_KEY` and `enable_auth_state = True`
"""

import os
import pprint
import warnings
Expand Down
21 changes: 21 additions & 0 deletions examples/mock-provider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generic OAuth with mock provider

This example uses [mock-oauth2-server] to launch a standalone local OAuth2 provider and configures GenericOAuthenticator to use it.

mock-auth2-server implements OpenID Connect (OIDC), and can be used to test GenericOAuthenticator configurations for use with OIDC providers without needing to register your application with a real OAuth provider.

[mock-oauth2-server]: https://github.com/navikt/mock-oauth2-server

To launch the oauth provider in a container:

```
docker run --rm -it -p 127.0.0.1:8080:8080 ghcr.io/navikt/mock-oauth2-server:2.1.1
```

Then launch JupyterHub:

```
jupyterhub
```

When you login, you will be presented with a form allowing you to specify the username, and (optionally) any additional fields that should be present in the `userinfo` response.
27 changes: 27 additions & 0 deletions examples/mock-provider/jupyterhub_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
c = get_config() # noqa

c.JupyterHub.authenticator_class = "generic-oauth"

# assumes oauth provider run with:
# docker run --rm -it -p 127.0.0.1:8080:8080 ghcr.io/navikt/mock-oauth2-server:2.1.1

provider = "http://127.0.0.1:8080/default"
c.GenericOAuthenticator.authorize_url = f"{provider}/authorize"
c.GenericOAuthenticator.token_url = f"{provider}/token"
c.GenericOAuthenticator.userdata_url = f"{provider}/userinfo"
c.GenericOAuthenticator.scope = ["openid", "somescope", "otherscope"]

# these are the defaults. They can be configured at http://localhost:8080/default/debugger
c.GenericOAuthenticator.client_id = "debugger"
c.GenericOAuthenticator.client_secret = "someSecret"

# 'sub' is the first field in the login form
c.GenericOAuthenticator.username_claim = "sub"

c.GenericOAuthenticator.allow_all = True
c.GenericOAuthenticator.admin_users = {"admin"}

# demo boilerplate
c.JupyterHub.default_url = "/hub/home"
c.JupyterHub.spawner_class = "simple"
c.JupyterHub.ip = "127.0.0.1"
2 changes: 1 addition & 1 deletion oauthenticator/_version.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# __version__ should be updated using tbump, based on configuration in
# pyproject.toml, according to instructions in RELEASE.md.
#
__version__ = "16.2.2.dev"
__version__ = "16.3.1.dev"

# version_info looks like (1, 2, 3, "dev") if __version__ is 1.2.3.dev
version_info = tuple(int(p) if p.isdigit() else p for p in __version__.split("."))
1 change: 1 addition & 0 deletions oauthenticator/auth0.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
A JupyterHub authenticator class for use with Auth0 as an identity provider.
"""

import os

from jupyterhub.auth import LocalAuthenticator
Expand Down
1 change: 1 addition & 0 deletions oauthenticator/azuread.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
A JupyterHub authenticator class for use with Azure AD as an identity provider.
"""

import os

import jwt
Expand Down
1 change: 1 addition & 0 deletions oauthenticator/bitbucket.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
A JupyterHub authenticator class for use with Bitbucket as an identity provider.
"""

import os

from jupyterhub.auth import LocalAuthenticator
Expand Down
1 change: 1 addition & 0 deletions oauthenticator/cilogon.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
A JupyterHub authenticator class for use with CILogon as an identity provider.
"""

import os
from fnmatch import fnmatch
from urllib.parse import urlparse
Expand Down

0 comments on commit 00360fa

Please sign in to comment.