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 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
- ("metadata.location.line1", "address_line1"),
123
- ("metadata.location.line2", "address_line2"),
124
- ("metadata.location.city", "city"),
125
- ("metadata.location.state", "state"),
126
- ("metadata.location.country", "country"),
127
- ("metadata.location.zip", "zipcode")
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
- if not self.checkPassword(password):
289
- # invalid password
290
- self.recordFailedLogin(request)
291
- return False
292
- else:
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 = str(uuid.uuid1())
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.login(request=request)
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.65
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=my4KT71BlS64d-Ozz9n6d57nH429kFpVdJQZZTlMqhs,21723
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=CxxhNnFCQPEw7MGc5147J8eandYqUj6HhhQ_YRyFQX4,50397
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=X8y1TCXupZZV-GF10nuPVmdpN0qYuzKe03RnaDlepP4,4116
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=eywdJSjsi_Ds2pM4wnltPJUfjIQqf6r6e99eKS7q3BA,15593
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=kLJs7pPu-4NUImnfU-E_eExXqJUChELhQ1dk5RGu0Gs,121
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=O9x4ggr5pQjpjJP1zwV6sv5-2_JNTvU877GLih6ceBY,7737
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=iNzWtGN4vZ-2ltClayCnPD-OPvB_ZGySnKD2EyXl6l8,4329
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.65.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
505
- django_restit-4.2.65.dist-info/METADATA,sha256=XwmVxTmyV_07GGXG6B0dJ9cuFtCO6VUJeFDEPBXFzcQ,7594
506
- django_restit-4.2.65.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
507
- django_restit-4.2.65.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.6.1
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
rest/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  from .uberdict import UberDict # noqa: F401
2
2
  from .settings_helper import settings # noqa: F401
3
3
 
4
- __version__ = "4.2.65"
4
+ __version__ = "4.2.66"
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 inline_css:
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 inline_css:
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
@@ -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
- return WebSocket(environ['wsgi.input'].stream)
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