dbca-utils 2.1.1__tar.gz → 2.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/PKG-INFO +2 -1
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/pyproject.toml +13 -1
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/src/dbca_utils/middleware.py +37 -45
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/LICENSE +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/README.md +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/src/dbca_utils/__init__.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/src/dbca_utils/models.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/src/dbca_utils/utils.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/__init__.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/apps.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/migrations/0001_initial.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/migrations/__init__.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/models.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/settings.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/templates/tests/test_model_list.html +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/tests.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/urls.py +0 -0
- {dbca_utils-2.1.1 → dbca_utils-2.1.2}/tests/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbca-utils
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Utilities for DBCA Django apps
|
|
5
5
|
Author-Email: Rocky Chen <rocky.chen@dbca.wa.gov.au>, Ashley Felton <ashley.felton@dbca.wa.gov.au>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -26,6 +26,7 @@ Project-URL: Changelog, https://github.com/dbca-wa/dbca-utils/blob/master/CHANGE
|
|
|
26
26
|
Project-URL: GitHub, https://github.com/dbca-wa/dbca-utils
|
|
27
27
|
Requires-Python: <4.0,>=3.10
|
|
28
28
|
Requires-Dist: django<6,>=4
|
|
29
|
+
Requires-Dist: markupsafe==3.0.2
|
|
29
30
|
Description-Content-Type: text/markdown
|
|
30
31
|
|
|
31
32
|
# Overview
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dbca-utils"
|
|
3
|
-
version = "2.1.
|
|
3
|
+
version = "2.1.2"
|
|
4
4
|
description = "Utilities for DBCA Django apps"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Rocky Chen", email = "rocky.chen@dbca.wa.gov.au" },
|
|
@@ -29,6 +29,7 @@ classifiers = [
|
|
|
29
29
|
requires-python = ">=3.10,<4.0"
|
|
30
30
|
dependencies = [
|
|
31
31
|
"django>=4,<6",
|
|
32
|
+
"markupsafe==3.0.2",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
[project.urls]
|
|
@@ -58,3 +59,14 @@ package = true
|
|
|
58
59
|
DJANGO_SETTINGS_MODULE = "tests.settings"
|
|
59
60
|
pythonpath = ". src"
|
|
60
61
|
python_files = "tests.py test_*.py"
|
|
62
|
+
|
|
63
|
+
[tool.ruff]
|
|
64
|
+
line-length = 140
|
|
65
|
+
indent-width = 4
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint]
|
|
68
|
+
ignore = [
|
|
69
|
+
"E265",
|
|
70
|
+
"E501",
|
|
71
|
+
"E722",
|
|
72
|
+
]
|
|
@@ -5,6 +5,7 @@ from django.contrib.auth.middleware import AuthenticationMiddleware, get_user
|
|
|
5
5
|
from django.utils import timezone
|
|
6
6
|
from django.utils.deprecation import MiddlewareMixin
|
|
7
7
|
from django.utils.functional import SimpleLazyObject
|
|
8
|
+
from markupsafe import escape
|
|
8
9
|
|
|
9
10
|
from dbca_utils.utils import env
|
|
10
11
|
|
|
@@ -13,18 +14,16 @@ LOCAL_USERGROUPS = env("LOCAL_USERGROUPS", default=[])
|
|
|
13
14
|
User = get_user_model()
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
def sync_usergroups(user, groups):
|
|
17
|
+
def sync_usergroups(user, groups=None):
|
|
17
18
|
from django.contrib.auth.models import Group
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
[Group.objects.get_or_create(name=name)[0] for name in groups.split(",")]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
if groups:
|
|
21
|
+
usergroups = [Group.objects.get_or_create(name=name)[0] for name in groups.split(",")]
|
|
22
|
+
else:
|
|
23
|
+
usergroups = []
|
|
24
|
+
|
|
24
25
|
usergroups.sort(key=lambda o: o.id)
|
|
25
|
-
existing_usergroups = list(
|
|
26
|
-
user.groups.exclude(name__in=LOCAL_USERGROUPS).order_by("id")
|
|
27
|
-
)
|
|
26
|
+
existing_usergroups = list(user.groups.exclude(name__in=LOCAL_USERGROUPS).order_by("id"))
|
|
28
27
|
index1 = 0
|
|
29
28
|
index2 = 0
|
|
30
29
|
len1 = len(usergroups)
|
|
@@ -76,9 +75,7 @@ if ENABLE_AUTH2_GROUPS:
|
|
|
76
75
|
existing_groups = request.session.get("usergroups")
|
|
77
76
|
if groups != existing_groups:
|
|
78
77
|
# User group is changed.
|
|
79
|
-
request.user = SimpleLazyUser(
|
|
80
|
-
lambda: get_user(request), request, groups
|
|
81
|
-
)
|
|
78
|
+
request.user = SimpleLazyUser(lambda: get_user(request), request, groups)
|
|
82
79
|
return
|
|
83
80
|
original_process_request(self, request)
|
|
84
81
|
|
|
@@ -102,11 +99,7 @@ class SSOLoginMiddleware(MiddlewareMixin):
|
|
|
102
99
|
def process_request(self, request):
|
|
103
100
|
# Logout headers included with request.
|
|
104
101
|
if (
|
|
105
|
-
(
|
|
106
|
-
request.path.startswith("/logout")
|
|
107
|
-
or request.path.startswith("/admin/logout")
|
|
108
|
-
or request.path.startswith("/ledger/logout")
|
|
109
|
-
)
|
|
102
|
+
(request.path.startswith("/logout") or request.path.startswith("/admin/logout") or request.path.startswith("/ledger/logout"))
|
|
110
103
|
and "HTTP_X_LOGOUT_URL" in request.META
|
|
111
104
|
and request.META["HTTP_X_LOGOUT_URL"]
|
|
112
105
|
):
|
|
@@ -114,18 +107,20 @@ class SSOLoginMiddleware(MiddlewareMixin):
|
|
|
114
107
|
return http.HttpResponseRedirect(request.META["HTTP_X_LOGOUT_URL"])
|
|
115
108
|
|
|
116
109
|
# Auth2 is not enabled, skip further processing.
|
|
117
|
-
if
|
|
118
|
-
"HTTP_REMOTE_USER" not in request.META
|
|
119
|
-
or not request.META["HTTP_REMOTE_USER"]
|
|
120
|
-
):
|
|
110
|
+
if "HTTP_REMOTE_USER" not in request.META or not request.META["HTTP_REMOTE_USER"]:
|
|
121
111
|
# auth2 not enabled
|
|
122
112
|
return
|
|
123
113
|
|
|
124
|
-
user_authenticated = request.user.is_authenticated
|
|
125
|
-
|
|
126
114
|
# Auth2 is enabled.
|
|
127
|
-
#
|
|
128
|
-
|
|
115
|
+
# Security check: if the logged-in request user's email does not match the email
|
|
116
|
+
# returned from Auth2, invalidate the current request session and force a new session
|
|
117
|
+
# using the returned SSO values.
|
|
118
|
+
if request.user.is_authenticated and request.user.email != request.META["HTTP_X_EMAIL"]:
|
|
119
|
+
logout(request)
|
|
120
|
+
|
|
121
|
+
# Request user is not authenticated locally: obtain user attributes from the request.META dict
|
|
122
|
+
# returned by SSO.
|
|
123
|
+
if not request.user.is_authenticated:
|
|
129
124
|
attributemap = {
|
|
130
125
|
"username": "HTTP_REMOTE_USER",
|
|
131
126
|
"last_name": "HTTP_X_LAST_NAME",
|
|
@@ -137,31 +132,28 @@ class SSOLoginMiddleware(MiddlewareMixin):
|
|
|
137
132
|
if value in request.META:
|
|
138
133
|
attributemap[key] = request.META[value]
|
|
139
134
|
|
|
135
|
+
# Sanitise first_name and last_name values, because end-users have control over these
|
|
136
|
+
# values and could conceivably inject malicious values into them (e.g. a XSS attack).
|
|
137
|
+
if "first_name" in attributemap:
|
|
138
|
+
attributemap["first_name"] = str(escape(attributemap["first_name"]))
|
|
139
|
+
if "last_name" in attributemap:
|
|
140
|
+
attributemap["last_name"] = str(escape(attributemap["last_name"]))
|
|
141
|
+
|
|
140
142
|
# Optional setting: projects may define accepted user email domains either as
|
|
141
143
|
# a list of strings, or a single string.
|
|
142
|
-
if (
|
|
143
|
-
hasattr(settings, "ALLOWED_EMAIL_SUFFIXES")
|
|
144
|
-
and settings.ALLOWED_EMAIL_SUFFIXES
|
|
145
|
-
):
|
|
146
|
-
allowed = settings.ALLOWED_EMAIL_SUFFIXES
|
|
144
|
+
if hasattr(settings, "ALLOWED_EMAIL_SUFFIXES") and settings.ALLOWED_EMAIL_SUFFIXES:
|
|
147
145
|
if isinstance(settings.ALLOWED_EMAIL_SUFFIXES, str):
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
allowed_email_suffixes = list(settings.ALLOWED_EMAIL_SUFFIXES)
|
|
147
|
+
else:
|
|
148
|
+
allowed_email_suffixes = settings.ALLOWED_EMAIL_SUFFIXES
|
|
149
|
+
# If the user email suffix is not in the allowed list, return a 404 response.
|
|
150
|
+
if not any([attributemap["email"].lower().endswith(suffix) for suffix in allowed_email_suffixes]):
|
|
152
151
|
return http.HttpResponseForbidden()
|
|
153
152
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
and User.objects.filter(email__iexact=attributemap["email"]).exists()
|
|
157
|
-
):
|
|
153
|
+
# Check for an existing User instance.
|
|
154
|
+
if attributemap["email"] and User.objects.filter(email__iexact=attributemap["email"]).exists():
|
|
158
155
|
user = User.objects.filter(email__iexact=attributemap["email"])[0]
|
|
159
|
-
elif (
|
|
160
|
-
User.__name__ != "EmailUser"
|
|
161
|
-
and User.objects.filter(
|
|
162
|
-
username__iexact=attributemap["username"]
|
|
163
|
-
).exists()
|
|
164
|
-
):
|
|
156
|
+
elif User.__name__ != "EmailUser" and User.objects.filter(username__iexact=attributemap["username"]).exists():
|
|
165
157
|
user = User.objects.filter(username__iexact=attributemap["username"])[0]
|
|
166
158
|
else:
|
|
167
159
|
user = User(last_login=timezone.localtime())
|
|
@@ -174,7 +166,7 @@ class SSOLoginMiddleware(MiddlewareMixin):
|
|
|
174
166
|
# Log the user in.
|
|
175
167
|
login(request, user)
|
|
176
168
|
|
|
177
|
-
# Synchronize the user groups
|
|
169
|
+
# Synchronize the user groups.
|
|
178
170
|
if ENABLE_AUTH2_GROUPS and "HTTP_X_GROUPS" in request.META:
|
|
179
171
|
groups = request.META["HTTP_X_GROUPS"] or None
|
|
180
172
|
sync_usergroups(user, groups)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|