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.
- account/migrations/0023_authsession_method.py +18 -0
- account/models/session.py +3 -2
- account/rpc/auth.py +13 -12
- account/rpc/oauth.py +16 -15
- account/rpc/passkeys.py +1 -1
- {django_restit-4.2.174.dist-info → django_restit-4.2.175.dist-info}/METADATA +1 -1
- {django_restit-4.2.174.dist-info → django_restit-4.2.175.dist-info}/RECORD +11 -10
- rest/__init__.py +1 -1
- rest/serializers/response.py +1 -1
- {django_restit-4.2.174.dist-info → django_restit-4.2.175.dist-info}/LICENSE.md +0 -0
- {django_restit-4.2.174.dist-info → django_restit-4.2.175.dist-info}/WHEEL +0 -0
@@ -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
|
-
|
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"{
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
75
|
-
|
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")
|
@@ -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=
|
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=
|
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=
|
51
|
-
account/rpc/passkeys.py,sha256=
|
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=
|
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=
|
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.
|
520
|
-
django_restit-4.2.
|
521
|
-
django_restit-4.2.
|
522
|
-
django_restit-4.2.
|
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
rest/serializers/response.py
CHANGED
@@ -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",
|
18
|
+
REST_LIST_CACHE_COUNT = settings.get("REST_LIST_CACHE_COUNT", False)
|
19
19
|
|
20
20
|
|
21
21
|
def get_query_hash(queryset):
|
File without changes
|
File without changes
|