Skip to content

Commit 4b8bd48

Browse files
committed
Class-based views rework
1 parent 7fe509c commit 4b8bd48

File tree

12 files changed

+309
-358
lines changed

12 files changed

+309
-358
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ following url::
634634
# lots of url definitions here
635635

636636
(r'^saml2/', include('djangosaml2.urls')),
637-
(r'^test/', 'djangosaml2.views.echo_attributes'),
637+
(r'^test/', 'djangosaml2.views.EchoAttributesView.as_view()'),
638638

639639
# more url definitions
640640
)

djangosaml2/backends.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def _user_lookup_attribute(self) -> str:
6868
return settings.SAML_DJANGO_USER_MAIN_ATTRIBUTE
6969
return getattr(self._user_model, 'USERNAME_FIELD', 'username')
7070

71-
def _extract_user_identifier_params(self, session_info, attributes, attribute_mapping) -> Tuple[str, Optional[Any]]:
71+
def _extract_user_identifier_params(self, session_info: dict, attributes: dict, attribute_mapping: dict) -> Tuple[str, Optional[Any]]:
7272
""" Returns the attribute to perform a user lookup on, and the value to use for it.
7373
The value could be the name_id, or any other saml attribute from the request.
7474
"""
@@ -89,7 +89,7 @@ def _extract_user_identifier_params(self, session_info, attributes, attribute_ma
8989

9090
return user_lookup_key, self.clean_user_main_attribute(user_lookup_value)
9191

92-
def _get_attribute_value(self, django_field, attributes, attribute_mapping):
92+
def _get_attribute_value(self, django_field: str, attributes: dict, attribute_mapping: dict):
9393
saml_attribute = None
9494
logger.debug('attribute_mapping: %s', attribute_mapping)
9595
for saml_attr, django_fields in attribute_mapping.items():
@@ -136,7 +136,7 @@ def authenticate(self, request, session_info=None, attribute_mapping=None, creat
136136

137137
return user
138138

139-
def _update_user(self, user, attributes, attribute_mapping, force_save=False):
139+
def _update_user(self, user, attributes: dict, attribute_mapping: dict, force_save: bool = False):
140140
""" Update a user with a set of attributes and returns the updated user.
141141
142142
By default it uses a mapping defined in the settings constant
@@ -249,11 +249,11 @@ def send_user_update_signal(self, user: settings.AUTH_USER_MODEL, attributes: di
249249

250250
def get_attribute_value(self, django_field, attributes, attribute_mapping):
251251
warnings.warn("get_attribute_value() is deprecated, look at the Saml2Backend on how to subclass it", DeprecationWarning)
252-
self._get_attribute_value(django_field, attributes, attribute_mapping)
252+
return self._get_attribute_value(django_field, attributes, attribute_mapping)
253253

254254
def get_django_user_main_attribute(self):
255255
warnings.warn("get_django_user_main_attribute() is deprecated, look at the Saml2Backend on how to subclass it", DeprecationWarning)
256-
self._user_lookup_attribute
256+
return self._user_lookup_attribute
257257

258258
def get_django_user_main_attribute_lookup(self):
259259
warnings.warn("get_django_user_main_attribute_lookup() is deprecated, look at the Saml2Backend on how to subclass it", DeprecationWarning)

djangosaml2/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
# limitations under the License.
1515

1616
import copy
17-
from importlib import import_module
1817
from typing import Callable, Optional, Union
1918

2019
from django.conf import settings

djangosaml2/templatetags/__init__.py

Whitespace-only changes.

djangosaml2/templatetags/idplist.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

djangosaml2/tests/__init__.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,18 @@
3737
from djangosaml2.middleware import SamlSessionMiddleware
3838
from djangosaml2.signals import post_authenticated, pre_user_save
3939
from djangosaml2.tests import conf
40-
from djangosaml2.tests.auth_response import auth_response
41-
from djangosaml2.tests.utils import SAMLPostFormParser
42-
from djangosaml2.utils import (get_idp_sso_supported_bindings,
40+
from djangosaml2.utils import (get_custom_setting,
41+
get_idp_sso_supported_bindings,
4342
get_session_id_from_saml2,
4443
get_subject_id_from_saml2,
4544
saml2_from_httpredirect_request)
4645
from djangosaml2.views import finish_logout
4746
from saml2.config import SPConfig
4847
from saml2.s_utils import decode_base64_and_inflate, deflate_and_base64_encode
4948

49+
from .auth_response import auth_response
50+
from .utils import SAMLPostFormParser
51+
5052
User = get_user_model()
5153

5254
PY_VERSION = sys.version_info[:2]
@@ -72,7 +74,7 @@ def test_get_config_nonsense_path(self):
7274
get_config('lalala.nonexisting.blabla')
7375

7476
def test_get_config_missing_function(self):
75-
with self.assertRaisesMessage(ImproperlyConfigured, 'Module "djangosaml2.tests" does not define a "nonexisting_function" config loader'):
77+
with self.assertRaisesMessage(ImproperlyConfigured, 'Module "djangosaml2.tests" does not define a "nonexisting_function" attribute/class'):
7678
get_config('djangosaml2.tests.nonexisting_function')
7779

7880

@@ -138,7 +140,7 @@ def test_get_idp_sso_supported_bindings_noargs(self):
138140
metadata_file='remote_metadata_one_idp.xml',
139141
)
140142
idp_id = 'https://idp.example.com/simplesaml/saml2/idp/metadata.php'
141-
self.assertEqual(list(get_idp_sso_supported_bindings())[0], list(settings.SAML_CONFIG['service']['sp']['idp'][idp_id]['single_sign_on_service'].keys())[0])
143+
self.assertEqual(get_idp_sso_supported_bindings()[0], list(settings.SAML_CONFIG['service']['sp']['idp'][idp_id]['single_sign_on_service'].keys())[0])
142144

143145
def test_get_idp_sso_supported_bindings_unknown_idp(self):
144146
settings.SAML_CONFIG = conf.create_conf(
@@ -592,7 +594,7 @@ def _test_metadata(self):
592594

593595
expected_metadata = expected_metadata % valid_until
594596

595-
response = self.client.get('/metadata/')
597+
response = self.client.get(reverse('saml2_metadata'))
596598
self.assertEqual(response['Content-type'], 'text/xml; charset=utf8')
597599
self.assertEqual(response.status_code, 200)
598600
self.assertEqual(response.content, expected_metadata)
@@ -643,33 +645,6 @@ def signal_handler(sender, instance, attributes, user_modified, **kwargs):
643645

644646
pre_user_save.disconnect(dispatch_uid='test_signal')
645647

646-
def test_idplist_templatetag(self):
647-
settings.SAML_CONFIG = conf.create_conf(
648-
sp_host='sp.example.com',
649-
idp_hosts=['idp1.example.com',
650-
'idp2.example.com',
651-
'idp3.example.com'],
652-
metadata_file='remote_metadata_three_idps.xml',
653-
)
654-
rendered = self.render_template(
655-
'{% load idplist %}'
656-
'{% idplist as idps %}'
657-
'{% for url, name in idps.items %}'
658-
'{{ url }} - {{ name }}; '
659-
'{% endfor %}'
660-
)
661-
662-
# the idplist is unordered, so convert the result into a set.
663-
rendered = set(rendered.split('; '))
664-
expected = set([
665-
u'https://idp1.example.com/simplesaml/saml2/idp/metadata.php - idp1.example.com IdP',
666-
u'https://idp2.example.com/simplesaml/saml2/idp/metadata.php - idp2.example.com IdP',
667-
u'https://idp3.example.com/simplesaml/saml2/idp/metadata.php - idp3.example.com IdP',
668-
u'',
669-
])
670-
671-
self.assertEqual(rendered, expected)
672-
673648
def test_sigalg_not_passed_when_not_signing_request(self):
674649
# monkey patch SAML configuration
675650
settings.SAML_CONFIG = conf.create_conf(
@@ -754,7 +729,7 @@ def test_custom_conf_loader_from_view(self):
754729
saml_session_name = getattr(settings, 'SAML_SESSION_COOKIE_NAME', 'saml_session')
755730
getattr(request, saml_session_name).save()
756731

757-
response = views.login(request, config_loader_path)
732+
response = views.LoginView.as_view(config_loader_path=config_loader_path)(request)
758733
self.assertEqual(response.status_code, 302)
759734
location = response['Location']
760735

djangosaml2/tests/urls.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,9 @@
1515
from django.conf.urls import include, url
1616
from django.contrib import admin
1717

18-
from djangosaml2 import views
19-
2018

2119
urlpatterns = [
22-
url(r'^login/$', views.login, name='saml2_login'),
23-
url(r'^acs/$', views.AssertionConsumerServiceView.as_view(), name='saml2_acs'),
24-
url(r'^logout/$', views.logout, name='saml2_logout'),
25-
url(r'^ls/$', views.logout_service, name='saml2_ls'),
26-
url(r'^ls/post/$', views.logout_service_post, name='saml2_ls_post'),
27-
url(r'^metadata/$', views.metadata, name='saml2_metadata'),
20+
path('saml2/', include('djangosaml2.urls')),
2821

2922
# this is needed for the tests
3023
url(r'^admin/', include(admin.site.urls)),

djangosaml2/urls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
urlpatterns = [
2121
path('login/', views.LoginView.as_view(), name='saml2_login'),
2222
path('acs/', views.AssertionConsumerServiceView.as_view(), name='saml2_acs'),
23-
path('logout/', views.logout, name='saml2_logout'),
24-
path('ls/', views.logout_service, name='saml2_ls'),
25-
path('ls/post/', views.logout_service_post, name='saml2_ls_post'),
23+
path('logout/', views.LogoutInitView.as_view(), name='saml2_logout'),
24+
path('ls/', views.LogoutView.as_view(), name='saml2_ls'),
25+
path('ls/post/', views.LogoutView.as_view(), name='saml2_ls_post'),
2626
path('metadata/', views.MetadataView.as_view(), name='saml2_metadata'),
2727
]

djangosaml2/utils.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@
1515
import re
1616
import urllib
1717
import zlib
18+
from typing import Optional
1819

1920
from django.conf import settings
2021
from django.core.exceptions import ImproperlyConfigured
2122
from django.utils.http import is_safe_url
2223
from django.utils.module_loading import import_string
24+
from saml2.config import SPConfig
2325
from saml2.s_utils import UnknownSystemEntity
2426

2527

26-
def get_custom_setting(name, default=None):
28+
def get_custom_setting(name: str, default=None):
2729
return getattr(settings, name, default)
2830

2931

30-
def available_idps(config, langpref=None):
32+
def available_idps(config: SPConfig, langpref=None) -> dict:
3133
if langpref is None:
3234
langpref = "en"
3335

@@ -44,7 +46,7 @@ def available_idps(config, langpref=None):
4446
}
4547

4648

47-
def get_idp_sso_supported_bindings(idp_entity_id=None, config=None):
49+
def get_idp_sso_supported_bindings(idp_entity_id: Optional[str] = None, config: Optional[SPConfig] = None) -> list:
4850
"""Returns the list of bindings supported by an IDP
4951
This is not clear in the pysaml2 code, so wrapping it in a util"""
5052
if config is None:
@@ -60,7 +62,7 @@ def get_idp_sso_supported_bindings(idp_entity_id=None, config=None):
6062
except IndexError:
6163
raise ImproperlyConfigured("No IdP configured!")
6264
try:
63-
return meta.service(idp_entity_id, 'idpsso_descriptor', 'single_sign_on_service').keys()
65+
return list(meta.service(idp_entity_id, 'idpsso_descriptor', 'single_sign_on_service').keys())
6466
except UnknownSystemEntity:
6567
return []
6668

@@ -97,8 +99,7 @@ def validate_referral_url(request, url):
9799

98100
if not is_safe_url(url=url, allowed_hosts=saml_allowed_hosts):
99101
return settings.LOGIN_REDIRECT_URL
100-
else:
101-
return url
102+
return url
102103

103104

104105
def saml2_from_httpredirect_request(url):

0 commit comments

Comments
 (0)