update docs for allow_all, allow_existing_users

This commit is contained in:
Min RK
2024-03-18 16:03:32 +01:00
parent 0d427338a1
commit e1e34a14a2
2 changed files with 154 additions and 28 deletions

View File

@@ -37,14 +37,14 @@ A [generic implementation](https://github.com/jupyterhub/oauthenticator/blob/mas
## The Dummy Authenticator
When testing, it may be helpful to use the
{class}`jupyterhub.auth.DummyAuthenticator`. This allows for any username and
password unless if a global password has been set. Once set, any username will
{class}`~.jupyterhub.auth.DummyAuthenticator`. This allows for any username and
password unless a global password has been set. Once set, any username will
still be accepted but the correct password will need to be provided.
## Additional Authenticators
A partial list of other authenticators is available on the
[JupyterHub wiki](https://github.com/jupyterhub/jupyterhub/wiki/Authenticators).
Additional authenticators can be found on GitHub
by searching for [topic:jupyterhub topic:authenticator](https://github.com/search?q=topic%3Ajupyterhub%20topic%3Aauthenticator&type=repositories).
## Technical Overview of Authentication
@@ -54,9 +54,9 @@ The base authenticator uses simple username and password authentication.
The base Authenticator has one central method:
#### Authenticator.authenticate method
#### Authenticator.authenticate
Authenticator.authenticate(handler, data)
{meth}`.Authenticator.authenticate`
This method is passed the Tornado `RequestHandler` and the `POST data`
from JupyterHub's login form. Unless the login form has been customized,
@@ -81,7 +81,7 @@ Writing an Authenticator that looks up passwords in a dictionary
requires only overriding this one method:
```python
from IPython.utils.traitlets import Dict
from traitlets import Dict
from jupyterhub.auth import Authenticator
class DictionaryAuthenticator(Authenticator):
@@ -136,7 +136,7 @@ To only allow usernames that start with 'w':
c.Authenticator.username_pattern = r'w.*'
```
### How to write a custom authenticator
## How to write a custom authenticator
You can use custom Authenticator subclasses to enable authentication
via other mechanisms. One such example is using [GitHub OAuth][].
@@ -148,11 +148,6 @@ and {meth}`.Authenticator.post_spawn_stop`, are hooks that can be used to do
auth-related startup (e.g. opening PAM sessions) and cleanup
(e.g. closing PAM sessions).
See a list of custom Authenticators [on the wiki](https://github.com/jupyterhub/jupyterhub/wiki/Authenticators).
If you are interested in writing a custom authenticator, you can read
[this tutorial](http://jupyterhub-tutorial.readthedocs.io/en/latest/authenticators.html).
### Registering custom Authenticators via entry points
As of JupyterHub 1.0, custom authenticators can register themselves via
@@ -188,6 +183,104 @@ Additionally, configurable attributes for your authenticator will
appear in jupyterhub help output and auto-generated configuration files
via `jupyterhub --generate-config`.
(authenticator-allow)=
### Allowing access
When dealing with logging in, there are generally two _separate_ steps:
authentication
: identifying who is logged in, and
authorization
: deciding whether an authenticated user is logged in
{meth}`Authenticator.authenticate` is responsible for authenticating users.
It is perfectly fine in the simplest cases for `Authenticator.authenticate` to be responsible for authentication _and_ authorization,
in which case `authenticate` may return `None` if the user is not authorized.
However, Authenticators also have have two methods {meth}`~.Authenticator.check_allowed` and {meth}`~.Authenticator.check_blocked_users`, which are called after successful authentication to further check if the user is allowed.
If `check_blocked_users()` returns False, authorization stops and the user is not allowed.
If `check_allowed()` returns True, authorization proceeds.
:::{versionadded} 5.0
{attr}`Authenticator.allow_all` and {attr}`Authenticator.allow_existing_users` are new in JupyterHub 5.0.
By default, `allow_all` is True when `allowed_users` is empty,
and `allow_existing_users` is True when `allowed_users` is not empty.
This is to ensure backward-compatibility, but subclasses are free to pick more restrictive defaults.
:::
### Overriding `check_allowed`
The base implementation of {meth}`~.Authenticator.check_allowed` checks:
- if `allow_all` is True, return True
- if username is in the `allowed_users` set, return True
- else return False
If a custom Authenticator defines additional sources of `allow` configuration,
such as membership in a group or other information,
it should override `check_allowed` to account for this.
`allow_` configuration should generally be _additive_,
i.e. if permission is granted by _any_ allow configuration,
a user should be authorized.
:::{note}
For backward-compatibility, it is the responsibility of `Authenticator.check_allowed()` to check `.allow_all`.
This is to avoid the backward-compatible default values from granting permissions unexpectedly.
:::
If an Authenticator defines additional `allow` configuration, it must at least:
1. override `check_allowed`, and
2. override the default for `allow_all`
The default for `allow_all` in a custom authenticator should be one of `False` or a dynamic default matching something like `if not any allow configuration specified`.
False is recommended for authenticators which source much larger pools of users than are _typically_ allowed to access a Hub (e.g. generic OAuth providers like Google, GitHub, etc.).
For example, here is how `PAMAuthenticator` extends the base class to add `allowed_groups`:
```python
from traitlets import default
@default("allow_all")
def _allow_all_default(self):
if self.allowed_users or self.allowed_groups:
# if any allow config is specified, default to False
return False
return True
def check_allowed(self, username, authentication=None):
if self.allow_all:
return True
if self.check_allowed_groups(username, authentication):
return True
return super().check_allowed(username, authentication)
```
Important points to note:
- overriding the default for `allow_all` is required to avoid `allow_all` being True when `allowed_groups` is specified, but `allowed_users` is not.
- `allow_all` must be checked inside `check_allowed`
- `allowed_groups` strictly expands who is authorized,
it does not apply restrictions `allowed_users`.
This is recommended for all `allow_` configuration added by Authenticators.
#### Custom error messages
Any of these authentication and authorization methods may
```python
from tornado import web
raise web.HTTPError(403, "informative message")
```
if you want to show a more informative login failure message rather than the generic one.
(authenticator-auth-state)=
### Authentication state

View File

@@ -13,15 +13,25 @@ You can restrict which users are allowed to login with a set,
```python
c.Authenticator.allowed_users = {'mal', 'zoe', 'inara', 'kaylee'}
c.Authenticator.allow_all = False
c.Authenticator.allow_existing_users = False
```
Users in the `allowed_users` set are added to the Hub database when the Hub is
started.
Users in the `allowed_users` set are added to the Hub database when the Hub is started.
```{warning}
If this configuration value is not set, then **all authenticated users will be allowed into your hub**.
If `allowed_users` is not specified, then by default **all authenticated users will be allowed into your hub**,
i.e. `allow_all` defaults to True if neither `allowed_users` nor `allow_all` are set.
```
:::{versionadded} 5.0
{attr}`Authenticator.allow_all` and {attr}`Authenticator.allow_existing_users` are new in JupyterHub 5.0.
By default, `allow_all` is True when `allowed_users` is empty,
and `allow_existing_users` is True when `allowed_users` is not empty.
This is to ensure backward-compatibility.
:::
## One Time Passwords ( request_otp )
By setting `request_otp` to true, the login screen will show and additional password input field
@@ -42,7 +52,7 @@ c.Authenticator.otp_prompt = 'Google Authenticator:'
```{note}
As of JupyterHub 2.0, the full permissions of `admin_users`
should not be required.
Instead, you can assign [roles](define-role-target) to users or groups
Instead, it is best to assign [roles](define-role-target) to users or groups
with only the scopes they require.
```
@@ -68,26 +78,49 @@ group. For example, we can let any user in the `wheel` group be an admin:
c.PAMAuthenticator.admin_groups = {'wheel'}
```
## Give admin access to other users' notebook servers (`admin_access`)
## Give some users access to other users' notebook servers
Since the default `JupyterHub.admin_access` setting is `False`, the admins
do not have permission to log in to the single user notebook servers
owned by _other users_. If `JupyterHub.admin_access` is set to `True`,
then admins have permission to log in _as other users_ on their
respective machines for debugging. **As a courtesy, you should make
sure your users know if admin_access is enabled.**
The `access:servers` scope can be granted to users to give them permission to visit other users' servers.
For example, to give members of the `teachers` group access to the servers of members of the `students` group:
```python
c.JupyterHub.load_roles = [
{
"name": "teachers",
"scopes": [
"admin-ui",
"list:users",
"access:servers!group=students",
],
"groups": ["teachers"],
}
]
```
By default, only the deprecated `admin` role has global `access` permissions.
**As a courtesy, you should make sure your users know if admin access is enabled.**
## Add or remove users from the Hub
Users can be added to and removed from the Hub via the admin
panel or the REST API. When a user is **added**, the user will be
automatically added to the `allowed_users` set and database. Restarting the Hub
will not require manually updating the `allowed_users` set in your config file,
panel or the REST API.
To enable this behavior, set:
```python
c.Authenticator.allow_existing_users = True
```
When a user is **added**, the user will be
automatically added to the `allowed_users` set and database.
If `allow_existing_users` is True, restarting the Hub will not require manually updating the `allowed_users` set in your config file,
as the users will be loaded from the database.
If `allow_existing_users` is False, users not granted access by configuration such as `allowed_users` will not be permitted to login,
even if they are present in the database.
After starting the Hub once, it is not sufficient to **remove** a user
from the allowed users set in your config file. You must also remove the user
from the Hub's database, either by deleting the user from JupyterHub's
from the Hub's database, either by deleting the user via JupyterHub's
admin page, or you can clear the `jupyterhub.sqlite` database and start
fresh.