From 48b7a7dc373a7ce9739e16fe48bf4b1aca60a730 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Fri, 27 Nov 2015 16:25:56 +0000 Subject: [PATCH] 108536490: Adding the login manager and csrf token. Still need to figure out how to override the load_user method, currently it is not working. --- app/__init__.py | 6 ++++ app/main/encryption.py | 2 +- app/main/forms.py | 2 +- app/main/views/index.py | 8 ++++++ app/main/views/sign_in.py | 35 +++++++++++++++-------- app/models.py | 24 ++++++++++++++++ app/templates/signin.html | 4 +-- app/templates/temp-create-users.html | 33 +++++++++++++++++++++ config.py | 7 +++-- migrations/versions/10_create_users.py | 3 +- migrations/versions/20_initialise_data.py | 12 ++++---- tests/app/main/views/test_sign_in.py | 21 +++++++++++++- 12 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 app/templates/temp-create-users.html diff --git a/app/__init__.py b/app/__init__.py index 011e4cc2a..17f2828d0 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,11 +4,15 @@ from flask import Flask from flask._compat import string_types from flask.ext import assets from flask.ext.sqlalchemy import SQLAlchemy +from flask_login import LoginManager +from flask_wtf import CsrfProtect from webassets.filter import get_filter from config import configs db = SQLAlchemy() +login_manager = LoginManager() +csrf = CsrfProtect() def create_app(config_name): @@ -18,6 +22,8 @@ def create_app(config_name): application.config.from_object(configs[config_name]) db.init_app(application) init_app(application) + csrf.init_app(application) + login_manager.init_app(application) from app.main import main as main_blueprint application.register_blueprint(main_blueprint) diff --git a/app/main/encryption.py b/app/main/encryption.py index e42a42f22..b070fe4aa 100644 --- a/app/main/encryption.py +++ b/app/main/encryption.py @@ -3,5 +3,5 @@ from flask import current_app def encrypt(value): - key = current_app.config['SECRET_KEY'] + key = current_app.config['PASS_SECRET_KEY'] return hashlib.sha256((key + value).encode('UTF-8')).hexdigest() diff --git a/app/main/forms.py b/app/main/forms.py index a2cf5d9e9..6b7119817 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -5,7 +5,7 @@ from wtforms.validators import DataRequired, Email, Length class LoginForm(Form): email_address = StringField('Email address', validators=[ - Length(255), + Length(min=5, max=255), DataRequired(message='Email cannot be empty'), Email(message='Please enter a valid email address') ]) diff --git a/app/main/views/index.py b/app/main/views/index.py index de48cb9e6..ff2c53bfa 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -1,4 +1,5 @@ from flask import render_template +from flask_login import login_required from app.main import main @@ -19,36 +20,43 @@ def helloworld(): @main.route("/register") +@login_required def register(): return render_template('register.html') @main.route("/register-from-invite") +@login_required def registerfrominvite(): return render_template('register-from-invite.html') @main.route("/verify") +@login_required def verify(): return render_template('verify.html') @main.route("/verify-mobile") +@login_required def verifymobile(): return render_template('verify-mobile.html') @main.route("/dashboard") +@login_required def dashboard(): return render_template('dashboard.html') @main.route("/add-service") +@login_required def addservice(): return render_template('add-service.html') @main.route("/two-factor") +@login_required def twofactor(): return render_template('two-factor.html') diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index e95f661bf..6ffa19144 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -1,6 +1,6 @@ from datetime import datetime -from flask import render_template, redirect, url_for, jsonify +from flask import render_template, redirect, jsonify from flask_login import login_user from app.main import main @@ -19,25 +19,36 @@ def render_sign_in(): def process_sign_in(): form = LoginForm() if form.validate_on_submit(): - user = users_dao.get_user_by_email(form.email_address) + user = users_dao.get_user_by_email(form.email_address.data) if user is None: return jsonify(authorization=False), 404 - if user.password == encrypt(form.password): + if user.password == encrypt(form.password.data): login_user(user) else: return jsonify(authorization=False), 404 - + else: + return jsonify(form.errors), 404 return redirect('/two-factor') -@main.route('/create_user', methods=(['POST'])) +@main.route('/temp-create-users', methods=(['GET'])) +def render_create_user(): + return render_template('temp-create-users.html', form=LoginForm()) + + +@main.route('/temp-create-users', methods=(['POST'])) def create_user_for_test(): form = LoginForm() - user = Users(email_address=form.email_address, - name=form.email_address, - password=form.password, - created_at=datetime.now(), - role_id=1) - users_dao.insert_user(user) + if form.validate_on_submit(): + user = Users(email_address=form.email_address.data, + name=form.email_address.data, + password=form.password.data, + created_at=datetime.now(), + mobile_number='+447651234534', + role_id=1) + users_dao.insert_user(user) - return 'created' + return redirect('/sign-in') + else: + print(form.errors) + return redirect(form.errors), 400 diff --git a/app/models.py b/app/models.py index 56c65e7f9..08068b978 100644 --- a/app/models.py +++ b/app/models.py @@ -43,6 +43,30 @@ class Users(db.Model): return filter_null_value_fields(serialized) + def is_authenticated(self): + return True + + 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 = Users.query.filter_by(id=user_id).first() + if user.is_active(): + return user + def filter_null_value_fields(obj): return dict( diff --git a/app/templates/signin.html b/app/templates/signin.html index 7aea22245..b47f53475 100644 --- a/app/templates/signin.html +++ b/app/templates/signin.html @@ -13,7 +13,7 @@ Sign in

If you do not have an account, you can register.

- + {{ form.hidden_tag() }}

{{ form.email_address(class="form-control-2-3", autocomplete="off") }}
@@ -27,7 +27,7 @@ Sign in

- Continue +

diff --git a/app/templates/temp-create-users.html b/app/templates/temp-create-users.html new file mode 100644 index 000000000..e99abfed4 --- /dev/null +++ b/app/templates/temp-create-users.html @@ -0,0 +1,33 @@ +{% extends "admin_template.html" %} + +{% block page_title %} +Temp create users +{% endblock %} + +{% block content %} + +
+
+

Temporary page to create user

+ +

This is a temporary page to create users, the name will be the same as the email address.

+ +
+ {{ form.hidden_tag() }} +

+ + {{ form.email_address(class="form-control-2-3", autocomplete="off") }}
+

+

+ + {{ form.password(class="form-control-1-4", autocomplete="off") }}
+

+ +

+ +

+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/config.py b/config.py index 5c1ecfea1..f0558df21 100644 --- a/config.py +++ b/config.py @@ -9,7 +9,10 @@ class Config(object): SQLALCHEMY_RECORD_QUERIES = True SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/notifications_admin' MAX_FAILED_LOGIN_COUNT = 10 - SECRET_KEY = 'secret-key-unique-changeme' + PASS_SECRET_KEY = 'secret-key-unique-changeme' + + WTF_CSRF_ENABLED = True + SECRET_KEY = 'secret-key' class Development(Config): @@ -19,7 +22,7 @@ class Development(Config): class Test(Config): DEBUG = False SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/test_notifications_admin' - + WTF_CSRF_ENABLED = False configs = { 'development': Development, diff --git a/migrations/versions/10_create_users.py b/migrations/versions/10_create_users.py index 4a1a0fe1d..fbd32f153 100644 --- a/migrations/versions/10_create_users.py +++ b/migrations/versions/10_create_users.py @@ -1,6 +1,6 @@ """empty message -Revision ID: create_users +Revision ID: 10_create_users Revises: None Create Date: 2015-11-24 10:39:19.827534 @@ -13,6 +13,7 @@ down_revision = None from alembic import op import sqlalchemy as sa + def upgrade(): op.create_table('roles', sa.Column('id', sa.Integer, primary_key=True), diff --git a/migrations/versions/20_initialise_data.py b/migrations/versions/20_initialise_data.py index 4784310e6..ce5726cfc 100644 --- a/migrations/versions/20_initialise_data.py +++ b/migrations/versions/20_initialise_data.py @@ -1,15 +1,13 @@ # revision identifiers, used by Alembic. revision = '20_initialise_data' -down_revision = None - +down_revision = '10_create_users' +from app.models import Roles from alembic import op def upgrade(): - op.bulk_insert('roles', - [ - {'role': 'plaform_admin'}, - {'role': 'service_user'} - ]) + op.execute("insert into roles(role) values('platform_admin')") + op.execute("insert into roles(role) values('service_user')") + def downgrade(): op.drop_table('users') diff --git a/tests/app/main/views/test_sign_in.py b/tests/app/main/views/test_sign_in.py index 66e2ebaa2..90b6b1af0 100644 --- a/tests/app/main/views/test_sign_in.py +++ b/tests/app/main/views/test_sign_in.py @@ -1,3 +1,7 @@ +from datetime import datetime + +from app.main.dao import users_dao +from app.models import Users def test_render_sign_in_returns_sign_in_template(notifications_admin): @@ -9,9 +13,24 @@ def test_render_sign_in_returns_sign_in_template(notifications_admin): assert 'Forgotten password?' in response.get_data(as_text=True) -def test_process_sign_in_return_2fa_template(notifications_admin): +def test_process_sign_in_return_2fa_template(notifications_admin, notifications_admin_db): + user = Users(email_address='valid@example.gov.uk', + password='val1dPassw0rd!', + mobile_number='+441234123123', + name='valid', + created_at=datetime.now(), + role_id=1) + users_dao.insert_user(user) response = notifications_admin.test_client().post('/sign-in', data={'email_address': 'valid@example.gov.uk', 'password': 'val1dPassw0rd!'}) assert response.status_code == 302 assert response.location == 'http://localhost/two-factor' + + +def test_temp_create_user(notifications_admin, notifications_admin_db): + response = notifications_admin.test_client().post('/temp-create-users', + data={'email_address': 'testing@example.gov.uk', + 'password': 'val1dPassw0rd!'}) + + assert response.status_code == 302