diff --git a/.travis.yml b/.travis.yml index 4c7783f29..912413eb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,3 +27,5 @@ deploy: app: notifications-admin on: repo: alphagov/notifications-admin + run: + - python app.py db upgrade diff --git a/app/__init__.py b/app/__init__.py index d88b74707..fc5c73b8e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -10,8 +10,10 @@ from webassets.filter import get_filter from werkzeug.exceptions import abort from app.its_dangerous_session import ItsdangerousSessionInterface +import app.proxy_fix from config import configs + db = SQLAlchemy() login_manager = LoginManager() csrf = CsrfProtect() @@ -32,7 +34,10 @@ def create_app(config_name): from app.main import main as main_blueprint application.register_blueprint(main_blueprint) + proxy_fix.init_app(application) + application.session_interface = ItsdangerousSessionInterface() + return application diff --git a/app/its_dangerous_session.py b/app/its_dangerous_session.py index ab644f057..d281b32d9 100644 --- a/app/its_dangerous_session.py +++ b/app/its_dangerous_session.py @@ -13,14 +13,14 @@ class ItsdangerousSession(CallbackDict, SessionMixin): class ItsdangerousSessionInterface(SessionInterface): - salt = 'cookie-session' session_class = ItsdangerousSession def get_serializer(self, app): + salt = app.config.get('DANGEROUS_SALT') if not app.secret_key: return None return URLSafeTimedSerializer(app.secret_key, - salt=self.salt) + salt=salt) def open_session(self, app, request): s = self.get_serializer(app) diff --git a/app/main/dao/users_dao.py b/app/main/dao/users_dao.py index 1cb7aeea2..0ebb54b6d 100644 --- a/app/main/dao/users_dao.py +++ b/app/main/dao/users_dao.py @@ -1,8 +1,13 @@ -from app import db +from app import db, login_manager from app.models import User from app.main.encryption import encrypt +@login_manager.user_loader +def load_user(user_id): + return get_user_by_id(user_id) + + def insert_user(user): user.password = encrypt(user.password) db.session.add(user) diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index abf14e4b4..78011390f 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -3,7 +3,6 @@ from datetime import datetime from flask import render_template, redirect, jsonify from flask_login import login_user -from app import login_manager from app.main import main from app.main.forms import LoginForm from app.main.dao import users_dao @@ -11,11 +10,6 @@ from app.models import User from app.main.encryption import encrypt -@login_manager.user_loader -def load_user(user_id): - return users_dao.get_user_by_id(user_id) - - @main.route("/sign-in", methods=(['GET'])) def render_sign_in(): return render_template('signin.html', form=LoginForm()) @@ -27,13 +21,13 @@ def process_sign_in(): if form.validate_on_submit(): user = users_dao.get_user_by_email(form.email_address.data) if user is None: - return jsonify(authorization=False), 404 + return jsonify(authorization=False), 401 if user.password == encrypt(form.password.data): login_user(user) else: - return jsonify(authorization=False), 404 + return jsonify(authorization=False), 401 else: - return jsonify(form.errors), 404 + return jsonify(form.errors), 400 return redirect('/two-factor') diff --git a/app/models.py b/app/models.py index 2ed53c257..7767c91a8 100644 --- a/app/models.py +++ b/app/models.py @@ -49,23 +49,17 @@ class User(db.Model): def is_active(self): return True - def is_locked(self): - if self.failed_login_count <= current_app.config['MAX_FAILED_LOGIN_COUNT']: - return False - else: - return True - def is_anonymous(self): return False def get_id(self): return self.id - @staticmethod - def load_user(user_id): - user = User.query.filter_by(id=user_id).first() - if user.is_active(): - return user + def is_locked(self): + if self.failed_login_count <= current_app.config['MAX_FAILED_LOGIN_COUNT']: + return False + else: + return True def filter_null_value_fields(obj): diff --git a/app/proxy_fix.py b/app/proxy_fix.py new file mode 100644 index 000000000..a31496411 --- /dev/null +++ b/app/proxy_fix.py @@ -0,0 +1,17 @@ +from werkzeug.contrib.fixers import ProxyFix + + +class CustomProxyFix(object): + def __init__(self, app, forwarded_proto): + self.app = ProxyFix(app) + self.forwarded_proto = forwarded_proto + + def __call__(self, environ, start_response): + environ.update({ + "HTTP_X_FORWARDED_PROTO": self.forwarded_proto + }) + return self.app(environ, start_response) + + +def init_app(app): + app.wsgi_app = CustomProxyFix(app.wsgi_app, app.config.get('HTTP_PROTOCOL', 'http')) \ No newline at end of file diff --git a/config.py b/config.py index f0558df21..6c799b561 100644 --- a/config.py +++ b/config.py @@ -13,6 +13,8 @@ class Config(object): WTF_CSRF_ENABLED = True SECRET_KEY = 'secret-key' + HTTP_PROTOCOL = 'http' + DANGEROUS_SALT = 'itsdangeroussalt' class Development(Config): @@ -24,6 +26,11 @@ class Test(Config): SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/test_notifications_admin' WTF_CSRF_ENABLED = False + +class Live(Config): + DEBUG = False + HTTP_PROTOCOL = 'https' + configs = { 'development': Development, 'test': Test diff --git a/requirements.txt b/requirements.txt index b66dabd3e..379db7cd1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ Flask-Script==2.0.5 Flask-Assets==0.11 Flask-Migrate==1.3.1 Flask-SQLAlchemy==2.0 -psycopg2==2.6.1 +psycopg2==2.6.2 SQLAlchemy==1.0.5 SQLAlchemy-Utils==0.30.5 Flask-WTF==0.11