Skip to content

Commit e57f537

Browse files
committed
Allow no attributes
1 parent 31e9254 commit e57f537

File tree

3 files changed

+20
-15
lines changed

3 files changed

+20
-15
lines changed

CHANGES

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changes
22
=======
33

4+
0.X.X (to-be-released)
5+
-------------------
6+
- Allow a SSO request without any attributes besides the NameID info. Backwards-incompatible changes to allow easier behaviour differentiation, two methods now receive the idp identifier (+ **kwargs were added to introduce possible similar changes in the future with less breaking effect):
7+
- Method signature changed on Saml2Backend.clean_attributes: from `clean_attributes(self, attributes: dict)` to `clean_attributes(self, attributes: dict, idp_entityid: str, **kwargs)`
8+
- Methodignature changed on Saml2Backend.is_authorized: from `is_authorized(self, attributes: dict, attribute_mapping: dict)` to `is_authorized(self, attributes: dict, attribute_mapping: dict, idp_entityid: str, **kwargs)`
9+
410
0.30.0 (2020-07-30)
511
-------------------
612
- SameSite workaround with a specialized cookie, decoupled from django default

djangosaml2/backends.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,13 @@ def authenticate(self, request, session_info=None, attribute_mapping=None, creat
110110
logger.error('"ava" key not found in session_info')
111111
return None
112112

113-
attributes = self.clean_attributes(session_info['ava'])
114-
if not attributes:
115-
logger.error('The (cleaned) attributes dictionary is empty')
116-
return None
113+
idp_entityid = session_info['issuer']
114+
115+
attributes = self.clean_attributes(session_info['ava'], idp_entityid)
117116

118117
logger.debug('attributes: %s', attributes)
119118

120-
if not self.is_authorized(attributes, attribute_mapping):
119+
if not self.is_authorized(attributes, attribute_mapping, idp_entityid):
121120
logger.error('Request not authorized')
122121
return None
123122

@@ -128,7 +127,7 @@ def authenticate(self, request, session_info=None, attribute_mapping=None, creat
128127

129128
user, created = self.get_or_create_user(
130129
user_lookup_key, user_lookup_value, create_unknown_user,
131-
idp_entityid=session_info['issuer'], attributes=attributes, attribute_mapping=attribute_mapping, request=request
130+
idp_entityid=idp_entityid, attributes=attributes, attribute_mapping=attribute_mapping, request=request
132131
)
133132

134133
# Update user with new attributes from incoming request
@@ -184,11 +183,11 @@ def _update_user(self, user, attributes, attribute_mapping, force_save=False):
184183
# Hooks to override by end-users in subclasses
185184
# ############################################
186185

187-
def clean_attributes(self, attributes: dict) -> dict:
186+
def clean_attributes(self, attributes: dict, idp_entityid: str, **kwargs) -> dict:
188187
""" Hook to clean or filter attributes from the SAML response. No-op by default. """
189188
return attributes
190189

191-
def is_authorized(self, attributes: dict, attribute_mapping: dict) -> bool:
190+
def is_authorized(self, attributes: dict, attribute_mapping: dict, idp_entityid: str, **kwargs) -> bool:
192191
""" Hook to allow custom authorization policies based on SAML attributes. True by default. """
193192
return True
194193

tests/testprofiles/tests.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ def test_user_lookup_attribute_default(self):
8989
self.assertEqual(self.backend._user_lookup_attribute, 'username')
9090

9191
def test_is_authorized(self):
92-
self.assertTrue(self.backend.is_authorized({}, {}))
92+
self.assertTrue(self.backend.is_authorized({}, {}, ''))
9393

9494
def test_clean_attributes(self):
9595
attributes = {'random': 'dummy', 'value': 123}
96-
self.assertEqual(self.backend.clean_attributes(attributes), attributes)
96+
self.assertEqual(self.backend.clean_attributes(attributes, ''), attributes)
9797

9898
def test_clean_user_main_attribute(self):
9999
self.assertEqual(self.backend.clean_user_main_attribute('value'), 'value')
@@ -273,11 +273,11 @@ def test_get_or_create_user_create(self):
273273
class CustomizedBackend(Saml2Backend):
274274
""" Override the available methods with some customized implementation to test customization
275275
"""
276-
def is_authorized(self, attributes, attribute_mapping):
276+
def is_authorized(self, attributes, attribute_mapping, idp_entityid: str, **kwargs):
277277
''' Allow only staff users from the IDP '''
278278
return attributes.get('is_staff', (None, ))[0] == True
279279

280-
def clean_attributes(self, attributes: dict) -> dict:
280+
def clean_attributes(self, attributes: dict, idp_entityid: str, **kwargs) -> dict:
281281
''' Keep only age attribute '''
282282
return {
283283
'age': attributes.get('age', (None, )),
@@ -306,13 +306,13 @@ def test_is_authorized(self):
306306
'cn': ('John', ),
307307
'sn': ('Doe', ),
308308
}
309-
self.assertFalse(self.backend.is_authorized(attributes, attribute_mapping))
309+
self.assertFalse(self.backend.is_authorized(attributes, attribute_mapping, ''))
310310
attributes['is_staff'] = (True, )
311-
self.assertTrue(self.backend.is_authorized(attributes, attribute_mapping))
311+
self.assertTrue(self.backend.is_authorized(attributes, attribute_mapping, ''))
312312

313313
def test_clean_attributes(self):
314314
attributes = {'random': 'dummy', 'value': 123, 'age': '28'}
315-
self.assertEqual(self.backend.clean_attributes(attributes), {'age': '28', 'is_staff': (None,), 'uid': (None,)})
315+
self.assertEqual(self.backend.clean_attributes(attributes, ''), {'age': '28', 'is_staff': (None,), 'uid': (None,)})
316316

317317
def test_clean_user_main_attribute(self):
318318
self.assertEqual(self.backend.clean_user_main_attribute('va--l__ u -e'), 'va__l___u__e')

0 commit comments

Comments
 (0)