Skip to content

Commit

Permalink
Merge pull request #365 from mitchdennett/email-verify
Browse files Browse the repository at this point in the history
Email Verification Feature
  • Loading branch information
josephmancuso committed Sep 29, 2018
2 parents fd72780 + 2186a09 commit b9fbc42
Show file tree
Hide file tree
Showing 13 changed files with 532 additions and 7 deletions.
21 changes: 21 additions & 0 deletions masonite/auth/MustVerifyEmail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
""" Verify Email Module """

import time

from masonite.auth.Sign import Sign


class MustVerifyEmail:
"""Class To Verify User Email
"""

def verify_email(self, mail_manager, request):
mail = mail_manager.helper()
sign = Sign()

token = sign.sign('{0}::{1}'.format(self.id, time.time()))
link = '{0}/email/verify/{1}'.format(request.environ['HTTP_HOST'], token)

mail.to(self.email) \
.template('auth/verifymail', {'name': self.name, 'email': self.email, 'link': link}) \
.subject('Please Confirm Your Email').send()
1 change: 1 addition & 0 deletions masonite/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .Auth import Auth
from .Csrf import Csrf
from .Sign import Sign
from .MustVerifyEmail import MustVerifyEmail
9 changes: 6 additions & 3 deletions masonite/commands/AuthCommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def handle(self):
f.write("Post().route('/login', 'LoginController@store'),\n ")
f.write("Get().route('/register', 'RegisterController@show'),\n ")
f.write("Post().route('/register', 'RegisterController@store'),\n ")
f.write("Get().route('/home', 'HomeController@show'),\n")
f.write("Get().route('/home', 'HomeController@show'),\n ")
f.write("Get().route('/email/verify', 'ConfirmController@verify_show'),\n ")
f.write("Get().route('/email/verify/@id:signed', 'ConfirmController@confirm_email'),\n")
f.write(']\n')

# move controllers
Expand All @@ -34,10 +36,11 @@ def handle(self):
os.getcwd() + "/app/http/controllers/RegisterController.py")
shutil.copyfile(module_path + "/../snippets/auth/controllers/HomeController.py",
os.getcwd() + "/app/http/controllers/HomeController.py")
shutil.copyfile(module_path + "/../snippets/auth/controllers/ConfirmController.py",
os.getcwd() + "/app/http/controllers/ConfirmController.py")

# move templates
shutil.copytree(module_path + "/../snippets/auth/templates/auth",
os.getcwd() + "/resources/templates/auth")

self.info(
'Project Scaffolded. You now have 4 new controllers, 5 new templates and 6 new routes')
self.info('Project Scaffolded. You now have 5 new controllers, 7 new templates and 9 new routes')
3 changes: 2 additions & 1 deletion masonite/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class Route:
'int': r'(\d+)',
'integer': r'(\d+)',
'string': r'([a-zA-Z]+)',
'default': r'([\w.-]+)'
'default': r'([\w.-]+)',
'signed': r'([\w\-=]+)'
}

def __init__(self, environ=None):
Expand Down
86 changes: 86 additions & 0 deletions masonite/snippets/auth/controllers/ConfirmController.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
""" The ConfirmController Module """
import time
import datetime

from masonite.auth import Auth
from masonite.auth.Sign import Sign
from masonite.request import Request
from masonite.view import View
from masonite.auth import MustVerifyEmail
from app.User import User


class ConfirmController:
"""The ConfirmController class.
"""

def __init__(self):
"""The ConfirmController Constructor
"""
pass

def verify_show(self, request: Request, view: View):
"""Show the Verify Email page for unverified users.
Arguments:
Request {masonite.request.request} -- The Masonite request class.
Request {masonite.view.view} -- The Masonite view class.
Returns:
[type] -- [description]
"""

return view.render('auth/verify', {'app': request.app().make('Application'), 'Auth': Auth(request)})

def confirm_email(self, request: Request, view: View):
"""Confirm User email and show the correct response.
Arguments:
Request {masonite.request.request} -- The Masonite request class.
Request {masonite.view.view} -- The Masonite view class.
Returns:
[type] -- [description]
"""

sign = Sign()
token = sign.unsign(request.param('id'))

if token is not None:
tokenParts = token.split("::")
if len(tokenParts) > 1:
id = tokenParts[0]
user = self.get_user(id)

if user.verified_at is None:
timestamp = datetime.datetime.fromtimestamp(float(tokenParts[1]))
now = datetime.datetime.now()
timestamp_plus_10 = timestamp + datetime.timedelta(minutes=10)

if now < timestamp_plus_10:
user.verified_at = datetime.datetime.now()
user.save()

return view.render('auth/confirm', {'app': request.app().make('Application'), 'Auth': Auth(request)})

return view.render('auth/error', {'app': request.app().make('Application'), 'Auth': Auth(request)})

def get_user(self, id):
"""Get the user from the database
Arguments:
id {str} -- The user id
Returns:
[User] -- [User model]
"""

return User.find(id)

def send_verify_email(self, request: Request):
user = request.user()

if isinstance(user, MustVerifyEmail):
request.app().resolve(user.verify_email)

return request.redirect('/home')
10 changes: 7 additions & 3 deletions masonite/snippets/auth/controllers/RegisterController.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from masonite.helpers import password as bcrypt_password
from masonite.request import Request
from masonite.view import View
from masonite.auth import MustVerifyEmail
from masonite.managers import MailManager


class RegisterController:
Expand All @@ -14,7 +16,6 @@ class RegisterController:
def __init__(self):
"""The RegisterController Constructor
"""

pass

def show(self, request: Request, view: View):
Expand All @@ -29,7 +30,7 @@ def show(self, request: Request, view: View):

return view.render('auth/register', {'app': request.app().make('Application'), 'Auth': Auth(request)})

def store(self, request: Request):
def store(self, request: Request, mail_manager: MailManager):
"""Register the user with the database.
Arguments:
Expand All @@ -41,12 +42,15 @@ def store(self, request: Request):

password = bcrypt_password(request.input('password'))

auth.AUTH['model'].create(
user = auth.AUTH['model'].create(
name=request.input('name'),
password=password,
email=request.input('email'),
)

if isinstance(user, MustVerifyEmail):
user.verify_email(mail_manager, request)

# Login the user
if Auth(request).login(request.input(auth.AUTH['model'].__auth__), request.input('password')):
# Redirect to the homepage
Expand Down
12 changes: 12 additions & 0 deletions masonite/snippets/auth/templates/auth/confirm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends 'auth/base.html' %}

{% block content %}
<div class="col-xs-4 col-xs-offset-4">
<div class="panel panel-default">
<div class="panel-heading">Verified</div>
<div class="panel-body">
Thank you for confirming your email. Click here to go to the <a href="/home">home</a> page.
</div>
</div>
</div>
{% endblock %}
12 changes: 12 additions & 0 deletions masonite/snippets/auth/templates/auth/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends 'auth/base.html' %}

{% block content %}
<div class="col-xs-4 col-xs-offset-4">
<div class="panel panel-default">
<div class="panel-heading">Verifying Error</div>
<div class="panel-body">
Confirming email failed. Click here to go <a href="/home">home</a>
</div>
</div>
</div>
{% endblock %}
13 changes: 13 additions & 0 deletions masonite/snippets/auth/templates/auth/verify.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends 'auth/base.html' %}

{% block content %}
<div class="col-xs-4 col-xs-offset-4">
<div class="panel panel-default">
<div class="panel-heading">Verify</div>
<div class="panel-body">
Please check your email and follow the link to verify your email. If
you need us to resend the email. Click <a href="/email/verify/send">here</a>
</div>
</div>
</div>
{% endblock %}
Loading

0 comments on commit b9fbc42

Please sign in to comment.