Add another type decorator for idna email support

master
Stefan Auditor 6 years ago
parent 792c720c13
commit 93d5254b3f

@ -14,12 +14,13 @@ import smtplib
import idna
class Idna(db.TypeDecorator):
class IdnaDomain(db.TypeDecorator):
""" Stores a Unicode string in it's IDNA representation (ASCII only)
"""
impl = db.String
def process_bind_param(self, value, dialect):
return idna.encode(value)
@ -27,10 +28,36 @@ class Idna(db.TypeDecorator):
return idna.decode(value)
class IdnaEmail(db.TypeDecorator):
""" Stores a Unicode string in it's IDNA representation (ASCII only)
"""
impl = db.String
def process_bind_param(self, value, dialect):
localpart, domain_name = value.split('@')
email = "{0}@{1}".format(
localpart,
idna.encode(domain_name).decode('ascii'),
)
return email
def process_result_value(self, value, dialect):
localpart, domain_name = value.split('@')
email = "{0}@{1}".format(
localpart,
idna.decode(domain_name),
)
return email
# Many-to-many association table for domain managers
managers = db.Table('manager',
db.Column('domain_name', Idna, db.ForeignKey('domain.name')),
db.Column('user_email', db.String(255), db.ForeignKey('user.email'))
db.Column('domain_name', IdnaDomain, db.ForeignKey('domain.name')),
db.Column('user_email', IdnaEmail, db.ForeignKey('user.email'))
)
@ -40,6 +67,7 @@ class CommaSeparatedList(db.TypeDecorator):
impl = db.String
def process_bind_param(self, value, dialect):
if type(value) is not list:
raise TypeError("Shoud be a list")
@ -68,7 +96,7 @@ class Domain(Base):
"""
__tablename__ = "domain"
name = db.Column(Idna, primary_key=True, nullable=False)
name = db.Column(IdnaDomain, primary_key=True, nullable=False)
managers = db.relationship('User', secondary=managers,
backref=db.backref('manager_of'), lazy='dynamic')
max_users = db.Column(db.Integer, nullable=False, default=0)
@ -124,8 +152,8 @@ class Alternative(Base):
__tablename__ = "alternative"
name = db.Column(Idna, primary_key=True, nullable=False)
domain_name = db.Column(Idna, db.ForeignKey(Domain.name))
name = db.Column(IdnaDomain, primary_key=True, nullable=False)
domain_name = db.Column(IdnaDomain, db.ForeignKey(Domain.name))
domain = db.relationship(Domain,
backref=db.backref('alternatives', cascade='all, delete-orphan'))
@ -155,19 +183,19 @@ class Email(object):
@declarative.declared_attr
def domain_name(cls):
return db.Column(Idna, db.ForeignKey(Domain.name),
nullable=False)
return db.Column(IdnaDomain, db.ForeignKey(Domain.name),
nullable=False, default=IdnaDomain)
# This field is redundant with both localpart and domain name.
# It is however very useful for quick lookups without joining tables,
# especially when the mail server il reading the database.
# especially when the mail server is reading the database.
@declarative.declared_attr
def email(cls):
updater = lambda context: "{0}@{1}".format(
context.current_parameters["localpart"],
idna.encode(context.current_parameters["domain_name"]).decode('ascii'),
context.current_parameters["domain_name"],
)
return db.Column(db.String(255, collation="NOCASE"),
return db.Column(IdnaEmail,
primary_key=True, nullable=False,
default=updater)

Loading…
Cancel
Save