浏览代码

Implement email registration

Fixes #1
pull/40/head
父节点
当前提交
46394af70f
共有 7 个文件被更改,包括 170 次插入10 次删除
  1. +2
    -0
      mysql/sql/init.sql
  2. +11
    -4
      src/app/models/register.py
  3. +80
    -0
      src/app/models/user.py
  4. +19
    -0
      src/app/templates/verify.html
  5. +2
    -1
      src/app/views/app.py
  6. +3
    -0
      src/app/views/login.py
  7. +53
    -5
      src/app/views/register.py

+ 2
- 0
mysql/sql/init.sql 查看文件

@@ -13,6 +13,8 @@ CREATE TABLE users (
country VARCHAR(50), country VARCHAR(50),
login_attempts INT UNSIGNED, login_attempts INT UNSIGNED,
last_login_attempt INT UNSIGNED, last_login_attempt INT UNSIGNED,
verified BOOLEAN,
token VARCHAR(50),
PRIMARY KEY (userid) PRIMARY KEY (userid)
); );




+ 11
- 4
src/app/models/register.py 查看文件

@@ -6,7 +6,7 @@ logger = logging.getLogger(__name__)




def set_user(username, password, full_name, company, email, def set_user(username, password, full_name, company, email,
street_address, city, state, postal_code, country):
street_address, city, state, postal_code, country, token):
""" """
Register a new user in the database Register a new user in the database
:param username: The users unique user name :param username: The users unique user name
@@ -19,6 +19,7 @@ def set_user(username, password, full_name, company, email,
:param state: The state where the user lives :param state: The state where the user lives
:param postal_code: The corresponding postal code :param postal_code: The corresponding postal code
:param country: The users country :param country: The users country
:param token: The account verification token
:type username: str :type username: str
:type password: str :type password: str
:type full_name: str :type full_name: str
@@ -29,13 +30,19 @@ def set_user(username, password, full_name, company, email,
:type state: str :type state: str
:type postal_code: str :type postal_code: str
:type country: str :type country: str
:type token: str
""" """
db.connect() db.connect()
cursor = db.cursor() cursor = db.cursor()
query = ("INSERT INTO users VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 0, 0)")
query = ("""
INSERT INTO users (userid, username, password, full_name, company,
email, street_address, city, state, postal_code,
country, login_attempts, last_login_attempt, verified, token)
VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 0, 0, 0, %s)
""")
try: try:
cursor.execute(query, (username, password, full_name, company, email, street_address,
city, state, postal_code, country))
cursor.execute(query, (username, password, full_name, company, email,
street_address, city, state, postal_code, country, token))
db.commit() db.commit()
except mysql.connector.Error as err: except mysql.connector.Error as err:
logger.error("Failed executing query: %s", err) logger.error("Failed executing query: %s", err)


+ 80
- 0
src/app/models/user.py 查看文件

@@ -122,3 +122,83 @@ def get_user_name_by_id(userid):
cursor.close() cursor.close()
db.close() db.close()
return username return username


def set_token(userid, token):
"""Set the given token for the given user."""
db.connect()
cursor = db.cursor()
query = ("UPDATE users SET token=%s WHERE userid=%s")
try:
cursor.execute(query, (token, userid))
db.commit()
except mysql.connector.Error as err:
print("Failed executing query: {}".format(err))
cursor.fetchall()
exit(1)
finally:
cursor.close()
db.close()


def get_userid_from_token(token):
"""Get the user with the given verify token."""
db.connect()
cursor = db.cursor()
query = ("SELECT userid FROM users WHERE token=%s")
try:
cursor.execute(query, (token,))
tokens = cursor.fetchall()
if tokens:
return tokens[0][0]
except mysql.connector.Error as err:
print("Failed executing query: {}".format(err))
cursor.fetchall()
exit(1)
finally:
cursor.close()
db.close()

return None


def verify_user(userid):
"""
Mark the user as verified.
"""
db.connect()
cursor = db.cursor()
query = ("UPDATE users SET verified=1 WHERE userid=%s AND verified=0")
try:
cursor.execute(query, (userid,))
db.commit()
except mysql.connector.Error as err:
print("Failed executing query: {}".format(err))
cursor.fetchall()
exit(1)
finally:
cursor.close()
db.close()


def is_verified(userid):
"""
Check whether the user has verified
"""
db.connect()
cursor = db.cursor()
query = ("SELECT userid FROM users WHERE verified=1 AND userid=%s")
try:
cursor.execute(query, (userid,))
users = cursor.fetchall()
if users:
return True
except mysql.connector.Error as err:
print("Failed executing query: {}".format(err))
cursor.fetchall()
exit(1)
finally:
cursor.close()
db.close()

return False

+ 19
- 0
src/app/templates/verify.html 查看文件

@@ -0,0 +1,19 @@
$def with (nav, message)

<head>
<title>Beelance2</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="static/stylesheet.css">
<link rel="shortcut icon" type="image/png" href="static/honeybee.png"/>

</head>

<body>

$:nav

<h2>$message</h2>

</body>

<footer></footer>

+ 2
- 1
src/app/views/app.py 查看文件

@@ -2,7 +2,7 @@ import os
import web import web
from views.login import Login from views.login import Login
from views.logout import Logout from views.logout import Logout
from views.register import Register
from views.register import Register, Verify
from views.new_project import New_project from views.new_project import New_project
from views.open_projects import Open_projects from views.open_projects import Open_projects
from views.project import Project from views.project import Project
@@ -17,6 +17,7 @@ urls = (
'/', 'Index', '/', 'Index',
'/login', 'Login', '/login', 'Login',
'/logout', 'Logout', '/logout', 'Logout',
'/verify', 'Verify',
'/register', 'Register', '/register', 'Register',
'/new_project', 'New_project', '/new_project', 'New_project',
'/open_projects', 'Open_projects', '/open_projects', 'Open_projects',


+ 3
- 0
src/app/views/login.py 查看文件

@@ -64,6 +64,9 @@ class Login():
if login_attempts > login_attempts_threshold: if login_attempts > login_attempts_threshold:
logger.info("User %s logged in succesfully after %s attempts", username, login_attempts) logger.info("User %s logged in succesfully after %s attempts", username, login_attempts)


if not models.user.is_verified(userid):
return render.login(nav, login_form, "- User not authenticated yet. Please check you email.")

models.user.set_login_attempts(userid, 0, time.time()) models.user.set_login_attempts(userid, 0, time.time())
self.login(username, userid, data.remember) self.login(username, userid, data.remember)
raise web.seeother("/") raise web.seeother("/")


+ 53
- 5
src/app/views/register.py 查看文件

@@ -1,6 +1,7 @@
import web import web
from views.forms import register_form from views.forms import register_form
from views.utils import get_nav_bar, csrf_protected, password_weakness, get_render
from views.utils import get_nav_bar, csrf_protected, password_weakness, get_render, sendmail
from uuid import uuid4
import models.register import models.register
import models.user import models.user
import logging import logging
@@ -49,9 +50,56 @@ class Register:


password_hash = bcrypt.hashpw(data.password.encode('UTF-8'), bcrypt.gensalt()) password_hash = bcrypt.hashpw(data.password.encode('UTF-8'), bcrypt.gensalt())


models.register.set_user(data.username, password_hash, data.full_name, data.company,
data.email, data.street_address, data.city, data.state,
data.postal_code, data.country)
# Create a verify token
while True:
token = uuid4().hex
if models.user.get_userid_from_token(token) is None:
break

models.register.set_user(
data.username,
password_hash,
data.full_name,
data.company,
data.email,
data.street_address,
data.city,
data.state,
data.postal_code,
data.country,
token,
)

verify_url = "https://{}/verify?token={}".format(web.ctx.host, token)

sendmail(
'Verify your Beelance account',
"""
Welcome to Beelance!

To verify your account, please go to this link: {url}
""".format(url=verify_url),
data.full_name,
data.email,
)


logger.info("User %s registered", data.username) logger.info("User %s registered", data.username)
return render.register(nav, register_form, "User registered!")
return render.register(nav, register_form, "User registered! We have sent you an email to verify your account.")


class Verify:
def GET(self):
"""
Verify the user email
"""
session = web.ctx.session
nav = get_nav_bar(session)
render = get_render()

token = web.input(token='').token
userid = models.user.get_userid_from_token(token)
if token and userid is not None:
models.user.verify_user(userid)
return render.verify(nav, "Your email has been verified. You can log in now.")
else:
return render.verify(nav, "Invalid token. Please try again.")

正在加载...
取消
保存