Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update send_verification_email and verify_email_and_activate #85

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 44 additions & 25 deletions verify_email/email_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,45 @@ def __init__(self):

# Private :
def __send_email(self, msg, useremail):
subject = self.settings.get('subject')
subject = self.settings.get("subject")
send_mail(
subject, strip_tags(msg),
from_email=self.settings.get('from_alias'),
recipient_list=[useremail], html_message=msg
subject,
strip_tags(msg),
from_email=self.settings.get("from_alias"),
recipient_list=[useremail],
html_message=msg,
)

# Public :
def send_verification_link(self, request, inactive_user=None, form=None):

if form:
inactive_user = form.save(commit=False)

inactive_user.is_active = False
inactive_user.save()

try:

useremail = form.cleaned_data.get(self.settings.get('email_field_name')) if form else inactive_user.email

useremail = (
form.cleaned_data.get(self.settings.get("email_field_name"))
if form
else inactive_user.email
)
if not useremail:
raise KeyError(
'No key named "email" in your form. Your field should be named as email in form OR set a variable'
' "EMAIL_FIELD_NAME" with the name of current field in settings.py if you want to use current name '
'as email field.'
"as email field."
)

verification_url = self.token_manager.generate_link(request, inactive_user, useremail)
verification_url = self.token_manager.generate_link(
request, inactive_user, useremail
)
msg = render_to_string(
self.settings.get('html_message_template', raise_exception=True),
{"link": verification_url, "inactive_user": inactive_user},
request=request
self.settings.get("html_message_template", raise_exception=True),
{"link": verification_url, "inactive_user": inactive_user},
request=request,
)

self.__send_email(msg, useremail)
Expand All @@ -67,35 +75,46 @@ def resend_verification_link(self, request, email, **kwargs):
- UserAlreadyActive (by) get_user_by_token()
- MaxRetryExceeded (by) request_new_link()
- InvalidTokenOrEmail

These exception should be handled in caller function.
"""
inactive_user = kwargs.get('user')
user_encoded_token = kwargs.get('token')
encoded = kwargs.get('encoded', True)
inactive_user = kwargs.get("user")
user_encoded_token = kwargs.get("token")
encoded = kwargs.get("encoded", True)

if encoded:
decoded_encrypted_user_token = self.token_manager.perform_decoding(user_encoded_token)
decoded_encrypted_user_token = self.token_manager.perform_decoding(
user_encoded_token
)
email = self.token_manager.perform_decoding(email)
inactive_user = self.token_manager.get_user_by_token(email, decoded_encrypted_user_token)
inactive_user = self.token_manager.get_user_by_token(
email, decoded_encrypted_user_token
)

if not inactive_user or not email:
raise InvalidTokenOrEmail(f'Either token or email is invalid. user: {inactive_user}, email: {email}')
raise InvalidTokenOrEmail(
f"Either token or email is invalid. user: {inactive_user}, email: {email}"
)

# At this point, we have decoded email(if it was encoded), and inactive_user, and we can request new link
link = self.token_manager.request_new_link(request, inactive_user, email)
msg = render_to_string(
self.settings.get('html_message_template', raise_exception=True),
{"link": link}, request=request
self.settings.get("html_message_template", raise_exception=True),
{"link": link},
request=request,
)
self.__send_email(msg, email)
return True



# These is supposed to be called outside of this module
def send_verification_email(request, form):
return _VerifyEmail().send_verification_link(request, form)
def send_verification_email(request, form=None, user=None):
if form is not None:
return _VerifyEmail().send_verification_link(request, form)
elif user is not None:
return _VerifyEmail().send_verification_link(request, user)
else:
raise ValueError("Either form or user must be provided")


# These is supposed to be called outside of this module
Expand Down
136 changes: 73 additions & 63 deletions verify_email/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@

pkg_configs = GetFieldFromSettings()

login_page = pkg_configs.get('login_page')
login_page = pkg_configs.get("login_page")

success_msg = pkg_configs.get('verification_success_msg')
failed_msg = pkg_configs.get('verification_failed_msg')
success_msg = pkg_configs.get("verification_success_msg")
failed_msg = pkg_configs.get("verification_failed_msg")

failed_template = pkg_configs.get('verification_failed_template')
success_template = pkg_configs.get('verification_success_template')
link_expired_template = pkg_configs.get('link_expired_template')
request_new_email_template = pkg_configs.get('request_new_email_template')
new_email_sent_template = pkg_configs.get('new_email_sent_template')
failed_template = pkg_configs.get("verification_failed_template")
success_template = pkg_configs.get("verification_success_template")
link_expired_template = pkg_configs.get("link_expired_template")
request_new_email_template = pkg_configs.get("request_new_email_template")
new_email_sent_template = pkg_configs.get("new_email_sent_template")


def verify_user_and_activate(request, useremail, usertoken):
Expand All @@ -42,7 +42,7 @@ def verify_user_and_activate(request, useremail, usertoken):

verify the user's email and token and redirect'em accordingly.
"""
if request.method == 'GET':
if request.method == "GET":
try:
verified = verify_user(useremail, usertoken)
if verified is True:
Expand All @@ -53,63 +53,69 @@ def verify_user_and_activate(request, useremail, usertoken):
request,
template_name=success_template,
context={
'msg': success_msg,
'status': 'Verification Successful!',
'link': reverse(login_page)
}
"msg": success_msg,
"status": "Verification Successful!",
"link": (
login_page
if login_page and login_page.startswith("/")
else reverse(login_page) if login_page else ""
),
},
)
else:
# we dont know what went wrong...
raise ValueError
except (ValueError, TypeError) as error:
logger.error(f'[ERROR]: Something went wrong while verifying user, exception: {error}')
logger.error(
f"[ERROR]: Something went wrong while verifying user, exception: {error}"
)
return render(
request,
template_name=failed_template,
context={
'msg': failed_msg,
'minor_msg': 'There is something wrong with this link...',
'status': 'Verification Failed!',
}
"msg": failed_msg,
"minor_msg": "There is something wrong with this link...",
"status": "Verification Failed!",
},
)
except SignatureExpired:
return render(
request,
template_name=link_expired_template,
context={
'msg': 'The link has lived its life :( Request a new one!',
'status': 'Expired!',
'encoded_email': useremail,
'encoded_token': usertoken
}
"msg": "The link has lived its life :( Request a new one!",
"status": "Expired!",
"encoded_email": useremail,
"encoded_token": usertoken,
},
)
except BadSignature:
return render(
request,
template_name=failed_template,
context={
'msg': 'This link was modified before verification.',
'minor_msg': 'Cannot request another verification link with faulty link.',
'status': 'Faulty Link Detected!',
}
"msg": "This link was modified before verification.",
"minor_msg": "Cannot request another verification link with faulty link.",
"status": "Faulty Link Detected!",
},
)
except MaxRetriesExceeded:
return render(
request,
template_name=failed_template,
context={
'msg': 'You have exceeded the maximum verification requests! Contact admin.',
'status': 'Maxed out!',
}
"msg": "You have exceeded the maximum verification requests! Contact admin.",
"status": "Maxed out!",
},
)
except InvalidToken:
return render(
request,
template_name=failed_template,
context={
'msg': 'This link is invalid or been used already, we cannot verify using this link.',
'status': 'Invalid Link',
}
"msg": "This link is invalid or been used already, we cannot verify using this link.",
"status": "Invalid Link",
},
)
except UserNotFound:
raise Http404("404 User not found")
Expand All @@ -119,36 +125,38 @@ def request_new_link(request, useremail=None, usertoken=None):
try:
if useremail is None or usertoken is None:
# request came from re-request email page
if request.method == 'POST':
if request.method == "POST":
form = RequestNewVerificationEmail(request.POST) # do not inflate data
if form.is_valid():
form_data: dict = form.cleaned_data
email = form_data['email']
email = form_data["email"]

inactive_user = get_user_model().objects.get(email=email)
if inactive_user.is_active:
raise UserAlreadyActive('User is already active')
raise UserAlreadyActive("User is already active")
else:
# resend email
status = resend_verification_email(request, email, user=inactive_user, encoded=False)
status = resend_verification_email(
request, email, user=inactive_user, encoded=False
)
if status:
return render(
request,
template_name=new_email_sent_template,
context={
'msg': "You have requested another verification email!",
'minor_msg': 'Your verification link has been sent',
'status': 'Email Sent!',
}
"msg": "You have requested another verification email!",
"minor_msg": "Your verification link has been sent",
"status": "Email Sent!",
},
)
else:
logger.error('something went wrong during sending email')
logger.error("something went wrong during sending email")
else:
form = RequestNewVerificationEmail()
return render(
request,
template_name=request_new_email_template,
context={'form': form}
context={"form": form},
)
else:
# request came from previously sent link
Expand All @@ -159,53 +167,55 @@ def request_new_link(request, useremail=None, usertoken=None):
request,
template_name=new_email_sent_template,
context={
'msg': "You have requested another verification email!",
'minor_msg': 'Your verification link has been sent',
'status': 'Email Sent!',
}
"msg": "You have requested another verification email!",
"minor_msg": "Your verification link has been sent",
"status": "Email Sent!",
},
)
else:
messages.info(request, 'Something went wrong during sending email :(')
logger.error('something went wrong during sending email')
messages.info(request, "Something went wrong during sending email :(")
logger.error("something went wrong during sending email")

except ObjectDoesNotExist as error:
messages.warning(request, 'User not found associated with given email!')
logger.error(f'[ERROR]: User not found. exception: {error}')
messages.warning(request, "User not found associated with given email!")
logger.error(f"[ERROR]: User not found. exception: {error}")
return HttpResponse(b"User Not Found", status=404)

except MultipleObjectsReturned as error:
logger.error(f'[ERROR]: Multiple users found. exception: {error}')
logger.error(f"[ERROR]: Multiple users found. exception: {error}")
return HttpResponse(b"Internal server error!", status=500)

except KeyError as error:
logger.error(f'[ERROR]: Key error for email in your form: {error}')
logger.error(f"[ERROR]: Key error for email in your form: {error}")
return HttpResponse(b"Internal server error!", status=500)

except MaxRetriesExceeded as error:
logger.error(f'[ERROR]: Maximum retries for link has been reached. exception: {error}')
logger.error(
f"[ERROR]: Maximum retries for link has been reached. exception: {error}"
)
return render(
request,
template_name=failed_template,
context={
'msg': 'You have exceeded the maximum verification requests! Contact admin.',
'status': 'Maxed out!',
}
"msg": "You have exceeded the maximum verification requests! Contact admin.",
"status": "Maxed out!",
},
)
except InvalidToken:
return render(
request,
template_name=failed_template,
context={
'msg': 'This link is invalid or been used already, we cannot verify using this link.',
'status': 'Invalid Link',
}
"msg": "This link is invalid or been used already, we cannot verify using this link.",
"status": "Invalid Link",
},
)
except UserAlreadyActive:
return render(
request,
template_name=failed_template,
context={
'msg': "This user's account is already active",
'status': 'Already Verified!',
}
"msg": "This user's account is already active",
"status": "Already Verified!",
},
)