codeforlife-portal 7.4.8__py2.py3-none-any.whl → 8.0.0__py2.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.

Potentially problematic release.


This version of codeforlife-portal might be problematic. Click here for more details.

cfl_common/setup.py CHANGED
@@ -17,12 +17,17 @@ setup(
17
17
  version=version,
18
18
  include_package_data=True,
19
19
  install_requires=[
20
- "django==3.2.25",
21
- "djangorestframework==3.13.1",
22
- "django-two-factor-auth==1.13.2",
23
- "django-countries==7.3.1",
24
- "pyjwt==2.6.0",
20
+ "django==4.2.16",
21
+ "django-countries==7.6.1",
22
+ "django-csp==3.8",
23
+ "django-import-export==4.2.0",
24
+ "django-pipeline==3.1.0",
25
+ "django-two-factor-auth==1.17.0",
26
+ "djangorestframework==3.15.1",
27
+ "libsass==0.23.0",
28
+ "more-itertools==8.7.0",
25
29
  "pgeocode==0.4.0",
30
+ "pyjwt==2.6.0",
26
31
  ],
27
32
  tests_require=[],
28
33
  test_suite="tests",
@@ -1,32 +1,25 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codeforlife-portal
3
- Version: 7.4.8
3
+ Version: 8.0.0
4
4
  Classifier: Programming Language :: Python
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Framework :: Django
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE.md
9
- Requires-Dist: django-pipeline==2.0.8
10
- Requires-Dist: django-recaptcha==2.0.6
9
+ Requires-Dist: django-recaptcha==4.0.0
11
10
  Requires-Dist: pyyaml==6.0.2
12
11
  Requires-Dist: importlib-metadata==4.13.0
13
- Requires-Dist: rapid-router>=6.3.6
14
12
  Requires-Dist: reportlab==3.6.13
15
- Requires-Dist: django-formtools==2.2
16
- Requires-Dist: django-otp==1.0.2
13
+ Requires-Dist: django-formtools==2.5.1
14
+ Requires-Dist: django-otp==1.5.4
17
15
  Requires-Dist: requests==2.32.2
18
- Requires-Dist: django-treebeard==4.3.1
19
- Requires-Dist: django-sekizai==2.0.0
20
- Requires-Dist: django-classy-tags==2.0.0
21
- Requires-Dist: libsass==0.23.0
16
+ Requires-Dist: django-treebeard==4.7.1
17
+ Requires-Dist: django-sekizai==4.1.0
18
+ Requires-Dist: django-classy-tags==4.1.0
22
19
  Requires-Dist: phonenumbers==8.12.12
23
- Requires-Dist: more-itertools==8.7.0
24
- Requires-Dist: cfl-common==7.4.8
25
20
  Requires-Dist: django-ratelimit==3.0.1
26
21
  Requires-Dist: django-preventconcurrentlogins==0.8.2
27
- Requires-Dist: django-csp==3.7
28
22
  Requires-Dist: setuptools==74.0.0
29
- Requires-Dist: django-import-export
30
23
 
31
24
  # Code for Life Portal
32
25
 
@@ -1,5 +1,5 @@
1
1
  cfl_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- cfl_common/setup.py,sha256=5Rk-FXyWToTujXqGRYqeA0A5nJ4NC5woXxyb6NLLbpo,818
2
+ cfl_common/setup.py,sha256=XLMyrOLiz1PqXQbnMUcYZEmT5dscDfDjlYF_8r7UmxI,978
3
3
  cfl_common/common/__init__.py,sha256=XlncBOpKp_gekbKH7Y_i6yu1qy5tJc3Y8sn8cDy-Vgk,48
4
4
  cfl_common/common/app_settings.py,sha256=Bw1DXkZpNIdwUJ-cIOjZnngH5_NbMXC0koW7NgQ0pKY,2495
5
5
  cfl_common/common/apps.py,sha256=49UXZ3bSkFKvIEOL4zM7y1sAhccQJyRtsoOg5XVd_8Y,129
@@ -93,7 +93,7 @@ deploy/middleware/admin_access.py,sha256=-uF9Wgm7WPmNCEQ2Ti-rBXMrYJb0NnrIfnUdRIX
93
93
  deploy/middleware/basicauth.py,sha256=DiSImGc9mepmsVK1Be5hc2iLnGzsOFx-S2i8AQnXMwY,1170
94
94
  deploy/middleware/exceptionlogging.py,sha256=qE3fJnaCwEslrCpF3k8jK1MeqldnvALPnlrUsiqV-5g,474
95
95
  deploy/middleware/screentime_warning.py,sha256=-YmaOn9_RQwTU8C6VxDLWct_0G3Y5oHjAv9AvKZSvIw,1152
96
- deploy/middleware/security.py,sha256=h9gXlWtLHAE1gARrZxsjJwZEyMXoQs1vZ0FpMwG4W-8,751
96
+ deploy/middleware/security.py,sha256=LOZ-OUFo2dVhbRzI_KdSCMh4TItcJhhqzZg5zNAH-6Q,870
97
97
  deploy/middleware/session_timeout.py,sha256=Vfl6_9d8KwPsWRvCIcpBm7kWjgvHC8I7exJOejbyI4k,1023
98
98
  deploy/static/apple-touch-icon-120x120.png,sha256=7GwelKO6CNbvjJJ1nqMvDGkPoQWqYomMKKP5EB5gmfM,24583
99
99
  deploy/static/apple-touch-icon-152x152.png,sha256=V57OGP0VeeKHen_iYVa72sHe_rEaMAJzU1FAiHWWBwY,36276
@@ -104,19 +104,19 @@ deploy/static/robots.txt,sha256=5cS4RITuQhbpNzvpk4AyDCXdlIBfmfCoBYRvCHY2VT8,24
104
104
  deploy/templates/deploy/csrf_failure.html,sha256=-pBRPn4Y7nUdYHGpTHCokT9Boi-isuwuivF8V2K1SgM,412
105
105
  example_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  example_project/manage.py,sha256=EUgybZlZ7xk2Zf2KCwBbK_z7gf7Ifqs0_bl4Kijhdgo,242
107
- example_project/portal_test_settings.py,sha256=_gI7-HMoPJ-cDGO6n5UlEIHKHuTR5SC_Xt-l5Vdbx-0,7312
108
- example_project/settings.py,sha256=HgGSG0n6Xggd0NieFVoJgn8vKGqyRbCddoB3CRuoT-Y,5640
109
- example_project/urls.py,sha256=3SsP5jvPAXV5xmduka4zbSZB5PbXggjsalu1ojY5KIo,434
107
+ example_project/portal_test_settings.py,sha256=7Q7SdosA0Ba85qz-xkAe3EHyMPpSIQ61CSlwYL4uNog,7321
108
+ example_project/settings.py,sha256=NjFhtDNLwdY5vhUAtqk9a9m3GRfcuWRVqIqnJ3u0o6E,5658
109
+ example_project/urls.py,sha256=FUTzHPlUS1O5kqMHjL5V4L552N2ln7uTDXcw9wjKUto,422
110
110
  example_project/wsgi.py,sha256=U1W6WzZxZaIdYZ5tks7w9fqp5WS5qvn2iThsVcskrWw,829
111
- portal/__init__.py,sha256=eAE4OHpS0-RnpxEDrTUasRa2OlGd49xIz11PQ0QSctc,22
112
- portal/admin.py,sha256=on1-zNRnZvf2cwBN6GVRVYRhkaksrCgfzX8XPWtkvz8,6062
111
+ portal/__init__.py,sha256=SWqJTEDnx2fOon29wQowBCNjEkhyhMbbqVsSu4EpdWI,22
112
+ portal/admin.py,sha256=RKJizTF6dPJKmGPZw7nZUM0X8jkiTjgyKhLQxtvHJ0I,6148
113
113
  portal/app_settings.py,sha256=DhWLQOwM0zVOXE3O5TNKbMM9K6agfLuCsHOdr1J7xEI,651
114
114
  portal/backends.py,sha256=2Dss6_WoQwPuDzJUF1yEaTQTNG4eUrD12ujJQ5cp5Tc,812
115
115
  portal/beta.py,sha256=0TCC-9_KZoM1nuzJ9FiuKR5n9JITdMYenHGQtRvn9UU,255
116
116
  portal/context_processors.py,sha256=Q68UhmArLPRchS2KmfVR4hKrijllXal3sO5cHYWC2Fc,222
117
117
  portal/handlers.py,sha256=gF99OfQrGcIGDnUyONhvylZNU8sl6XHYEurwu0fuiss,422
118
118
  portal/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
119
- portal/urls.py,sha256=4bGQQWiQI9zSG1oceVjQctUnDKggzzZsbYAxuBvijpY,18002
119
+ portal/urls.py,sha256=_EJQ7XNVzeqN6g9pbtKg7DHZHlz31-HgWgft6_kOwXw,18415
120
120
  portal/wsgi.py,sha256=3yRcNxBQG30NhzrVi93bX-DrbXtsIQBc70HiW5wbOyE,401
121
121
  portal/forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
122
  portal/forms/admin.py,sha256=Cdl8-wvasAzvMfgUlFYzQjYeuyC7gIsSiy8V_-jMp7w,2080
@@ -124,13 +124,13 @@ portal/forms/dotmailer.py,sha256=j-o9qthQc7xmOsHSQGEEoHvaEyOpzlWrj6bOnV0PpsI,156
124
124
  portal/forms/error_messages.py,sha256=8d3z_3e2L-5zwj5hFhnUByC5k2CEpIVVuJg2nYkCUQ8,148
125
125
  portal/forms/invite_teacher.py,sha256=jkDNcCfkts4_lXRzhcI3xBam21Zn2yX9wMpMVhDtW1w,880
126
126
  portal/forms/organisation.py,sha256=QcQyd7AiqBmvt4y8uQSQylguUbKOKqo2pjqWIkpWjDg,7433
127
- portal/forms/play.py,sha256=z9P5LzyS3jjYcnfco84d2x8ptgLxRmh94Dnj05plmbY,11505
128
- portal/forms/registration.py,sha256=gWcY7rllhWO3c9as6QHUDWZx1Jme7DqtGHYaKcvxe-U,5990
129
- portal/forms/teach.py,sha256=Fd7zOdwpKTFmxxa3q_Tsj1E9B_M5DZlStF5YXiCA-ys,22688
127
+ portal/forms/play.py,sha256=j_QYAKb4qyW0uy0VUUG49dmzDLINc3jR6ddK-djrbc4,11523
128
+ portal/forms/registration.py,sha256=a-TuzikRc0Q-BAYs3rJ3Sz1wmQ1o_rTEtZmWdF7-xss,6008
129
+ portal/forms/teach.py,sha256=VutOzDz3ATbL08cs09mcyfWDIX4NV1UQEJWs_OYEvUQ,22706
130
130
  portal/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
131
  portal/helpers/captcha.py,sha256=Amwg3HZ9eIh1LGYVYWYruk1ccNj6P3nYP_evufOY8BY,254
132
- portal/helpers/decorators.py,sha256=AnHbcRg42cUYkpMnJMImEMueSKHZRBZ0o67B7_Wp_Zs,3695
133
- portal/helpers/password.py,sha256=SCs-CgBp2y-k7PdEocgNZp03MYzkKZvXsW7Ed68qs_Q,5371
132
+ portal/helpers/decorators.py,sha256=Bs6AVCvcgxfHLdJVWzTmZpxlYbJZXJh5e9AGRXf7cwo,3617
133
+ portal/helpers/password.py,sha256=tvCRwul_70XE7D_RkEbs7Ox7kMZlECzZEhDVn_0yfFw,5546
134
134
  portal/helpers/ratelimit.py,sha256=AyQ31YbhPUIE6JFyc8RARiTzLoMDeCSwKFNIZ7u0MNM,6744
135
135
  portal/helpers/regexes.py,sha256=LlMsbmjGow70QWUh__3PnQWVIce4W9kKSzKnYSGN9AA,813
136
136
  portal/helpers/request_handlers.py,sha256=eRubxSYj17tpwnLqpO90Sk0qt8W0bg3kTmzKsoJItZA,354
@@ -430,8 +430,8 @@ portal/templates/404.html,sha256=Tk6HmuzDdIT4ojoIHvH2MibU_0OekU5gmuJmk5M4K_Q,563
430
430
  portal/templates/500.html,sha256=aqJfDXPzaEw9K6LToPpE4rARuunLSxiIrJ_GnuJr8pI,1703
431
431
  portal/templates/email.html,sha256=MXB64zc1Sd34eIq8mGT1HhrjAiZUNvRx0_XjZllfKUU,268
432
432
  portal/templates/email.txt,sha256=z3bkT2WnkCrVa1RLVbGd-pE81cLEHlZ_QowfbSQAOvg,19
433
- portal/templates/captcha/widget_v2_invisible.html,sha256=wPBAvvjm-qhSCrQruwJ2SSBkxFg4oUNFl26Axs1IjZw,306
434
- portal/templates/captcha/includes/js_v2_invisible.html,sha256=FUyDf1RDyS7whzW2B_TnD1ovS8an9sDavCJFqAsrnmQ,1409
433
+ portal/templates/django_recaptcha/widget_v2_invisible.html,sha256=3t5JHgyr_so5FQEUtbq501kEjz80KYBOoVz3PuU1JOc,318
434
+ portal/templates/django_recaptcha/includes/js_v2_invisible.html,sha256=QQnBzZBqdtPeJeYFOIihJM18YoSUWbsGjuoMy-5gxNM,1408
435
435
  portal/templates/portal/about.html,sha256=_iD0GCP6q3-XuZ2LC-9O0KYY-mKL6c9qk3O-NRRqsRM,10321
436
436
  portal/templates/portal/base.html,sha256=4xLACNgKmRQTdEsdNNGYhLzMozzYzWIIzk31HrLGBf0,12109
437
437
  portal/templates/portal/base_no_userprofile.html,sha256=PlRufyYmUUGWBZ6CvbYhJWOMTqKqdcee4xnO5--AogA,447
@@ -512,11 +512,11 @@ portal/templates/two_factor/_wizard_forms_token.html,sha256=5JwDoRj_X1EM8RBISx6C
512
512
  portal/templates/two_factor/backup_token.html,sha256=5JwDoRj_X1EM8RBISx6Ckb_QZioHknwk_Xi04jUD19U,262
513
513
  portal/templates/two_factor/setup_wizard_token.html,sha256=Z0Et2sknJNLtFYvPnVZssG4zCsADuH77xllPIQb22rg,266
514
514
  portal/templates/two_factor/core/backup_tokens.html,sha256=u0T-LqtZtGRitlT6lwx_-U8DLPMQERRL2uh_rRL77rI,1442
515
- portal/templates/two_factor/core/login.html,sha256=BCM5SSepCC4J1RtuGNt6FJXwR8gwdCKxN5NTIl7dP2U,2723
516
- portal/templates/two_factor/core/setup.html,sha256=k5CIx8Xf9aQNR98c5BTTxOJhS3r1F8VVEBcCe47Zt8s,3145
515
+ portal/templates/two_factor/core/login.html,sha256=Pd9sEvuif-C9W5TcHzUT79v_MCetelotFt-uS3TPMWQ,2198
516
+ portal/templates/two_factor/core/setup.html,sha256=B4lg0htUt9JzmTTGG9jGm_1H04FICoMTRwZ-VyQk2E8,3025
517
517
  portal/templates/two_factor/core/setup_complete.html,sha256=aE67CIwicwn6W8-JwUqf-bSgje_9G_2Nm9XfnXU-Uhk,600
518
518
  portal/templates/two_factor/profile/disable.html,sha256=fKIxqmB8RqN_DQrNMiZPTcSmoH-hChNcGWR0HKl1r-s,1010
519
- portal/templates/two_factor/profile/profile.html,sha256=N5nlY4CV_OBSyQld7nUXBMVZfAsByZ7MRIGRjjfTWkE,1800
519
+ portal/templates/two_factor/profile/profile.html,sha256=31dWbU-IL1ogn4mNvLYaPix4WQwmoP1Ho-UW66jXywI,2333
520
520
  portal/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
521
521
  portal/templatetags/app_tags.py,sha256=SvOc4sPGNYpUtnJYxS80EmWYSE-C2mzmvxgRqUHet_o,3794
522
522
  portal/templatetags/banner_tags.py,sha256=7jk8avxiot0jMM1xWnUbs2QDsOg1v2aJDJA4p0-SJF0,823
@@ -533,14 +533,14 @@ portal/tests/selenium_test_case.py,sha256=eWUF_5SqkI178bkay5SUDa06r0QTIKUUT8jTAh
533
533
  portal/tests/test_2FA.py,sha256=0N4C9Ab3TvO9W__oQLCo-fLDH1Ho3CiGGsSg-2TiZUE,3597
534
534
  portal/tests/test_admin.py,sha256=AM2dgv8j9m4L-SDO-sMA9tQvQH9GwRBrlwRG9OgqtfI,1451
535
535
  portal/tests/test_api.py,sha256=Yo5s_nEGOoG35jA39yZ6nuDOUZvuCZ8o8o8XhZos61w,13819
536
- portal/tests/test_captcha_forms.py,sha256=lirhIli-sHovun8VdrF0he7KRFTAd8DMCpkJ8cQNotg,1015
536
+ portal/tests/test_captcha_forms.py,sha256=Yn_VYO_6jbq6AeKeLcv-YFL1YwXZpU0C3y7SK8fRUm4,1033
537
537
  portal/tests/test_class.py,sha256=43g2HslNosbSuSnnMBzM6VWk4LaV0HXkWS3f8VKVRmw,15930
538
538
  portal/tests/test_emails.py,sha256=pLr06j3uMBxP1raoZQWzUTBVFvsEDFtUh85J8OnqCwE,9238
539
539
  portal/tests/test_global_forms.py,sha256=A5JpAe4AYK-wpu0o1qU4THmeNv_wr7lhzaMbjz5czpY,1543
540
540
  portal/tests/test_helper_methods.py,sha256=-SQCDZm2XUtyXGEp0CHIb_SSC9CPD-XOSnpnY8QclHk,890
541
541
  portal/tests/test_independent_student.py,sha256=NrRjTEr6V4WXpCE74N8LYNVocvLSvddkjuo3dYpfAZc,27245
542
542
  portal/tests/test_invite_teacher.py,sha256=gUe1spFp60v3i6kMqGoNgJd0OlBEcwplPPNYLomTJS4,12269
543
- portal/tests/test_middleware.py,sha256=b6jfNmiRZ2snqLKsyJUG-RivoX5fmrqLlQkG9MeVnqM,8034
543
+ portal/tests/test_middleware.py,sha256=HV7AdgWyvgrtPyDMw3np5YDbUstFpKif7Qqw-e8gs5s,8258
544
544
  portal/tests/test_organisation.py,sha256=kCMUNzLN6EzaMUBcFkqXwnqLGgOuQxQWIHHt63nhqBs,7574
545
545
  portal/tests/test_partials.py,sha256=cSLNLjdsriROjPxkZlM3HKD3CSKDuKgpaDIdL3fPyBs,1794
546
546
  portal/tests/test_ratelimit.py,sha256=XWq1A9XgRrlcMHibGoJ0kc4gLc5U_u5UhKHjthxCfYA,19376
@@ -606,14 +606,14 @@ portal/tests/utils/messages.py,sha256=LeYqCEU-ubEO1lrPSAaXF6WsM8M7C-ZIfVwwlbAFT2
606
606
  portal/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
607
607
  portal/views/about.py,sha256=-muXy17UhxCSKkjnMAkSLXiCvT_pBPlf2ykTYr794dI,443
608
608
  portal/views/admin.py,sha256=4Xt3zEyQH7sUwQSrwuRtoCodWidjOzd7gJUwWU96pXY,957
609
- portal/views/api.py,sha256=lCwiclR98G-yTgK55u8IjkueIH8iremeiZSa3jAvO-M,6990
609
+ portal/views/api.py,sha256=mdWmFkV5mxmR-mvj6ttIe8uwW9tbcx9-5ykk5dpX0LI,7047
610
610
  portal/views/dotmailer.py,sha256=x49p89TtwA1MLysRLtq5yRRzVtIpzGoU__Xb5hPuHak,3208
611
611
  portal/views/email.py,sha256=V3wXRxIjeZ4OJBVqGCQrPn-GQWKZK1PCXbR1f2Zpa_4,2174
612
- portal/views/home.py,sha256=qt5kW7TAaRFXz7BA2BrnHe5oYc69jjoLksEbfr0Aa3c,9715
612
+ portal/views/home.py,sha256=cKCby5xDYImMeJqNnrnfkDbb3tAcQX-lw6IgBS7RmR4,10050
613
613
  portal/views/legal.py,sha256=nUunsTHnhMcXBcDlg1GmUal86k9Vhinne4A2FWfq78M,342
614
614
  portal/views/organisation.py,sha256=sPDbiM7hdtpF8GKyh_4n4VPl2a-WnAgnF4q9aSvQCVI,3341
615
615
  portal/views/play_landing_page.py,sha256=FFmjUFub3ZdlbMqkB8yX3jAImCzqrUqgb8AZcpKywZ4,308
616
- portal/views/registration.py,sha256=L9AzIG2nOU946cSOXmUMQRtDo3uxApHX-0ceXopbOCw,10888
616
+ portal/views/registration.py,sha256=9cDKFVXVyJcMJBVZLwHoucMI4UwsOhbUxMdcxIEOixs,10889
617
617
  portal/views/teach.py,sha256=nzlyTcgq9ImAjnqrF3esqi212qBLH5Ww1LKE2gSjoRY,210
618
618
  portal/views/cron/__init__.py,sha256=5rxXyhJmLOExRdrYZ1VJttTsyRIPRybzdftbUDwFByI,20
619
619
  portal/views/cron/user.py,sha256=iKM_FOcSQvrXtRSZUtxSiMFD3M869JP5jvEfRWHLpvM,10731
@@ -628,11 +628,11 @@ portal/views/teacher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
628
628
  portal/views/teacher/dashboard.py,sha256=pjzOOCz29d4VHukaCI5QhkDsu-RPvy7SXC9dDcPo50k,27422
629
629
  portal/views/teacher/teach.py,sha256=-9zzpNLR2oRkyBFXzXjL0_ew_Qpk27w7uca5rKNeCgE,35988
630
630
  portal/views/two_factor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
631
- portal/views/two_factor/core.py,sha256=O_wcBeFqdPYSGNGv-pT_vbs5-Dj1Z-Jfkd6f9-E5yZI,760
631
+ portal/views/two_factor/core.py,sha256=Lk32z2SN2Pg0rRkK-N-LXMvXC1kKKsH3l692kiSDQ4E,964
632
632
  portal/views/two_factor/form.py,sha256=lnHNKI-BMlpncTuW3zUzjPaJJNuEra2I_nOam0eOKFY,257
633
- portal/views/two_factor/profile.py,sha256=tkl_ludo8arMtd5LKNmohM66vpC_YQiP-0nspTSJiJ4,383
634
- codeforlife_portal-7.4.8.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
635
- codeforlife_portal-7.4.8.dist-info/METADATA,sha256=8LxLcXj1qTb6mYTLNjjb5_bkRFBpSwz0Z2vCVSXPQOs,3317
636
- codeforlife_portal-7.4.8.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
637
- codeforlife_portal-7.4.8.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
638
- codeforlife_portal-7.4.8.dist-info/RECORD,,
633
+ portal/views/two_factor/profile.py,sha256=SHSg_xHccE5PtD-OfuOkYhREYz_er4bj5ro1RjJ88Yw,393
634
+ codeforlife_portal-8.0.0.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
635
+ codeforlife_portal-8.0.0.dist-info/METADATA,sha256=oS_PmOPePrPiHlAe_ABue3QQe_ZslSp3b2AWeRLqPGc,3078
636
+ codeforlife_portal-8.0.0.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
637
+ codeforlife_portal-8.0.0.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
638
+ codeforlife_portal-8.0.0.dist-info/RECORD,,
@@ -4,8 +4,8 @@ from django.middleware.security import SecurityMiddleware
4
4
  class CustomSecurityMiddleware(SecurityMiddleware):
5
5
  """
6
6
  Extends Django's Security Middleware.
7
- See https://docs.djangoproject.com/en/3.2/_modules/django/middleware/security/ for
8
- the source code, as well as https://docs.djangoproject.com/en/3.2/ref/middleware/#module-django.middleware.security
7
+ See https://docs.djangoproject.com/en/4.2/_modules/django/middleware/security/ for
8
+ the source code, as well as https://docs.djangoproject.com/en/4.2/ref/middleware/#module-django.middleware.security
9
9
  for docs on security middleware.
10
10
  """
11
11
 
@@ -13,10 +13,9 @@ class CustomSecurityMiddleware(SecurityMiddleware):
13
13
  """
14
14
  Extends the original security middleware to ensure the X-XSS-Protection header
15
15
  is set to 1.
16
+ https://docs.djangoproject.com/en/5.1/releases/4.0/#securitymiddleware-no-longer-sets-the-x-xss-protection-header
16
17
  """
17
18
  super().process_response(request, response)
18
-
19
- if self.xss_filter:
20
- response["X-XSS-Protection"] = "1"
19
+ response.headers.setdefault("X-XSS-Protection", "1; mode=block")
21
20
 
22
21
  return response
@@ -86,7 +86,7 @@ INSTALLED_APPS = [
86
86
  "game",
87
87
  "pipeline",
88
88
  "portal",
89
- "captcha",
89
+ "django_recaptcha",
90
90
  "common",
91
91
  "django.contrib.admin",
92
92
  "django.contrib.admindocs",
@@ -36,13 +36,13 @@ LOGIN_REDIRECT_URL = "/teach/dashboard/"
36
36
 
37
37
  EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
38
38
 
39
- SILENCED_SYSTEM_CHECKS = ["captcha.recaptcha_test_key_error"]
39
+ SILENCED_SYSTEM_CHECKS = ["django_recaptcha.recaptcha_test_key_error"]
40
40
 
41
41
  INSTALLED_APPS = [
42
42
  "game",
43
43
  "pipeline",
44
44
  "portal",
45
- "captcha",
45
+ "django_recaptcha",
46
46
  "common",
47
47
  "django.contrib.admin",
48
48
  "django.contrib.admindocs",
example_project/urls.py CHANGED
@@ -1,6 +1,5 @@
1
- from django.conf.urls import include, url
2
1
  from django.contrib import admin
3
- from django.urls import path
2
+ from django.urls import include, path, re_path
4
3
  from game import python_den_urls
5
4
  from game import urls as game_urls
6
5
 
@@ -9,8 +8,8 @@ from portal import urls as portal_urls
9
8
  admin.autodiscover()
10
9
 
11
10
  urlpatterns = [
12
- url(r"^", include(portal_urls)),
11
+ re_path(r"^", include(portal_urls)),
13
12
  path("administration/", admin.site.urls),
14
- url(r"^rapidrouter/", include(game_urls)),
15
- url(r"^pythonden/", include(python_den_urls)),
13
+ re_path(r"^rapidrouter/", include(game_urls)),
14
+ re_path(r"^pythonden/", include(python_den_urls)),
16
15
  ]
portal/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "7.4.8"
1
+ __version__ = "8.0.0"
portal/admin.py CHANGED
@@ -12,7 +12,7 @@ from common.models import (
12
12
  UserProfile,
13
13
  )
14
14
  from django.contrib import admin
15
- from django.contrib.auth.admin import UserAdmin
15
+ from django.contrib.auth.admin import UserAdmin as _UserAdmin
16
16
  from django.contrib.auth.models import User
17
17
  from django.http import HttpResponse
18
18
  from import_export.admin import ExportActionMixin
@@ -172,38 +172,40 @@ class TotalActivityAdmin(admin.ModelAdmin, ExportActionMixin):
172
172
  return False
173
173
 
174
174
 
175
- def anonymise_user(user_admin, request, queryset):
176
- for user in queryset:
177
- anonymise(user)
175
+ _UserAdmin.list_display += ("date_joined", "id")
176
+ _UserAdmin.list_filter += ("date_joined",)
178
177
 
179
178
 
180
- def export_as_csv(self, request, queryset):
181
- meta = self.model._meta
182
- field_names = [
183
- field.name for field in meta.fields if field.name != "password"
184
- ]
179
+ class UserAdmin(_UserAdmin):
180
+ actions = ["anonymise_user", "export_as_csv"]
181
+ add_form = AdminUserCreationForm
182
+ change_password_form = AdminChangeUserPasswordForm
185
183
 
186
- response = HttpResponse(content_type="text/csv")
187
- response["Content-Disposition"] = "attachment; filename={}.csv".format(meta)
188
- writer = csv.writer(response)
184
+ def anonymise_user(self, request, queryset):
185
+ for user in queryset:
186
+ anonymise(user)
189
187
 
190
- writer.writerow(field_names)
191
- for obj in queryset:
192
- writer.writerow([getattr(obj, field) for field in field_names])
188
+ anonymise_user.short_description = "Anonymise selected users"
193
189
 
194
- return response
190
+ def export_as_csv(self, request, queryset):
191
+ meta = self.model._meta
192
+ field_names = [
193
+ field.name for field in meta.fields if field.name != "password"
194
+ ]
195
195
 
196
+ response = HttpResponse(content_type="text/csv")
197
+ response["Content-Disposition"] = "attachment; filename={}.csv".format(
198
+ meta
199
+ )
200
+ writer = csv.writer(response)
196
201
 
197
- anonymise_user.short_description = "Anonymise selected users"
198
- export_as_csv.short_description = "Export selected users data as CSV"
202
+ writer.writerow(field_names)
203
+ for obj in queryset:
204
+ writer.writerow([getattr(obj, field) for field in field_names])
199
205
 
206
+ return response
200
207
 
201
- UserAdmin.list_display += ("date_joined", "id")
202
- UserAdmin.list_filter += ("date_joined",)
203
- UserAdmin.add_form = AdminUserCreationForm
204
- UserAdmin.change_password_form = AdminChangeUserPasswordForm
205
- UserAdmin.actions.append(anonymise_user)
206
- UserAdmin.actions.append(export_as_csv)
208
+ export_as_csv.short_description = "Export selected users data as CSV"
207
209
 
208
210
 
209
211
  admin.site.register(Class, ClassAdmin)
portal/forms/play.py CHANGED
@@ -1,8 +1,6 @@
1
1
  import re
2
2
  from datetime import timedelta, date
3
3
 
4
- from captcha.fields import ReCaptchaField
5
- from captcha.widgets import ReCaptchaV2Invisible
6
4
  from common.helpers.emails import send_verification_email
7
5
  from common.models import Class, Student, stripStudentName
8
6
  from common.permissions import logged_in_as_independent_student
@@ -10,6 +8,8 @@ from django import forms
10
8
  from django.contrib.auth import authenticate
11
9
  from django.contrib.auth.forms import AuthenticationForm
12
10
  from django.utils import timezone
11
+ from django_recaptcha.fields import ReCaptchaField
12
+ from django_recaptcha.widgets import ReCaptchaV2Invisible
13
13
 
14
14
  from portal.forms.error_messages import INVALID_LOGIN_MESSAGE
15
15
  from portal.helpers.password import PasswordStrength, form_clean_password
@@ -1,5 +1,3 @@
1
- from captcha.fields import ReCaptchaField
2
- from captcha.widgets import ReCaptchaV2Invisible
3
1
  from common.mail import campaign_ids, send_dotdigital_email
4
2
  from common.models import Student, Teacher
5
3
  from django import forms
@@ -10,6 +8,8 @@ from django.contrib.sites.shortcuts import get_current_site
10
8
  from django.urls import reverse_lazy
11
9
  from django.utils.encoding import force_bytes
12
10
  from django.utils.http import urlsafe_base64_encode
11
+ from django_recaptcha.fields import ReCaptchaField
12
+ from django_recaptcha.widgets import ReCaptchaV2Invisible
13
13
 
14
14
  from portal.helpers.password import PasswordStrength, form_clean_password
15
15
 
portal/forms/teach.py CHANGED
@@ -2,14 +2,14 @@ import itertools
2
2
  import re
3
3
  from builtins import map, range, str
4
4
 
5
- from captcha.fields import ReCaptchaField
6
- from captcha.widgets import ReCaptchaV2Invisible
7
5
  from common.helpers.emails import send_verification_email
8
6
  from common.models import Student, stripStudentName, UserSession, Teacher
9
7
  from django import forms
10
8
  from django.contrib.auth import authenticate
11
9
  from django.contrib.auth.forms import AuthenticationForm
12
10
  from django.contrib.auth.models import User
11
+ from django_recaptcha.fields import ReCaptchaField
12
+ from django_recaptcha.widgets import ReCaptchaV2Invisible
13
13
  from game.models import Episode
14
14
 
15
15
  from portal.forms.error_messages import INVALID_LOGIN_MESSAGE
@@ -1,28 +1,24 @@
1
1
  from __future__ import absolute_import
2
2
 
3
3
  import datetime
4
- import pytz
5
- import re
6
-
4
+ from functools import wraps
7
5
 
6
+ import pytz
8
7
  from common.models import Teacher, Student
9
8
  from django.contrib.auth import logout
10
9
  from django.shortcuts import render
11
- from functools import wraps
12
10
  from ratelimit import ALL, UNSAFE
13
11
 
14
12
  from portal.helpers.ratelimit import is_ratelimited
15
- from portal.templatetags.app_tags import is_logged_in
16
- from portal.views.registration import blocked_and_not_expired
17
- from portal.views.login import has_user_lockout_expired
18
- from portal.helpers.ratelimit import get_ratelimit_count_for_user
19
-
20
13
  from portal.helpers.request_handlers import get_access_code_from_request
14
+ from portal.templatetags.app_tags import is_logged_in
21
15
 
22
16
  __all__ = ["ratelimit"]
23
17
 
24
18
 
25
- def ratelimit(group=None, key=None, rate=None, method=ALL, block=False, is_teacher=True):
19
+ def ratelimit(
20
+ group=None, key=None, rate=None, method=ALL, block=False, is_teacher=True
21
+ ):
26
22
  """
27
23
  Ratelimit decorator, adding custom functionality to django-ratelimit's default
28
24
  decorator. On block, the user is logged out, redirected to the "locked out" page,
@@ -63,25 +59,29 @@ def ratelimit(group=None, key=None, rate=None, method=ALL, block=False, is_teach
63
59
 
64
60
  access_code = get_access_code_from_request(request)
65
61
  model_finder = model.objects.get
66
- # look for school student
67
- # if access code not found (AttributeError)
68
- # if student not found (IndexError)
69
- # move on to another try block
70
- # similar logic followed afterwards
62
+ # look for school student if access code not found
63
+ # (AttributeError) if student not found (IndexError) move
64
+ # on to another try block similar logic followed afterwards
71
65
  if access_code:
72
66
  user_to_lockout = model_finder(
73
67
  new_user__first_name=username,
74
68
  class_field__access_code=access_code, # extract the found text from regex
75
69
  )
76
- lockout_template = "portal/locked_out_school_student.html"
70
+ lockout_template = (
71
+ "portal/locked_out_school_student.html"
72
+ )
77
73
  # look for indy student or teacher
78
74
  else:
79
- user_to_lockout = model_finder(new_user__username=username)
75
+ user_to_lockout = model_finder(
76
+ new_user__username=username
77
+ )
80
78
  else:
81
79
  user_to_lockout = model.objects.get(new_user=request.user)
82
80
 
83
81
  if user_to_lockout:
84
- user_to_lockout.blocked_time = datetime.datetime.now(tz=pytz.utc)
82
+ user_to_lockout.blocked_time = datetime.datetime.now(
83
+ tz=pytz.utc
84
+ )
85
85
  user_to_lockout.save()
86
86
 
87
87
  if is_logged_in(request.user):
@@ -1,16 +1,14 @@
1
+ import hashlib
1
2
  import re
2
3
  from enum import Enum, auto
3
4
 
5
+ import requests
4
6
  from django import forms
5
7
  from django.contrib.auth import update_session_auth_hash
6
8
  from django.contrib.auth.hashers import PBKDF2PasswordHasher as ph
7
9
  from django.core.exceptions import ValidationError
8
10
 
9
11
 
10
- import hashlib
11
- import requests
12
-
13
-
14
12
  def is_password_pwned(password):
15
13
  # Create SHA1 hash of the password
16
14
  sha1_hash = hashlib.sha1(password.encode()).hexdigest()
@@ -41,56 +39,56 @@ class PasswordStrength(Enum):
41
39
  TEACHER = auto()
42
40
 
43
41
  def password_test(self, password):
44
- if self is PasswordStrength.STUDENT:
45
- minimum_password_length = 6
46
- # Make student password case insensitive
47
- password = password.lower()
48
- if password and not password_strength_test(
49
- password=password,
50
- minimum_password_length=minimum_password_length,
51
- upper=False,
52
- lower=False,
53
- numbers=False,
54
- special_char=False,
55
- ):
56
- raise forms.ValidationError(
57
- f"Password not strong enough, consider using at least {minimum_password_length} characters and making it hard to guess."
58
- )
59
- if is_password_pwned(password):
60
- raise forms.ValidationError("Password is too common, consider using a different password.")
61
-
62
- elif self is PasswordStrength.INDEPENDENT:
63
- minimum_password_length = 8
64
- if password and not password_strength_test(
65
- password=password,
66
- minimum_password_length=minimum_password_length,
67
- upper=True,
68
- lower=True,
69
- numbers=True,
70
- special_char=False,
71
- ):
72
- raise forms.ValidationError(
73
- f"Password not strong enough, consider using at least {minimum_password_length} characters, "
74
- "upper and lower case letters, and numbers and making it hard to guess."
75
- )
76
- if is_password_pwned(password):
77
- raise forms.ValidationError("Password is too common, consider using a different password.")
78
- else:
79
- minimum_password_length = 10
80
- if password and not password_strength_test(
81
- password=password,
82
- minimum_password_length=minimum_password_length,
83
- upper=True,
84
- lower=True,
85
- numbers=True,
86
- special_char=True,
87
- ):
88
- raise forms.ValidationError(
89
- f"Password not strong enough, consider using at least {minimum_password_length} characters, "
90
- "upper and lower case letters, numbers, special characters and making it hard to guess."
91
- )
92
- if is_password_pwned(password):
93
- raise forms.ValidationError("Password is too common, consider using a different password.")
42
+ if password:
43
+ if self is PasswordStrength.STUDENT:
44
+ minimum_password_length = 6
45
+ # Make student password case insensitive
46
+ password = password.lower()
47
+ if not password_strength_test(
48
+ password=password,
49
+ minimum_password_length=minimum_password_length,
50
+ upper=False,
51
+ lower=False,
52
+ numbers=False,
53
+ special_char=False,
54
+ ):
55
+ raise forms.ValidationError(
56
+ f"Password not strong enough, consider using at least {minimum_password_length} characters and making it hard to guess."
57
+ )
58
+ if is_password_pwned(password):
59
+ raise forms.ValidationError("Password is too common, consider using a different password.")
60
+ elif self is PasswordStrength.INDEPENDENT:
61
+ minimum_password_length = 8
62
+ if not password_strength_test(
63
+ password=password,
64
+ minimum_password_length=minimum_password_length,
65
+ upper=True,
66
+ lower=True,
67
+ numbers=True,
68
+ special_char=False,
69
+ ):
70
+ raise forms.ValidationError(
71
+ f"Password not strong enough, consider using at least {minimum_password_length} characters, "
72
+ "upper and lower case letters, and numbers and making it hard to guess."
73
+ )
74
+ if is_password_pwned(password):
75
+ raise forms.ValidationError("Password is too common, consider using a different password.")
76
+ else:
77
+ minimum_password_length = 10
78
+ if not password_strength_test(
79
+ password=password,
80
+ minimum_password_length=minimum_password_length,
81
+ upper=True,
82
+ lower=True,
83
+ numbers=True,
84
+ special_char=True,
85
+ ):
86
+ raise forms.ValidationError(
87
+ f"Password not strong enough, consider using at least {minimum_password_length} characters, "
88
+ "upper and lower case letters, numbers, special characters and making it hard to guess."
89
+ )
90
+ if is_password_pwned(password):
91
+ raise forms.ValidationError("Password is too common, consider using a different password.")
94
92
 
95
93
  return password
96
94
 
@@ -1,8 +1,8 @@
1
- {# This is adapted from django-recaptcha 2.0.5 to work on multiple reCAPTCHA's on the same page #}
2
- {# See verifyCaptcha_{{ widget_uuid}} for the edit #}
1
+ <!-- This is adapted from django-recaptcha 4.0.0 to work on multiple
2
+ reCAPTCHA's on the same page
3
+ See verifyCaptcha_{{ widget_uuid}} for the edit #} -->
3
4
  <script src="https://{{ recaptcha_domain }}/recaptcha/api.js{% if api_params %}?{{ api_params }}{% endif %}"></script>
4
5
  <script type="text/javascript">
5
-
6
6
  // Submit function to be called, after reCAPTCHA was successful.
7
7
  var onSubmit_{{ widget_uuid }} = function(token) {
8
8
  console.log("reCAPTCHA validated for 'data-widget-uuid=\"{{ widget_uuid }}\"'. Submitting form...")
@@ -1,5 +1,5 @@
1
- {# copied from django-recaptcha 2.0.5 #}
2
- {% include "captcha/includes/js_v2_invisible.html" %}
1
+ <!-- copied from django-recaptcha 4.0.0 -->
2
+ {% include "django_recaptcha/includes/js_v2_invisible.html" %}
3
3
  <div class="g-recaptcha"
4
4
  {% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}
5
5
  >