Skip to content

Commit d44b46a

Browse files
authored
Merge pull request #89 from foo290/massive-code-refactor
Massive code refactor
2 parents 725e77a + 33a220f commit d44b46a

File tree

15 files changed

+953
-459
lines changed

15 files changed

+953
-459
lines changed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = Django-Verify-Email
3-
version = 2.0.3
3+
version = 3.0.0
44
author = Nitin
55
author_email = ns290670@gamil.com
66
description = A Django app for email verification.

verify_email/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
from .email_handler import *
2-
3-

verify_email/app_configurations.py

Lines changed: 69 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,94 @@
1+
from dataclasses import dataclass
2+
13
from django.conf import settings
4+
from .interface import DefaultConfig
25

36

7+
@dataclass
48
class GetFieldFromSettings:
59
"""
610
This class fetches the attributes that are defined in settings.py of your project by user OR Django itself.
7-
self.default_configs : is a dict with keys as the names used in this app and values being a tuple of
11+
self.default_configs : is a dict with keys as the names used in this app and values being a tuple of
812
attributes defined in settings.py and their corresponding default values if not found.
9-
10-
There is a special case in "get" method, if you set "VERIFICATION_SUCCESS_TEMPLATE" as None is settings.py, it
13+
14+
There is a special case in "get" method, if you set "VERIFICATION_SUCCESS_TEMPLATE" as None is settings.py, it
1115
will skip the intermediate page where success information is displayed. (This is better explained in docs.)
1216
13-
The "get" method takes the name of the attributes as input, checks for it in settings.py,
17+
The "get" method takes the name of the attributes as input, checks for it in settings.py,
1418
if found:
1519
returns the corresponding value.
1620
else:
1721
returns the default value from "self.defaults_configs".
1822
"""
1923

20-
def __init__(self):
21-
24+
def __post_init__(self):
2225
self.defaults_configs = {
23-
'debug_settings': (
24-
'DEBUG',
25-
False
26-
),
27-
28-
'subject': (
29-
"SUBJECT",
30-
"Email Verification Mail"
31-
),
32-
33-
'email_field_name': (
34-
"EMAIL_FIELD_NAME",
35-
"email",
36-
),
37-
38-
'html_message_template': (
39-
"HTML_MESSAGE_TEMPLATE",
40-
'verify_email/email_verification_msg.html'
41-
),
42-
43-
'from_alias': (
44-
"DEFAULT_FROM_EMAIL",
45-
'noreply<noreply@gmail.com>',
46-
),
47-
48-
'login_page': (
49-
'LOGIN_URL',
50-
'accounts_login'
51-
),
52-
53-
'verification_success_template': (
54-
'VERIFICATION_SUCCESS_TEMPLATE',
55-
'verify_email/email_verification_successful.html'
56-
),
57-
58-
'verification_success_msg': (
59-
'VERIFICATION_SUCCESS_MSG',
60-
"Your Email is verified successfully and account has been activated. "
61-
"You can login with the credentials now..."
62-
),
63-
64-
'verification_failed_template': (
65-
'VERIFICATION_FAILED_TEMPLATE',
66-
'verify_email/email_verification_failed.html'
67-
),
68-
69-
'link_expired_template': (
70-
'LINK_EXPIRED_TEMPLATE',
71-
'verify_email/link_expired.html'
72-
),
73-
74-
'verification_failed_msg': (
75-
'VERIFICATION_FAILED_MSG',
76-
"There is something wrong with this link, can't verify the user..."
77-
),
78-
'request_new_email_template': (
79-
'REQUEST_NEW_EMAIL_TEMPLATE',
80-
'verify_email/request_new_email.html'
81-
),
82-
'new_email_sent_template': (
83-
'NEW_EMAIL_SENT_TEMPLATE',
84-
'verify_email/new_email_sent.html'
85-
),
86-
87-
'salt': (
88-
'HASH_SALT',
89-
None
90-
),
91-
92-
'sep': (
93-
'SEPARATOR',
94-
':'
95-
),
96-
97-
'key': (
98-
'HASHING_KEY',
99-
None
100-
),
101-
102-
'max_age': (
103-
'EXPIRE_AFTER',
104-
None
105-
),
106-
107-
'max_retries': (
108-
'MAX_RETRIES',
109-
2
110-
)
26+
"debug": DefaultConfig(setting_field="DEBUG", default_value=False),
27+
"subject": DefaultConfig(
28+
setting_field="SUBJECT", default_value="Email Verification Mail"
29+
),
30+
"email_field_name": DefaultConfig(
31+
setting_field="EMAIL_FIELD_NAME", default_value="email"
32+
),
33+
"html_message_template": DefaultConfig(
34+
setting_field="HTML_MESSAGE_TEMPLATE",
35+
default_value="verify_email/email_verification_msg.html",
36+
),
37+
"from_alias": DefaultConfig(
38+
setting_field="DEFAULT_FROM_EMAIL",
39+
default_value="<noreply<noreply@gmail.com>",
40+
),
41+
"login_page": DefaultConfig(
42+
setting_field="LOGIN_URL", default_value="accounts_login"
43+
),
44+
"verification_success_template": DefaultConfig(
45+
setting_field="VERIFICATION_SUCCESS_TEMPLATE",
46+
default_value="verify_email/email_verification_successful.html",
47+
),
48+
"verification_success_msg": DefaultConfig(
49+
setting_field="VERIFICATION_SUCCESS_MSG",
50+
default_value=(
51+
"Your Email is verified successfully and account has been activated."
52+
" You can login with the credentials now..."
53+
),
54+
),
55+
"verification_failed_template": DefaultConfig(
56+
setting_field="VERIFICATION_FAILED_TEMPLATE",
57+
default_value="verify_email/email_verification_failed.html",
58+
),
59+
"link_expired_template": DefaultConfig(
60+
setting_field="LINK_EXPIRED_TEMPLATE",
61+
default_value="verify_email/link_expired.html",
62+
),
63+
"verification_failed_msg": DefaultConfig(
64+
setting_field="VERIFICATION_FAILED_MSG",
65+
default_value="There is something wrong with this link, can't verify the user...",
66+
),
67+
"request_new_email_template": DefaultConfig(
68+
setting_field="REQUEST_NEW_EMAIL_TEMPLATE",
69+
default_value="verify_email/request_new_email.html",
70+
),
71+
"new_email_sent_template": DefaultConfig(
72+
setting_field="NEW_EMAIL_SENT_TEMPLATE",
73+
default_value="verify_email/new_email_sent.html",
74+
),
75+
"salt": DefaultConfig(setting_field="HASH_SALT", default_value=None),
76+
"sep": DefaultConfig(setting_field="SEPARATOR", default_value=":"),
77+
"key": DefaultConfig(setting_field="HASHING_KEY", default_value=None),
78+
"max_age": DefaultConfig(setting_field="EXPIRE_AFTER", default_value=None),
79+
"max_retries": DefaultConfig(setting_field="MAX_RETRIES", default_value=2),
11180
}
11281

11382
def get(self, field_name, raise_exception=True, default_type=str):
11483
attr = getattr(
11584
settings,
116-
self.defaults_configs[field_name][0], # get field from settings
117-
self.defaults_configs[field_name][1] # get default value if field not defined
85+
self.defaults_configs[field_name].setting_field, # get field from settings
86+
self.defaults_configs[
87+
field_name
88+
].default_value, # get default value if field not defined
11889
)
119-
if (attr == '' or attr is None or not isinstance(field_name, default_type)) and raise_exception:
120-
if field_name == 'verification_success_template' and attr is None:
90+
if not attr and not isinstance(field_name, default_type) and raise_exception:
91+
if field_name == "verification_success_template" and attr is None:
12192
return None
12293
raise AttributeError
12394
return attr

verify_email/apps.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77

88
class VerifyEmailConfig(AppConfig):
9-
name = 'verify_email'
9+
name = "verify_email"
1010

1111
def ready(self):
12-
logger.info('[Email Verification] : importing signals - OK.')
12+
logger.info("[Email Verification] : importing signals - OK.")
1313
import verify_email.signals

verify_email/confirm.py

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,92 @@
1-
from django.utils import timezone
1+
import logging
2+
from dataclasses import dataclass
3+
24
from .token_manager import TokenManager
5+
from .custom_types import User
6+
from django.utils import timezone
7+
8+
9+
logger = logging.getLogger(__name__)
310

411

5-
class _UserActivationProcess:
12+
@dataclass
13+
class UserActivationProcess:
614
"""
7-
This class is pretty self.explanatory...
15+
This class handles the process of activating a user account
16+
by verifying a token associated with the user's email.
17+
18+
Attributes
19+
----------
20+
token_manager : TokenManager
21+
An instance of TokenManager used to manage token operations.
22+
23+
Methods
24+
-------
25+
activate_user(encoded_email: str, encoded_token: str) -> None:
26+
Activates the user account after verifying the provided token.
27+
828
Exceptions Raised
929
------------------
10-
- MaxRetriesExceeded (by) verify_user()
11-
- BadSignature (by) verify_user()
12-
- SignatureExpired (by) verify_user()
13-
- ValueError (by) verify_user()
14-
- TypeError (by) verify_user()
15-
- InvalidToken (by) verify_user()
30+
- MaxRetriesExceeded: Raised when the maximum number of retries for verification is exceeded.
31+
- BadSignature: Raised when the token signature is invalid.
32+
- SignatureExpired: Raised when the token has expired.
33+
- ValueError: Raised for invalid values during verification.
34+
- TypeError: Raised for incorrect types during verification.
35+
- InvalidToken: Raised when the provided token is invalid.
1636
"""
1737

18-
def __init__(self):
19-
self.token_manager = TokenManager()
38+
token_manager: TokenManager = TokenManager()
39+
40+
@classmethod
41+
def activate_user(cls, encoded_email: str, encoded_token: str) -> User:
42+
"""
43+
Steps to activate a user account:
44+
1. Verify the token.
45+
2. Set the user as active.
46+
3. Update the last login time.
2047
21-
@staticmethod
22-
def __activate_user(user):
23-
user.is_active = True
24-
user.last_login = timezone.now()
25-
user.save()
48+
Parameters
49+
----------
50+
encoded_email : str
51+
The encoded email address of the user.
52+
encoded_token : str
53+
The encoded token for user verification.
2654
27-
def verify_token(self, useremail, usertoken):
28-
user = self.token_manager.decrypt_link(useremail, usertoken)
29-
if user:
30-
self.__activate_user(user)
31-
return True
32-
return False
55+
Raises
56+
------
57+
MaxRetriesExceeded
58+
If the maximum number of retries for token verification is exceeded.
59+
BadSignature
60+
If the token signature is invalid.
61+
SignatureExpired
62+
If the token has expired.
63+
ValueError
64+
If an invalid value is encountered during verification.
65+
TypeError
66+
If an incorrect type is provided.
67+
InvalidToken
68+
If the provided token is invalid.
3369
70+
Notes
71+
-----
72+
This method instantiates the class to access its methods. This
73+
approach may be revised in the future as part of the class design
74+
considerations.
75+
"""
76+
self = cls() # Consider if instantiation is necessary here
77+
try:
78+
# Verify token and retrieve user object
79+
# this will return inactive user
80+
user = self.token_manager.decrypt_token_and_get_user(
81+
encoded_email, encoded_token
82+
)
3483

35-
def verify_user(useremail, usertoken):
36-
return _UserActivationProcess().verify_token(useremail, usertoken)
84+
# Activate the user account
85+
user.is_active = True
86+
user.last_login = timezone.now()
87+
user.save()
88+
return user
89+
except Exception as err:
90+
logger.exception(err)
91+
# Perform any necessary cleanup if required
92+
raise

verify_email/custom_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from typing import TypeVar
2+
3+
User = TypeVar("User")

0 commit comments

Comments
 (0)