dbca-utils 2.0.0__tar.gz → 2.0.1__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.0.0/dbca_utils.egg-info → dbca-utils-2.0.1}/PKG-INFO +1 -1
- dbca-utils-2.0.1/dbca_utils/middleware.py +178 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1/dbca_utils.egg-info}/PKG-INFO +1 -1
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/setup.py +1 -1
- dbca-utils-2.0.0/dbca_utils/middleware.py +0 -89
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/LICENSE +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/MANIFEST.in +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/README.md +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils/__init__.py +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils/models.py +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils/utils.py +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils.egg-info/SOURCES.txt +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils.egg-info/dependency_links.txt +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils.egg-info/not-zip-safe +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils.egg-info/requires.txt +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/dbca_utils.egg-info/top_level.txt +0 -0
- {dbca-utils-2.0.0 → dbca-utils-2.0.1}/setup.cfg +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from django import http, VERSION
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
from django.contrib.auth import login, logout, get_user_model
|
|
4
|
+
from django.utils.deprecation import MiddlewareMixin
|
|
5
|
+
from django.utils.functional import SimpleLazyObject
|
|
6
|
+
from django.contrib.auth.middleware import AuthenticationMiddleware, get_user
|
|
7
|
+
|
|
8
|
+
from dbca_utils.utils import env
|
|
9
|
+
|
|
10
|
+
ENABLE_AUTH2_GROUPS = env("ENABLE_AUTH2_GROUPS", default=False)
|
|
11
|
+
LOCAL_USERGROUPS = env("LOCAL_USERGROUPS", default=[])
|
|
12
|
+
User = get_user_model()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def sync_usergroups(user, groups):
|
|
16
|
+
from django.contrib.auth.models import Group
|
|
17
|
+
|
|
18
|
+
usergroups = (
|
|
19
|
+
[Group.objects.get_or_create(name=name)[0] for name in groups.split(",")] if groups else []
|
|
20
|
+
)
|
|
21
|
+
usergroups.sort(key=lambda o: o.id)
|
|
22
|
+
existing_usergroups = list(user.groups.exclude(name__in=LOCAL_USERGROUPS).order_by("id"))
|
|
23
|
+
index1 = 0
|
|
24
|
+
index2 = 0
|
|
25
|
+
len1 = len(usergroups)
|
|
26
|
+
len2 = len(existing_usergroups)
|
|
27
|
+
|
|
28
|
+
while True:
|
|
29
|
+
group1 = usergroups[index1] if index1 < len1 else None
|
|
30
|
+
group2 = existing_usergroups[index2] if index2 < len2 else None
|
|
31
|
+
if not group1 and not group2:
|
|
32
|
+
break
|
|
33
|
+
if not group1:
|
|
34
|
+
user.groups.remove(group2)
|
|
35
|
+
index2 += 1
|
|
36
|
+
elif not group2:
|
|
37
|
+
user.groups.add(group1)
|
|
38
|
+
index1 += 1
|
|
39
|
+
elif group1.id == group2.id:
|
|
40
|
+
index1 += 1
|
|
41
|
+
index2 += 1
|
|
42
|
+
elif group1.id < group2.id:
|
|
43
|
+
user.groups.add(group1)
|
|
44
|
+
index1 += 1
|
|
45
|
+
else:
|
|
46
|
+
user.groups.remove(group2)
|
|
47
|
+
index2 += 1
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SimpleLazyUser(SimpleLazyObject):
|
|
51
|
+
def __init__(self, func, request, groups):
|
|
52
|
+
super().__init__(func)
|
|
53
|
+
self.request = request
|
|
54
|
+
self.usergroups = groups
|
|
55
|
+
|
|
56
|
+
def __getattr__(self, name):
|
|
57
|
+
if name == "groups":
|
|
58
|
+
sync_usergroups(self._wrapped, self.usergroups)
|
|
59
|
+
self.request.session["usergroups"] = self.usergroups
|
|
60
|
+
|
|
61
|
+
return super().__getattr__(name)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Monkey patch AuthenticationMiddleware to add logic to process user groups.
|
|
65
|
+
if ENABLE_AUTH2_GROUPS:
|
|
66
|
+
original_process_request = AuthenticationMiddleware.process_request
|
|
67
|
+
|
|
68
|
+
def _process_request(self, request):
|
|
69
|
+
if "HTTP_X_GROUPS" in request.META:
|
|
70
|
+
groups = request.META["HTTP_X_GROUPS"] or None
|
|
71
|
+
existing_groups = request.session.get("usergroups")
|
|
72
|
+
if groups != existing_groups:
|
|
73
|
+
# User group is changed.
|
|
74
|
+
request.user = SimpleLazyUser(
|
|
75
|
+
lambda: get_user(request), request, groups
|
|
76
|
+
)
|
|
77
|
+
return
|
|
78
|
+
original_process_request(self, request)
|
|
79
|
+
|
|
80
|
+
AuthenticationMiddleware.process_request = _process_request
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SSOLoginMiddleware(MiddlewareMixin):
|
|
84
|
+
"""Django middleware to process HTTP requests containing headers set by the Auth2
|
|
85
|
+
SSO service, specificially:
|
|
86
|
+
- `HTTP_REMOTE_USER`
|
|
87
|
+
- `HTTP_X_LAST_NAME`
|
|
88
|
+
- `HTTP_X_FIRST_NAME`
|
|
89
|
+
- `HTTP_X_EMAIL`
|
|
90
|
+
The middleware assesses requests containing these headers, and (having deferred user
|
|
91
|
+
authentication to the upstream service), retrieves the local Django User and logs
|
|
92
|
+
the user in automatically.
|
|
93
|
+
If the request path starts with one of the defined logout paths and a `HTTP_X_LOGOUT_URL`
|
|
94
|
+
value is set in the response, log out the user and redirect to that URL instead.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def process_request(self, request):
|
|
98
|
+
|
|
99
|
+
# Logout headers included with request.
|
|
100
|
+
if (
|
|
101
|
+
(
|
|
102
|
+
request.path.startswith("/logout")
|
|
103
|
+
or request.path.startswith("/admin/logout")
|
|
104
|
+
or request.path.startswith("/ledger/logout")
|
|
105
|
+
)
|
|
106
|
+
and "HTTP_X_LOGOUT_URL" in request.META
|
|
107
|
+
and request.META["HTTP_X_LOGOUT_URL"]
|
|
108
|
+
):
|
|
109
|
+
logout(request)
|
|
110
|
+
return http.HttpResponseRedirect(request.META["HTTP_X_LOGOUT_URL"])
|
|
111
|
+
|
|
112
|
+
# Auth2 is not enabled, skip further processing.
|
|
113
|
+
if (
|
|
114
|
+
"HTTP_REMOTE_USER" not in request.META
|
|
115
|
+
or not request.META["HTTP_REMOTE_USER"]
|
|
116
|
+
):
|
|
117
|
+
# auth2 not enabled
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
if VERSION < (2, 0):
|
|
121
|
+
user_authenticated = request.user.is_authenticated()
|
|
122
|
+
else:
|
|
123
|
+
user_authenticated = request.user.is_authenticated
|
|
124
|
+
|
|
125
|
+
# Auth2 is enabled.
|
|
126
|
+
# Request user is not authenticated.
|
|
127
|
+
if not user_authenticated:
|
|
128
|
+
attributemap = {
|
|
129
|
+
"username": "HTTP_REMOTE_USER",
|
|
130
|
+
"last_name": "HTTP_X_LAST_NAME",
|
|
131
|
+
"first_name": "HTTP_X_FIRST_NAME",
|
|
132
|
+
"email": "HTTP_X_EMAIL",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for key, value in attributemap.items():
|
|
136
|
+
if value in request.META:
|
|
137
|
+
attributemap[key] = request.META[value]
|
|
138
|
+
|
|
139
|
+
# Optional setting: projects may define accepted user email domains either as
|
|
140
|
+
# a list of strings, or a single string.
|
|
141
|
+
if (
|
|
142
|
+
hasattr(settings, "ALLOWED_EMAIL_SUFFIXES")
|
|
143
|
+
and settings.ALLOWED_EMAIL_SUFFIXES
|
|
144
|
+
):
|
|
145
|
+
allowed = settings.ALLOWED_EMAIL_SUFFIXES
|
|
146
|
+
if isinstance(settings.ALLOWED_EMAIL_SUFFIXES, str):
|
|
147
|
+
allowed = [settings.ALLOWED_EMAIL_SUFFIXES]
|
|
148
|
+
if not any(
|
|
149
|
+
[attributemap["email"].lower().endswith(x) for x in allowed]
|
|
150
|
+
):
|
|
151
|
+
return http.HttpResponseForbidden()
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
attributemap["email"]
|
|
155
|
+
and User.objects.filter(email__iexact=attributemap["email"]).exists()
|
|
156
|
+
):
|
|
157
|
+
user = User.objects.filter(email__iexact=attributemap["email"])[0]
|
|
158
|
+
elif (
|
|
159
|
+
User.__name__ != "EmailUser"
|
|
160
|
+
and User.objects.filter(username__iexact=attributemap["username"]).exists()
|
|
161
|
+
):
|
|
162
|
+
user = User.objects.filter(username__iexact=attributemap["username"])[0]
|
|
163
|
+
else:
|
|
164
|
+
user = User()
|
|
165
|
+
|
|
166
|
+
# Set the user's details from the supplied information.
|
|
167
|
+
user.__dict__.update(attributemap)
|
|
168
|
+
user.save()
|
|
169
|
+
user.backend = "django.contrib.auth.backends.ModelBackend"
|
|
170
|
+
|
|
171
|
+
# Log the user in.
|
|
172
|
+
login(request, user)
|
|
173
|
+
|
|
174
|
+
# Synchronize the user groups
|
|
175
|
+
if ENABLE_AUTH2_GROUPS and "HTTP_X_GROUPS" in request.META:
|
|
176
|
+
groups = request.META["HTTP_X_GROUPS"] or None
|
|
177
|
+
sync_usergroups(user, groups)
|
|
178
|
+
request.session["usergroups"] = groups
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
from django import http
|
|
2
|
-
from django.conf import settings
|
|
3
|
-
from django.contrib.auth import login, logout, get_user_model
|
|
4
|
-
from django.utils.deprecation import MiddlewareMixin
|
|
5
|
-
|
|
6
|
-
User = get_user_model()
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SSOLoginMiddleware(MiddlewareMixin):
|
|
10
|
-
"""Django middleware to process HTTP requests containing headers set by the Auth2
|
|
11
|
-
SSO service, specificially:
|
|
12
|
-
- `HTTP_REMOTE_USER`
|
|
13
|
-
- `HTTP_X_LAST_NAME`
|
|
14
|
-
- `HTTP_X_FIRST_NAME`
|
|
15
|
-
- `HTTP_X_EMAIL`
|
|
16
|
-
|
|
17
|
-
The middleware assesses requests containing these headers, and (having deferred user
|
|
18
|
-
authentication to the upstream service), retrieves the local Django User and logs
|
|
19
|
-
the user in automatically.
|
|
20
|
-
|
|
21
|
-
If the request path starts with one of the defined logout paths and a `HTTP_X_LOGOUT_URL`
|
|
22
|
-
value is set in the response, log out the user and redirect to that URL instead.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def process_request(self, request):
|
|
26
|
-
|
|
27
|
-
# Logout headers included with request.
|
|
28
|
-
if (
|
|
29
|
-
(
|
|
30
|
-
request.path.startswith("/logout")
|
|
31
|
-
or request.path.startswith("/admin/logout")
|
|
32
|
-
)
|
|
33
|
-
and "HTTP_X_LOGOUT_URL" in request.META
|
|
34
|
-
and request.META["HTTP_X_LOGOUT_URL"]
|
|
35
|
-
):
|
|
36
|
-
logout(request)
|
|
37
|
-
return http.HttpResponseRedirect(request.META["HTTP_X_LOGOUT_URL"])
|
|
38
|
-
|
|
39
|
-
# Auth2 is not enabled, skip further processing.
|
|
40
|
-
if (
|
|
41
|
-
"HTTP_REMOTE_USER" not in request.META
|
|
42
|
-
or not request.META["HTTP_REMOTE_USER"]
|
|
43
|
-
):
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
# Auth2 is enabled.
|
|
47
|
-
# Request user is not authenticated.
|
|
48
|
-
if not request.user.is_authenticated:
|
|
49
|
-
attributemap = {
|
|
50
|
-
"username": "HTTP_REMOTE_USER",
|
|
51
|
-
"last_name": "HTTP_X_LAST_NAME",
|
|
52
|
-
"first_name": "HTTP_X_FIRST_NAME",
|
|
53
|
-
"email": "HTTP_X_EMAIL",
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
for key, value in attributemap.items():
|
|
57
|
-
if value in request.META:
|
|
58
|
-
attributemap[key] = request.META[value]
|
|
59
|
-
|
|
60
|
-
# Optional setting: projects may define accepted user email domains either as
|
|
61
|
-
# a list of strings, or a single string.
|
|
62
|
-
if (
|
|
63
|
-
hasattr(settings, "ALLOWED_EMAIL_SUFFIXES")
|
|
64
|
-
and settings.ALLOWED_EMAIL_SUFFIXES
|
|
65
|
-
):
|
|
66
|
-
allowed = settings.ALLOWED_EMAIL_SUFFIXES
|
|
67
|
-
if isinstance(settings.ALLOWED_EMAIL_SUFFIXES, str):
|
|
68
|
-
allowed = [settings.ALLOWED_EMAIL_SUFFIXES]
|
|
69
|
-
if not any(
|
|
70
|
-
[attributemap["email"].lower().endswith(x) for x in allowed]
|
|
71
|
-
):
|
|
72
|
-
return http.HttpResponseForbidden()
|
|
73
|
-
|
|
74
|
-
# Check if the supplied request user email already exists as a local User.
|
|
75
|
-
if (
|
|
76
|
-
attributemap["email"]
|
|
77
|
-
and User.objects.filter(email__iexact=attributemap["email"]).exists()
|
|
78
|
-
):
|
|
79
|
-
user = User.objects.get(email__iexact=attributemap["email"])
|
|
80
|
-
else:
|
|
81
|
-
user = User()
|
|
82
|
-
|
|
83
|
-
# Set the user's details from the supplied information.
|
|
84
|
-
user.__dict__.update(attributemap)
|
|
85
|
-
user.save()
|
|
86
|
-
user.backend = "django.contrib.auth.backends.ModelBackend"
|
|
87
|
-
|
|
88
|
-
# Log the user in.
|
|
89
|
-
login(request, user)
|
|
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
|