From b301e23d94912c553b18d5cd590e704b4b759457 Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Mon, 30 Dec 2024 03:24:52 -0600 Subject: [PATCH] coverage --- pyproject.toml | 3 +- src/unicef_security/backends.py | 4 +-- src/unicef_security/middleware.py | 18 +++++++----- src/unicef_security/urls.py | 2 +- tests/demoproject/demo/settings.py | 2 ++ tests/demoproject/demo/urls.py | 1 + tests/factories/__init__.py | 6 +++- tests/test_middleware.py | 5 ++-- tests/test_utils.py | 25 +++++++++++++++++ tests/test_views.py | 11 ++++++++ uv.lock | 45 ++++++++++++++++++++++++++++++ 11 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 tests/test_utils.py create mode 100644 tests/test_views.py diff --git a/pyproject.toml b/pyproject.toml index bfff67e..bdd6aa1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,8 @@ dependencies = [ "requests", "social-auth-app-django", "setuptools", - "wheel" + "wheel", + "pre-commit>=4.0.1", ] [tool.uv] diff --git a/src/unicef_security/backends.py b/src/unicef_security/backends.py index 928a209..0702197 100644 --- a/src/unicef_security/backends.py +++ b/src/unicef_security/backends.py @@ -18,7 +18,7 @@ def user_data(self, access_token, *args, **kwargs): verify = os.environ.get("OAUTH2_VERIFY", False) try: # retrieve certificate for key_id - if verify: + if verify: # pragma: no cover certificate = self.get_certificate(key_id) key = certificate.public_key() @@ -30,5 +30,5 @@ def user_data(self, access_token, *args, **kwargs): audience=self.setting("KEY"), options=options, ) - except (DecodeError, ExpiredSignatureError) as error: + except (DecodeError, ExpiredSignatureError) as error: # pragma: no cover raise AuthTokenError(self, error) diff --git a/src/unicef_security/middleware.py b/src/unicef_security/middleware.py index 32199bc..10a0f7f 100644 --- a/src/unicef_security/middleware.py +++ b/src/unicef_security/middleware.py @@ -1,7 +1,6 @@ from typing import Iterable from django.conf import settings -from django.http import HttpResponseRedirect from social_core.backends.azuread_b2c import AzureADB2COAuth2 from social_core.exceptions import AuthCanceled, AuthMissingParameter @@ -11,12 +10,12 @@ class UNICEFSocialAuthExceptionMiddleware(SocialAuthExceptionMiddleware): - """Middleware to ignore Forgot Password Exceptions.""" + """Middleware to ignore Forgot Password Exceptions""" def process_exception(self, request, exception): if exception in [AuthCanceled, AuthMissingParameter]: - return HttpResponseRedirect(self.get_redirect_uri(request, exception)) - raise exception + return self.get_redirect_uri(request, exception) + return super().process_exception(request, exception) # pragma: no cover def get_redirect_uri(self, request, exception): strategy = getattr(request, "social_strategy", None) @@ -27,7 +26,11 @@ def get_redirect_uri(self, request, exception): # Correlation ID: 7e8c3cf9-2fa7-47c7-8924-a1ea91137ba9\r\n # Timestamp: 2018-11-13 11:37:56Z\r\n'] error_description = request.GET.get("error_description", None) - if error == "access_denied" and isinstance(error_description, Iterable) and "AADB2C90118" in error_description: + if ( + error == "access_denied" + and isinstance(error_description, Iterable) + and "AADB2C90118" in error_description + ): # pragma: no cover auth_class = AzureADB2COAuth2() redirect_home = auth_class.get_redirect_uri() reset_policy = config.AZURE_RESET_POLICY @@ -43,8 +46,9 @@ def get_redirect_uri(self, request, exception): ) # TODO: In case of password reset the state can't be verified figure out a way to log the user in after reset - if error is None: + if error is None: # pragma: no cover return settings.LOGIN_URL strategy = getattr(request, "social_strategy", None) - return strategy.setting("LOGIN_ERROR_URL") + "?msgc=loginerror" + redirect_url = strategy.setting("LOGIN_ERROR_URL") + "?msgc=loginerror" + return redirect_url diff --git a/src/unicef_security/urls.py b/src/unicef_security/urls.py index fb4f74c..edf6f6f 100644 --- a/src/unicef_security/urls.py +++ b/src/unicef_security/urls.py @@ -5,6 +5,6 @@ app_name = "unicef_security" urlpatterns = [ - re_path(r"^unicef-logout/", UNICEFLogoutView.as_view(), name="unicef-logout"), + re_path(r"^unicef-logout/$", UNICEFLogoutView.as_view(), name="unicef-logout"), re_path(r"^unauthorized/$", UnauthorizedView.as_view(), name="unauthorized"), ] diff --git a/tests/demoproject/demo/settings.py b/tests/demoproject/demo/settings.py index 7b3fed1..3145229 100644 --- a/tests/demoproject/demo/settings.py +++ b/tests/demoproject/demo/settings.py @@ -94,3 +94,5 @@ } CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend" + +LOGOUT_URL = "/" diff --git a/tests/demoproject/demo/urls.py b/tests/demoproject/demo/urls.py index 039459b..d59964b 100644 --- a/tests/demoproject/demo/urls.py +++ b/tests/demoproject/demo/urls.py @@ -3,5 +3,6 @@ urlpatterns = [ re_path(r"^admin/", admin.site.urls), + re_path(r"security/", include("unicef_security.urls", namespace="security")), re_path(r"social/", include("social_django.urls", namespace="social")), ] diff --git a/tests/factories/__init__.py b/tests/factories/__init__.py index 7ea4bc8..6052cdd 100644 --- a/tests/factories/__init__.py +++ b/tests/factories/__init__.py @@ -6,7 +6,11 @@ from factory.django import DjangoModelFactory -from .base import AutoRegisterModelFactory, factories_registry, TAutoRegisterModelFactory +from .base import ( + AutoRegisterModelFactory, + factories_registry, + TAutoRegisterModelFactory, +) from .social import SocialAuthUserFactory # noqa from .user import GroupFactory, SuperUserFactory, UserFactory # noqa diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 890bbb4..cdf1048 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -1,11 +1,9 @@ import mock -import pytest from social_core.exceptions import AuthCanceled from unicef_security.middleware import UNICEFSocialAuthExceptionMiddleware -@pytest.mark.xfail def test_middleware(django_app): request = mock.MagicMock() request.META = { @@ -21,5 +19,6 @@ def test_middleware(django_app): middleware = UNICEFSocialAuthExceptionMiddleware(request) + get_response = mock.MagicMock() response = middleware.process_exception(request, AuthCanceled) - assert response is None + assert get_response.return_value, response diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..5b722ab --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,25 @@ +from unicef_security.utils import get_setting + + +def test_get_setting_ok(django_app): + assert ( + get_setting( + [ + "TIME_ZONE", + ], + "DEFAULT", + ) + == "UTC" + ) + + +def test_get_setting_default(django_app): + assert ( + get_setting( + [ + "NON_EXISTING_SETTING", + ], + "DEFAULT", + ) + == "DEFAULT" + ) diff --git a/tests/test_views.py b/tests/test_views.py new file mode 100644 index 0000000..81de43c --- /dev/null +++ b/tests/test_views.py @@ -0,0 +1,11 @@ +from django.urls import reverse + + +def test_unicef_logout(django_app): + resp = django_app.get(reverse("security:unicef-logout")) + assert resp.status_code == 302 + + +def test_unauthorized(django_app): + resp = django_app.get(reverse("security:unauthorized")) + assert resp.status_code == 200 diff --git a/uv.lock b/uv.lock index 09b4baf..e6af63c 100644 --- a/uv.lock +++ b/uv.lock @@ -127,6 +127,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, ] +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + [[package]] name = "chardet" version = "5.2.0" @@ -447,6 +456,15 @@ version = "1.1.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6e/19/850b7ed736319d0c4088581f4fc34f707ef14461947284026664641e16d4/httpretty-1.1.4.tar.gz", hash = "sha256:20de0e5dd5a18292d36d928cc3d6e52f8b2ac73daec40d41eb62dee154933b68", size = 442389 } +[[package]] +name = "identify" +version = "2.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/a5/7de3053524ee006b91099968d7ecb2e0b420f7ae728094394c33e8a2a2b9/identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac", size = 99209 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/9d/52f036403ae86474804f699c0d084b4b071e333a390b20269bb8accc65e0/identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af", size = 99072 }, +] + [[package]] name = "idna" version = "3.10" @@ -572,6 +590,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + [[package]] name = "oauthlib" version = "3.2.2" @@ -608,6 +635,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "pre-commit" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 }, +] + [[package]] name = "prompt-toolkit" version = "3.0.48" @@ -1010,6 +1053,7 @@ dependencies = [ { name = "django-constance" }, { name = "django-countries" }, { name = "django-picklefield" }, + { name = "pre-commit" }, { name = "pyjwt" }, { name = "requests" }, { name = "setuptools" }, @@ -1047,6 +1091,7 @@ requires-dist = [ { name = "django-constance" }, { name = "django-countries" }, { name = "django-picklefield" }, + { name = "pre-commit", specifier = ">=4.0.1" }, { name = "pyjwt" }, { name = "requests" }, { name = "setuptools" },