Skip to content

Commit 8b3fc95

Browse files
committed
massive refactor
1 parent 154812d commit 8b3fc95

File tree

5 files changed

+120
-22
lines changed

5 files changed

+120
-22
lines changed

verify_email/custom_types.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
from typing import TypeVar
22

3-
from django.contrib.auth import get_user_model
4-
5-
User = TypeVar("User", bound=get_user_model())
3+
User = TypeVar("User")

verify_email/tests.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,76 @@
1-
from django.test import TestCase
1+
# verify_email_tests/test_verify_email.py
2+
import time
23

3-
# Create your tests here.
4+
from django.test import TestCase, override_settings
5+
from django.urls import reverse
6+
from django.contrib.auth import get_user_model
7+
from verify_email.email_handler import ActivationMailManager
8+
from verify_email.token_manager import TokenManager, SafeURL, ActivationLinkManager
9+
from django.core import mail
10+
from django.conf import settings
11+
from verify_email.app_configurations import GetFieldFromSettings
12+
13+
14+
User = get_user_model()
15+
16+
17+
class VerifyEmailTests(TestCase):
18+
def setUp(self):
19+
self.user = User.objects.create_user(
20+
username='testuser',
21+
22+
password='testpass'
23+
)
24+
self.user.is_active = False
25+
self.user.save()
26+
27+
def test_send_verification_email(self):
28+
"""Test that verification email is sent."""
29+
response = ActivationMailManager.send_verification_link(self.user)
30+
self.assertIsNotNone(response)
31+
self.assertEquals(len(mail.outbox), 1)
32+
33+
def test_verification_view_by_token_and_email(self):
34+
"""Test the email verification view."""
35+
user_token = TokenManager().generate_token_for_user(self.user)
36+
user_email = SafeURL.perform_encoding(self.user.email)
37+
response = self.client.get(reverse('verify-email', args=[user_email, user_token]))
38+
self.assertEqual(response.status_code, 200)
39+
40+
def test_verification_link(self):
41+
user_token = TokenManager().generate_token_for_user(self.user)
42+
user_email = self.user.email
43+
link = ActivationLinkManager.generate_link(user_token, user_email)
44+
45+
full_url = f"http://testserver{link}"
46+
47+
resp = self.client.get(full_url)
48+
49+
self.assertEquals(resp.status_code, 200)
50+
51+
def test_process(self):
52+
user_token = TokenManager().generate_token_for_user(self.user)
53+
time.sleep(2)
54+
55+
link = ActivationLinkManager.generate_link(user_token, self.user.email)
56+
full_url = f"http://testserver{link}"
57+
resp = self.client.get(full_url)
58+
self.assertEquals(resp.status_code, 200)
59+
60+
def test_timestamp_invalid_link(self):
61+
user_token = TokenManager(max_age='2s').generate_token_for_user(self.user)
62+
time.sleep(3)
63+
64+
link = ActivationLinkManager(max_age='2s').generate_link(user_token, self.user.email)
65+
full_url = f"http://testserver{link}"
66+
resp = self.client.get(full_url)
67+
self.assertEquals(resp.status_code, 401)
68+
69+
def test_timestamp_valid_link(self):
70+
user_token = TokenManager().generate_token_for_user(self.user)
71+
time.sleep(3)
72+
73+
link = ActivationLinkManager.generate_link(user_token, self.user.email)
74+
full_url = f"http://testserver{link}"
75+
resp = self.client.get(full_url)
76+
self.assertEquals(resp.status_code, 200)

verify_email/token_manager.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
logger = logging.getLogger(__name__)
2626

2727

28-
@dataclass(frozen=True)
28+
@dataclass
2929
class GeneralConfig:
3030
settings: GetFieldFromSettings = GetFieldFromSettings()
31-
max_age: int = settings.get("max_age", raise_exception=False)
31+
max_age: str = settings.get("max_age", raise_exception=False)
3232
max_retries: int = settings.get("max_retries") + 1
3333
time_units: List = field(default_factory=lambda: ["s", "m", "h", "d"])
3434

@@ -47,7 +47,7 @@ def perform_decoding(encoded_entity):
4747
return False
4848

4949

50-
@dataclass(frozen=True)
50+
@dataclass
5151
class ActivationLinkManager(GeneralConfig):
5252

5353
@staticmethod
@@ -131,8 +131,8 @@ def request_new_link(self, request, inactive_user, token, user_email):
131131
)
132132

133133

134-
@dataclass(frozen=True)
135-
class TokenManager(GeneralConfig, signing.TimestampSigner):
134+
@dataclass
135+
class TokenManager(signing.TimestampSigner, GeneralConfig):
136136
"""
137137
This class is responsible for creating encrypted links / verifying them / applying several checks for token lifetime
138138
and generating new verification links on request.
@@ -157,11 +157,11 @@ class TokenManager(GeneralConfig, signing.TimestampSigner):
157157
link_manager: ActivationLinkManager = ActivationLinkManager()
158158

159159
def __post_init__(self):
160-
key = self.settings.get("key", raise_exception=False)
161-
salt = self.settings.get("salt", raise_exception=False)
162-
sep = self.settings.get("sep", raise_exception=False)
160+
self.key = self.settings.get("key", raise_exception=False)
161+
self.salt = self.settings.get("salt", raise_exception=False)
162+
self.sep = self.settings.get("sep", raise_exception=False)
163163

164-
super().__init__(key, sep, salt)
164+
super().__init__()
165165

166166
@staticmethod
167167
def is_token_valid(plain_email, encrypted_user_token) -> bool:

verify_email/urls.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from django.urls import path
2-
from .views import verify_user_and_activate, request_new_link
2+
from .views import verify_and_activate_user, request_new_link
33

44
urlpatterns = [
55
path(
6-
"user/verify-email/<useremail>/<usertoken>/",
7-
verify_user_and_activate,
6+
"user/verify-email/<user_email>/<user_token>/",
7+
verify_and_activate_user,
88
name="verify-email",
99
),
1010
path(
11-
"user/verify-email/request-new-link/<useremail>/<usertoken>/",
11+
"user/verify-email/request-new-link/<user_email>/<user_token>/",
1212
request_new_link,
1313
name="request-new-link-from-token",
1414
),

verify_email/views.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040

4141
@require_GET
42-
def verify_user_and_activate(request, user_email, user_token):
42+
def verify_and_activate_user(request, user_email, user_token):
4343
"""
4444
A view function already implemented for you so you don't have to implement any function for verification
4545
as this function will be automatically be called when user clicks on verification link.
@@ -70,6 +70,7 @@ def verify_user_and_activate(request, user_email, user_token):
7070
)
7171
return render(
7272
request,
73+
status=401,
7374
template_name=failed_template,
7475
context={
7576
"msg": failed_msg,
@@ -80,6 +81,7 @@ def verify_user_and_activate(request, user_email, user_token):
8081
except SignatureExpired:
8182
return render(
8283
request,
84+
status=401,
8385
template_name=link_expired_template,
8486
context={
8587
"msg": "The link has lived its life :( Request a new one!",
@@ -91,6 +93,7 @@ def verify_user_and_activate(request, user_email, user_token):
9193
except BadSignature:
9294
return render(
9395
request,
96+
status=401,
9497
template_name=failed_template,
9598
context={
9699
"msg": "This link was modified before verification.",
@@ -101,6 +104,7 @@ def verify_user_and_activate(request, user_email, user_token):
101104
except MaxRetriesExceeded:
102105
return render(
103106
request,
107+
status=401,
104108
template_name=failed_template,
105109
context={
106110
"msg": "You have exceeded the maximum verification requests! Contact admin.",
@@ -110,6 +114,7 @@ def verify_user_and_activate(request, user_email, user_token):
110114
except InvalidToken:
111115
return render(
112116
request,
117+
status=401,
113118
template_name=failed_template,
114119
context={
115120
"msg": "This link is invalid or been used already, we cannot verify using this link.",
@@ -119,10 +124,29 @@ def verify_user_and_activate(request, user_email, user_token):
119124
except UserNotFound:
120125
raise Http404("404 User not found")
121126

127+
except Exception as err:
128+
logger.exception(err)
129+
flash_msg = "Something went wrong during this process!"
130+
if debug:
131+
flash_msg = f"""{flash_msg} Developer should look into this.
132+
Error Details: {err}
133+
(You are seeing error details because app is running in debug mode)
134+
"""
135+
return render(
136+
request,
137+
status=403,
138+
template_name=failed_template,
139+
context={
140+
"msg": flash_msg,
141+
"status": "Failed!",
142+
},
143+
)
144+
145+
122146

123-
def request_new_link(request, useremail=None, usertoken=None):
147+
def request_new_link(request, user_email=None, user_token=None):
124148
try:
125-
if useremail is None or usertoken is None:
149+
if user_email is None or user_token is None:
126150
# request came from re-request email page
127151
if request.method == "POST":
128152
form = RequestNewVerificationEmail(request.POST) # do not inflate data
@@ -157,7 +181,7 @@ def request_new_link(request, useremail=None, usertoken=None):
157181
else:
158182
# request came from previously sent link
159183
status = ActivationMailManager.resend_verification_link(
160-
request, useremail, token=usertoken
184+
request, user_email, token=user_token
161185
)
162186
if status:
163187
return render(
@@ -192,6 +216,7 @@ def request_new_link(request, useremail=None, usertoken=None):
192216
)
193217
return render(
194218
request,
219+
status=403,
195220
template_name=failed_template,
196221
context={
197222
"msg": "You have exceeded the maximum verification requests! Contact admin.",
@@ -210,6 +235,7 @@ def request_new_link(request, useremail=None, usertoken=None):
210235
except UserAlreadyActive:
211236
return render(
212237
request,
238+
status=403,
213239
template_name=failed_template,
214240
context={
215241
"msg": "This user's account is already active",
@@ -226,6 +252,7 @@ def request_new_link(request, useremail=None, usertoken=None):
226252
"""
227253
return render(
228254
request,
255+
status=403,
229256
template_name=failed_template,
230257
context={
231258
"msg": flash_msg,

0 commit comments

Comments
 (0)