2024-11-19 08:11:02 +00:00
import logging
2023-08-15 14:17:19 +00:00
from unittest import mock
2024-05-02 08:26:32 +00:00
import time_machine
2024-11-12 08:18:55 +00:00
from flask import current_app
2024-03-15 18:58:06 +00:00
from flask import url_for
2023-08-15 14:17:19 +00:00
from canaille . app import models
2023-12-25 23:23:47 +00:00
from canaille . core . endpoints . account import RegistrationPayload
2023-08-15 14:17:19 +00:00
2023-12-24 11:05:18 +00:00
def test_registration_without_email_validation ( testclient , backend , foo_group ) :
2023-12-28 17:31:57 +00:00
""" Tests a nominal registration without email validation. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
2023-08-15 14:17:19 +00:00
2024-04-10 13:44:11 +00:00
assert not backend . query ( models . User , user_name = " newuser " )
2023-08-15 14:17:19 +00:00
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
2024-11-05 14:44:25 +00:00
res . form [ " password1 " ] = " i ' m a little pea "
res . form [ " password2 " ] = " i ' m a little pea "
2023-08-15 14:17:19 +00:00
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
res = res . form . submit ( )
2023-12-24 11:05:18 +00:00
assert ( " success " , " Your account has been created successfully. " ) in res . flashes
2023-08-15 14:17:19 +00:00
2024-04-14 15:30:59 +00:00
user = backend . get ( models . User , user_name = " newuser " )
2023-08-15 14:17:19 +00:00
assert user
2024-04-14 18:37:52 +00:00
backend . delete ( user )
2023-08-15 14:17:19 +00:00
2023-12-24 11:05:18 +00:00
def test_registration_with_email_validation ( testclient , backend , smtpd , foo_group ) :
2023-12-28 17:31:57 +00:00
""" Tests a nominal registration with email validation. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
2023-08-15 14:17:19 +00:00
2024-05-02 08:26:32 +00:00
with time_machine . travel ( " 2020-01-01 02:00:00+00:00 " , tick = False ) :
2023-08-15 14:17:19 +00:00
res = testclient . get ( url_for ( " core.account.join " ) )
res . form [ " email " ] = " foo@bar.com "
res = res . form . submit ( )
assert res . flashes == [
(
" success " ,
" You will receive soon an email to continue the registration process. " ,
)
]
assert len ( smtpd . messages ) == 1
payload = RegistrationPayload (
creation_date_isoformat = " 2020-01-01T02:00:00+00:00 " ,
user_name = " " ,
user_name_editable = True ,
email = " foo@bar.com " ,
groups = [ ] ,
)
registration_url = url_for (
" core.account.registration " ,
data = payload . b64 ( ) ,
hash = payload . build_hash ( ) ,
_external = True ,
)
2023-12-14 21:01:08 +00:00
text_mail = smtpd . messages [ 0 ] . get_payload ( ) [ 0 ] . get_payload ( decode = True ) . decode ( )
2023-08-15 14:17:19 +00:00
assert registration_url in text_mail
2024-04-10 13:44:11 +00:00
assert not backend . query ( models . User , user_name = " newuser " )
2024-05-02 08:26:32 +00:00
with time_machine . travel ( " 2020-01-01 02:01:00+00:00 " , tick = False ) :
2023-08-15 14:17:19 +00:00
res = testclient . get ( registration_url , status = 200 )
res . form [ " user_name " ] = " newuser "
2024-11-05 14:44:25 +00:00
res . form [ " password1 " ] = " i ' m a little pea "
res . form [ " password2 " ] = " i ' m a little pea "
2023-08-15 14:17:19 +00:00
res . form [ " family_name " ] = " newuser "
res = res . form . submit ( )
2023-12-23 17:02:08 +00:00
assert res . flashes == [
( " success " , " Your account has been created successfully. " ) ,
]
2024-04-14 15:30:59 +00:00
user = backend . get ( models . User , user_name = " newuser " )
2023-08-15 14:17:19 +00:00
assert user
2024-04-14 18:37:52 +00:00
backend . delete ( user )
2023-08-15 14:17:19 +00:00
2023-12-24 11:05:18 +00:00
def test_registration_with_email_already_taken (
testclient , backend , smtpd , user , foo_group
) :
2023-12-28 17:31:57 +00:00
""" Be sure to not leak email existence if HIDE_INVALID_LOGINS is true. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
2023-08-15 14:17:19 +00:00
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " HIDE_INVALID_LOGINS " ] = True
2023-08-15 14:17:19 +00:00
res = testclient . get ( url_for ( " core.account.join " ) )
res . form [ " email " ] = " john@doe.com "
res = res . form . submit ( )
assert res . flashes == [
(
" success " ,
" You will receive soon an email to continue the registration process. " ,
)
]
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " HIDE_INVALID_LOGINS " ] = False
2023-08-15 14:17:19 +00:00
res = testclient . get ( url_for ( " core.account.join " ) )
res . form [ " email " ] = " john@doe.com "
res = res . form . submit ( )
assert res . flashes == [ ]
res . mustcontain ( " The email 'john@doe.com' is already used " )
def test_registration_with_email_validation_needs_a_valid_link (
2023-12-24 11:05:18 +00:00
testclient , backend , smtpd , foo_group
2023-08-15 14:17:19 +00:00
) :
2023-12-28 17:31:57 +00:00
""" Tests a nominal registration without email validation. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
2023-08-15 14:17:19 +00:00
testclient . get ( url_for ( " core.account.registration " ) , status = 403 )
2023-12-24 11:05:18 +00:00
def test_join_page_registration_disabled ( testclient , backend , smtpd , foo_group ) :
2023-12-28 17:31:57 +00:00
""" The join page should not be available if registration is disabled. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = False
2023-08-15 14:17:19 +00:00
testclient . get ( url_for ( " core.account.join " ) , status = 404 )
2023-12-24 11:05:18 +00:00
def test_join_page_email_confirmation_disabled ( testclient , backend , smtpd , foo_group ) :
2023-12-28 17:31:57 +00:00
""" The join page should directly redirect to the registration page if email
confirmation is disabled . """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
2023-08-15 14:17:19 +00:00
res = testclient . get ( url_for ( " core.account.join " ) , status = 302 )
assert res . location == url_for ( " core.account.registration " )
2023-12-24 11:05:18 +00:00
def test_join_page_already_logged_in ( testclient , backend , logged_user , foo_group ) :
2023-12-28 17:31:57 +00:00
""" The join page should not be accessible for logged users. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
2023-08-15 14:17:19 +00:00
testclient . get ( url_for ( " core.account.join " ) , status = 403 )
@mock.patch ( " smtplib.SMTP " )
2023-12-24 11:05:18 +00:00
def test_registration_mail_error ( SMTP , testclient , backend , smtpd , foo_group ) :
2023-12-28 17:31:57 +00:00
""" Display an error message if the registration mail could not be sent. """
2023-12-18 17:06:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
2023-08-15 14:17:19 +00:00
SMTP . side_effect = mock . Mock ( side_effect = OSError ( " unit test mail error " ) )
res = testclient . get ( url_for ( " core.account.join " ) )
res . form [ " email " ] = " foo@bar.com "
res = res . form . submit ( expect_errors = True )
assert res . flashes == [
(
" error " ,
" An error happened while sending your registration mail. "
" Please try again in a few minutes. "
" If this still happens, please contact the administrators. " ,
)
]
assert len ( smtpd . messages ) == 0
2024-11-05 15:59:03 +00:00
2024-11-19 13:49:36 +00:00
@mock.patch ( " requests.api.get " )
def test_registration_with_compromised_password ( api_get , testclient , backend ) :
2024-11-05 15:59:03 +00:00
""" Tests a nominal registration with compromised password. """
2024-11-14 15:10:43 +00:00
current_app . config [ " CANAILLE " ] [ " ENABLE_PASSWORD_COMPROMISSION_CHECK " ] = True
2024-11-05 15:59:03 +00:00
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
2024-11-19 13:49:36 +00:00
# This content simulates a result from the hibp api containing the suffixes of the following password hashes: 'password', '987654321', 'correct horse battery staple', 'zxcvbn123', 'azertyuiop123'
class Response :
content = b " 1E4C9B93F3F0682250B6CF8331B7EE68FD8:3 \r \n CAA6D483CC3887DCE9D1B8EB91408F1EA7A:3 \r \n AD6438836DBE526AA231ABDE2D0EEF74D42:3 \r \n 8289894DDB6317178960AB5AE98B81BBF97:1 \r \n 5FF0B6F9EAC40D5CA7B4DAA7B64F0E6F4AA:2 \r \n "
api_get . return_value = Response
2024-11-05 15:59:03 +00:00
assert not backend . query ( models . User , user_name = " newuser " )
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
2024-11-19 13:49:36 +00:00
res . form [ " password1 " ] = " 987654321 "
res . form [ " password2 " ] = " 987654321 "
2024-11-05 15:59:03 +00:00
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
res = res . form . submit ( )
2024-11-13 15:21:52 +00:00
res . mustcontain (
" This password appears on public compromission databases and is not secure. "
)
2024-11-05 15:59:03 +00:00
user = backend . get ( models . User , user_name = " newuser " )
assert user is None
2024-11-07 14:51:21 +00:00
@mock.patch ( " requests.api.get " )
def test_registration_with_compromised_password_request_api_failed_but_account_created (
2024-11-19 08:11:02 +00:00
api_get , testclient , backend , caplog
2024-11-07 14:51:21 +00:00
) :
2024-11-14 15:10:43 +00:00
current_app . config [ " CANAILLE " ] [ " ENABLE_PASSWORD_COMPROMISSION_CHECK " ] = True
2024-11-07 14:51:21 +00:00
api_get . side_effect = mock . Mock ( side_effect = Exception ( ) )
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
assert not backend . query ( models . User , user_name = " newuser " )
2024-11-12 08:18:55 +00:00
2024-11-07 14:51:21 +00:00
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
res . form [ " password1 " ] = " 123456789 "
res . form [ " password2 " ] = " 123456789 "
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
2024-11-12 15:50:51 +00:00
res = res . form . submit ( )
2024-11-19 08:11:02 +00:00
assert (
" canaille " ,
logging . ERROR ,
" Password compromise investigation failed on HIBP API. " ,
) in caplog . record_tuples
2024-11-12 15:50:51 +00:00
assert (
" error " ,
" Password compromise investigation failed. Please contact the administrators. " ,
) in res . flashes
assert ( " success " , " Your account has been created successfully. " ) in res . flashes
user = backend . get ( models . User , user_name = " newuser " )
assert user
backend . delete ( user )
@mock.patch ( " requests.api.get " )
2024-11-13 15:21:52 +00:00
def test_compromised_password_validator_with_failure_of_api_request_and_success_mail_to_admin_from_register_form (
2024-11-19 08:13:31 +00:00
api_get , testclient , backend , caplog
2024-11-12 15:50:51 +00:00
) :
2024-11-14 15:10:43 +00:00
current_app . config [ " CANAILLE " ] [ " ENABLE_PASSWORD_COMPROMISSION_CHECK " ] = True
2024-11-12 15:50:51 +00:00
api_get . side_effect = mock . Mock ( side_effect = Exception ( ) )
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
assert not backend . query ( models . User , user_name = " newuser " )
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
res . form [ " password1 " ] = " 123456789 "
res . form [ " password2 " ] = " 123456789 "
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
2024-11-07 14:51:21 +00:00
res = res . form . submit ( )
2024-11-19 08:11:02 +00:00
assert (
" canaille " ,
logging . ERROR ,
" Password compromise investigation failed on HIBP API. " ,
) in caplog . record_tuples
2024-11-07 14:51:21 +00:00
assert (
" error " ,
" Password compromise investigation failed. Please contact the administrators. " ,
) in res . flashes
2024-11-12 08:18:55 +00:00
assert (
" success " ,
" We have informed your administrator about the failure of the password compromise investigation. " ,
) in res . flashes
assert ( " success " , " Your account has been created successfully. " ) in res . flashes
user = backend . get ( models . User , user_name = " newuser " )
assert user
backend . delete ( user )
@mock.patch ( " requests.api.get " )
2024-11-13 15:21:52 +00:00
def test_compromised_password_validator_with_failure_of_api_request_and_fail_to_send_mail_to_admin_from_register_form (
2024-11-19 08:13:31 +00:00
api_get , testclient , backend , caplog
2024-11-12 08:18:55 +00:00
) :
2024-11-14 15:10:43 +00:00
current_app . config [ " CANAILLE " ] [ " ENABLE_PASSWORD_COMPROMISSION_CHECK " ] = True
2024-11-12 08:18:55 +00:00
api_get . side_effect = mock . Mock ( side_effect = Exception ( ) )
current_app . config [ " CANAILLE " ] [ " SMTP " ] [ " TLS " ] = False
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
assert not backend . query ( models . User , user_name = " newuser " )
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
res . form [ " password1 " ] = " 123456789 "
res . form [ " password2 " ] = " 123456789 "
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
res = res . form . submit ( )
2024-11-19 08:11:02 +00:00
assert (
" canaille " ,
logging . ERROR ,
" Password compromise investigation failed on HIBP API. " ,
) in caplog . record_tuples
2024-11-12 08:18:55 +00:00
assert (
" error " ,
" Password compromise investigation failed. Please contact the administrators. " ,
) in res . flashes
assert (
" error " ,
" An error occurred while communicating the incident to the administrators. "
" Please update your password as soon as possible. "
" If this still happens, please contact the administrators. " ,
) in res . flashes
2024-11-07 14:51:21 +00:00
assert ( " success " , " Your account has been created successfully. " ) in res . flashes
user = backend . get ( models . User , user_name = " newuser " )
assert user
backend . delete ( user )
2024-11-19 13:49:36 +00:00
@mock.patch ( " requests.api.get " )
def test_compromised_password_validator_with_failure_of_api_request_without_smtp_from_register_form (
api_get , testclient , backend , caplog
) :
current_app . config [ " CANAILLE " ] [ " ENABLE_PASSWORD_COMPROMISSION_CHECK " ] = True
api_get . side_effect = mock . Mock ( side_effect = Exception ( ) )
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
assert not backend . query ( models . User , user_name = " newuser " )
current_app . config [ " CANAILLE " ] [ " SMTP " ] = None
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
res . form [ " password1 " ] = " 123456789 "
res . form [ " password2 " ] = " 123456789 "
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
res = res . form . submit ( )
assert (
" canaille " ,
logging . ERROR ,
" Password compromise investigation failed on HIBP API. " ,
) in caplog . record_tuples
assert (
" error " ,
" Password compromise investigation failed. Please contact the administrators. " ,
) not in res . flashes
user = backend . get ( models . User , user_name = " newuser " )
assert user
backend . delete ( user )
@mock.patch ( " requests.api.get " )
def test_compromised_password_validator_with_failure_of_api_request_without_admin_email_from_register_form (
api_get , testclient , backend , caplog
) :
current_app . config [ " CANAILLE " ] [ " ENABLE_PASSWORD_COMPROMISSION_CHECK " ] = True
api_get . side_effect = mock . Mock ( side_effect = Exception ( ) )
testclient . app . config [ " CANAILLE " ] [ " ENABLE_REGISTRATION " ] = True
testclient . app . config [ " CANAILLE " ] [ " EMAIL_CONFIRMATION " ] = False
assert not backend . query ( models . User , user_name = " newuser " )
current_app . config [ " CANAILLE " ] [ " ADMIN_EMAIL " ] = None
res = testclient . get ( url_for ( " core.account.registration " ) , status = 200 )
res . form [ " user_name " ] = " newuser "
res . form [ " password1 " ] = " 123456789 "
res . form [ " password2 " ] = " 123456789 "
res . form [ " family_name " ] = " newuser "
res . form [ " emails-0 " ] = " newuser@example.com "
res = res . form . submit ( )
assert (
" canaille " ,
logging . ERROR ,
" Password compromise investigation failed on HIBP API. " ,
) in caplog . record_tuples
assert (
" error " ,
" Password compromise investigation failed. Please contact the administrators. " ,
) not in res . flashes
user = backend . get ( models . User , user_name = " newuser " )
assert user
backend . delete ( user )