def test_verify_success_if_correct_information_supplied(): # Subtests: # a) Verify 'authbcrypt' table has new hash # b) Verify 'forgotpassword' row is removed. # > Requirement: Get token set from request() user_id = db_utils.create_user(email_addr=email_addr, username=user_name) password = '******' form_for_request = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year) resetpassword.request(form_for_request) pw_reset_token = d.engine.scalar( "SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) # Force update link_time (required) resetpassword.prepare(pw_reset_token) form = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year, token=pw_reset_token, password=password, passcheck=password) resetpassword.reset(form) # 'forgotpassword' row should not exist after a successful reset row_does_not_exist = d.engine.execute( "SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) assert row_does_not_exist.first() is None bcrypt_hash = d.engine.scalar( "SELECT hashsum FROM authbcrypt WHERE userid = %(id)s", id=user_id) assert bcrypt.checkpw(password.encode('utf-8'), bcrypt_hash.encode('utf-8'))
def test_password_reset_fails_if_attempted_from_different_ip_address(): # Two parts: Set forgot password record; attempt reset with incorrect IP Address in forgotpassword table vs. requesting IP # Requirement: Get token set from request() user_id = db_utils.create_user(email_addr=email_addr, username=user_name) password = '******' form_for_request = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year) resetpassword.request(form_for_request) pw_reset_token = d.engine.scalar( "SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) # Change IP detected when request was made (required for test) d.engine.execute( "UPDATE forgotpassword SET address = %(addr)s WHERE token = %(token)s", addr="127.42.42.42", token=pw_reset_token) # Force update link_time (required) resetpassword.prepare(pw_reset_token) form_for_reset = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year, token=pw_reset_token, password=password, passcheck=password) with pytest.raises(WeasylError) as err: resetpassword.reset(form_for_reset) assert 'addressInvalid' == err.value.value
def test_emailIncorrect_WeasylError_if_email_address_doesnt_match_stored_email( ): # Two parts: Set forgot password record; attempt reset with incorrect email # Requirement: Get token set from request() user_id = db_utils.create_user(email_addr=email_addr, username=user_name) password = '******' form_for_request = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year) resetpassword.request(form_for_request) pw_reset_token = d.engine.scalar( "SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) # Force update link_time (required) resetpassword.prepare(pw_reset_token) email_addr_mismatch = "*****@*****.**" form_for_reset = Bag(email=email_addr_mismatch, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year, token=pw_reset_token, password=password, passcheck=password) with pytest.raises(WeasylError) as err: resetpassword.reset(form_for_reset) assert 'emailIncorrect' == err.value.value
def test_username_cant_be_blank_or_have_semicolon(): form = Bag(username='******', password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value form.username = '******' with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value
def test_create_fails_if_username_is_a_prohibited_name(): form = Bag(username='******', password='******', passcheck='0123456789', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year=arrow.now().year - 19) prohibited_names = ["admin", "administrator", "mod", "moderator", "weasyl", "weasyladmin", "weasylmod", "staff", "security"] for name in prohibited_names: form.username = name with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value
def test_verify_subdomains_of_blocked_sites_blocked(self): """ Blacklisted: badsite.net Blocked: badsite.net Also blocked: subdomain.badsite.net """ d.engine.execute( d.meta.tables["emailblacklist"].insert(), { "domain_name": "blacklisted.com", "reason": "test case for login.create()", "added_by": db_utils.create_user(), }) # Test the domains from the emailblacklist table blacklisted_email = "*****@*****.**" form = Bag(username=user_name, password='******', passcheck='0123456789', email=blacklisted_email, emailcheck=blacklisted_email, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'emailBlacklisted' == err.value.value # Test the domains from the code that would download the list of disposable domains blacklisted_email = "*****@*****.**" form = Bag(username=user_name, password='******', passcheck='0123456789', email=blacklisted_email, emailcheck=blacklisted_email, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'emailBlacklisted' == err.value.value # Ensure address in the form of <domain.domain> is blocked blacklisted_email = "*****@*****.**" form = Bag(username=user_name, password='******', passcheck='0123456789', email=blacklisted_email, emailcheck=blacklisted_email, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'emailBlacklisted' == err.value.value
def test_forcing_password_reset_with_mismatched_pw_fails(): user_id = db_utils.create_user() password = '******' form = Bag(password=password, passcheck='1234567890987') with pytest.raises(WeasylError) as err: resetpassword.force(user_id, form) assert 'passwordMismatch' == err.value.value
def test_create_fails_if_pending_account_has_same_email(): """ Test checks to see if an email is tied to a pending account creation entry in logincreate. If so, login.create() will not permit another account to be made for the same address. """ d.engine.execute( d.meta.tables["logincreate"].insert(), { "token": 40 * "a", "username": "******", "login_name": "existing", "hashpass": login.passhash(raw_password), "email": email_addr, "birthday": arrow.Arrow(2000, 1, 1), "unixtime": arrow.now(), }) form = Bag(username="******", password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) login.create(form) query = d.engine.scalar(""" SELECT username FROM logincreate WHERE username = %(username)s AND invalid IS TRUE """, username=form.username) assert query == "test"
def test_username_cant_be_blank_or_have_semicolon(): form = Bag(username='******', password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value form.username = '******' with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value
def test_verify_success_if_correct_information_provided(): user_name = 'test' user_id = db_utils.create_user(password='******', username=user_name) password = '******' form = Bag(password=password, passcheck=password) resetpassword.force(user_id, form) result = login.authenticate_bcrypt(username=user_name, password=password, request=None) assert result == (user_id, None)
def test_forcing_password_reset_with_too_short_length_fails(): # Anything under len(login._PASSWORD) characters triggers this case user_id = db_utils.create_user() password = '******' form = Bag(password=password, passcheck=password) with pytest.raises(WeasylError) as err: resetpassword.force(user_id, form) assert 'passwordInsecure' == err.value.value
def test_verify_success_if_valid_information_provided(): email_addr = "*****@*****.**" user_id = db_utils.create_user(email_addr=email_addr) form = Bag(email=email_addr) resetpassword.request(form) pw_reset_token = d.engine.scalar("SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) assert 100 == len(pw_reset_token) assert resetpassword.prepare(pw_reset_token)
def test_user_must_exist_for_a_forgotten_password_request_to_be_made(): email_addr = "*****@*****.**" form = Bag(email=email_addr) resetpassword.request(form) record_count = d.engine.scalar(""" SELECT COUNT(*) FROM forgotpassword """) assert record_count == 0
def test_stale_records_get_deleted_when_function_is_called(): token_store = [] for i in range(20): user_name = "testPrepare%d" % (i, ) email_addr = "*****@*****.**" % (i, ) user_id = db_utils.create_user(email_addr=email_addr, username=user_name) form_for_request = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year) resetpassword.request(form_for_request) pw_reset_token = d.engine.scalar( "SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) token_store.append(pw_reset_token) # All tokens should exist at this point for i in range(20): assert resetpassword.checktoken(token_store[i]) # Set 5 tokens to be two hours old (0,5) (7200) for i in range(0, 5): d.engine.execute( "UPDATE forgotpassword SET set_time = %(time)s WHERE token = %(token)s", time=d.get_time() - 7200, token=token_store[i]) # Set 5 tokens to be 30 minutes old (5,10) (1800) for i in range(5, 10): d.engine.execute( "UPDATE forgotpassword SET set_time = %(time)s WHERE token = %(token)s", time=d.get_time() - 1800, token=token_store[i]) # Set 5 tokens to be 10 minutes old for the last visit time (10,15) (600) for i in range(10, 15): d.engine.execute( "UPDATE forgotpassword SET link_time = %(time)s WHERE token = %(token)s", time=d.get_time() - 600, token=token_store[i]) # Set 5 tokens to be 2 minutes old for the last visit time (10,15) (120) for i in range(15, 20): d.engine.execute( "UPDATE forgotpassword SET link_time = %(time)s WHERE token = %(token)s", time=d.get_time() - 120, token=token_store[i]) # This should clear all tokens >1hr old, and all tokens >5 minutes from last visit (10 total) resetpassword.prepare('foo') # This range should be cleared (set_time > 3600) for i in range(0, 5): assert not resetpassword.checktoken(token_store[i]) # This range should still be present (set_time < 3600) for i in range(5, 10): assert resetpassword.checktoken(token_store[i]) # This range should be cleared (link_time > 300) for i in range(10, 15): assert not resetpassword.checktoken(token_store[i]) # This range should still be present (link_time < 300) for i in range(15, 20): assert resetpassword.checktoken(token_store[i])
def test_create_fails_if_username_is_a_prohibited_name(): form = Bag(username='******', password='******', passcheck='0123456789', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year=arrow.now().year - 19) prohibited_names = [ "admin", "administrator", "mod", "moderator", "weasyl", "weasyladmin", "weasylmod", "staff", "security" ] for name in prohibited_names: form.username = name with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value
def test_username_cant_be_blank_or_have_semicolon(): form = Bag(username='******', password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameInvalid' == err.value.value form.username = '******' login.create(form) assert d.engine.scalar( "SELECT username FROM logincreate WHERE email = %(email)s LIMIT 1", email=form.email, ) == "testloginsuite"
def test_similarly_named_domains_are_not_blocked(self): """ Blacklisted: badsite.net /Not/ Blocked: notabadsite.net Also /Not/ blocked: subdomain.notabadsite.net """ d.engine.execute( d.meta.tables["emailblacklist"].insert(), { "domain_name": "blacklisted.com", "reason": "test case for login.create()", "added_by": db_utils.create_user(), }) mail = "*****@*****.**" form = Bag(username=user_name, password='******', passcheck='0123456789', email=mail, emailcheck=mail, day='12', month='12', year=arrow.now().year - 19) login.create(form) mail = "*****@*****.**" form = Bag(username=user_name + "1", password='******', passcheck='0123456789', email=mail, emailcheck=mail, day='12', month='12', year=arrow.now().year - 19) login.create(form) mail = "*****@*****.**" form = Bag(username=user_name + "2", password='******', passcheck='0123456789', email=mail, emailcheck=mail, day='12', month='12', year=arrow.now().year - 19) login.create(form)
def test_email_must_match_email_stored_in_DB(): email_addr = "*****@*****.**" user_id = db_utils.create_user(email_addr=email_addr) email_addr = "*****@*****.**" form = Bag(email=email_addr) resetpassword.request(form) query = d.engine.scalar(""" SELECT userid FROM forgotpassword WHERE userid = %(userid)s """, userid=user_id) assert not query
def test_verify_login_record_is_updated(): # Use a fake session for this test. get_current_request().weasyl_session = Bag() user_id = db_utils.create_user() d.engine.execute("UPDATE login SET last_login = -1 WHERE userid = %(id)s", id=user_id) login.signin(user_id) last_login = d.engine.scalar( "SELECT last_login FROM login WHERE userid = %(id)s", id=user_id) assert last_login > -1
def test_create_fails_if_email_is_invalid(): form = Bag(username=user_name, password='******', passcheck='0123456789', email=';--', emailcheck=';--', day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'emailInvalid' == err.value.value
def test_create_fails_if_email_and_emailcheck_dont_match(): form = Bag(username=user_name, password='******', passcheck='0123456789', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'emailMismatch' == err.value.value
def test_passwordInsecure_WeasylError_if_password_length_insufficient(): db_utils.create_user(email_addr=email_addr, username=user_name) password = '' form = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year, token=token, password=password, passcheck=password) # Considered insecure... for i in range(0, login._PASSWORD): with pytest.raises(WeasylError) as err: resetpassword.reset(form) assert 'passwordInsecure' == err.value.value password += 'a' form.password = password form.passcheck = password # Considered secure... password += 'a' form.password = password form.passcheck = password # Success at WeasylError/forgotpasswordRecordMissing; we didn't make one yet with pytest.raises(WeasylError) as err: resetpassword.reset(form) assert 'forgotpasswordRecordMissing' == err.value.value
def test_link_time_field_is_updated_when_valid_token_supplied_to_function(): user_name = "test" email_addr = "*****@*****.**" user_id = db_utils.create_user(email_addr=email_addr, username=user_name) form_for_request = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year) resetpassword.request(form_for_request) pw_reset_token = d.engine.scalar("SELECT token FROM forgotpassword WHERE userid = %(id)s", id=user_id) link_time = d.engine.scalar("SELECT link_time FROM forgotpassword WHERE token = %(token)s", token=pw_reset_token) assert link_time == 0 resetpassword.prepare(pw_reset_token) link_time = d.engine.scalar("SELECT link_time FROM forgotpassword WHERE token = %(token)s", token=pw_reset_token) assert link_time >= d.get_time() - 10
def test_DMY_not_integer_raises_birthdayInvalid_WeasylError(): # Check for failure state if 'day' is not an integer, e.g., string form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='test', month='31', year='1942') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value # Check for failure state if 'month' is not an integer, e.g., string form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='test', year='1942') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value # Check for failure state if 'year' is not an integer, e.g., string form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='31', year='test') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value
def test_under_13_age_raises_birthdayInvalid_WeasylError(): # Check for failure state if computed birthday is <13 years old form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year=arrow.now().year - 11) with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value
def test_DMY_missing_raises_birthdayInvalid_WeasylError(): # Check for failure state if 'year' is not an valid year e.g., -1 form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day=None, month='12', year='2000') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value # Check for failure state if 'year' is not an valid year e.g., -1 form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month=None, year='2000') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value # Check for failure state if 'year' is not an valid year e.g., -1 form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year=None) with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value
def test_passwordInsecure_WeasylError_if_password_length_insufficient(): db_utils.create_user(email_addr=email_addr, username=user_name) password = '' form = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year, token=token, password=password, passcheck=password) # Considered insecure... for i in range(0, login._PASSWORD): with pytest.raises(WeasylError) as err: resetpassword.reset(form) assert 'passwordInsecure' == err.value.value password += 'a' form.password = password form.passcheck = password # Considered secure... password += 'a' form.password = password form.passcheck = password # Success at WeasylError/forgotpasswordRecordMissing; we didn't make one yet with pytest.raises(WeasylError) as err: resetpassword.reset(form) assert 'forgotpasswordRecordMissing' == err.value.value
def test_passwords_must_match(): # Check for failure if password != passcheck form = Bag(username=user_name, password='******', passcheck='qwe', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'passwordMismatch' == err.value.value
def test_DMY_out_of_valid_ranges_raises_birthdayInvalid_WeasylError(): # Check for failure state if 'day' is not an valid day e.g., 42 form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='42', month='12', year='2000') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value # Check for failure state if 'month' is not an valid month e.g., 42 form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='42', year='2000') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value # Check for failure state if 'year' is not an valid year e.g., -1 form = Bag(username=user_name, password='', passcheck='', email='*****@*****.**', emailcheck='*****@*****.**', day='12', month='12', year='-1') with pytest.raises(WeasylError) as err: login.create(form) assert 'birthdayInvalid' == err.value.value
def test_passwordMismatch_WeasylError_if_supplied_passwords_dont_match(): db_utils.create_user(email_addr=email_addr, username=user_name) form = Bag(email=email_addr, username=user_name, day=arrow.now().day, month=arrow.now().month, year=arrow.now().year, token=token, password='******', passcheck='asd') with pytest.raises(WeasylError) as err: resetpassword.reset(form) assert 'passwordMismatch' == err.value.value
def test_usernames_must_be_unique(): db_utils.create_user(username=user_name, email_addr="*****@*****.**") form = Bag(username=user_name, password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) with pytest.raises(WeasylError) as err: login.create(form) assert 'usernameExists' == err.value.value
def test_verify_correct_information_creates_account(): form = Bag(username=user_name, password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) login.create(form) # This record should exist when this function completes successfully assert d.engine.scalar( "SELECT EXISTS (SELECT 0 FROM logincreate WHERE login_name = %(name)s)", name=form.username)
def test_acct_verif_token_returned_if_username_provided_to_function(): form = Bag(username=user_name, password='******', passcheck='0123456789', email=email_addr, emailcheck=email_addr, day='12', month='12', year=arrow.now().year - 19) d.engine.execute(d.meta.tables["logincreate"].insert(), { "token": token, "username": form.username, "login_name": form.username, "hashpass": login.passhash(raw_password), "email": form.email, "birthday": arrow.Arrow(2000, 1, 1), }) acct_verification_token = login.get_account_verification_token(email=None, username=form.username) assert token == acct_verification_token