django-restit 4.2.174__py3-none-any.whl → 4.2.175__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.
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.18 on 2025-03-23 19:54
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('account', '0022_alter_memberdevice_modified'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='authsession',
15
+ name='method',
16
+ field=models.CharField(blank=True, db_index=True, default=None, max_length=127, null=True),
17
+ ),
18
+ ]
account/models/session.py CHANGED
@@ -42,6 +42,7 @@ class AuthSession(models.Model, RestModel):
42
42
  signature = models.CharField(max_length=127, db_index=True)
43
43
  ip = models.CharField(max_length=127, null=True, blank=True, db_index=True)
44
44
  buid = models.CharField(max_length=127, null=True, blank=True, default=None)
45
+ method = models.CharField(max_length=127, null=True, blank=True, db_index=True, default=None)
45
46
 
46
47
  member = models.ForeignKey("account.Member", null=True, blank=True, related_name="auth_sessions", on_delete=models.CASCADE)
47
48
  location = models.ForeignKey("location.GeoIP", related_name="auth_sessions", blank=True, null=True, default=None, on_delete=models.CASCADE)
@@ -72,8 +73,9 @@ class AuthSession(models.Model, RestModel):
72
73
  return f"{self.member.username} - {self.ip} - {self.os} - {self.browser}"
73
74
 
74
75
  @classmethod
75
- def NewSession(cls, request):
76
+ def NewSession(cls, request, method="jwt"):
76
77
  obj = cls(ip=request.ip, member=request.member, signature=request.signature)
78
+ obj.method = method
77
79
  obj.user_agent = request.META.get('HTTP_USER_AGENT', None)
78
80
  obj.last_activity = datetime.now()
79
81
  obj.buid = request.POST.get("__buid__", request.GET.get("__buid__", None))
@@ -92,4 +94,3 @@ class AuthSession(models.Model, RestModel):
92
94
  session.touch()
93
95
  return session
94
96
  return None
95
-
account/rpc/auth.py CHANGED
@@ -37,6 +37,7 @@ def member_login(request):
37
37
  @rd.never_cache
38
38
  def jwt_login(request):
39
39
  # poor mans JWT, carried over
40
+ auth_method = "basic"
40
41
  username = request.DATA.get('username', None)
41
42
  if not username:
42
43
  return rv.restPermissionDenied(request, "Password and/or Username is incorrect", error_code=422)
@@ -45,29 +46,31 @@ def jwt_login(request):
45
46
  return rv.restPermissionDenied(request, error=f"Password and/or Username is incorrect for {username}", error_code=422)
46
47
  auth_code = request.DATA.get(["auth_code", "code", "invite_token"], None)
47
48
  if username and auth_code:
49
+ # this is typically used for OAUTH (final)
48
50
  return member_login_uname_code(request, username, auth_code)
49
51
  password = request.DATA.get('password', None)
50
52
  member.canLogin(request) # throws exception if cannot login
51
53
  if member.requires_totp or member.has_totp:
54
+ auth_method = "basic+totp"
52
55
  resp = checkForTOTP(request, member)
53
56
  if resp is not None:
54
57
  return resp
55
58
  if not member.login(request=request, password=password, use_jwt=True):
56
59
  # we do not want permission denied catcher invoked as it is already handled in login method
57
60
  return rv.restStatus(request, False, error=f"Invalid Credentials {username}", error_code=401)
58
- return on_complete_jwt(request, member)
61
+ return on_complete_jwt(request, member, auth_method)
59
62
 
60
63
 
61
- def on_complete_jwt(request, member):
64
+ def on_complete_jwt(request, member, method="basic"):
62
65
  if member.security_token is None or member.security_token == JWT_KEY or member.force_single_session:
63
66
  member.refreshSecurityToken()
64
67
 
65
68
  member.log(
66
- "jwt_login", "jwt login succesful",
69
+ "jwt_login", "jwt login succesful",
67
70
  request, method="login", level=7)
68
71
 
69
72
  device_id = request.DATA.get(["device_id", "deviceID"])
70
-
73
+
71
74
  token = JWToken(
72
75
  user_id=member.pk,
73
76
  key=member.security_token,
@@ -80,13 +83,13 @@ def on_complete_jwt(request, member):
80
83
  request.signature = token.session_id
81
84
  request.device_id = device_id
82
85
  request.buid = request.DATA.get("__buid__", None)
83
- request.auth_session = am.AuthSession.NewSession(request)
86
+ request.auth_session = am.AuthSession.NewSession(request, method)
84
87
  if bool(device_id):
85
88
  am.MemberDevice.register(request, member, device_id)
86
-
89
+
87
90
  request.jwt_token = token.access_token # this tells the middleware to store in cookie
88
91
  return rv.restGet(
89
- request,
92
+ request,
90
93
  dict(
91
94
  access=token.access_token,
92
95
  refresh=token.refresh_token,
@@ -221,9 +224,9 @@ def member_login_uname_code(request, username, auth_code):
221
224
  member.login(request=request)
222
225
  member.save()
223
226
  member.log("code_login", "code login", request, method="login", level=8)
224
- if request.DATA.get("auth_method") == "basic":
227
+ if request.DATA.get("auth_method") == "basic" and ALLOW_BASIC_LOGIN:
225
228
  return rv.restGet(request, dict(id=member.pk, session_key=request.session.session_key))
226
- return on_complete_jwt(request, member)
229
+ return on_complete_jwt(request, member, "auth_code")
227
230
 
228
231
 
229
232
  @rd.url(r'^logout$')
@@ -296,7 +299,7 @@ def get_member_from_request(request):
296
299
  request.member = member
297
300
  member.log("login_blocked", "account is locked out", request, method="login", level=31)
298
301
  return member, rv.restPermissionDenied(request, error=f"{member.username} Account locked out", error_code=411)
299
- return member, None
302
+ return member, None
300
303
 
301
304
 
302
305
  @rd.urlPOST('forgot')
@@ -408,5 +411,3 @@ def totp_verify(request):
408
411
  return rv.restPermissionDenied(request, "invalid code")
409
412
  request.member.setProperty("totp_verified", 1)
410
413
  return rv.restStatus(request, True)
411
-
412
-
account/rpc/oauth.py CHANGED
@@ -20,37 +20,43 @@ def oauth_google_login(request):
20
20
  state = request.DATA.get("state")
21
21
  app_url = settings.DEFAULT_LOGIN_URL
22
22
 
23
- rh.log_print("google/login", request.DATA.toDict(), request.session.get("state"))
23
+ # rh.log_print("google/login", request.DATA.toDict(), request.session.get("state"))
24
24
 
25
25
  if state:
26
+ # this is where we should pull out the passed in state and get the proper URL
26
27
  state = objict.fromJSON(rh.hexToString(state))
27
28
  rh.log_print("state", state)
28
29
  app_url = state.url
29
30
 
30
31
  if not code:
31
32
  params = urlencode({'error': error})
32
- return redirect(f"{app_url}?{params}")
33
+ separator = '&' if '?' in app_url and app_url[-1] != '?' else '?'
34
+ return redirect(f"{app_url}{separator}{params}")
33
35
 
34
- redirect_uri = f"{settings.BASE_URL_SECURE}{REST_PREFIX}account/oauth/google/login"
36
+ redirect_uri = f"{request.scheme}://{request.get_host()}/{REST_PREFIX}account/oauth/google/login"
35
37
  auth_data = google.getAccessToken(code, redirect_uri)
36
38
  if auth_data is None or auth_data.access_token is None:
37
39
  params = urlencode({'error': "failed to get access token from google"})
38
- return redirect(f"{app_url}?{params}")
40
+ separator = '&' if '?' in app_url and app_url[-1] != '?' else '?'
41
+ return redirect(f"{app_url}{separator}{params}")
39
42
 
40
43
  user_data = google.getUserInfo(auth_data.access_token)
41
44
  if user_data is None:
42
45
  params = urlencode({'error': "failed to get user data from google"})
43
- return redirect(f"{app_url}?{params}")
46
+ separator = '&' if '?' in app_url and app_url[-1] != '?' else '?'
47
+ return redirect(f"{app_url}{separator}{params}")
44
48
 
45
49
  if not user_data.email:
46
50
  params = urlencode({'error': "no email with account"})
47
- return redirect(f"{app_url}?{params}")
51
+ separator = '&' if '?' in app_url and app_url[-1] != '?' else '?'
52
+ return redirect(f"{app_url}{separator}{params}")
48
53
 
49
54
  # TODO allow new accounts?
50
55
  member = Member.objects.filter(email=user_data.email).last()
51
56
  if member is None:
52
57
  params = urlencode({'error': "user not found"})
53
- return redirect(f"{app_url}?{params}")
58
+ separator = '&' if '?' in app_url and app_url[-1] != '?' else '?'
59
+ return redirect(f"{app_url}{separator}{params}")
54
60
 
55
61
  member.setProperties(auth_data, category="google_auth")
56
62
  member.setProperties(user_data, category="google")
@@ -69,13 +75,8 @@ def oauth_google_login(request):
69
75
  member.save()
70
76
  member.auditLog("user succesfully authenticated with google", "google_oauth", level=17)
71
77
 
72
- params = urlencode({'oauth_code': member.auth_code, "username":member.username})
78
+ params = urlencode({'oauth_code': member.auth_code, "username":member.username, "auth_method":"google_oauth"})
73
79
  rurl = None
74
- if "?" in app_url:
75
- if app_url[-1] == "?":
76
- rurl = f"{app_url}{params}"
77
- else:
78
- rurl = f"{app_url}&{params}"
79
- else:
80
- rurl = f"{app_url}?{params}"
80
+ separator = '&' if '?' in app_url and app_url[-1] != '?' else '?'
81
+ rurl = f"{app_url}{separator}{params}"
81
82
  return redirect(rurl)
account/rpc/passkeys.py CHANGED
@@ -51,4 +51,4 @@ def rest_on_passkeys_auth_complete(request):
51
51
  request.session.pop("fido2_state"),
52
52
  request.session.pop("fido2_rp_id"))
53
53
  # we now want to handle the JWT or basic login flow
54
- return on_complete_jwt(request, uk.member)
54
+ return on_complete_jwt(request, uk.member, "passkey")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-restit
3
- Version: 4.2.174
3
+ Version: 4.2.175
4
4
  Summary: A Rest Framework for DJANGO
5
5
  License: MIT
6
6
  Author: Ian Starnes
@@ -24,6 +24,7 @@ account/migrations/0019_group_location.py,sha256=EfMB_w4qWUGDqQeNc453PFZwpjpTeoA
24
24
  account/migrations/0020_cloudcredentials_cloudcredentialsmetadata.py,sha256=mHwxkyDfA4ueQOt34w5ndJB4XwNTDLv79CkKgzhlz-c,2250
25
25
  account/migrations/0021_alter_cloudcredentials_group.py,sha256=zoFYmE-hd3uRGX6DRO9k-osPwH0jFeTU7S-pjCOtakk,561
26
26
  account/migrations/0022_alter_memberdevice_modified.py,sha256=9eeKcdr9p6qFJ8ZxSnKSj1KxZjW8NZfM0YCMck6i0QQ,424
27
+ account/migrations/0023_authsession_method.py,sha256=5oIOXyj24rlrLFq7frij5VBaWk_ckh7hRPqq_jpO_b0,452
27
28
  account/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
29
  account/models/__init__.py,sha256=cV_lMnT2vL_mjiYtT4hlcIHo52ocFbGSNVkOIHHLXZY,385
29
30
  account/models/device.py,sha256=8D-Sbv9PZWAnX6UVpp1lNJ03P24fknNnN1VOhqY7RVg,6306
@@ -34,7 +35,7 @@ account/models/member.py,sha256=qmLCOVbNTRr4L-E7BbOMtv4V64QN7K-0pXDgnuB-AbY,5472
34
35
  account/models/membership.py,sha256=90EpAhOsGaqphDAkONP6j_qQ0OWSRaQsI8H7E7fgMkE,9249
35
36
  account/models/notify.py,sha256=YKYEXT56i98b7-ydLt5UuEVOqW7lipQMi-KuiPhcSwY,15627
36
37
  account/models/passkeys.py,sha256=lObapudvL--ABSTZTIELmYvHE3dPF0tO_KmuYk0ZJXc,1699
37
- account/models/session.py,sha256=ELkWjB_2KXQvPtRPrvuGJpJsqrxCQX_4J53SbqGz_2U,3737
38
+ account/models/session.py,sha256=5tpyRF1BHTPBL5gBIx8Eu55sWc6pTziqDpm7Zm5bdJI,3876
38
39
  account/models/settings.py,sha256=gOyRWBVd3BQpjfj_hJPtqX3H46ztyRAFxBrPbv11lQg,2137
39
40
  account/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
41
  account/oauth/google.py,sha256=q5M6Qhpfp9QslKRVYFZBvtG6kgXV6vYMrR5fp6Xdb9I,2078
@@ -42,13 +43,13 @@ account/passkeys/__init__.py,sha256=FwXYJXwSJXfkLojGBcVpF1dFpgFhzDdd9N_3naYQ0cc,
42
43
  account/passkeys/core.py,sha256=4aUBNCuF_kjOvE1zFapK1Pj28ap5slO71dRyfnWi0YU,4148
43
44
  account/periodic.py,sha256=-u0n-7QTJgDOkasGhBAPwHAwjpqWGA-MZLEFkVTqCGU,874
44
45
  account/rpc/__init__.py,sha256=SGF0M_-H0dKh3b1apSX29BotNWAvITYccGQVC0MIjL8,336
45
- account/rpc/auth.py,sha256=O-co_vZM9T4lsu3hidjKAeL2tTPWgbmoLsrjhmFZ7lA,17064
46
+ account/rpc/auth.py,sha256=0LUTb-dO6wk6rWitPVAXeR0IqpbX5Yw8tHM5U9QJibA,17234
46
47
  account/rpc/device.py,sha256=lU2BHNPreHV0dDTjAPc7Sc-5m2JP8SiWVqiKuBfV7Fo,2281
47
48
  account/rpc/group.py,sha256=hw7iczZ6W_IrRbx5ZDw6cZ5I_ztqxhtUFJD9WR91_4s,4948
48
49
  account/rpc/member.py,sha256=8XnJX-iri0Om4nc-V2_tDJzfCSzziKLw6dUx9egtEZE,2236
49
50
  account/rpc/notify.py,sha256=Q2YWejP36egeF060Hih5uX4Psv_B8NWlLLPi7iDYlIw,3344
50
- account/rpc/oauth.py,sha256=ISLVsR5HvKALANokaOFRvF4FTRxWtXPvVnZAYANKxpo,2864
51
- account/rpc/passkeys.py,sha256=5x28nYILJUMMSwfVuWYL66hfoGUXahMqOwiHhM4I3Do,1729
51
+ account/rpc/oauth.py,sha256=1dMvEpoMcvGXEdgDq2OBZcRMnXcE1jD18-itVrZwH9A,3333
52
+ account/rpc/passkeys.py,sha256=AKgF2xgaGJi8UCFgfSMBh7rUqrJgVL8-RfDDRoPWiDM,1740
52
53
  account/rpc/settings.py,sha256=EvPuwW63Gp_Va0ANIPAZ894tnS_JCctQ0FzqYRdKUNM,271
53
54
  account/settings.py,sha256=XEvZdcA6p_iUpDq9NmICK8rxzIQ8NViKfrpyuYgSV4o,53
54
55
  account/templates/email/base.html,sha256=GUuatccaZtO_hLLNZmMQQKew1Bjfz3e6Z7p3dM6BrWk,9669
@@ -379,7 +380,7 @@ pushit/utils.py,sha256=IeTCGa-164nmB1jIsK1lu1O1QzUhS3BKfuXHGjCW-ck,2121
379
380
  rest/.gitignore,sha256=TbEvWRMnAiajCTOdhiNrd9eeCAaIjRp9PRjE_VkMM5g,118
380
381
  rest/README.md,sha256=V3ETc-cJu8PZIbKr9xSe_pA4JEUpC8Dhw4bQeVCDJPw,5460
381
382
  rest/RemoteEvents.py,sha256=nL46U7AuxIrlw2JunphR1tsXyqi-ep_gD9CYGpYbNgE,72
382
- rest/__init__.py,sha256=j76Rqm_TIvJvezmNJkUripR1cpoqTtqZAXhwIQ0t_Uk,122
383
+ rest/__init__.py,sha256=5nuojPskvcFtHA31BnuuT6Z7MtZov2YpOn2IHnzm9cw,122
383
384
  rest/arc4.py,sha256=y644IbF1ec--e4cUJ3KEYsewTCITK0gmlwa5mJruFC0,1967
384
385
  rest/cache.py,sha256=1Qg0rkaCJCaVP0-l5hZg2CIblTdeBSlj_0fP6vlKUpU,83
385
386
  rest/crypto/__init__.py,sha256=Tl0U11rgj1eBYqd6OXJ2_XSdNLumW_JkBZnaJqI6Ldw,72
@@ -429,7 +430,7 @@ rest/serializers/legacy.py,sha256=a5O-x2PqMKX8wYWrhCmdcivVbkPnru7UdyLbrhCaAdY,61
429
430
  rest/serializers/localizers.py,sha256=BegaCvTQVaruhWzvGHq3zWeVFmtBChatquRqAtkke10,410
430
431
  rest/serializers/model.py,sha256=08HJeqpmytjxvyiJFfsSRRG0uH-iK2mXCw6w0oMfWrI,8598
431
432
  rest/serializers/profiler.py,sha256=OxOimhEyvCAuzUBC9Q1dz2xaakjAqmSnekMATsjduXM,997
432
- rest/serializers/response.py,sha256=aSZ9bvhsYGHR26NA6sin7fGqFptjTsBhmIcDKrqeeoY,8656
433
+ rest/serializers/response.py,sha256=fsGgS9mtZSceXzNjMcjEJplisn4bG8qQSSdRGrq5Spo,8657
433
434
  rest/serializers/util.py,sha256=-In89fpuVTd6_Ul8nwEUt3DjVKdpeoEyAxudlyB8K6Y,2734
434
435
  rest/service.py,sha256=jl8obnMDEUzB8y3LROGPvmfKKoFU_SzOvywUQjoQZpg,4046
435
436
  rest/settings_helper.py,sha256=_Vn9nmL5_GPss9zIsXzacbTQkn99NbO42CqvOZC3ge4,1532
@@ -516,7 +517,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
516
517
  ws4redis/settings.py,sha256=KKq00EwoGnz1yLwCZr5Dfoq2izivmAdsNEEM4EhZwN4,1610
517
518
  ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
518
519
  ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
519
- django_restit-4.2.174.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
520
- django_restit-4.2.174.dist-info/METADATA,sha256=r1t8GFE4qKAq75b6CnYkpJCE6BbfW7-t2qt2dy-XkaA,7714
521
- django_restit-4.2.174.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
522
- django_restit-4.2.174.dist-info/RECORD,,
520
+ django_restit-4.2.175.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
521
+ django_restit-4.2.175.dist-info/METADATA,sha256=DuVZg0LP0bkuNNdfm9IlJXwcpPE-O4-V6qSZYRHICTk,7714
522
+ django_restit-4.2.175.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
523
+ django_restit-4.2.175.dist-info/RECORD,,
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.174"
4
+ __version__ = "4.2.175"
@@ -15,7 +15,7 @@ from . import csv
15
15
  from . import excel
16
16
  # from . import profiler
17
17
  STATUS_ON_PERM_DENIED = settings.get("STATUS_ON_PERM_DENIED", 403)
18
- REST_LIST_CACHE_COUNT = settings.get("REST_LIST_CACHE_COUNT", True)
18
+ REST_LIST_CACHE_COUNT = settings.get("REST_LIST_CACHE_COUNT", False)
19
19
 
20
20
 
21
21
  def get_query_hash(queryset):