django-restit 4.2.65__py3-none-any.whl → 4.2.68__py3-none-any.whl
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.
- account/models/group.py +12 -6
- account/models/member.py +20 -9
- account/passkeys/core.py +1 -1
- account/rpc/auth.py +3 -1
- {django_restit-4.2.65.dist-info → django_restit-4.2.68.dist-info}/METADATA +2 -1
- {django_restit-4.2.65.dist-info → django_restit-4.2.68.dist-info}/RECORD +11 -11
- {django_restit-4.2.65.dist-info → django_restit-4.2.68.dist-info}/WHEEL +1 -1
- rest/__init__.py +1 -1
- rest/mail.py +11 -2
- ws4redis/servers/django.py +59 -2
- {django_restit-4.2.65.dist-info → django_restit-4.2.68.dist-info}/LICENSE.md +0 -0
account/models/group.py
CHANGED
@@ -119,12 +119,16 @@ class Group(models.Model, RestModel, MetaDataModel):
|
|
119
119
|
("metadata__timezone", "timezone"),
|
120
120
|
("metadata__eod", "end_of_day"),
|
121
121
|
("parent__name", "parent"),
|
122
|
-
("
|
123
|
-
("
|
124
|
-
("
|
125
|
-
("
|
126
|
-
("
|
127
|
-
("
|
122
|
+
("location.line1", "address_line1"),
|
123
|
+
("location.line2", "address_line2"),
|
124
|
+
("location.city", "city"),
|
125
|
+
("location.state", "state"),
|
126
|
+
("location.county", "county"),
|
127
|
+
("location.country", "country"),
|
128
|
+
("location.postalcode", "zipcode"),
|
129
|
+
("location.postalcode_suffix", "zipcode_ext"),
|
130
|
+
("location.lat", "lat"),
|
131
|
+
("location.lng", "lng")
|
128
132
|
]
|
129
133
|
}
|
130
134
|
|
@@ -229,6 +233,7 @@ class Group(models.Model, RestModel, MetaDataModel):
|
|
229
233
|
q = q | Q(group__parent=self)
|
230
234
|
if great_grand_children:
|
231
235
|
q = q | Q(group__parent__parent=self)
|
236
|
+
q = q | Q(group__parent__parent__parent=self)
|
232
237
|
if hasattr(Model, "objects"):
|
233
238
|
return Model.objects.filter(q)
|
234
239
|
return Model.filter(q)
|
@@ -244,6 +249,7 @@ class Group(models.Model, RestModel, MetaDataModel):
|
|
244
249
|
q = q | Q(parent__parent=self)
|
245
250
|
if great_grand_children:
|
246
251
|
q = q | Q(parent__parent__parent=self)
|
252
|
+
q = q | Q(group__parent__parent__parent=self)
|
247
253
|
return Group.objects.filter(q)
|
248
254
|
|
249
255
|
def getAllChildrenIds(self, include_me=False, grand_children=False, great_grand_children=False, depth=0):
|
account/models/member.py
CHANGED
@@ -284,13 +284,12 @@ class Member(User, RestModel, MetaDataModel):
|
|
284
284
|
# can force login
|
285
285
|
if not request:
|
286
286
|
request = rh.getActiveRequest()
|
287
|
-
if password:
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
self.recordSuccessLogin(request)
|
287
|
+
if not self.checkPassword(password):
|
288
|
+
# invalid password
|
289
|
+
self.recordFailedLogin(request)
|
290
|
+
return False
|
291
|
+
else:
|
292
|
+
self.recordSuccessLogin(request)
|
294
293
|
if use_jwt:
|
295
294
|
self.recordSuccessLogin(request)
|
296
295
|
self.locateByIP(request.ip)
|
@@ -300,6 +299,17 @@ class Member(User, RestModel, MetaDataModel):
|
|
300
299
|
self.locateByIP(request.ip)
|
301
300
|
return True
|
302
301
|
|
302
|
+
def loginNoPassword(self, request=None):
|
303
|
+
if not self.is_active or self.is_blocked:
|
304
|
+
return False
|
305
|
+
# can force login
|
306
|
+
if not request:
|
307
|
+
request = rh.getActiveRequest()
|
308
|
+
self.user_ptr.backend = 'django.contrib.auth.backends.ModelBackend'
|
309
|
+
auth_login(request, self.user_ptr)
|
310
|
+
self.locateByIP(request.ip)
|
311
|
+
return True
|
312
|
+
|
303
313
|
def canLogin(self, request=None, throw_exception=True):
|
304
314
|
if not self.is_active:
|
305
315
|
self.log("login_blocked", F"account is not active {self.username}", request, method="login", level=31)
|
@@ -1213,17 +1223,18 @@ class AuthToken(models.Model, RestModel):
|
|
1213
1223
|
signature = models.CharField(max_length=128, null=True, default=None, blank=True, db_index=True)
|
1214
1224
|
|
1215
1225
|
def generateToken(self, commit=True):
|
1216
|
-
self.token =
|
1226
|
+
self.token = None
|
1217
1227
|
self.updateSignature()
|
1218
1228
|
if commit:
|
1219
1229
|
self.save()
|
1220
1230
|
|
1221
1231
|
def updateSignature(self):
|
1232
|
+
if not self.token:
|
1233
|
+
self.token = uuid.uuid4().hex
|
1222
1234
|
self.signature = crypto.hashSHA256(self.token)
|
1223
1235
|
|
1224
1236
|
def on_rest_pre_save(self, request, **kwargs):
|
1225
1237
|
if not self.token:
|
1226
|
-
self.token = uuid.uuid4().hex
|
1227
1238
|
self.updateSignature()
|
1228
1239
|
if not self.member_id:
|
1229
1240
|
self.member = request.member
|
account/passkeys/core.py
CHANGED
@@ -115,7 +115,7 @@ def authComplete(request, fido2_state, rp_id):
|
|
115
115
|
response=credential)
|
116
116
|
request.member = upk.member
|
117
117
|
request.member.canLogin(request) # throws exception if cannot login
|
118
|
-
request.member.
|
118
|
+
request.member.loginNoPassword(request=request)
|
119
119
|
request.member.log(
|
120
120
|
"passkey_login", "passkey login succesful",
|
121
121
|
request, method="login", level=7)
|
account/rpc/auth.py
CHANGED
@@ -37,6 +37,9 @@ def jwt_login(request):
|
|
37
37
|
member = getMemberByUsername(username)
|
38
38
|
if not member:
|
39
39
|
return rv.restPermissionDenied(request, error=f"Password and/or Username is incorrect for {username}", error_code=422)
|
40
|
+
auth_code = request.DATA.get(["auth_code", "code", "invite_token"], None)
|
41
|
+
if username and auth_code:
|
42
|
+
return member_login_uname_code(request, username, auth_code)
|
40
43
|
password = request.DATA.get('password', None)
|
41
44
|
member.canLogin(request) # throws exception if cannot login
|
42
45
|
if member.requires_totp or member.has_totp:
|
@@ -205,7 +208,6 @@ def member_login_uname_code(request, username, auth_code):
|
|
205
208
|
member.log("code_login", "code login", request, method="login", level=8)
|
206
209
|
if request.DATA.get("auth_method") == "basic":
|
207
210
|
return rv.restGet(request, dict(id=member.pk, session_key=request.session.session_key))
|
208
|
-
|
209
211
|
return on_complete_jwt(request, member)
|
210
212
|
|
211
213
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: django-restit
|
3
|
-
Version: 4.2.
|
3
|
+
Version: 4.2.68
|
4
4
|
Summary: A Rest Framework for DJANGO
|
5
5
|
License: MIT
|
6
6
|
Author: Ian Starnes
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.9
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
16
|
Requires-Dist: boto3 (>=1.26.160,<2.0.0)
|
16
17
|
Requires-Dist: django
|
17
18
|
Requires-Dist: django-redis-cache (>=3.0.1,<4.0.0)
|
@@ -26,9 +26,9 @@ account/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
26
26
|
account/models/__init__.py,sha256=cV_lMnT2vL_mjiYtT4hlcIHo52ocFbGSNVkOIHHLXZY,385
|
27
27
|
account/models/device.py,sha256=MVWZEYrX_4zJaqmJS_feFplfVXYyqBJ-0_Fm3B1SVg8,5226
|
28
28
|
account/models/feeds.py,sha256=vI7fG4ASY1M0Zjke24RdnfDcuWeATl_yR_25jPmT64g,2011
|
29
|
-
account/models/group.py,sha256=
|
29
|
+
account/models/group.py,sha256=fgWWYS69Ll5a1jH7syFQKkEGEU54cO-rn6HTbAvPbRY,21984
|
30
30
|
account/models/legacy.py,sha256=zYdtv4LC0ooxPVqWM-uToPwV-lYWQLorSE6p6yn1xDw,2720
|
31
|
-
account/models/member.py,sha256=
|
31
|
+
account/models/member.py,sha256=Pp4Np75L8TzVBC1N3g7Vnt5zy4cMBy7W0OG7mZ5Cfqg,50756
|
32
32
|
account/models/membership.py,sha256=GJ6bSFLfU1CN9466k0XjSwn1sQIEwFeC8-oUYd2MrSs,9217
|
33
33
|
account/models/notify.py,sha256=YnZujSHJHY7B09e6FIyZIEJRWLPYk1Sk1e92tFzB1IA,12078
|
34
34
|
account/models/passkeys.py,sha256=TJxITUi4DT4_1tW2K7ZlOcRjJuMVl2NtKz7pKQU8-Tw,1516
|
@@ -37,10 +37,10 @@ account/models/settings.py,sha256=gOyRWBVd3BQpjfj_hJPtqX3H46ztyRAFxBrPbv11lQg,21
|
|
37
37
|
account/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
38
|
account/oauth/google.py,sha256=q5M6Qhpfp9QslKRVYFZBvtG6kgXV6vYMrR5fp6Xdb9I,2078
|
39
39
|
account/passkeys/__init__.py,sha256=FwXYJXwSJXfkLojGBcVpF1dFpgFhzDdd9N_3naYQ0cc,89
|
40
|
-
account/passkeys/core.py,sha256=
|
40
|
+
account/passkeys/core.py,sha256=xj-vXjSrfWDvc5MYtEmXzwaMkNHl-cXrQKVrN9soRCg,4126
|
41
41
|
account/periodic.py,sha256=-u0n-7QTJgDOkasGhBAPwHAwjpqWGA-MZLEFkVTqCGU,874
|
42
42
|
account/rpc/__init__.py,sha256=SGF0M_-H0dKh3b1apSX29BotNWAvITYccGQVC0MIjL8,336
|
43
|
-
account/rpc/auth.py,sha256=
|
43
|
+
account/rpc/auth.py,sha256=Vrg8j_bb5jeIahIMlEm86LTKBi-pCKBagpTC4EcN8i0,15770
|
44
44
|
account/rpc/device.py,sha256=fbbZFp3cUdhVXvD7gVFOqFWj4hKS3bjZKD_aF5fQxd8,2852
|
45
45
|
account/rpc/group.py,sha256=FD9GymgPY68y-gtDLsZxYVdwQJeLGpqcP4hjcDUh-GM,4022
|
46
46
|
account/rpc/member.py,sha256=PU-Uz5KUI_BZFy-F-taDqAfnt_AwONYXSzUvfm7eyTw,1264
|
@@ -368,7 +368,7 @@ pushit/utils.py,sha256=IeTCGa-164nmB1jIsK1lu1O1QzUhS3BKfuXHGjCW-ck,2121
|
|
368
368
|
rest/.gitignore,sha256=TbEvWRMnAiajCTOdhiNrd9eeCAaIjRp9PRjE_VkMM5g,118
|
369
369
|
rest/README.md,sha256=V3ETc-cJu8PZIbKr9xSe_pA4JEUpC8Dhw4bQeVCDJPw,5460
|
370
370
|
rest/RemoteEvents.py,sha256=nL46U7AuxIrlw2JunphR1tsXyqi-ep_gD9CYGpYbNgE,72
|
371
|
-
rest/__init__.py,sha256=
|
371
|
+
rest/__init__.py,sha256=yUGGhaiZHnz8BlV-outvcNP7abr5Samxuo6VesvF84Y,121
|
372
372
|
rest/arc4.py,sha256=y644IbF1ec--e4cUJ3KEYsewTCITK0gmlwa5mJruFC0,1967
|
373
373
|
rest/cache.py,sha256=1Qg0rkaCJCaVP0-l5hZg2CIblTdeBSlj_0fP6vlKUpU,83
|
374
374
|
rest/crypto/__init__.py,sha256=Tl0U11rgj1eBYqd6OXJ2_XSdNLumW_JkBZnaJqI6Ldw,72
|
@@ -387,7 +387,7 @@ rest/helpers.py,sha256=Af1EXjgf8JYMrP76rkgmB0SsEGES6iF9eam3jipcu48,28213
|
|
387
387
|
rest/joke.py,sha256=0PpKaX2iN7jlS62kgjfmmqkFBYLPURz15aQ8R7OJkJ8,260
|
388
388
|
rest/jwtoken.py,sha256=2BjRrnQSzm7ydHgYl6LIjfGW1YPmqjt-gDIo21O0WTk,2388
|
389
389
|
rest/log.py,sha256=hd1_4HBOS395sfXJIL6BTw9yekm1SLgBwYx_PdfIhKA,20930
|
390
|
-
rest/mail.py,sha256=
|
390
|
+
rest/mail.py,sha256=Rm40hWDYop0tMqxdN-J2NT-dCnP-f4SfCZxSO02ajzs,7965
|
391
391
|
rest/mailman.py,sha256=v5O1G5s3HiAKmz-J1z0uT6_q3xsONPpxVl9saEyQQ2I,9174
|
392
392
|
rest/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
393
393
|
rest/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -496,12 +496,12 @@ ws4redis/exceptions.py,sha256=EGLoRTdqJVwz900pwhciqPuSjBBd08hhLgFu6umHrI4,636
|
|
496
496
|
ws4redis/redis.py,sha256=IfT4p3bUtlqso9rryNliH9Ebzlx8-Q2VJcs1kFioeGA,6093
|
497
497
|
ws4redis/servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
498
498
|
ws4redis/servers/base.py,sha256=3nYZF5jSsQxNLbnLtKLFJ82xJs_Mc7N1H2kEOx8wT6o,3747
|
499
|
-
ws4redis/servers/django.py,sha256=
|
499
|
+
ws4redis/servers/django.py,sha256=EF8sHw4--hes7nbswT8M0R6PobtjEvPOb_M_tDrEoR0,5898
|
500
500
|
ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,1723
|
501
501
|
ws4redis/settings.py,sha256=K0yBiLUuY81iDM4Yr-k8hbvjn5VVHu5zQhmMK8Dtz0s,1536
|
502
502
|
ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
|
503
503
|
ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
|
504
|
-
django_restit-4.2.
|
505
|
-
django_restit-4.2.
|
506
|
-
django_restit-4.2.
|
507
|
-
django_restit-4.2.
|
504
|
+
django_restit-4.2.68.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
|
505
|
+
django_restit-4.2.68.dist-info/METADATA,sha256=E9U9owcQp_3cq2b5YoLRfJDJji83qeVzc70nOCFUvbo,7645
|
506
|
+
django_restit-4.2.68.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
507
|
+
django_restit-4.2.68.dist-info/RECORD,,
|
rest/__init__.py
CHANGED
rest/mail.py
CHANGED
@@ -15,6 +15,11 @@ try:
|
|
15
15
|
except Exception:
|
16
16
|
inline_css = None
|
17
17
|
|
18
|
+
try:
|
19
|
+
from premailer import transform as premailer
|
20
|
+
except Exception:
|
21
|
+
premailer = None
|
22
|
+
|
18
23
|
from django.template.loader import render_to_string
|
19
24
|
from django.template import TemplateDoesNotExist
|
20
25
|
|
@@ -75,7 +80,9 @@ def renderBody(body, template=None, context=None):
|
|
75
80
|
if template[-4:] not in ["html", ".txt"]:
|
76
81
|
template += ".html"
|
77
82
|
body = render_to_string(template, context)
|
78
|
-
if
|
83
|
+
if premailer:
|
84
|
+
body = premailer(body)
|
85
|
+
elif inline_css:
|
79
86
|
body = inline_css(body)
|
80
87
|
return body
|
81
88
|
|
@@ -203,7 +210,9 @@ def render_to_mail(name, context):
|
|
203
210
|
|
204
211
|
try:
|
205
212
|
html_content = render_to_string(name + ".html", context)
|
206
|
-
if
|
213
|
+
if premailer:
|
214
|
+
html_content = premailer(html_content)
|
215
|
+
elif inline_css:
|
207
216
|
html_content = inline_css(html_content)
|
208
217
|
except TemplateDoesNotExist:
|
209
218
|
html_content = None
|
ws4redis/servers/django.py
CHANGED
@@ -4,6 +4,7 @@ import base64
|
|
4
4
|
import select
|
5
5
|
from hashlib import sha1
|
6
6
|
from wsgiref import util
|
7
|
+
from django.core.handlers import wsgi as django_wsgi
|
7
8
|
from django.core.wsgi import get_wsgi_application
|
8
9
|
from django.core.servers.basehttp import WSGIServer, WSGIRequestHandler, ServerHandler
|
9
10
|
|
@@ -14,12 +15,61 @@ from django.utils.encoding import force_str
|
|
14
15
|
from ws4redis.websocket import WebSocket
|
15
16
|
from ws4redis.servers.base import WebsocketServerBase, HandshakeError, UpgradeRequiredError
|
16
17
|
|
18
|
+
from io import IOBase
|
19
|
+
|
20
|
+
|
17
21
|
from rest.log import getLogger
|
18
22
|
logger = getLogger("async", filename="async.log")
|
19
23
|
|
20
24
|
util._hoppish = {}.__contains__
|
21
25
|
|
22
26
|
|
27
|
+
class LimitedStreamPatched(IOBase):
|
28
|
+
"""
|
29
|
+
Wrap another stream to disallow reading it past a number of bytes.
|
30
|
+
|
31
|
+
Based on the implementation from werkzeug.wsgi.LimitedStream
|
32
|
+
See https://github.com/pallets/werkzeug/blob/dbf78f67/src/werkzeug/wsgi.py#L828
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, stream, limit):
|
36
|
+
self.stream = stream
|
37
|
+
self._read = stream.read
|
38
|
+
self._readline = stream.readline
|
39
|
+
self._pos = 0
|
40
|
+
self.limit = limit
|
41
|
+
|
42
|
+
def read(self, size=-1, /):
|
43
|
+
_pos = self._pos
|
44
|
+
limit = self.limit
|
45
|
+
if _pos >= limit:
|
46
|
+
return b""
|
47
|
+
if size == -1 or size is None:
|
48
|
+
size = limit - _pos
|
49
|
+
else:
|
50
|
+
size = min(size, limit - _pos)
|
51
|
+
data = self._read(size)
|
52
|
+
self._pos += len(data)
|
53
|
+
return data
|
54
|
+
|
55
|
+
def readline(self, size=-1, /):
|
56
|
+
_pos = self._pos
|
57
|
+
limit = self.limit
|
58
|
+
if _pos >= limit:
|
59
|
+
return b""
|
60
|
+
if size == -1 or size is None:
|
61
|
+
size = limit - _pos
|
62
|
+
else:
|
63
|
+
size = min(size, limit - _pos)
|
64
|
+
line = self._readline(size)
|
65
|
+
self._pos += len(line)
|
66
|
+
return line
|
67
|
+
|
68
|
+
|
69
|
+
def patchLimitedStream():
|
70
|
+
django_wsgi.LimitedStream = LimitedStreamPatched
|
71
|
+
|
72
|
+
|
23
73
|
class WSGIRequestHandlerRunServer(WSGIRequestHandler):
|
24
74
|
def handle(self):
|
25
75
|
self.raw_requestline = self.rfile.readline(65537)
|
@@ -81,13 +131,17 @@ class WebsocketRunServer(WebsocketServerBase):
|
|
81
131
|
logger.debug('WebSocket request accepted, switching protocols')
|
82
132
|
start_response(force_str('101 Switching Protocols'), headers)
|
83
133
|
six.get_method_self(start_response).finish_content()
|
84
|
-
|
134
|
+
winput = environ['wsgi.input']
|
135
|
+
if not hasattr(winput, "stream"):
|
136
|
+
# hack to get the stream back in later DJANGO
|
137
|
+
winput.stream = winput._read.__self__
|
138
|
+
return WebSocket(winput.stream)
|
85
139
|
|
86
140
|
def select(self, rlist, wlist, xlist, timeout=None):
|
87
141
|
return select.select(rlist, wlist, xlist, timeout)
|
88
142
|
|
89
143
|
|
90
|
-
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=None):
|
144
|
+
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=None, **kwargs):
|
91
145
|
"""
|
92
146
|
Function to monkey patch the internal Django command: manage.py runserver
|
93
147
|
"""
|
@@ -101,6 +155,8 @@ def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=None):
|
|
101
155
|
httpd.serve_forever()
|
102
156
|
|
103
157
|
|
158
|
+
patchLimitedStream()
|
159
|
+
|
104
160
|
runserver.run = run
|
105
161
|
|
106
162
|
_django_app = get_wsgi_application()
|
@@ -110,6 +166,7 @@ _websocket_url = getattr(settings, 'WEBSOCKET_URL')
|
|
110
166
|
|
111
167
|
def application(environ, start_response):
|
112
168
|
if _websocket_url and environ.get('PATH_INFO').startswith(_websocket_url):
|
169
|
+
logger.info("environ", environ)
|
113
170
|
return _websocket_app(environ, start_response)
|
114
171
|
return _django_app(environ, start_response)
|
115
172
|
|
File without changes
|