diff --git a/README.md b/README.md index 687db62..cf712b4 100644 --- a/README.md +++ b/README.md @@ -35,23 +35,38 @@ class MyPamAutenticator(PAMAuthenticator): login_service = "PAM" c.MultiAuthenticator.authenticators = [ - ('github', '/github', { - 'client_id': 'XXXX', - 'client_secret': 'YYYY', - 'oauth_callback_url': 'https://jupyterhub.example.com/hub/github/oauth_callback' - }), - ('google', '/google', { - 'client_id': 'xxxx', - 'client_secret': 'yyyy', - 'oauth_callback_url': 'https://jupyterhub.example.com/hub/google/oauth_callback' - }), - ('gitlab', '/gitlab', { - "client_id": "ZZZZ", - "client_secret": "AAAAA", - "oauth_callback_url": "https://jupyterhub.example.com/hub/gitlab/oauth_callback", - "gitlab_url": "https://gitlab.example.com" - }), - (MyPamAutenticator, "/pam", {}), + { + "authenticator_class": 'github', + "url_prefix": '/github', + "config": { + 'client_id': 'XXXX', + 'client_secret': 'YYYY', + 'oauth_callback_url': 'https://jupyterhub.example.com/hub/github/oauth_callback' + } + }, + { + "authenticator_class": 'google', + "url_prefix": '/google', + "config": { + 'client_id': 'xxxx', + 'client_secret': 'yyyy', + 'oauth_callback_url': 'https://jupyterhub.example.com/hub/google/oauth_callback' + } + }, + { + "authenticator_class": 'gitlab', + "url_prefix": '/gitlab', + "config": { + 'client_id': 'ZZZZ', + 'client_secret': 'AAAA', + 'oauth_callback_url': 'https://jupyterhub.example.com/hub/gitlab/oauth_callback' + "gitlab_url": "https://gitlab.example.com" + } + }, + { + "authenticator_class": MyPamAuthenticator, + "url_prefix": "/pam", + }, ] c.JupyterHub.authenticator_class = 'multiauthenticator' diff --git a/multiauthenticator/multiauthenticator.py b/multiauthenticator/multiauthenticator.py index 09862a2..ad354e3 100644 --- a/multiauthenticator/multiauthenticator.py +++ b/multiauthenticator/multiauthenticator.py @@ -7,17 +7,28 @@ Example of configuration: c.MultiAuthenticator.authenticators = [ - ("github", '/github', { - 'client_id': 'xxxx', - 'client_secret': 'xxxx', - 'oauth_callback_url': 'http://example.com/hub/github/oauth_callback' - }), - ("google", '/google', { - 'client_id': 'xxxx', - 'client_secret': 'xxxx', - 'oauth_callback_url': 'http://example.com/hub/google/oauth_callback' - }), - ("pam", "/pam", {"service_name": "PAM"}), + { + "authenticator_class": 'github', + "url_prefix": '/github', + "config": { + 'client_id': 'XXXX', + 'client_secret': 'YYYY', + 'oauth_callback_url': 'https://jupyterhub.example.com/hub/github/oauth_callback' + } + }, + { + "authenticator_class": 'google', + "url_prefix": '/google', + "config": { + 'client_id': 'xxxx', + 'client_secret': 'yyyy', + 'oauth_callback_url': 'https://jupyterhub.example.com/hub/google/oauth_callback' + } + }, + { + "authenticator_class": "pam", + "url_prefix": "/pam", + }, ] c.JupyterHub.authenticator_class = 'multiauthenticator' @@ -30,6 +41,7 @@ from importlib_metadata import entry_points except ImportError: from importlib.metadata import entry_points +import warnings from jupyterhub.auth import Authenticator from jupyterhub.utils import url_path_join @@ -95,11 +107,24 @@ class MultiAuthenticator(Authenticator): def __init__(self, *arg, **kwargs): super().__init__(*arg, **kwargs) self._authenticators = [] - for ( - authenticator_klass, - url_scope_authenticator, - authenticator_configuration, - ) in self.authenticators: + for entry in self.authenticators: + if isinstance(entry, (list, tuple)): + tuple_entry = entry + entry = { + "authenticator_class": tuple_entry[0], + "url_prefix": tuple_entry[1], + "config": tuple_entry[2], + } + warnings.warn( + "Configuring subauthenticators with tuples is deprecated." + f" Use a dict like: {entry!r}", + DeprecationWarning, + ) + + authenticator_klass = entry["authenticator_class"] + url_scope_authenticator = entry["url_prefix"] + authenticator_configuration = entry.get("config", {}) + if isinstance(authenticator_klass, str): authenticator_klass = _load_authenticator(authenticator_klass) diff --git a/multiauthenticator/tests/test_deprecated.py b/multiauthenticator/tests/test_deprecated.py index 2043d4f..10b63ba 100644 --- a/multiauthenticator/tests/test_deprecated.py +++ b/multiauthenticator/tests/test_deprecated.py @@ -16,26 +16,26 @@ def test_service_name(): gitlab_service_name = "gitlab-service" google_service_name = "google-service" authenticators = [ - ( - GitLabOAuthenticator, - "/gitlab", - { + { + "authenticator_class": GitLabOAuthenticator, + "url_prefix": "/gitlab", + "config": { "service_name": gitlab_service_name, "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/gitlab/oauth_callback", }, - ), - ( - GoogleOAuthenticator, - "/google", - { + }, + { + "authenticator_class": GoogleOAuthenticator, + "url_prefix": "/google", + "config": { "service_name": google_service_name, "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/othergoogle/oauth_callback", }, - ), + }, ] MultiAuthenticator.authenticators = authenticators @@ -49,26 +49,26 @@ def test_service_name(): def test_same_authenticators(): MultiAuthenticator.authenticators = [ - ( - GoogleOAuthenticator, - "/mygoogle", - { + { + "authenticator_class": GoogleOAuthenticator, + "url_prefix": "/mygoogle", + "config": { "service_name": "My Google", "client_id": "yyyyy", "client_secret": "yyyyy", "oauth_callback_url": "http://example.com/hub/mygoogle/oauth_callback", }, - ), - ( - GoogleOAuthenticator, - "/othergoogle", - { + }, + { + "authenticator_class": GoogleOAuthenticator, + "url_prefix": "/othergoogle", + "config": { "service_name": "Other Google", "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/othergoogle/oauth_callback", }, - ), + }, ] multi_authenticator = MultiAuthenticator() @@ -89,11 +89,11 @@ def test_same_authenticators(): def test_username_prefix_validation_with_service_name(invalid_name, caplog): MultiAuthenticator.authenticators = [ - ( - PAMAuthenticator, - "/pam", - {"service_name": invalid_name, "allowed_users": {"test"}}, - ), + { + "authenticator_class": PAMAuthenticator, + "url_prefix": "/pam", + "config": {"service_name": invalid_name, "allowed_users": {"test"}}, + }, ] with pytest.raises(ValueError) as excinfo: @@ -105,3 +105,18 @@ def test_username_prefix_validation_with_service_name(invalid_name, caplog): caplog.records[0].message == "service_name is deprecated, please create a subclass and set the login_service class variable" ) + + +def test_tuple_config(): + MultiAuthenticator.authenticators = [ + ( + PAMAuthenticator, + "/pam", + {"service_name": "pam", "allowed_users": {"test"}}, + ), + ] + with pytest.deprecated_call(): + authenticator = MultiAuthenticator() + sub_authenticator = authenticator._authenticators[0] + assert sub_authenticator.service_name == "pam" + assert sub_authenticator.allowed_users == {"test"} diff --git a/multiauthenticator/tests/test_multiauthenticator.py b/multiauthenticator/tests/test_multiauthenticator.py index 2de6bbc..3e29a3a 100644 --- a/multiauthenticator/tests/test_multiauthenticator.py +++ b/multiauthenticator/tests/test_multiauthenticator.py @@ -30,25 +30,25 @@ class CustomPAMAuthenticator(PAMAuthenticator): def test_different_authenticators(): MultiAuthenticator.authenticators = [ - ( - "gitlab", - "/gitlab", - { + { + "authenticator_class": "gitlab", + "url_prefix": "/gitlab", + "config": { "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/gitlab/oauth_callback", }, - ), - ( - "oauthenticator.github.GitHubOAuthenticator", - "/github", - { + }, + { + "authenticator_class": "oauthenticator.github.GitHubOAuthenticator", + "url_prefix": "/github", + "config": { "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/github/oauth_callback", }, - ), - (CustomPAMAuthenticator, "/pam", {}), + }, + {"authenticator_class": CustomPAMAuthenticator, "url_prefix": "/pam"}, ] multi_authenticator = MultiAuthenticator() @@ -98,23 +98,23 @@ def test_extra_configuration(): allowed_users = {"test_user1", "test_user2"} authenticators = [ - ( - GitLabOAuthenticator, - "/gitlab", - { + { + "authenticator_class": GitLabOAuthenticator, + "url_prefix": "/gitlab", + "config": { "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/gitlab/oauth_callback", "allowed_users": allowed_users, }, - ), - ( - CustomDummyAuthenticator, - "/pam", - { + }, + { + "authenticator_class": CustomDummyAuthenticator, + "url_prefix": "/pam", + "config": { "allowed_users": allowed_users, }, - ), + }, ] MultiAuthenticator.authenticators = authenticators @@ -126,17 +126,17 @@ def test_extra_configuration(): def test_username_prefix(): MultiAuthenticator.authenticators = [ - ( - GitLabOAuthenticator, - "/gitlab", - { + { + "authenticator_class": GitLabOAuthenticator, + "url_prefix": "/gitlab", + "config": { "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/hub/gitlab/oauth_callback", }, - ), - (CustomPAMAuthenticator, "/pam", {}), - (CustomDummyAuthenticator, "/dummy", {}), + }, + {"authenticator_class": CustomPAMAuthenticator, "url_prefix": "/pam"}, + {"authenticator_class": CustomDummyAuthenticator, "url_prefix": "/dummy"}, ] multi_authenticator = MultiAuthenticator() @@ -158,7 +158,7 @@ def test_username_prefix(): @pytest.mark.asyncio async def test_authenticated_username_prefix(): MultiAuthenticator.authenticators = [ - (CustomDummyAuthenticator, "/dummy", {}), + {"authenticator_class": CustomDummyAuthenticator, "url_prefix": "/dummy"}, ] multi_authenticator = MultiAuthenticator() @@ -177,25 +177,29 @@ class CustomDummyAuthenticator2(CustomDummyAuthenticator): login_service = "Dummy2" MultiAuthenticator.authenticators = [ - (CustomPAMAuthenticator, "/pam", {"allowed_users": {"test"}}), - ( - CustomPAMAuthenticator2, - "/pam2", - {"blocked_users": {"test2"}}, - ), - ( - CustomDummyAuthenticator, - "/dummy", - {"allowed_users": {"TEST3"}}, - ), - ( - CustomDummyAuthenticator2, - "/dummy2", - { + { + "authenticator_class": CustomPAMAuthenticator, + "url_prefix": "/pam", + "config": {"allowed_users": {"test"}}, + }, + { + "authenticator_class": CustomPAMAuthenticator2, + "url_prefix": "/pam2", + "config": {"blocked_users": {"test2"}}, + }, + { + "authenticator_class": CustomDummyAuthenticator, + "url_prefix": "/dummy", + "config": {"allowed_users": {"TEST3"}}, + }, + { + "authenticator_class": CustomDummyAuthenticator2, + "url_prefix": "/dummy2", + "config": { "allowed_users": {"TEST3"}, "blocked_users": {"TEST4"}, }, - ), + }, ] multi_authenticator = MultiAuthenticator() @@ -253,15 +257,15 @@ class MyAuthenticator(OAuthenticator): login_service = invalid_name MultiAuthenticator.authenticators = [ - ( - MyAuthenticator, - "/myauth", - { + { + "authenticator_class": MyAuthenticator, + "url_prefix": "/myauth", + "config": { "client_id": "xxxx", "client_secret": "xxxx", "oauth_callback_url": "http://example.com/myauth/oauth_callback", }, - ), + }, ] with pytest.raises(ValueError) as excinfo: @@ -272,11 +276,11 @@ class MyAuthenticator(OAuthenticator): def test_next_handling(): MultiAuthenticator.authenticators = [ - ( - CustomDummyAuthenticator, - "/dummy", - {"allowed_users": {"test"}}, - ), + { + "authenticator_class": CustomDummyAuthenticator, + "url_prefix": "/dummy", + "config": {"allowed_users": {"test"}}, + }, ] multi_authenticator = MultiAuthenticator() @@ -301,7 +305,10 @@ def test_next_handling(): async def test_prefix(prefix, expected): MultiAuthenticator.username_prefix = prefix MultiAuthenticator.authenticators = [ - (CustomDummyAuthenticator, "/dummy", {}), + { + "authenticator_class": CustomDummyAuthenticator, + "url_prefix": "/dummy", + }, ] multi_authenticator = MultiAuthenticator()