diff --git a/app/models/users.py b/app/models/users.py index 68e18f2..8d868ad 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -143,7 +143,7 @@ class User(db.Model, UserMixin): # User email information email = db.Column(db.String(255), nullable=True, unique=True) - email_confirmed_at = db.Column(db.DateTime(), nullable=True) + email_confirmed_at = db.Column(db.DateTime(), nullable=True, server_default=None) # User information profile_pic = db.Column(db.String(255), nullable=True, server_default=None) @@ -178,7 +178,6 @@ class User(db.Model, UserMixin): def __init__(self, username=None, active=False, email=None, password=None): self.username = username - self.email_confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000) self.display_name = username self.is_active = active self.email = email diff --git a/app/tests/integ/test_user.py b/app/tests/integ/test_user.py new file mode 100644 index 0000000..e5df950 --- /dev/null +++ b/app/tests/integ/test_user.py @@ -0,0 +1,86 @@ +from flask import url_for + +from app.models import User, UserEmailVerification +from .utils import login, logout, is_logged_in +from .utils import client # noqa + + +def test_login_logout(client): + rv = client.get("/") + assert not is_logged_in(rv) + + rv = login(client, "rubenwardy", "tuckfrump") + assert b"Logged in successfully." in rv.data + assert is_logged_in(rv) + + rv = client.get("/") + assert is_logged_in(rv) + + rv = logout(client) + assert not is_logged_in(rv) + + rv = login(client, "rubenwardy", "wrongpass") + assert b"Incorrect password. Did you set one?" in rv.data + assert not is_logged_in(rv) + + rv = login(client, "badname", "wrongpass") + assert b"User badname does not exist" in rv.data + assert not is_logged_in(rv) + + rv = login(client, "bad@email.com", "wrongpass") + assert b"Incorrect email or password" in rv.data + assert not is_logged_in(rv) + + +def register(client, username, display_name, password, email, question): + return client.post("/user/register/", data=dict( + username=username, + display_name=display_name, + email=email, + password=password, + question=question, + agree=True + ), follow_redirects=True) + + +def test_register(client): + username = "testuser123" + assert User.query.filter_by(username=username).first() is None + + rv = register(client, username, "Test User", "password", "test@example.com", "13") + assert b"Incorrect captcha answer" in rv.data + + rv = register(client, "££££!!!", "Test User", "password", "test@example.com", "13") + assert b"invalid-feedback" in rv.data + assert b"Only a-zA-Z0-9._ allowed

" in rv.data + + +def test_register_flow(client): + username = "testuser123" + + assert User.query.filter_by(username=username).first() is None + + rv = register(client, username, "Test User", "password", "test@example.com", "19") + assert b"Check your email address to verify your account" in rv.data + + user = User.query.filter_by(username=username).first() + assert user is not None + assert user.username == username + assert user.display_name == "Test User" + assert not user.is_active + assert user.email_confirmed_at is None + assert user.email == "test@example.com" + + rv = login(client, username, "password") + assert b"You need to confirm the registration email" in rv.data + assert not is_logged_in(rv) + + email = UserEmailVerification.query.filter_by(user_id=user.id).first() + assert email is not None + + rv = client.get(url_for('users.verify_email', token=email.token), follow_redirects=True) + assert b"You may now log in" in rv.data + + rv = login(client, username, "password") + assert b"Logged in successfully." in rv.data + assert is_logged_in(rv) diff --git a/app/tests/integ/utils.py b/app/tests/integ/utils.py index 4afaaab..099dc2e 100644 --- a/app/tests/integ/utils.py +++ b/app/tests/integ/utils.py @@ -35,6 +35,7 @@ def is_int(v): @pytest.fixture def client(): app.config["TESTING"] = True + app.config['WTF_CSRF_ENABLED'] = False recreate_db() assert User.query.count() == 1 @@ -43,6 +44,7 @@ def client(): yield client app.config["TESTING"] = False + app.config['WTF_CSRF_ENABLED'] = True def validate_package_list(packages, strict=False): @@ -65,3 +67,18 @@ def validate_package_list(packages, strict=False): assert is_optional(str, package.get("thumbnail")) assert is_str(package.get("title")) assert is_str(package.get("type")) + + +def login(client, username, password): + return client.post("/user/login/", data=dict( + username=username, + password=password, + ), follow_redirects=True) + + +def logout(client): + return client.post("/user/logout/", follow_redirects=True) + + +def is_logged_in(rv): + return b"/user/login/" not in rv.data and b"/user/logout/" in rv.data diff --git a/migrations/versions/105d4c740ad6_.py b/migrations/versions/105d4c740ad6_.py index c55c280..92336a5 100644 --- a/migrations/versions/105d4c740ad6_.py +++ b/migrations/versions/105d4c740ad6_.py @@ -5,6 +5,8 @@ Revises: 886c92dc6eaa Create Date: 2020-12-15 17:28:56.559801 """ +import datetime + from alembic import op import sqlalchemy as sa @@ -25,6 +27,7 @@ def upgrade(): conn = op.get_bind() system_user = User("ContentDB", active=False) + system_user.email_confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000) system_user.rank = UserRank.BOT session = orm.Session(bind=conn) diff --git a/migrations/versions/8ee3cf3fb312_.py b/migrations/versions/8ee3cf3fb312_.py new file mode 100644 index 0000000..5dc25c5 --- /dev/null +++ b/migrations/versions/8ee3cf3fb312_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: 8ee3cf3fb312 +Revises: e82c2141fae3 +Create Date: 2021-05-03 22:21:02.167758 + +""" +from alembic import op +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '8ee3cf3fb312' +down_revision = 'e82c2141fae3' +branch_labels = None +depends_on = None + + +def upgrade(): + op.alter_column('user', 'email_confirmed_at', + existing_type=postgresql.TIMESTAMP(), + nullable=True) + op.execute("""UPDATE "user" SET email_confirmed_at = NULL WHERE email_confirmed_at < '2016-01-01'::date""") + + +def downgrade(): + op.alter_column('user', 'email_confirmed_at', + existing_type=postgresql.TIMESTAMP(), + nullable=False) + op.execute( + """UPDATE "user" SET email_confirmed_at = '2004-01-01'::date WHERE email_confirmed_at IS NULL""")