From 41ce62653088a28377ceeb2549663bd082140bce Mon Sep 17 00:00:00 2001 From: jmish Date: Fri, 4 Oct 2024 08:24:56 -0500 Subject: [PATCH 1/3] su-staff setting: * django-radius will now accept a role=su-staff Class 25 attribute from the RADIUS server to set both is_superuser and is_staff to True for the user in one step. --- README.md | 1 + radiusauth/backends/radius.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f2c56c..001bc37 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,7 @@ For each role (is_staff and is_superuser) and group mapping one RAIDUS Attribute The syntax allows the following mappings: * `role=staff` (sets is_staff=True in the User object) * `role=superuser` (sets is_superuser=True for the User object) +* `role=su-staff` (sets both is_superuser and is_staff te True for the User object) * `group=Group1` (add the User object to `Group1`) To avoid namespace clashes in the RADIUS Attribute 25 values that may be diff --git a/radiusauth/backends/radius.py b/radiusauth/backends/radius.py index eab137c..0772b03 100644 --- a/radiusauth/backends/radius.py +++ b/radiusauth/backends/radius.py @@ -155,8 +155,12 @@ def _perform_radius_auth(self, client, packet): is_staff = True elif role == "superuser": is_superuser = True + elif role == "su-staff": + # su-staff role assignment sets both is_staff and is_superuser to True for the user in one step. + is_staff = True + is_superuser = True else: - logging.warning("RADIUS Attribute Class contains unknown role '%s'. Only roles 'staff' and 'superuser' are allowed" % cl) + logging.warning("RADIUS Attribute Class contains unknown role '%s'. Only roles 'staff', 'superuser' and 'su-staff' are allowed" % cl) return groups, is_staff, is_superuser def _radius_auth(self, server, username, password): From b72f4f72c07c160e74142d0257c7f2e0a1696efa Mon Sep 17 00:00:00 2001 From: jmish Date: Fri, 4 Oct 2024 08:28:26 -0500 Subject: [PATCH 2/3] Fixes #23: * Placed a try/except block in the _perform_radius_auth function that caught an invalid start byte from Windows Systems. --- radiusauth/backends/radius.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/radiusauth/backends/radius.py b/radiusauth/backends/radius.py index eab137c..e77d4c4 100644 --- a/radiusauth/backends/radius.py +++ b/radiusauth/backends/radius.py @@ -146,17 +146,21 @@ def _perform_radius_auth(self, client, packet): role_class_prefix = app_class_prefix + "role=" for cl in reply['Class']: - cl = cl.decode("utf-8") - if cl.lower().find(group_class_prefix) == 0: - groups.append(cl[len(group_class_prefix):]) - elif cl.lower().find(role_class_prefix) == 0: - role = cl[len(role_class_prefix):] - if role == "staff": - is_staff = True - elif role == "superuser": - is_superuser = True - else: - logging.warning("RADIUS Attribute Class contains unknown role '%s'. Only roles 'staff' and 'superuser' are allowed" % cl) + try: + cl = cl.decode("utf-8") + if cl.lower().find(group_class_prefix) == 0: + groups.append(cl[len(group_class_prefix):]) + elif cl.lower().find(role_class_prefix) == 0: + role = cl[len(role_class_prefix):] + if role == "staff": + is_staff = True + elif role == "superuser": + is_superuser = True + else: + logging.warning("RADIUS Attribute Class contains unknown role '%s'. Only roles 'staff' and 'superuser' are allowed" % cl) + except UnicodeDecodeError: + logging.warning("RADIUS Attribute Class contains non-unicode value '%s'. Only unicode values are " + "supported" % cl) return groups, is_staff, is_superuser def _radius_auth(self, server, username, password): From d3eb06804b048b558ec8615bd56c20c1881c1529 Mon Sep 17 00:00:00 2001 From: jmish Date: Fri, 4 Oct 2024 08:31:27 -0500 Subject: [PATCH 3/3] RADIUS_IMPORT_GROUPS setting * This allows you to implicitly set django to be in charge of group assignments, while still keeping remote_roles setting that will still bring over the is_staff and is_superuser flags from RADIUS. --- README.md | 12 ++++++++++++ radiusauth/backends/radius.py | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f2c56c..3d9c98e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,18 @@ to `True`, as django-radius has functioned in earlier versions. RADIUS_REMOTE_ROLES = True ``` +The default behavior is for django-radius to bring groups in from RADIUS +when a user is authenticated. You may overwrite this behavior by setting the +following in settings.py of your Django project: + +```python +RADIUS_IMPORT_GROUPS = False +``` + +This will still import the is_staff, is_superuser flags from RADIUS according +to the role assignment but ignore any group assignments, putting django +in charge of group to user assignment(s). + When a user is successfully authenticated via the RADIUS backend, a `User` object is created in Django's built-in auth application with the same username. This user's password is set to the password which they logged into the RADIUS diff --git a/radiusauth/backends/radius.py b/radiusauth/backends/radius.py index eab137c..462b4d9 100644 --- a/radiusauth/backends/radius.py +++ b/radiusauth/backends/radius.py @@ -191,7 +191,17 @@ def get_django_user(self, username, password=None, groups=[], is_staff=False, is user.set_password(password) user.save() - user.groups.set(groups) + + # If RADIUS_IMPORT_GROUPS is not set, configure it to default value False. + # False means that a user import from RADIUS to Django will NOT overwrite the group + # assignment of the user. + # The default is TRUE to mimic django-radius's current behavior pre Pull Request. + if not hasattr(settings, "RADIUS_IMPORT_GROUPS"): + settings.RADIUS_IMPORT_GROUPS = True + + if settings.RADIUS_IMPORT_GROUPS: + user.groups.set(groups) + return user def get_user_groups(self, group_names):