Merge pull request #447 from sanduhrs/feature/446

Add a sqlalchemy custom type for unicode to idna conversion of domain names
master
kaiyou 6 years ago committed by GitHub
commit dfaedb76f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,12 +11,53 @@ import time
import os
import glob
import smtplib
import idna
class IdnaDomain(db.TypeDecorator):
""" Stores a Unicode string in it's IDNA representation (ASCII only)
"""
impl = db.String(80)
def process_bind_param(self, value, dialect):
return idna.encode(value)
def process_result_value(self, value, dialect):
return idna.decode(value)
class IdnaEmail(db.TypeDecorator):
""" Stores a Unicode string in it's IDNA representation (ASCII only)
"""
impl = db.String(255, collation="NOCASE")
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', db.String(80), 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'))
)
@ -26,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")
@ -54,7 +96,7 @@ class Domain(Base):
"""
__tablename__ = "domain"
name = db.Column(db.String(80), 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)
@ -110,8 +152,8 @@ class Alternative(Base):
__tablename__ = "alternative"
name = db.Column(db.String(80), primary_key=True, nullable=False)
domain_name = db.Column(db.String(80), 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'))
@ -141,19 +183,19 @@ class Email(object):
@declarative.declared_attr
def domain_name(cls):
return db.Column(db.String(80), 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"],
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