Skip to content

Commit

Permalink
Merge pull request #185 from StackFocus/1.2
Browse files Browse the repository at this point in the history
Release v1.2.0
  • Loading branch information
thatarchguy committed Mar 15, 2017
2 parents 647f333 + de8ac4a commit e80a06a
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 130 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ python:
install:
- cp config.default.py config.py
- pip install -r requirements.txt
- pip install -r test-requirements.txt
before_script:
- ./pylint-check.py
script:
Expand Down
7 changes: 4 additions & 3 deletions docs/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
### Unreleased
### v1.2.0 - Rubber Soul

BACKWARDS INCOMPATIBILITIES / NOTES:

* The default MySQL Python connector of "mysqlclient" has been replaced with "pymysql" for a pure Python replacement.
Upgrades will be unaffected by this, but please note that reinstalls will require you to either install "mysqlclient"
([Ubuntu Instructions](https://github.com/PyMySQL/mysqlclient-python#install) or change the start of your database URI
with "mysql+pymysql://" instead of "mysql://" [GH-170].
with "mysql+pymysql://" instead of "mysql://" [GH-170]

Features:

* Supports Two-Factor Authentication on the backend [GH-169]

Improvements:

* Replaced GIF spinner with a pure CSS spinner using CSS3 animations [GH-177].
* Replaced GIF spinner with a pure CSS spinner using CSS3 animations [GH-177]
* Adds Python 3.5 support [GH-179] [GH-180]
* Update Python dependencies when updating PostMaster using the deb package (apt-get) [GH-185]

Bug Fixes:

Expand Down
4 changes: 4 additions & 0 deletions ops/ansible/roles/postmaster_deploy/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
- name: Install python dependencies
pip: requirements=/opt/postmaster/git/requirements.txt virtualenv=/opt/postmaster/env

- name: Install python test dependencies
pip: requirements=/opt/postmaster/git/test-requirements.txt virtualenv=/opt/postmaster/env
when: provision_type == "dev"

- name: Checking if config.py exists
stat: path=/opt/postmaster/git/config.py
register: config_file
Expand Down
3 changes: 3 additions & 0 deletions ops/ansible/roles/postmaster_upgrade/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
args:
chdir: /opt/postmaster/git

- name: Upgrade python dependencies
pip: requirements=/opt/postmaster/git/requirements.txt virtualenv=/opt/postmaster/env

- name: Ensure apache is reloaded
become: yes
service: name=apache2 state=reloaded
2 changes: 1 addition & 1 deletion postmaster/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from flask_login import LoginManager
from flask_bcrypt import Bcrypt

__version__ = 'v1.1.0.0'
__version__ = 'v1.2.0.0'
app = Flask(__name__)

if environ.get('POSTMASTER_DEV') == 'TRUE':
Expand Down
4 changes: 2 additions & 2 deletions postmaster/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
Purpose: form definitions for the app
"""

from flask_wtf import Form
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SelectField, IntegerField
from wtforms.validators import DataRequired, Optional
from postmaster import models
from postmaster.utils import validate_wtforms_password


class LoginForm(Form):
class LoginForm(FlaskForm):
""" Class for login form on /login
"""
username = StringField(label='Username', validators=[DataRequired()])
Expand Down
10 changes: 2 additions & 8 deletions postmaster/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
from re import search, match
from os import urandom
import base64
from passlib.hash import sha512_crypt as sha512 # pylint: disable=no-name-in-module
from hashlib import sha1
import passlib.hash
import onetimepass


Expand Down Expand Up @@ -119,12 +118,7 @@ def from_json(self, json):

@staticmethod
def encrypt_password(password):
salt = (sha1(urandom(16)).hexdigest())[:16]
protectedPassword = sha512.encrypt(password,
rounds=5000,
salt=salt,
implicit_rounds=True)
return protectedPassword
return passlib.hash.sha512_crypt.hash(password, rounds=5000)


class VirtualAliases(db.Model):
Expand Down
5 changes: 0 additions & 5 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
coverage
Flask
Flask-Bcrypt
Flask-Login
Expand All @@ -7,11 +6,7 @@ Flask-Script
Flask-SQLAlchemy
Flask-WTF
ldap3
mock
onetimepass
passlib
pylint
pymysql
pyqrcode
pytest
pytest-cov
65 changes: 25 additions & 40 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,30 @@
#
# pip-compile --output-file requirements.txt requirements.in
#
alembic==0.8.7 # via flask-migrate
astroid==1.4.8 # via pylint
backports.functools-lru-cache==1.2.1 # via pylint
bcrypt==3.1.0 # via flask-bcrypt
cffi==1.7.0 # via bcrypt
click==6.6 # via flask
configparser==3.5.0 # via pylint
coverage==4.2
Flask-Bcrypt==0.7.1
Flask-Login==0.3.2
Flask-Migrate==2.0.0
Flask-Script==2.0.5 # via flask-migrate
Flask-SQLAlchemy==2.1 # via flask-migrate
Flask-WTF==0.12
Flask==0.11.1 # via flask-bcrypt, flask-login, flask-migrate, flask-script, flask-sqlalchemy, flask-wtf
funcsigs==1.0.2 # via mock
isort==4.2.5 # via pylint
alembic==0.9.1 # via flask-migrate
bcrypt==3.1.3 # via flask-bcrypt
cffi==1.9.1 # via bcrypt
click==6.7 # via flask
flask-bcrypt==0.7.1
flask-login==0.4.0
flask-migrate==2.0.3
flask-script==2.0.5
flask-sqlalchemy==2.2
flask-wtf==0.14.2
flask==0.12
itsdangerous==0.24 # via flask
Jinja2==2.8 # via flask
lazy-object-proxy==1.2.2 # via astroid
ldap3==2.1.0
Mako==1.0.4 # via alembic
MarkupSafe==0.23 # via jinja2, mako
mccabe==0.5.2 # via pylint
mock==2.0.0
jinja2==2.9.5 # via flask
ldap3==2.2.1
mako==1.0.6 # via alembic
markupsafe==1.0 # via jinja2, mako
onetimepass==1.0.1
passlib==1.6.5
pbr==1.10.0 # via mock
py==1.4.31 # via pytest
pyasn1==0.1.9 # via ldap3
pycparser==2.14 # via cffi
pylint==1.6.4
pymysql==0.7.9
PyQRCode==1.2.1
pytest-cov==2.3.1
pytest==2.9.2
python-editor==1.0.1 # via alembic
six==1.10.0 # via astroid, bcrypt, mock, onetimepass, pylint
SQLAlchemy==1.0.14 # via alembic, flask-sqlalchemy
Werkzeug==0.11.10 # via flask, flask-wtf
wrapt==1.10.8 # via astroid
WTForms==2.1 # via flask-wtf
passlib==1.7.1
pyasn1==0.2.3 # via ldap3
pycparser==2.17 # via cffi
pymysql==0.7.10
pyqrcode==1.2.1
python-editor==1.0.3 # via alembic
six==1.10.0 # via bcrypt, onetimepass
sqlalchemy==1.1.6 # via alembic, flask-sqlalchemy
werkzeug==0.12 # via flask
wtforms==2.1 # via flask-wtf
5 changes: 5 additions & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage
mock
pylint
pytest
pytest-cov
118 changes: 47 additions & 71 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,54 @@

app.config.from_object('config.TestConfiguration')

def initialize():
try:
db.session.remove()
db.drop_all()
db.create_all()
add_default_configuration_settings()
admin2 = models.Admins().from_json(
{'username': 'admin2', 'password': 'PostMaster2', 'name': 'Some Admin'})
enable_ldap_auth = models.Configs.query.filter_by(setting='Enable LDAP Authentication').first()
enable_ldap_auth.value = 'True'
ldap_server = models.Configs.query.filter_by(setting='AD Server LDAP String').first()
ldap_server.value = 'LDAPS://postmaster.local:636'
domain = models.Configs.query.filter_by(setting='AD Domain').first()
domain.value = 'postmaster.local'
ldap_admin_group = models.Configs.query.filter_by(setting='AD PostMaster Group').first()
ldap_admin_group.value = 'PostMaster Admins'
ldap_auth_method = models.Configs.query.filter_by(setting='LDAP Authentication Method').first()
# Use SIMPLE auth because ldap3 testing only allows this authentication method
ldap_auth_method.value = 'SIMPLE'

try:
db.session.add(admin2)
db.session.add(enable_ldap_auth)
db.session.add(ldap_server)
db.session.add(domain)
db.session.add(ldap_admin_group)
db.session.add(ldap_auth_method)
db.session.commit()
except:
return False

domain = models.VirtualDomains().from_json({'name': 'postmaster.com'})
domain2 = models.VirtualDomains().from_json({'name': 'postmaster.org'})

try:
db.session.add(domain)
db.session.add(domain2)
db.session.commit()
except:
return False

emailUser = models.VirtualUsers().from_json({'email': 'email@postmaster.com', 'password': 'password'})
emailUser2 = models.VirtualUsers().from_json({'email': 'email2@postmaster.com', 'password': 'password'})
emailUser3 = models.VirtualUsers().from_json({'email': 'email@postmaster.org', 'password': 'password'})

try:
db.session.add(emailUser)
db.session.add(emailUser2)
db.session.add(emailUser3)
db.session.commit()
except:
return False

alias = models.VirtualAliases().from_json({'domain_id': 1, 'source': 'aliasemail@postmaster.com', 'destination': 'email@postmaster.com'})
alias2 = models.VirtualAliases().from_json({'domain_id': 1, 'source': 'aliasemail2@postmaster.com', 'destination': 'email2@postmaster.com'})
alias3 = models.VirtualAliases().from_json({'domain_id': 1, 'source': 'aliasemail3@postmaster.com', 'destination': 'email@postmaster.org'})

try:
db.session.add(alias)
db.session.add(alias2)
db.session.add(alias3)
db.session.commit()
except:
return False

return True

except Exception as e:
print("Unexpected error: {0}".format(e.message))
return False

return False
def initialize():
db.session.remove()
db.drop_all()
db.create_all()
add_default_configuration_settings()

admin2 = models.Admins().from_json(
{'username': 'admin2', 'password': 'PostMaster2', 'name': 'Some Admin'})
db.session.add(admin2)

enable_ldap_auth = models.Configs.query.filter_by(setting='Enable LDAP Authentication').first()
enable_ldap_auth.value = 'True'
ldap_server = models.Configs.query.filter_by(setting='AD Server LDAP String').first()
ldap_server.value = 'LDAPS://postmaster.local:636'
domain = models.Configs.query.filter_by(setting='AD Domain').first()
domain.value = 'postmaster.local'
ldap_admin_group = models.Configs.query.filter_by(setting='AD PostMaster Group').first()
ldap_admin_group.value = 'PostMaster Admins'
ldap_auth_method = models.Configs.query.filter_by(setting='LDAP Authentication Method').first()
# Use SIMPLE auth because ldap3 testing only allows this authentication method
ldap_auth_method.value = 'SIMPLE'
db.session.add(enable_ldap_auth)
db.session.add(ldap_server)
db.session.add(domain)
db.session.add(ldap_admin_group)
db.session.add(ldap_auth_method)

domain = models.VirtualDomains().from_json({'name': 'postmaster.com'})
domain2 = models.VirtualDomains().from_json({'name': 'postmaster.org'})
db.session.add(domain)
db.session.add(domain2)

emailUser = models.VirtualUsers().from_json({'email': 'email@postmaster.com', 'password': 'password'})
emailUser2 = models.VirtualUsers().from_json({'email': 'email2@postmaster.com', 'password': 'password'})
emailUser3 = models.VirtualUsers().from_json({'email': 'email@postmaster.org', 'password': 'password'})
db.session.add(emailUser)
db.session.add(emailUser2)
db.session.add(emailUser3)

alias = models.VirtualAliases().from_json({'domain_id': 1, 'source': 'aliasemail@postmaster.com', 'destination': 'email@postmaster.com'})
alias2 = models.VirtualAliases().from_json({'domain_id': 1, 'source': 'aliasemail2@postmaster.com', 'destination': 'email2@postmaster.com'})
alias3 = models.VirtualAliases().from_json({'domain_id': 1, 'source': 'aliasemail3@postmaster.com', 'destination': 'email@postmaster.org'})
db.session.add(alias)
db.session.add(alias2)
db.session.add(alias3)

db.session.commit()


# Reinitialize the database before each test
Expand Down

0 comments on commit e80a06a

Please sign in to comment.