codeforlife-portal 6.41.8__py2.py3-none-any.whl → 6.41.9__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.

@@ -1,17 +1,4 @@
1
- from django.urls import reverse, reverse_lazy
2
-
3
-
4
- def resetEmailPasswordMessage(request, domain, uid, token, protocol):
5
- password_reset_uri = reverse_lazy("password_reset_check_and_confirm", kwargs={"uidb64": uid, "token": token})
6
- url = f"{protocol}://{domain}{password_reset_uri}"
7
- return {
8
- "subject": f"Password reset request",
9
- "message": (
10
- f"You are receiving this email because you requested "
11
- f"a password reset for your Code For Life user account.\n\n"
12
- f"Please go to the following page and choose a new password: {url}"
13
- ),
14
- }
1
+ from django.urls import reverse
15
2
 
16
3
 
17
4
  def userAlreadyRegisteredEmail(request, email, is_independent_student=False):
cfl_common/common/mail.py CHANGED
@@ -7,6 +7,7 @@ from common import app_settings
7
7
  campaign_ids = {
8
8
  "email_change_notification": 1551600,
9
9
  "email_change_verification": 1551594,
10
+ "reset_password": 1557153,
10
11
  "verify_new_user": 1551577,
11
12
  "verify_new_user_first_reminder": 1557170,
12
13
  "verify_new_user_second_reminder": 1557173,
@@ -1,4 +1,3 @@
1
- import re
2
1
  from builtins import str
3
2
 
4
3
 
@@ -35,9 +34,7 @@ def _follow_duplicate_account_email_link(page, email):
35
34
  page.browser.get(message[i:j])
36
35
 
37
36
 
38
- def follow_reset_email_link(browser, email):
39
- message = str(email.body)
40
- link = re.search("http.+/", message).group(0)[:-1]
37
+ def follow_reset_email_link(browser, link):
41
38
  browser.get(link)
42
39
 
43
40
  from portal.tests.pageObjects.portal.password_reset_form_page import (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codeforlife-portal
3
- Version: 6.41.8
3
+ Version: 6.41.9
4
4
  Classifier: Programming Language :: Python
5
5
  Classifier: Programming Language :: Python :: 3.8
6
6
  Classifier: Framework :: Django
@@ -25,7 +25,7 @@ Requires-Dist: sqlparse ==0.4.4
25
25
  Requires-Dist: libsass ==0.22.0
26
26
  Requires-Dist: phonenumbers ==8.12.12
27
27
  Requires-Dist: more-itertools ==8.7.0
28
- Requires-Dist: cfl-common ==6.41.8
28
+ Requires-Dist: cfl-common ==6.41.9
29
29
  Requires-Dist: django-ratelimit ==3.0.1
30
30
  Requires-Dist: django-preventconcurrentlogins ==0.8.2
31
31
  Requires-Dist: django-csp ==3.7
@@ -5,8 +5,8 @@ cfl_common/common/app_settings.py,sha256=x2ROLY5Xl5LgqjxyTiChZvQorZYUXpFzEkaLsjh
5
5
  cfl_common/common/apps.py,sha256=49UXZ3bSkFKvIEOL4zM7y1sAhccQJyRtsoOg5XVd_8Y,129
6
6
  cfl_common/common/context_processors.py,sha256=X0iuX5qu9kMWa7q8osE9CJ2LgM7pPOYQFGdjm8X3rk0,236
7
7
  cfl_common/common/csp_config.py,sha256=sZT6s9zMT5FFIqNODsURT0ifxbDgXpDlki8UxaBq2iE,2940
8
- cfl_common/common/email_messages.py,sha256=res-Uh_0KihJdvXKZg9EbvQ4ohQwS2rlR2wNHcazLoo,5009
9
- cfl_common/common/mail.py,sha256=EsqghHcRbrvCHI-eVcd88PrVVvsz1ZMbUzHrWne3nyE,4192
8
+ cfl_common/common/email_messages.py,sha256=DRiz6MCKUGdFsC-pN9EwFqzPhpzMWXaT9HPcji1BkvE,4437
9
+ cfl_common/common/mail.py,sha256=5iwvedYfaJUv7v8vVpV1kyBtnw04EJhHPy3FRGI9WHM,4223
10
10
  cfl_common/common/models.py,sha256=vnvy8U-sHopyaxgJK9wTxelbKsCnYMjuEu3HIuAEkrs,14974
11
11
  cfl_common/common/permissions.py,sha256=gC6RQGZI2QDBbglx-xr_V4Hl2C2nf1V2_uPmEuoEcJo,2416
12
12
  cfl_common/common/utils.py,sha256=Nn2Npao9Uqad5Js_IdHwF-ow6wrPNpBLW4AO1LxoEBc,1727
@@ -72,7 +72,7 @@ cfl_common/common/tests/test_migration_remove_teacher_title.py,sha256=wwm6tayb75
72
72
  cfl_common/common/tests/test_models.py,sha256=xMdzonW5CADMjas_zfg8V1YPQpUetleyn6TE95hbO9k,3723
73
73
  cfl_common/common/tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  cfl_common/common/tests/utils/classes.py,sha256=ZA2pp9Pyx3rwi0VFwtuUA2Pys9xQJ-L_zE0u2tpwEH4,1094
75
- cfl_common/common/tests/utils/email.py,sha256=lNLl1SoQUnRAI0Z7nuaUS8HQwt9YHVc-YB9rqdYzyrw,2190
75
+ cfl_common/common/tests/utils/email.py,sha256=x3DjWCT997wwj33YuiA3mDqkNahr63zVmz_mX7XIomo,2094
76
76
  cfl_common/common/tests/utils/organisation.py,sha256=vNgKFtU3VPcWRnZfh82yCS90PLAK1XTYJNIxGwfgUI4,966
77
77
  cfl_common/common/tests/utils/student.py,sha256=XlgWT0TdbIY6w9uB4SqOoXmhxxCRnucEcPY9Q5Xva0U,4415
78
78
  cfl_common/common/tests/utils/teacher.py,sha256=kY9LuP1mTEj_andYxF9k54xEHiJ36a6dokHxA9cB9f0,2500
@@ -100,7 +100,7 @@ example_project/portal_test_settings.py,sha256=fkbeU36YhWvNbR_hm28Rq4pV6ln-dGHK1
100
100
  example_project/settings.py,sha256=GLelTfsnhAuf_rqjXUoIWoLtzKvr-9l8UQDQ23rxPQc,5580
101
101
  example_project/urls.py,sha256=OVeRQ-TCpzHISBRuzqD0yd3ewF7H5U3c-f2p2alfUD0,430
102
102
  example_project/wsgi.py,sha256=U1W6WzZxZaIdYZ5tks7w9fqp5WS5qvn2iThsVcskrWw,829
103
- portal/__init__.py,sha256=pyl7O78P7TAJri2pK5Y-iTzCD_xdXYcgFo2SPmfimVk,23
103
+ portal/__init__.py,sha256=nAOTZ1PFPrTXtaItbh3AKoAgKP-NWvT-PzlyaRCQ8gE,23
104
104
  portal/admin.py,sha256=k5Hsiln43DlVPoufnrx5AXWu_RijX8xi_n7wwBuuCJo,5132
105
105
  portal/app_settings.py,sha256=DhWLQOwM0zVOXE3O5TNKbMM9K6agfLuCsHOdr1J7xEI,651
106
106
  portal/backends.py,sha256=2Dss6_WoQwPuDzJUF1yEaTQTNG4eUrD12ujJQ5cp5Tc,812
@@ -118,7 +118,7 @@ portal/forms/error_messages.py,sha256=8d3z_3e2L-5zwj5hFhnUByC5k2CEpIVVuJg2nYkCUQ
118
118
  portal/forms/invite_teacher.py,sha256=jkDNcCfkts4_lXRzhcI3xBam21Zn2yX9wMpMVhDtW1w,880
119
119
  portal/forms/organisation.py,sha256=QcQyd7AiqBmvt4y8uQSQylguUbKOKqo2pjqWIkpWjDg,7433
120
120
  portal/forms/play.py,sha256=IO0gfKfTv7lXEN1K9w0XG8vY-55obBqLpiEBWaUluZ8,11351
121
- portal/forms/registration.py,sha256=xNNu_5FH8UaoX5hULVpT4ml7J1djCvjivXcu7VdCC2E,6245
121
+ portal/forms/registration.py,sha256=gWcY7rllhWO3c9as6QHUDWZx1Jme7DqtGHYaKcvxe-U,5990
122
122
  portal/forms/teach.py,sha256=-3dMQxIQtYq2xg5DgtIJMpN7RajNhTvc56Clr5QjsHo,20440
123
123
  portal/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
124
124
  portal/helpers/captcha.py,sha256=Amwg3HZ9eIh1LGYVYWYruk1ccNj6P3nYP_evufOY8BY,254
@@ -538,16 +538,16 @@ portal/tests/test_class.py,sha256=V6Fkc6PqdisefKD3xs9PbfE2pKp-9e0gwQVkPUiu6bk,14
538
538
  portal/tests/test_daily_activities.py,sha256=-siDCMGBD1ijjccHVk7eEmrk4bgTsvbh0B6hDoj2fo0,1803
539
539
  portal/tests/test_emails.py,sha256=-rI3FlJO7n9qfZ8Vz_Fe3DmjOngr4r23PCpjIoRxNY0,9133
540
540
  portal/tests/test_helper_methods.py,sha256=-SQCDZm2XUtyXGEp0CHIb_SSC9CPD-XOSnpnY8QclHk,890
541
- portal/tests/test_independent_student.py,sha256=Kl2dyZh6-4TdlwWpsi2w1q54SoC_P2YWEvjJXrd6LdY,25857
541
+ portal/tests/test_independent_student.py,sha256=ysWpkYiwjPdB7gO3ow-5JuxqLi0IywRSxG1s3rKzTK0,26282
542
542
  portal/tests/test_invite_teacher.py,sha256=oeOaoJV1IqJSYPlaPFjnhVXdB2mq8otCTLp_lfjuCfk,12224
543
543
  portal/tests/test_middleware.py,sha256=b6jfNmiRZ2snqLKsyJUG-RivoX5fmrqLlQkG9MeVnqM,8034
544
544
  portal/tests/test_newsletter_footer.py,sha256=MdVUX53mEoDTa4Krq-jg9LFNo-QyghqvTvhHeNXBGnE,838
545
545
  portal/tests/test_organisation.py,sha256=fOtck-0MkPM2F0V4RFH-QUeWEk6yUIXDv_GI5cl8sdg,7649
546
546
  portal/tests/test_partials.py,sha256=ydh1nef6BqvMfah2BSBS9QDiKY0xopY74k_W1YVobAE,3687
547
- portal/tests/test_ratelimit.py,sha256=-nlQHOT0Mk_9IKnKYa_t82Y6EL6oZD8uVZ5rof4C1O0,19144
547
+ portal/tests/test_ratelimit.py,sha256=XWq1A9XgRrlcMHibGoJ0kc4gLc5U_u5UhKHjthxCfYA,19376
548
548
  portal/tests/test_school_student.py,sha256=bFZwY4twaFHQLp0cltMq8cLNDZGgCHTZBCZHK0JcV8s,8604
549
549
  portal/tests/test_security.py,sha256=FGrlRfnzi-Xx2_bn4fTZlYORKm7w_GhGkD3havvplwc,3239
550
- portal/tests/test_teacher.py,sha256=mft_xw97AkNi5OGprafpMMdGM1L1J_sMedBBg_Unb9k,36139
550
+ portal/tests/test_teacher.py,sha256=_VmQCWq07uCFbvq6Vd7GN00mE7vY7WNMeQTk6bHxFPI,36898
551
551
  portal/tests/test_teacher_student.py,sha256=NWITbUw1kijqu3c8eRHLHJKaYQMOsOMvl7PAVx5QghI,21567
552
552
  portal/tests/test_views.py,sha256=6y4ICpo5KPTWQed3J1Hg74ZGBKI2y3-HNHAowOsge80,38769
553
553
  portal/tests/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -636,8 +636,8 @@ portal/views/two_factor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
636
636
  portal/views/two_factor/core.py,sha256=O_wcBeFqdPYSGNGv-pT_vbs5-Dj1Z-Jfkd6f9-E5yZI,760
637
637
  portal/views/two_factor/form.py,sha256=lnHNKI-BMlpncTuW3zUzjPaJJNuEra2I_nOam0eOKFY,257
638
638
  portal/views/two_factor/profile.py,sha256=tkl_ludo8arMtd5LKNmohM66vpC_YQiP-0nspTSJiJ4,383
639
- codeforlife_portal-6.41.8.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
640
- codeforlife_portal-6.41.8.dist-info/METADATA,sha256=KJ07q2JnnR-2DEPePhUc1DtBIp_-1aj6lxsj8WgQRbw,1169
641
- codeforlife_portal-6.41.8.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
642
- codeforlife_portal-6.41.8.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
643
- codeforlife_portal-6.41.8.dist-info/RECORD,,
639
+ codeforlife_portal-6.41.9.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
640
+ codeforlife_portal-6.41.9.dist-info/METADATA,sha256=KJR7skjhTgCC6qAImEMgWNl5VRqDo4I4hpSNERjawsY,1169
641
+ codeforlife_portal-6.41.9.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
642
+ codeforlife_portal-6.41.9.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
643
+ codeforlife_portal-6.41.9.dist-info/RECORD,,
portal/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "6.41.8"
1
+ __version__ = "6.41.9"
@@ -1,15 +1,16 @@
1
1
  from captcha.fields import ReCaptchaField
2
2
  from captcha.widgets import ReCaptchaV2Invisible
3
- from common.email_messages import resetEmailPasswordMessage
4
- from common.helpers.emails import NOTIFICATION_EMAIL, send_email
3
+ from common.mail import campaign_ids, send_dotdigital_email
5
4
  from common.models import Student, Teacher
6
5
  from django import forms
7
6
  from django.contrib.auth import forms as django_auth_forms
8
7
  from django.contrib.auth import get_user_model
9
8
  from django.contrib.auth.tokens import default_token_generator
10
9
  from django.contrib.sites.shortcuts import get_current_site
10
+ from django.urls import reverse_lazy
11
11
  from django.utils.encoding import force_bytes
12
12
  from django.utils.http import urlsafe_base64_encode
13
+
13
14
  from portal.helpers.password import PasswordStrength, form_clean_password
14
15
 
15
16
 
@@ -74,30 +75,24 @@ class PasswordResetForm(forms.Form):
74
75
  continue
75
76
  if not domain_override:
76
77
  current_site = get_current_site(request)
77
- site_name = current_site.name
78
78
  domain = current_site.domain
79
79
  else:
80
- site_name = domain = domain_override
81
- context = {
82
- "email": user.email,
83
- "domain": domain,
84
- "site_name": site_name,
85
- "uid": urlsafe_base64_encode(force_bytes(user.pk)),
86
- "user": user,
87
- "token": token_generator.make_token(user),
88
- "protocol": self._compute_protocol(use_https),
89
- }
90
-
91
- email_subject_content = resetEmailPasswordMessage(
92
- request, domain, context["uid"], context["token"], context["protocol"]
80
+ domain = domain_override
81
+
82
+ reset_password_uri = reverse_lazy(
83
+ "password_reset_check_and_confirm",
84
+ kwargs={
85
+ "uidb64": urlsafe_base64_encode(force_bytes(user.pk)),
86
+ "token": token_generator.make_token(user),
87
+ },
93
88
  )
89
+ protocol = self._compute_protocol(use_https)
90
+ reset_password_url = f"{protocol}://{domain}{reset_password_uri}"
94
91
 
95
- send_email(
96
- NOTIFICATION_EMAIL,
92
+ send_dotdigital_email(
93
+ campaign_ids["reset_password"],
97
94
  [user.email],
98
- email_subject_content["subject"],
99
- email_subject_content["message"],
100
- email_subject_content["subject"],
95
+ personalization_values={"RESET_PASSWORD_LINK": reset_password_url},
101
96
  )
102
97
 
103
98
  def _compute_protocol(self, use_https):
@@ -284,7 +284,8 @@ class TestIndependentStudentFrontend(BaseTest):
284
284
 
285
285
  assert self.is_dashboard(page)
286
286
 
287
- def test_reset_password(self):
287
+ @patch("portal.forms.registration.send_dotdigital_email")
288
+ def test_reset_password(self, mock_send_dotdigital_email: Mock):
288
289
  page = self.go_to_homepage()
289
290
 
290
291
  page, name, username, _, _ = create_independent_student(page)
@@ -292,9 +293,13 @@ class TestIndependentStudentFrontend(BaseTest):
292
293
 
293
294
  page.reset_email_submit(username)
294
295
 
295
- self.wait_for_email()
296
+ mock_send_dotdigital_email.assert_called_with(campaign_ids["reset_password"], ANY, personalization_values=ANY)
296
297
 
297
- page = email_utils.follow_reset_email_link(self.selenium, mail.outbox[0])
298
+ reset_password_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"][
299
+ "RESET_PASSWORD_LINK"
300
+ ]
301
+
302
+ page = email_utils.follow_reset_email_link(self.selenium, reset_password_url)
298
303
 
299
304
  new_password = "AnotherPassword12"
300
305
 
@@ -312,14 +317,13 @@ class TestIndependentStudentFrontend(BaseTest):
312
317
  page = page.go_to_account_page()
313
318
  assert page.check_account_details({"name": name})
314
319
 
315
- def test_reset_password_fail(self):
320
+ @patch("portal.forms.registration.send_dotdigital_email")
321
+ def test_reset_password_fail(self, mock_send_dotdigital_email: Mock):
316
322
  page = self.get_to_forgotten_password_page()
317
323
  fake_email = "fake_email@fakeemail.com"
318
324
  page.reset_email_submit(fake_email)
319
325
 
320
- time.sleep(5)
321
-
322
- assert len(mail.outbox) == 0
326
+ mock_send_dotdigital_email.assert_not_called()
323
327
 
324
328
  def test_update_name_success(self):
325
329
  homepage = self.go_to_homepage()
@@ -326,7 +326,8 @@ class TestRatelimit(TestCase):
326
326
 
327
327
  assert get_ratelimit_count_for_user(email) == 1
328
328
 
329
- def test_teacher_reset_password_unblocks_user(self):
329
+ @patch("portal.forms.registration.send_dotdigital_email")
330
+ def test_teacher_reset_password_unblocks_user(self, mock_send_dotdigital_email: Mock):
330
331
  """
331
332
  Given a blocked teacher,
332
333
  When they reset they password,
@@ -344,15 +345,17 @@ class TestRatelimit(TestCase):
344
345
  # Ask for reset password link
345
346
  self._reset_password_request(email)
346
347
 
347
- assert len(mail.outbox) == 1
348
+ mock_send_dotdigital_email.assert_called_once_with(
349
+ campaign_ids["reset_password"], ANY, personalization_values=ANY
350
+ )
348
351
 
349
- # Get reset link from email
350
- message = str(mail.outbox[0].body)
351
- url = re.search("http.+/", message).group(0)
352
+ reset_password_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"][
353
+ "RESET_PASSWORD_LINK"
354
+ ]
352
355
 
353
356
  new_password = "AnotherPassword12!"
354
357
 
355
- self._reset_password(url, new_password)
358
+ self._reset_password(reset_password_url, new_password)
356
359
 
357
360
  login_response = self._teacher_login(email, new_password)
358
361
 
@@ -628,7 +628,8 @@ class TestTeacherFrontend(BaseTest):
628
628
 
629
629
  assert self.is_dashboard_page(page)
630
630
 
631
- def test_reset_password(self):
631
+ @patch("portal.forms.registration.send_dotdigital_email")
632
+ def test_reset_password(self, mock_send_dotdigital_email: Mock):
632
633
  email, _ = signup_teacher_directly()
633
634
  create_organisation_directly(email)
634
635
  _, _, access_code = create_class_directly(email)
@@ -638,9 +639,13 @@ class TestTeacherFrontend(BaseTest):
638
639
 
639
640
  page.reset_email_submit(email)
640
641
 
641
- self.wait_for_email()
642
+ mock_send_dotdigital_email.assert_called_with(campaign_ids["reset_password"], ANY, personalization_values=ANY)
642
643
 
643
- page = email_utils.follow_reset_email_link(self.selenium, mail.outbox[0])
644
+ reset_password_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"][
645
+ "RESET_PASSWORD_LINK"
646
+ ]
647
+
648
+ page = email_utils.follow_reset_email_link(self.selenium, reset_password_url)
644
649
 
645
650
  new_password = "AnotherPassword12!"
646
651
 
@@ -650,7 +655,8 @@ class TestTeacherFrontend(BaseTest):
650
655
  page = HomePage(self.selenium).go_to_teacher_login_page().login(email, new_password)
651
656
  assert self.is_dashboard_page(page)
652
657
 
653
- def test_reset_with_same_password(self):
658
+ @patch("portal.forms.registration.send_dotdigital_email")
659
+ def test_reset_with_same_password(self, mock_send_dotdigital_email: Mock):
654
660
  email, password = signup_teacher_directly()
655
661
  create_organisation_directly(email)
656
662
  _, _, access_code = create_class_directly(email)
@@ -660,23 +666,26 @@ class TestTeacherFrontend(BaseTest):
660
666
 
661
667
  page.reset_email_submit(email)
662
668
 
663
- self.wait_for_email()
669
+ mock_send_dotdigital_email.assert_called_with(campaign_ids["reset_password"], ANY, personalization_values=ANY)
670
+
671
+ reset_password_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"][
672
+ "RESET_PASSWORD_LINK"
673
+ ]
664
674
 
665
- page = email_utils.follow_reset_email_link(self.selenium, mail.outbox[0])
675
+ page = email_utils.follow_reset_email_link(self.selenium, reset_password_url)
666
676
 
667
677
  page.reset_password_fail(password)
668
678
 
669
679
  message = page.browser.find_element(By.CLASS_NAME, "errorlist")
670
680
  assert "Please choose a password that you haven't used before" in message.text
671
681
 
672
- def test_reset_password_fail(self):
682
+ @patch("portal.forms.registration.send_dotdigital_email")
683
+ def test_reset_password_fail(self, mock_send_dotdigital_email: Mock):
673
684
  page = self.get_to_forgotten_password_page()
674
685
  fake_email = "fake_email@fakeemail.com"
675
686
  page.reset_email_submit(fake_email)
676
687
 
677
- time.sleep(5)
678
-
679
- assert len(mail.outbox) == 0
688
+ mock_send_dotdigital_email.assert_not_called()
680
689
 
681
690
  def test_admin_sees_all_school_classes(self):
682
691
  email, password = signup_teacher_directly()