forked from Github-Mirrors/canaille
324 lines
9.9 KiB
Python
324 lines
9.9 KiB
Python
import datetime
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
import time_machine
|
|
from flask import g
|
|
|
|
from canaille.app import models
|
|
|
|
|
|
def test_index(testclient, user, backend):
|
|
res = testclient.get("/", status=302)
|
|
assert res.location == "/login"
|
|
|
|
g.user = user
|
|
res = testclient.get("/", status=302)
|
|
assert res.location == "/profile/user"
|
|
|
|
testclient.app.config["CANAILLE"]["ACL"]["DEFAULT"]["PERMISSIONS"] = []
|
|
backend.reload(g.user)
|
|
res = testclient.get("/", status=302)
|
|
assert res.location == "/about"
|
|
|
|
|
|
def test_user_deleted_in_session(testclient, backend):
|
|
u = models.User(
|
|
formatted_name="Jake Doe",
|
|
family_name="Jake",
|
|
user_name="jake",
|
|
emails=["jake@doe.test"],
|
|
password="correct horse battery staple",
|
|
)
|
|
backend.save(u)
|
|
testclient.get("/profile/jake", status=403)
|
|
|
|
with testclient.session_transaction() as session:
|
|
session["user_id"] = [u.id]
|
|
|
|
backend.delete(u)
|
|
|
|
testclient.get("/profile/jake", status=404)
|
|
with testclient.session_transaction() as session:
|
|
assert not session.get("user_id")
|
|
|
|
|
|
def test_impersonate(testclient, logged_admin, user):
|
|
res = testclient.get("/", status=302).follow(status=200).click("Account settings")
|
|
assert "admin" == res.form["user_name"].value
|
|
|
|
res = (
|
|
testclient.get("/impersonate/user", status=302)
|
|
.follow(status=302)
|
|
.follow(status=200)
|
|
.click("Account settings")
|
|
)
|
|
assert "user" == res.form["user_name"].value
|
|
|
|
testclient.get("/logout", status=302).follow(status=302).follow(status=200)
|
|
|
|
res = testclient.get("/", status=302).follow(status=200).click("Account settings")
|
|
assert "admin" == res.form["user_name"].value
|
|
|
|
|
|
def test_admin_self_deletion(testclient, backend):
|
|
admin = models.User(
|
|
formatted_name="Temp admin",
|
|
family_name="admin",
|
|
user_name="temp",
|
|
emails=["temp@temp.test"],
|
|
password="admin",
|
|
)
|
|
backend.save(admin)
|
|
with testclient.session_transaction() as sess:
|
|
sess["user_id"] = [admin.id]
|
|
|
|
res = testclient.get("/profile/temp/settings")
|
|
res = res.form.submit(name="action", value="confirm-delete")
|
|
res = (
|
|
res.form.submit(name="action", value="delete", status=302)
|
|
.follow(status=302)
|
|
.follow(status=200)
|
|
)
|
|
|
|
assert backend.get(models.User, user_name="temp") is None
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
|
|
def test_user_self_deletion(testclient, backend):
|
|
user = models.User(
|
|
formatted_name="Temp user",
|
|
family_name="user",
|
|
user_name="temp",
|
|
emails=["temp@temp.test"],
|
|
password="correct horse battery staple",
|
|
)
|
|
backend.save(user)
|
|
with testclient.session_transaction() as sess:
|
|
sess["user_id"] = [user.id]
|
|
|
|
testclient.app.config["CANAILLE"]["ACL"]["DEFAULT"]["PERMISSIONS"] = ["edit_self"]
|
|
res = testclient.get("/profile/temp/settings")
|
|
res.mustcontain(no="Delete my account")
|
|
|
|
testclient.app.config["CANAILLE"]["ACL"]["DEFAULT"]["PERMISSIONS"] = [
|
|
"edit_self",
|
|
"delete_account",
|
|
]
|
|
# Simulate an app restart
|
|
backend.reload(user)
|
|
|
|
res = testclient.get("/profile/temp/settings")
|
|
res.mustcontain("Delete my account")
|
|
res = res.form.submit(name="action", value="confirm-delete")
|
|
res = (
|
|
res.form.submit(name="action", value="delete", status=302)
|
|
.follow(status=302)
|
|
.follow(status=200)
|
|
)
|
|
|
|
assert backend.get(models.User, user_name="temp") is None
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.app.config["CANAILLE"]["ACL"]["DEFAULT"]["PERMISSIONS"] = []
|
|
|
|
|
|
def test_account_locking(user, backend):
|
|
assert not user.locked
|
|
assert not user.lock_date
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
True,
|
|
None,
|
|
)
|
|
|
|
user.lock_date = datetime.datetime.now(datetime.timezone.utc)
|
|
assert user.locked
|
|
backend.save(user)
|
|
assert user.locked
|
|
assert backend.get(models.User, id=user.id).locked
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
False,
|
|
"Your account has been locked.",
|
|
)
|
|
|
|
user.lock_date = None
|
|
backend.save(user)
|
|
assert not user.locked
|
|
assert not backend.get(models.User, id=user.id).locked
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
True,
|
|
None,
|
|
)
|
|
|
|
|
|
def test_account_locking_past_date(user, backend):
|
|
assert not user.locked
|
|
assert not user.lock_date
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
True,
|
|
None,
|
|
)
|
|
|
|
user.lock_date = datetime.datetime.now(datetime.timezone.utc).replace(
|
|
microsecond=0
|
|
) - datetime.timedelta(days=30)
|
|
backend.save(user)
|
|
assert user.locked
|
|
assert backend.get(models.User, id=user.id).locked
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
False,
|
|
"Your account has been locked.",
|
|
)
|
|
|
|
|
|
def test_account_locking_future_date(user, backend):
|
|
assert not user.locked
|
|
assert not user.lock_date
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
True,
|
|
None,
|
|
)
|
|
|
|
user.lock_date = datetime.datetime.now(datetime.timezone.utc).replace(
|
|
microsecond=0
|
|
) + datetime.timedelta(days=365 * 4)
|
|
backend.save(user)
|
|
assert not user.locked
|
|
assert not backend.get(models.User, id=user.id).locked
|
|
assert backend.check_user_password(user, "correct horse battery staple") == (
|
|
True,
|
|
None,
|
|
)
|
|
|
|
|
|
def test_account_locked_during_session(testclient, logged_user, backend):
|
|
logged_user.lock_date = datetime.datetime.now(datetime.timezone.utc)
|
|
backend.save(logged_user)
|
|
testclient.get("/profile/user/settings", status=403)
|
|
|
|
|
|
def test_expired_password_redirection_and_register_new_password_for_memory_and_sql(
|
|
testclient,
|
|
logged_user,
|
|
user,
|
|
backend,
|
|
admin,
|
|
):
|
|
"""time_machine does not work with ldap."""
|
|
if "ldap" in backend.__class__.__module__:
|
|
pytest.skip()
|
|
|
|
testclient.app.config["WTF_CSRF_ENABLED"] = False
|
|
backend.reload(logged_user)
|
|
res = testclient.get("/profile/user/settings", status=200)
|
|
res.form["password1"] = "123456789"
|
|
res.form["password2"] = "123456789"
|
|
|
|
with time_machine.travel("2020-01-01 01:00:00+00:00", tick=False) as traveller:
|
|
res = res.form.submit(name="action", value="edit-settings")
|
|
|
|
testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = "P5D"
|
|
|
|
traveller.shift(datetime.timedelta(days=5, minutes=1))
|
|
|
|
backend.reload(g.user)
|
|
|
|
res = testclient.get("/profile/user/settings")
|
|
|
|
testclient.get("/reset/admin", status=403)
|
|
|
|
assert (
|
|
"info",
|
|
"Your password has expired, please choose a new password.",
|
|
) in res.flashes
|
|
assert res.location == "/reset/user"
|
|
|
|
backend.reload(logged_user)
|
|
res = testclient.get("/reset/user")
|
|
|
|
res.form["password"] = "foobarbaz"
|
|
res.form["confirmation"] = "foobarbaz"
|
|
res = res.form.submit()
|
|
assert ("success", "Your password has been updated successfully") in res.flashes
|
|
|
|
|
|
@mock.patch("canaille.core.models.User.has_expired_password")
|
|
def test_expired_password_redirection_and_register_new_password_for_ldap_sql_and_memory(
|
|
has_expired,
|
|
testclient,
|
|
logged_user,
|
|
user,
|
|
backend,
|
|
admin,
|
|
):
|
|
"""time_machine does not work with ldap."""
|
|
has_expired.return_value = False
|
|
assert user.password_last_update is None
|
|
res = testclient.get("/profile/user/settings", status=200)
|
|
res.form["password1"] = "123456789"
|
|
res.form["password2"] = "123456789"
|
|
res = res.form.submit(name="action", value="edit-settings")
|
|
backend.reload(logged_user)
|
|
assert user.password_last_update is not None
|
|
|
|
has_expired.return_value = True
|
|
res = testclient.get("/profile/user/settings")
|
|
testclient.get("/reset/admin", status=403)
|
|
assert (
|
|
"info",
|
|
"Your password has expired, please choose a new password.",
|
|
) in res.flashes
|
|
assert res.location == "/reset/user"
|
|
backend.reload(logged_user)
|
|
backend.reload(g.user)
|
|
backend.reload(user)
|
|
|
|
res = testclient.get("/reset/user")
|
|
|
|
res.form["password"] = "foobarbaz"
|
|
res.form["confirmation"] = "foobarbaz"
|
|
res = res.form.submit()
|
|
assert ("success", "Your password has been updated successfully") in res.flashes
|
|
|
|
|
|
def test_not_expired_password_or_wrong_user_redirection(
|
|
testclient, logged_user, user, backend, admin
|
|
):
|
|
assert user.password_last_update is None
|
|
res = testclient.get("/profile/user/settings", status=200)
|
|
res.form["password1"] = "123456789"
|
|
res.form["password2"] = "123456789"
|
|
res = res.form.submit(name="action", value="edit-settings")
|
|
backend.reload(logged_user)
|
|
assert user.password_last_update is not None
|
|
|
|
def test_two_redirections(password_lifetime):
|
|
testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = password_lifetime
|
|
testclient.get("/reset/user", status=403)
|
|
testclient.get("/reset/admin", status=403)
|
|
|
|
test_two_redirections(None)
|
|
|
|
testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = "PT0S"
|
|
res = testclient.get("/profile/user/settings")
|
|
assert (
|
|
"info",
|
|
"Your password has expired, please choose a new password.",
|
|
) in res.flashes
|
|
assert res.location == "/reset/user"
|
|
|
|
testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = "P1D"
|
|
res = testclient.get("/profile/user/settings")
|
|
res.form["password1"] = "123456789"
|
|
res.form["password2"] = "123456789"
|
|
res = res.form.submit(name="action", value="edit-settings")
|
|
|
|
test_two_redirections("P1D")
|
|
|
|
|
|
def test_expired_password_needed_without_current_user(testclient, user):
|
|
testclient.get("/reset/user", status=403)
|