Skip to content

Commit 343c7e4

Browse files
committed
Signals removal
1 parent 4b8bd48 commit 343c7e4

File tree

6 files changed

+26
-124
lines changed

6 files changed

+26
-124
lines changed

README.rst

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -493,28 +493,6 @@ Learn more about Django profile models at:
493493
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model
494494

495495

496-
Sometimes you need to use special logic to update the user object
497-
depending on the SAML2 attributes and the mapping described above
498-
is simply not enough. For these cases djangosaml2 provides a Django
499-
signal that you can listen to. In order to do so you can add the
500-
following code to your app::
501-
502-
from djangosaml2.signals import pre_user_save
503-
504-
def custom_update_user(sender=User, instance, attributes, user_modified, **kargs)
505-
...
506-
return True # I modified the user object
507-
508-
509-
Your handler will receive the user object, the list of SAML attributes
510-
and a flag telling you if the user is already modified and need
511-
to be saved after your handler is executed. If your handler
512-
modifies the user object it should return True. Otherwise it should
513-
return False. This way djangosaml2 will know if it should save
514-
the user object so you don't need to do it and no more calls to
515-
the save method are issued.
516-
517-
518496
IdP setup
519497
=========
520498
Congratulations, you have finished configuring the SP side of the federation.

djangosaml2/apps.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,3 @@
44
class DjangoSaml2Config(AppConfig):
55
name = 'djangosaml2'
66
verbose_name = "DjangoSAML2"
7-
8-
def ready(self):
9-
from . import signals # noqa

djangosaml2/backends.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
from django.core.exceptions import (ImproperlyConfigured,
2525
MultipleObjectsReturned)
2626

27-
from .signals import pre_user_save
28-
2927
logger = logging.getLogger('djangosaml2')
3028

3129

@@ -143,14 +141,15 @@ def _update_user(self, user, attributes: dict, attribute_mapping: dict, force_sa
143141
SAML_ATTRIBUTE_MAPPING. For each attribute, if the user object has
144142
that field defined it will be set.
145143
"""
146-
# Always save a brand new user instance
147-
user_modified = user.pk is None
148144

145+
# No attributes to set on the user instance, nothing to update
149146
if not attribute_mapping:
150-
if user_modified:
151-
user.save()
147+
# Always save a brand new user instance
148+
if user.pk is None:
149+
user = self.save_user(user)
152150
return user
153151

152+
has_updated_fields = False
154153
for saml_attr, django_attrs in attribute_mapping.items():
155154
attr_value_list = attributes.get(saml_attr)
156155
if not attr_value_list:
@@ -167,15 +166,12 @@ def _update_user(self, user, attributes: dict, attribute_mapping: dict, force_sa
167166
else:
168167
modified = set_attribute(user, attr, attr_value_list[0])
169168

170-
user_modified = user_modified or modified
169+
has_updated_fields = has_updated_fields or modified
171170
else:
172171
logger.debug('Could not find attribute "%s" on user "%s"', attr, user)
173172

174-
signal_modified = self.send_user_update_signal(user, attributes, user_modified)
175-
176-
if user_modified or signal_modified or force_save:
177-
user.save()
178-
logger.debug('User updated with incoming attributes')
173+
if has_updated_fields or force_save:
174+
user = self.save_user(user)
179175

180176
return user
181177

@@ -228,20 +224,18 @@ def get_or_create_user(self,
228224

229225
return user, created
230226

231-
def send_user_update_signal(self, user: settings.AUTH_USER_MODEL, attributes: dict, user_modified: bool) -> bool:
232-
""" Send out a pre-save signal after the user has been updated with the SAML attributes.
233-
This does not have to be overwritten, but depending on your custom implementation of get_or_create_user,
234-
you might want to not send out this signal. In that case, just override this method to return False.
227+
def save_user(self, user: settings.AUTH_USER_MODEL, *args, **kwargs) -> settings.AUTH_USER_MODEL:
228+
""" Hook to add custom logic around saving a user. Return the saved user instance.
235229
"""
236-
logger.debug('Sending the pre_save signal')
237-
signal_modified = any(
238-
[response for receiver, response
239-
in pre_user_save.send_robust(sender=user.__class__,
240-
instance=user,
241-
attributes=attributes,
242-
user_modified=user_modified)]
243-
)
244-
return signal_modified
230+
is_new_instance = user.pk is None
231+
user.save()
232+
233+
if is_new_instance:
234+
logger.debug('New user created')
235+
else:
236+
logger.debug('User %s updated with incoming attributes', user)
237+
238+
return user
245239

246240
# ############################################
247241
# Backwards-compatibility stubs

djangosaml2/signals.py

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

djangosaml2/tests/__init__.py

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
from djangosaml2.cache import OutstandingQueriesCache
3636
from djangosaml2.conf import get_config
3737
from djangosaml2.middleware import SamlSessionMiddleware
38-
from djangosaml2.signals import post_authenticated, pre_user_save
3938
from djangosaml2.tests import conf
4039
from djangosaml2.utils import (get_custom_setting,
4140
get_idp_sso_supported_bindings,
@@ -599,52 +598,6 @@ def _test_metadata(self):
599598
self.assertEqual(response.status_code, 200)
600599
self.assertEqual(response.content, expected_metadata)
601600

602-
def test_post_authenticated_signal(self):
603-
self.called = []
604-
605-
def signal_handler(sender, instance, session_info, request, **kwargs):
606-
self.called.append({'sender': sender, 'instance': instance, 'request': request, 'session_info': session_info})
607-
608-
post_authenticated.connect(signal_handler, dispatch_uid='test_signal')
609-
610-
self.do_login()
611-
612-
# make sure the handler is only called once
613-
self.assertEqual(len(self.called), 1)
614-
# test 'sender', this should be User.__class__
615-
self.assertEqual(self.called[0]['sender'], get_user_model(), 'post_authenticated signal sender is not a User')
616-
# test 'instance', this should be User
617-
self.assertIsInstance(self.called[0]['instance'], get_user_model(), 'post_authenticated signal did not send a User instance')
618-
# test the request
619-
self.assertIsInstance(self.called[0]['request'], HttpRequest, 'post_authenticated signal did not send a request')
620-
# test the session_info
621-
self.assertIsInstance(self.called[0]['session_info'], dict, 'post_authenticated signal did not send a session_info dict')
622-
623-
post_authenticated.disconnect(dispatch_uid='test_signal')
624-
625-
def test_pre_user_save_signal(self):
626-
self.called = []
627-
628-
def signal_handler(sender, instance, attributes, user_modified, **kwargs):
629-
self.called.append({'sender': sender, 'instance': instance, 'attributes': attributes, 'user_modified': user_modified})
630-
631-
pre_user_save.connect(signal_handler, dispatch_uid='test_signal')
632-
633-
self.do_login()
634-
635-
# make sure the handler is only called once
636-
self.assertEqual(len(self.called), 1)
637-
# test 'sender', this should be User.__class__
638-
self.assertEqual(self.called[0]['sender'], get_user_model(), 'pre_user_save signal sender is not a User')
639-
# test 'instance', this should be User
640-
self.assertIsInstance(self.called[0]['instance'], get_user_model(), 'pre_user_save signal did not send a User instance')
641-
# test the attributes
642-
self.assertIsInstance(self.called[0]['attributes'], dict, 'pre_user_save signal did not send attributes')
643-
# test the user_modified
644-
self.assertIsInstance(self.called[0]['user_modified'], bool, 'pre_user_save signal did not send a user_modified bool')
645-
646-
pre_user_save.disconnect(dispatch_uid='test_signal')
647-
648601
def test_sigalg_not_passed_when_not_signing_request(self):
649602
# monkey patch SAML configuration
650603
settings.SAML_CONFIG = conf.create_conf(

djangosaml2/views.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
from .conf import get_config
4949
from .exceptions import IdPConfigurationMissing
5050
from .overrides import Saml2Client
51-
from .signals import post_authenticated
5251
from .utils import (available_idps, fail_acs_response, get_custom_setting,
5352
get_idp_sso_supported_bindings, get_location,
5453
validate_referral_url)
@@ -340,14 +339,8 @@ def post(self, request, attribute_mapping=None, create_unknown_user=None):
340339
auth.login(self.request, user)
341340
_set_subject_id(request.saml_session, session_info['name_id'])
342341
logger.debug("User %s authenticated via SSO.", user)
343-
logger.debug('Sending the post_authenticated signal')
344-
345-
# post_authenticated.send_robust(sender=user, session_info=session_info)
346-
# https://github.com/knaperek/djangosaml2/issues/117
347-
post_authenticated.send_robust(sender=user.__class__,
348-
instance=user,
349-
session_info=session_info,
350-
request=request)
342+
343+
self.post_login_hook(request, user, session_info)
351344
self.customize_session(user, session_info)
352345

353346
relay_state = self.build_relay_state()
@@ -358,6 +351,11 @@ def post(self, request, attribute_mapping=None, create_unknown_user=None):
358351
logger.debug('Redirecting to the RelayState: %s', relay_state)
359352
return HttpResponseRedirect(relay_state)
360353

354+
def post_login_hook(self, request: HttpRequest, user: settings.AUTH_USER_MODEL, session_info: dict) -> None:
355+
""" If desired, a hook to add logic after a user has succesfully logged in.
356+
"""
357+
pass
358+
361359
def build_relay_state(self) -> str:
362360
""" The relay state is a URL used to redirect the user to the view where they came from.
363361
"""

0 commit comments

Comments
 (0)