Skip to content

Commit a999a2d

Browse files
authored
feat: support identifying authenticators with strings (#33)
* support identifying authenticators with strings enables fully declarative config (e.g. yaml in helm chart) requires importlib_metadata backport for entrypoints on Python < 3.10
1 parent edfa7e5 commit a999a2d

File tree

4 files changed

+38
-17
lines changed

4 files changed

+38
-17
lines changed

README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,23 @@ This property shall contain a list of tuple with the following content:
2929
As an example:
3030

3131
```python
32-
from oauthenticator.github import GitHubOAuthenticator
33-
from oauthenticator.google import GoogleOAuthenticator
34-
from oauthenticator.gitlab import GitLabOAuthenticator
3532
from jupyterhub.auth import PAMAuthenticator
3633

3734
class MyPamAutenticator(PAMAuthenticator):
3835
login_service = "PAM"
3936

4037
c.MultiAuthenticator.authenticators = [
41-
(GitHubOAuthenticator, '/github', {
38+
('github', '/github', {
4239
'client_id': 'XXXX',
4340
'client_secret': 'YYYY',
4441
'oauth_callback_url': 'https://jupyterhub.example.com/hub/github/oauth_callback'
4542
}),
46-
(GoogleOAuthenticator, '/google', {
43+
('google', '/google', {
4744
'client_id': 'xxxx',
4845
'client_secret': 'yyyy',
4946
'oauth_callback_url': 'https://jupyterhub.example.com/hub/google/oauth_callback'
5047
}),
51-
(GitLabOAuthenticator, '/gitlab', {
48+
('gitlab', '/gitlab', {
5249
"client_id": "ZZZZ",
5350
"client_secret": "AAAAA",
5451
"oauth_callback_url": "https://jupyterhub.example.com/hub/gitlab/oauth_callback",
@@ -57,5 +54,5 @@ c.MultiAuthenticator.authenticators = [
5754
(MyPamAutenticator, "/pam", {}),
5855
]
5956

60-
c.JupyterHub.authenticator_class = 'multiauthenticator.multiauthenticator.MultiAuthenticator'
57+
c.JupyterHub.authenticator_class = 'multiauthenticator'
6158
```

multiauthenticator/multiauthenticator.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,54 @@
66
77
Example of configuration:
88
9-
from oauthenticator.github import GitHubOAuthenticator
10-
from oauthenticator.google import GoogleOAuthenticator
11-
129
c.MultiAuthenticator.authenticators = [
13-
(GitHubOAuthenticator, '/github', {
10+
("github", '/github', {
1411
'client_id': 'xxxx',
1512
'client_secret': 'xxxx',
1613
'oauth_callback_url': 'http://example.com/hub/github/oauth_callback'
1714
}),
18-
(GoogleOAuthenticator, '/google', {
15+
("google", '/google', {
1916
'client_id': 'xxxx',
2017
'client_secret': 'xxxx',
2118
'oauth_callback_url': 'http://example.com/hub/google/oauth_callback'
2219
}),
23-
(PAMAuthenticator, "/pam", {"service_name": "PAM"}),
20+
("pam", "/pam", {"service_name": "PAM"}),
2421
]
2522
26-
c.JupyterHub.authenticator_class = 'oauthenticator.MultiAuthenticator.MultiAuthenticator'
23+
c.JupyterHub.authenticator_class = 'multiauthenticator'
2724
2825
The same Authenticator class can be used several to support different providers.
2926
3027
"""
28+
try:
29+
# Python < 3.10
30+
from importlib_metadata import entry_points
31+
except ImportError:
32+
from importlib.metadata import entry_points
33+
3134
from jupyterhub.auth import Authenticator
3235
from jupyterhub.utils import url_path_join
3336
from traitlets import List
3437
from traitlets import Unicode
38+
from traitlets import import_item
3539

3640
PREFIX_SEPARATOR = ":"
3741

3842

43+
def _load_authenticator(authenticator_name):
44+
"""Load an authenticator from a string
45+
46+
Looks up authenticators entrypoint registration (e.g. 'github')
47+
or full import name ('jupyterhub.auth.PAMAuthenticator').
48+
49+
Returns the Authenticator subclass.
50+
"""
51+
for entry_point in entry_points(group="jupyterhub.authenticators"):
52+
if authenticator_name.lower() == entry_point.name.lower():
53+
return entry_point.load()
54+
return import_item(authenticator_name)
55+
56+
3957
class URLScopeMixin:
4058
"""Mixin class that adds the"""
4159

@@ -82,6 +100,8 @@ def __init__(self, *arg, **kwargs):
82100
url_scope_authenticator,
83101
authenticator_configuration,
84102
) in self.authenticators:
103+
if isinstance(authenticator_klass, str):
104+
authenticator_klass = _load_authenticator(authenticator_klass)
85105

86106
class WrapperAuthenticator(URLScopeMixin, authenticator_klass):
87107
url_scope = url_scope_authenticator

multiauthenticator/tests/test_multiauthenticator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class CustomPAMAuthenticator(PAMAuthenticator):
3131
def test_different_authenticators():
3232
MultiAuthenticator.authenticators = [
3333
(
34-
GitLabOAuthenticator,
34+
"gitlab",
3535
"/gitlab",
3636
{
3737
"client_id": "xxxx",
@@ -40,7 +40,7 @@ def test_different_authenticators():
4040
},
4141
),
4242
(
43-
GitHubOAuthenticator,
43+
"oauthenticator.github.GitHubOAuthenticator",
4444
"/github",
4545
{
4646
"client_id": "xxxx",

pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ classifiers = [
2424
]
2525
dependencies = [
2626
"jupyterhub",
27-
"oauthenticator"
27+
"oauthenticator",
28+
"importlib_metadata>=4.6; python_version < '3.10'",
2829
]
2930

3031
[project.optional-dependencies]
3132
test = ["pytest", "pytest-cov", "pytest-asyncio"]
3233
dev = ["pre-commit", "jupyterhub-multiauthenticator[test]"]
3334

35+
[project.entry-points."jupyterhub.authenticators"]
36+
multiauthenticator = "multiauthenticator:MultiAuthenticator"
37+
3438
[tool.setuptools]
3539
packages = ["multiauthenticator"]
3640

0 commit comments

Comments
 (0)