Skip to content

New packaging #88

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

Open
wants to merge 18 commits 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
35 changes: 23 additions & 12 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@

name: Upload Python Package
name: Upload Python Package to PyPi

on: [push]
on:
push:
branches: []
tags-ignore: []

release:
types: [prereleased, published]

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
python3 -m pip install --upgrade build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload --skip-existing dist/*
name: ${{ github.event_name == 'push' && 'testpypi' || 'pypi' }}
url: ${{ github.event_name == 'push' && 'https://test.pypi.org/p/' || 'https://pypi.org/project/' }}Django-Verify-Email
with:
user: __token__
password: ${{ github.event_name == 'push' && secrets.TEST_PYPI_SECRET_TOKEN || secrets.PYPI_SECRET_TOKEN }}
repository-url: ${{ github.event_name == 'push' && 'https://test.pypi.org/legacy/' || 'https://pypi.org/legacy/' }}

39 changes: 33 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@


<h1 style='text-align:center'>Email-Verification for Django</h1>
# Email Verification for Django

Email verification for new signups or new users is a two-step verification process and adds a layer for security for valid users.

<b> verify_email </b> is a django app that provides this functionality right of the bat without any complex implementation.
**verify_email** is a django app that provides this functionality right of the bat without any complex implementation.

<hr>
## Version Update (2.0.4)
#### Bug fixes
* Solves incompatibility with Django 5.1 (exception when initializing the TokenManager)
* Fixes implementation of sending verification without a form (initial PR by )

## Version Update (2.0.0):
#### Enhancements
* Verification emails can now be sent without a form if a user has already been created (eg when an admin wants to activate an inactive user) (Inspired by other PRs which were buggy, see above)
* Modernized the Python packaging (removed warnings on build)
* Use of GITHUB TOKEN to build

## Version Update (v2.0.3)

#### Bug Fixes:
* Variable name in view function was different than the one passed in URL

#### Enhancement:
* Raising Http404 instead of returning 404 with a string so we can have the default 404 page
* Using python's logging system instead of print statements

<hr>

## Version Update (2.0.2):
#### Bug Fixes:
* Using normal form instead of model forms to allow unique emails to re-request verification email
* Not returning HTTP response in one of the exception blocks

#### Enhancements:
* Added new settings variable NEW_EMAIL_SENT_TEMPLATE to configure template shown after successful email sent.
* Using get_username() method to account for apps that have changed the USERNAME_FIELD of their user model
* migrations updated according to Django 4.x


## Version Update (2.0.0):

> This version contains breaking changes and is not compatible with the previous version 1.0.9

Expand All @@ -29,7 +56,7 @@ Read about this feature <a href='#resending-email-using-form'>here</a>
* Using exceptions instead of normal string errors
* code cleanup

<hr><hr>
<hr>

## The app takes care of :
* Settings user's is_active status to False.
Expand Down
35 changes: 35 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "django-email-verification"
version = "2.0.4"
#dynamic = ["version"]
authors = [
{ name="Nitin Sharma", email="ns290670@gamil.com"},
{ name="Olivier LEVILLAIN", email="levillain.olivier@gmail.com" },
]
description = " A Django app for email verification."
readme = "README.md"
requires-python = ">=3.6"
dependencies=["binascii", "django"]
classifiers = [
"Environment :: Web Environment",
"Framework :: Django CMS :: 3.8",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.6",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent"
]

keywords = ["email", "validation", "check"]

[project.urls]
Homepage = "https://github.com/foo290/Django-Verify-Email/"
Repository = "https://github.com/foo290/Django-Verify-Email.git"
Issues = "https://github.com/foo290/Django-Verify-Email//issues"
Download = "https://github.com/foo290/Django-Verify-Email/archive/refs/tags/2.0.4.tar.gz"

[tool.hatch.build.targets.wheel]
packages = ["src/verify_email"]
20 changes: 0 additions & 20 deletions setup.cfg

This file was deleted.

9 changes: 0 additions & 9 deletions setup.py

This file was deleted.

1 change: 1 addition & 0 deletions src/verify_email/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .email_handler import * # noqa
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,20 @@ def __send_email(self, msg, useremail):
)

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

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

if not form and not inactive_user:
raise ValueError('Either form or inactive_user must be provided')
elif form and inactive_user:
raise ValueError('Either form or inactive_user must be provided')
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
if not useremail:
raise KeyError(
Expand Down Expand Up @@ -67,7 +71,7 @@ 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')
Expand All @@ -92,10 +96,9 @@ def resend_verification_link(self, request, email, **kwargs):
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, inactive_user=None):
return _VerifyEmail().send_verification_link(request, form, inactive_user)


# These is supposed to be called outside of this module
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self):
salt = self.settings.get('salt', raise_exception=False)
sep = self.settings.get('sep', raise_exception=False)

super().__init__(key, sep, salt)
super().__init__(key=key, sep=sep, salt=salt)

# Private :
def __get_seconds(self, interval):
Expand Down
File renamed without changes.
134 changes: 68 additions & 66 deletions verify_email/views.py → src/verify_email/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

from django.forms import ValidationError
from django.http import Http404, HttpResponse
from django.urls import reverse
from django.shortcuts import render, redirect
Expand Down Expand Up @@ -42,77 +43,78 @@ def verify_user_and_activate(request, useremail, usertoken):

verify the user's email and token and redirect'em accordingly.
"""
if request.method == 'GET':
try:
verified = verify_user(useremail, usertoken)
if verified is True:
if login_page and not success_template:
messages.success(request, success_msg)
return redirect(to=login_page)
return render(
request,
template_name=success_template,
context={
'msg': success_msg,
'status': 'Verification Successful!',
'link': reverse(login_page)
}
)
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}')
return render(
request,
template_name=failed_template,
context={
'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
}
)
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!',
}
)
except MaxRetriesExceeded:
return render(
request,
template_name=failed_template,
context={
'msg': 'You have exceeded the maximum verification requests! Contact admin.',
'status': 'Maxed out!',
}
)
except InvalidToken:
if request.method != 'GET':
raise ValidationError('Method not allowed')
try:
verified = verify_user(useremail, usertoken)
if verified is True:
if login_page and not success_template:
messages.success(request, success_msg)
return redirect(to=login_page)
return render(
request,
template_name=failed_template,
template_name=success_template,
context={
'msg': 'This link is invalid or been used already, we cannot verify using this link.',
'status': 'Invalid Link',
'msg': success_msg,
'status': 'Verification Successful!',
'link': reverse(login_page)
}
)
except UserNotFound:
raise Http404("404 User not found")
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}')
return render(
request,
template_name=failed_template,
context={
'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
}
)
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!',
}
)
except MaxRetriesExceeded:
return render(
request,
template_name=failed_template,
context={
'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',
}
)
except UserNotFound:
raise Http404("404 User not found")


def request_new_link(request, useremail=None, usertoken=None):
Expand Down
3 changes: 0 additions & 3 deletions verify_email/__init__.py

This file was deleted.