Skip to content

Commit 8593358

Browse files
Add user model, normalize email and hash password
1 parent c83dea3 commit 8593358

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

app/models.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,54 @@
11
from datetime import datetime
22

33
from sqlalchemy import Index
4+
from werkzeug.security import generate_password_hash, check_password_hash
5+
from email_validator import validate_email, EmailNotValidError
6+
from email_normalize import normalize
47

58
from app import db
69

710

11+
class User(db.Model):
12+
__tablename__ = 'user'
13+
id = db.Column(db.Integer, primary_key=True)
14+
email = db.Column(db.String(120), nullable=False)
15+
email_normalized = db.Column(db.String(120), nullable=False, unique=True)
16+
password_hash = db.Column(db.String(256), nullable=False)
17+
created_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
18+
19+
# Does not check for non-deliverable mails. Use check_deliverability or resolve for that which does DNS checks
20+
# For more stricter validation, use confirmation emails, or a third party API
21+
@staticmethod
22+
def _normalize_email(email):
23+
# Follows RFCs, allows aliases and only lowers the domain part
24+
validated = validate_email(email, check_deliverability=False)
25+
# Lowers the local part and normalizes, removes aliases for popular email providers (gmail, yahoo etc)
26+
normalized = normalize(validated.email, resolve=False)
27+
return normalized
28+
29+
@staticmethod
30+
def get(email):
31+
try:
32+
email_normalized = User._normalize_email(email)
33+
except EmailNotValidError:
34+
return None
35+
return User.query.filter_by(email_normalized=email_normalized).scalar()
36+
37+
def set_email(self, email):
38+
try:
39+
self.email_normalized = self._normalize_email(email)
40+
self.email = email
41+
except EmailNotValidError as e:
42+
raise e
43+
44+
def set_password(self, password):
45+
# scrypt stores salt with the hash, which it uses to verify the password
46+
self.password_hash = generate_password_hash(password, method='scrypt')
47+
48+
def check_password(self, password):
49+
return check_password_hash(self.password_hash, password)
50+
51+
852
# https://stackoverflow.com/questions/2190272/sql-many-to-many-table-primary-key
953
category_subcategory = db.Table("category_subcategory",
1054
db.Column("category_id", db.Integer, db.ForeignKey("category.id", ondelete="CASCADE", onupdate="CASCADE"), primary_key=True),

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ psycopg2-binary==2.9.10
55
python-dotenv==1.0.1
66
flasgger==0.9.7.1
77
Flask-Migrate==4.1.0
8+
email_validator==1.3.1
9+
email-normalize==0.2.1

0 commit comments

Comments
 (0)