Permalink
Browse files

Wtf?

  • Loading branch information...
1 parent 0f110bb commit 4d9fdedcb7b2144d70a0ed8233c83f406d804276 Kura committed Mar 19, 2012
Showing with 4,292 additions and 0 deletions.
  1. 0 README.rst
  2. +36 −0 postfixmgmt/__init__.py
  3. BIN postfixmgmt/__init__.pyc
  4. +63 −0 postfixmgmt/auth.py
  5. BIN postfixmgmt/auth.pyc
  6. +11 −0 postfixmgmt/config.py
  7. BIN postfixmgmt/config.pyc
  8. +121 −0 postfixmgmt/forms.py
  9. BIN postfixmgmt/forms.pyc
  10. +111 −0 postfixmgmt/models.py
  11. BIN postfixmgmt/models.pyc
  12. +6 −0 postfixmgmt/regex.py
  13. BIN postfixmgmt/regex.pyc
  14. +363 −0 postfixmgmt/server.py
  15. BIN postfixmgmt/static/fonts/style_381.eot
  16. +356 −0 postfixmgmt/static/fonts/style_381.svg
  17. BIN postfixmgmt/static/fonts/style_381.ttf
  18. BIN postfixmgmt/static/fonts/style_381.woff
  19. BIN postfixmgmt/static/images/bullet.gif
  20. BIN postfixmgmt/static/images/footer.jpg
  21. BIN postfixmgmt/static/images/header.jpg
  22. +4 −0 postfixmgmt/static/js/functions.js
  23. +858 −0 postfixmgmt/static/js/jquery.fileupload.js
  24. +165 −0 postfixmgmt/static/js/jquery.iframe-transport.js
  25. +1,186 −0 postfixmgmt/static/js/jquery.validate.js
  26. +282 −0 postfixmgmt/static/js/vendor/jquery.ui.widget.js
  27. +13 −0 postfixmgmt/static/mobile.css
  28. +112 −0 postfixmgmt/static/style.css
  29. +13 −0 postfixmgmt/templates/_formhelpers.html
  30. +49 −0 postfixmgmt/templates/address_add.html
  31. +46 −0 postfixmgmt/templates/address_edit.html
  32. +41 −0 postfixmgmt/templates/address_index.html
  33. +32 −0 postfixmgmt/templates/address_password_edit.html
  34. +35 −0 postfixmgmt/templates/admin_add.html
  35. +36 −0 postfixmgmt/templates/admin_index.html
  36. +46 −0 postfixmgmt/templates/alias_add.html
  37. +46 −0 postfixmgmt/templates/alias_edit.html
  38. +37 −0 postfixmgmt/templates/alias_index.html
  39. +45 −0 postfixmgmt/templates/base.html
  40. +33 −0 postfixmgmt/templates/domain_add.html
  41. +27 −0 postfixmgmt/templates/domain_edit.html
  42. +34 −0 postfixmgmt/templates/domain_index.html
  43. +6 −0 postfixmgmt/templates/index.html
  44. +34 −0 postfixmgmt/templates/login.html
  45. +6 −0 postfixmgmt/templates/page_not_found.html
  46. +39 −0 setup.py
View
No changes.
View
@@ -0,0 +1,36 @@
+import os
+from flask import Flask
+from flaskext.sqlalchemy import SQLAlchemy
+from postfixmgmt.config import *
+
+
+__version__ = "Postfix MGMT 0.0.1 Alpha"
+
+
+app = Flask(__name__)
+app.debug = True
+app.secret_key = os.urandom(128)
+app.config['SQLALCHEMY_DATABASE_URI'] = DB
+db = SQLAlchemy(app)
+
+
+# Lifted from Django
+def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
+ """
+ Returns a bytestring version of 's', encoded as specified in 'encoding'.
+
+ If strings_only is True, don't convert (some) non-string-like objects.
+ """
+ if strings_only and isinstance(s, (types.NoneType, int)):
+ return s
+ if not isinstance(s, basestring):
+ try:
+ return str(s)
+ except UnicodeEncodeError:
+ return unicode(s).encode(encoding, errors)
+ elif isinstance(s, unicode):
+ return s.encode(encoding, errors)
+ elif s and encoding != 'utf-8':
+ return s.decode('utf-8', errors).encode(encoding, errors)
+ else:
+ return s
View
Binary file not shown.
View
@@ -0,0 +1,63 @@
+import base64
+import hashlib
+import bcrypt
+import hmac
+import pdb
+from postfixmgmt.config import *
+from postfixmgmt import smart_str
+
+
+latest_key_id = max(HMAC_KEYS.keys())
+shared_key = HMAC_KEYS[latest_key_id]
+
+
+def md5_password(password):
+ m = hashlib.md5()
+ m.update(password)
+ return m.hexdigest()
+
+def _hmac_create(userpwd, shared_key):
+ hmac_value = base64.b64encode(hmac.new(
+ smart_str(shared_key), smart_str(userpwd), hashlib.sha512).digest())
+ return hmac_value
+
+def _bcrypt_create(hmac_value):
+ bcrypt_value = bcrypt.hashpw(hmac_value, bcrypt.gensalt(12))
+ return bcrypt_value
+
+def create_password(userpwd):
+ return ''.join((
+ 'bcrypt', _bcrypt_create(_hmac_create(userpwd, shared_key)),
+ '$', latest_key_id))
+
+def check_user_password(user, raw_password):
+ from postfixmgmt.models import db, Admin
+
+ algo_and_hash, key_ver = user.password.rsplit('$', 1)
+ try:
+ ushared_key = HMAC_KEYS[key_ver]
+ except KeyError:
+ return False
+
+ if check_password(user.password, raw_password):
+ if key_ver != shared_key:
+ p = create_password(raw_password)
+ user.password = p
+ db.session.add(user)
+ db.session.commit()
+ return True
+
+def check_password(enc_password, raw_password):
+ algo_and_hash, key_ver = enc_password.rsplit('$', 1)
+ try:
+ ushared_key = HMAC_KEYS[key_ver]
+ except KeyError:
+ return False
+
+ bc_value = algo_and_hash[6:]
+ hmac_value = _hmac_create(raw_password, ushared_key)
+ return _bcrypt_verify(hmac_value, bc_value)
+
+def _bcrypt_verify(hmac_value, bcrypt_value):
+ """Verify an hmac hash against a bcrypt value."""
+ return bcrypt.hashpw(hmac_value, bcrypt_value) == bcrypt_value
View
Binary file not shown.
View
@@ -0,0 +1,11 @@
+DB = "sqlite:////tmp/postfixmgmt.db"
+
+CSRF_ENABLED = True
+CSRF_SESSION_KEY = "postfixmgmt_csrf_token"
+
+
+HMAC_KEYS = {
+ '2011-02-09': 'SHwn8r2BssIRrfjF0DIqxc5EIJstybgQ',
+ '2012-02-13': """30"m$wC5{uy~>h3W(_pu;R4W6]dCHL\1IcMhO~dya"R*hggZ=N"&P/k6+NwElAx2o}Z+i(.+K=n[iQt9;rfuAY\upB"q:hwFHYwjr59c9^4)`K6x~p<ksX2'"pEz_NNU""",
+ '2012-02-14': """pGr[=],u\BOA13I6?Huw"U_,y%7r<MT~IY#pyhPiU{jkQ%2|HgnKp2IBQ3urA+U](9vU}a9OeB-wbC5cG+[w3%id[uhGe4APA#&Tx+~{/UB5=^K(:t\jks~JIYZ<F<OV""",
+}
View
Binary file not shown.
View
@@ -0,0 +1,121 @@
+from wtforms import Form, validators
+from flaskext.wtf import Form, TextField, BooleanField, PasswordField, FileField, TextAreaField, HiddenField, SelectField
+from wtforms.validators import ValidationError, StopValidation
+from postfixmgmt.auth import check_user_password, check_password
+from postfixmgmt.models import Admin, Domain, Address
+from postfixmgmt.regex import DOMAIN_REGEX, EMAIL_USERNAME_REGEX, EMAIL_ADDRESS_REGEX
+
+
+def validate_login_user(form, field):
+ if not Admin.query.filter_by(email=field.data).first():
+ raise ValidationError("Invalid login")
+ u = Admin.query.filter_by(email=form.data['email']).first()
+ if not check_user_password(u, form.data['password']):
+ raise ValidationError("Invalid login")
+ return True
+
+def validate_active_user(form, field):
+ if not Admin.query.filter_by(email=field.data, active=True).first():
+ raise ValidationError("Account is not active")
+
+def validate_domain_doesnt_exist(form, field):
+ if Domain.query.filter_by(name=field.data).first():
+ raise ValidationError("Domain already exists")
+
+def validate_domain_name(form, field):
+ if not DOMAIN_REGEX.match(field.data):
+ raise ValidationError("Invalid domain name format")
+
+def validate_email_username(form, field):
+ if not EMAIL_USERNAME_REGEX.match(field.data):
+ raise ValidationError("Invalid username format")
+
+def validate_combined_email_address(form, field):
+ if not EMAIL_ADDRESS_REGEX.match("%s@%s" % (form.data['username'], form.data['domain'])):
+ raise ValidationError("Invalid email address format")
+
+def validate_email_address(form, field):
+ if not EMAIL_ADDRESS_REGEX.match(field.data):
+ raise ValidationError("Invalid email address format")
+
+def validate_combined_email_address_doesnt_exist(form, field):
+ if Address.query.filter_by(username=form.data['username'],
+ domain=Domain.query.filter_by(name=form.data['domain']).first()
+ ).first():
+ raise ValidationError("Account already exists")
+
+def validate_admin_doesnt_exist(form, field):
+ if Admin.query.filter_by(email=field.data).first():
+ raise ValidationError("Account already exists")
+
+class LoginForm(Form):
+ email = TextField('Email', [validators.Required(message="Please enter your email address"), validate_login_user, validate_active_user])
+ password = PasswordField('Password', [validators.Required(message="Please enter your password")])
+
+
+class DomainAddForm(Form):
+ name = TextField('Name', [validators.Required(message="You need to provide a domain name"), validate_domain_doesnt_exist, validate_domain_name])
+ description = TextAreaField('Description')
+
+
+class DomainEditForm(Form):
+ id = HiddenField()
+ name = TextField("Name", [validators.Required(message="You need to provide a domain name"), validate_domain_doesnt_exist, validate_domain_name])
+ description = TextAreaField('Description')
+
+
+class AddressAddForm(Form):
+ username = TextField('Username', [validators.Required(message="You need to provide a username"),
+ validators.Length(min=1, max=128), validate_email_username, validate_combined_email_address,
+ validate_combined_email_address_doesnt_exist])
+ domain = SelectField('Domain', [validators.Required("You need to select a domain"), validate_domain_name], choices=[])
+ password = PasswordField('Password', [validators.Required(message="You need to provide a password"), validators.Length(min=6)])
+ active = BooleanField('Active')
+
+
+class AddressEditForm(Form):
+ id = HiddenField()
+ username = TextField('Username', [validators.Required(message="You need to provide a username"),
+ validators.Length(min=1, max=128), validate_email_username, validate_combined_email_address])
+ domain = SelectField('Domain', [validators.Required("You need to select a domain"), ], choices=[])
+ active = BooleanField('Active')
+
+
+class AddressPasswordEditForm(Form):
+ id = HiddenField()
+ password = PasswordField('Password', [validators.Required("You need to provide a password"), validators.Length(min=6)])
+
+
+class AliasAddForm(Form):
+ username = TextField("Username", [validators.Required(message="You need to provide a username"),
+ validators.Length(min=1, max=128), validate_email_username, validate_email_address,
+ validate_combined_email_address_doesnt_exist])
+ domain = SelectField('Domain', [validators.Required("You need to select a domain"), validate_domain_name], choices=[])
+ goto = TextField("Goto", [validators.Required(message="You need to provide a goto address"), validate_email_address])
+
+
+class AliasEditForm(Form):
+ id = HiddenField()
+ username = TextField("Username", [validators.Required(message="You need to provide a username"),
+ validators.Length(min=1, max=128), validate_email_username, validate_email_address,
+ validate_combined_email_address_doesnt_exist])
+ domain = SelectField('Domain', [validators.Required("You need to select a domain")], choices=[])
+ goto = TextField("Goto", [validators.Required(message="You need to provide a goto address"), validate_email_address])
+
+class AdminAddForm(Form):
+ email = TextField("Email", [validators.Required(message="You need to provide an email address"), validate_email_address,
+ validate_admin_doesnt_exist])
+ password = PasswordField("Password", [validators.Required(message="You need to provide a password"), validators.Length(min=6)])
+ active = BooleanField("Active")
+
+
+class AdminEditForm(Form):
+ id = HiddenField()
+ email = TextField("Email", [validators.Required(message="You need to provide an email address"), validate_email_address,
+ validate_admin_doesnt_exist])
+ active = BooleanField("Active")
+
+
+class AdminEditPassword(Form):
+ id = HiddenField()
+ password = PasswordField("Password", [validators.Required(message="You need to provide a password"), validators.Length(min=6)])
View
Binary file not shown.
View
@@ -0,0 +1,111 @@
+import datetime
+from flask import Flask
+from flaskext.sqlalchemy import SQLAlchemy
+from postfixmgmt import app, db
+from postfixmgmt.auth import create_password
+
+
+def get_or_create(session, model, **kwargs):
+ instance = session.query(model).filter_by(**kwargs).first()
+ if instance:
+ return instance
+ else:
+ instance = model(**kwargs)
+ session.add(instance)
+ session.commit()
+ return instance
+
+
+class Admin(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ email = db.Column(db.String(80), unique=True)
+ password = db.Column(db.String(128))
+ active = db.Column(db.Boolean(), default=False)
+
+ def __init__(self, email, password, active):
+ self.email = email
+ self.password = password
+ self.active = active
+
+ def __repr__(self):
+ return '<Admin %r>' % self.email
+
+
+class Session(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ admin_id = db.Column(db.Integer, db.ForeignKey('admin.id'))
+ admin = db.relationship('Admin', single_parent=True)
+ key = db.Column(db.String(128), unique=True)
+
+ def __init__(self, user, key):
+ self.user = user
+ self.key = key
+
+ def __repr__(self):
+ return '<Session %r>' % self.key
+
+
+class Domain(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(128), unique=True, index=True)
+ description = db.Column(db.String())
+
+ def __init__(self, name, description):
+ self.name = name
+ self.description = description
+
+ def __repr__(self):
+ return '<Domain %r>' % self.name
+
+ def __unicode__(self):
+ return self.name
+
+ def __str__(self):
+ return str(self.__unicode__())
+
+
+class Address(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ username = db.Column(db.String(128), index=True)
+ domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'), index=True)
+ domain = db.relationship('Domain', single_parent=True, backref=db.backref('addresses', lazy='dynamic'))
+ password = db.Column(db.String(128))
+ active = db.Column(db.Boolean(), default=False)
+
+ def __init__(self, username, domain, password, active):
+ self.username = username
+ self.domain = domain
+ self.password = self.password
+ self.active = active
+
+ def __repr__(self):
+ return '<Address %r@%r>' % (self.username, self.domain.name)
+
+ def __unicode__(self):
+ return '%s@%s' % (self.username, self.domain)
+
+ def __str__(self):
+ return str(self.__unicode__())
+
+class Alias(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ username = db.Column(db.String(128), index=True)
+ domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'), index=True)
+ domain = db.relationship('Domain', single_parent=True, backref=db.backref('aliases', lazy='dynamic'))
+ goto = db.Column(db.String(254), index=True)
+ active = db.Column(db.Boolean(), default=False)
+
+ def __init__(self, username, domain, goto, active):
+ self.username = username
+ self.domain = domain
+ self.goto = goto
+ self.active = active
+
+ def __repr__(self):
+ return '<Alias %r@%r:%r>' % (self.username, self.domain.name, self.goto)
+
+ def __unicode__(self):
+ return self.goto
+
+ def __str__(self):
+ return str(self.__unicode__())
View
Binary file not shown.
View
@@ -0,0 +1,6 @@
+import re
+
+DOMAIN_REGEX = re.compile(r"([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|h[kmnrtu]|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])", re.IGNORECASE)
+
+EMAIL_USERNAME_REGEX = re.compile(r"^[A-Z0-9._%+-]+$", re.IGNORECASE)
+EMAIL_ADDRESS_REGEX = re.compile(r"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", re.IGNORECASE)
View
Binary file not shown.
Oops, something went wrong.

0 comments on commit 4d9fded

Please sign in to comment.