def test_delete_all_failed_login_attempts_by_user(db, user): """Delete all failed login attempts for a specific user.""" failed_login_attempt = FailedLoginAttempt(user.email, '127.0.0.1') failed_login_attempt.save() other_failed_login_attempt = FailedLoginAttempt('foo', '127.0.0.1') other_failed_login_attempt.save() FailedLoginAttempt.delete_all_by_user(user) db.session.commit() attempts = FailedLoginAttempt.query.all() assert failed_login_attempt not in attempts assert [other_failed_login_attempt] == attempts
def test_get_all_failed_login_attempts_by_user(db, user): """Get all failed login attempts for a specific user.""" failed_login_attempt = FailedLoginAttempt(user.email, '127.0.0.1') failed_login_attempt.save() other_failed_login_attempt = FailedLoginAttempt('foo', '127.0.0.1') other_failed_login_attempt.save() attempts = FailedLoginAttempt.get_all_by_user(user) assert other_failed_login_attempt not in attempts assert [failed_login_attempt] == attempts
def test_login_attempt_purge(): """Test purging login attempts for username/IP combo.""" foo_login_attempt = FailedLoginAttempt('foo', '127.0.0.1') foo_login_attempt.save() bar_login_attempt = FailedLoginAttempt('bar', '127.0.0.1') bar_login_attempt.save() attempts = FailedLoginAttempt.query.all() assert len(attempts) == 2 FailedLoginAttempt.purge_failed_for_username_and_ip('foo', '127.0.0.1') attempts = FailedLoginAttempt.query.all() assert len(attempts) == 1 assert bar_login_attempt in attempts
def test_login_attempt_repr(): """Check repr output.""" FailedLoginAttempt('foo', '127.0.0.1').save() attempt = FailedLoginAttempt.query.first() repr = '<FailedLoginAttempt({id}:{username!r})>' assert attempt.__repr__() == repr.format(id=attempt.id, username=attempt.username)
def forget_user(email, dry_run): """Remove all traces of a user from the system.""" user = User.get_by_email(email) if user: if user.is_admin: click.echo('User "{}" is a sysadmin, refusing to delete.'.format(user)) sys.exit(1) if len(User.get_modified_and_created_by_user(user)) > 0: click.echo('User "{}" has created or modified users, refusing to delete.'.format(user)) sys.exit(1) if len(Collection.get_modified_and_created_by_user(user)) > 0: click.echo('User "{}" has created or modified collections, ' 'refusing to delete.'.format(user)) sys.exit(1) if len(Permission.get_modified_and_created_by_user(user)) > 0: click.echo('User "{}" has created or modified permissions, ' 'refusing to delete.'.format(user)) sys.exit(1) if dry_run: tokens = Token.get_all_by_user(user) grants = Grant.get_all_by_user(user) failed_login_attempts = FailedLoginAttempt.get_all_by_user(user) permissions = user.permissions password_resets = user.password_resets click.echo('These tokens would be deleted: {}'.format(tokens)) click.echo('These grants would be deleted: {}'.format(grants)) click.echo('These failed login attempts would be deleted: {}'.format( failed_login_attempts)) click.echo('These permissions would be deleted: {}'.format(permissions)) click.echo('These password_resets would be deleted: {}'.format(password_resets)) else: if click.confirm('Are you sure you want to delete all information ' 'related to user "{}"?'.format(user)): Token.delete_all_by_user(user) Grant.delete_all_by_user(user) Permission.delete_all_by_user(user) PasswordReset.delete_all_by_user(user) FailedLoginAttempt.delete_all_by_user(user) user.delete() else: click.echo('User "{}" not found. Aborting...'.format(email)) sys.exit(1)
def test_login_attempt_too_many_recent_failures(app): """Ensure login attempt is not allowed after too many failed attempts.""" username = '******' remote_addr = '127.0.0.1' app.config['XL_AUTH_FAILED_LOGIN_MAX_ATTEMPTS'] = 1 app.config['XL_AUTH_FAILED_LOGIN_TIMEFRAME'] = 5 * 60 assert FailedLoginAttempt.too_many_recent_failures_for(username) is False login_attempt = FailedLoginAttempt(username, remote_addr) login_attempt.save() assert FailedLoginAttempt.too_many_recent_failures_for(username) is True login_attempt.created_at = datetime.utcnow() - timedelta(seconds=10 * 60) login_attempt.save() assert FailedLoginAttempt.too_many_recent_failures_for(username) is False
def test_block_login_on_too_many_failed_attempts(user, testapp): """Temporarily block login if too many failed attempts.""" current_app.config['XL_AUTH_FAILED_LOGIN_MAX_ATTEMPTS'] = 1 # Goes to homepage. res = testapp.get('/') # Fills out login form, password incorrect. form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits. res = form.submit() # Sees error. assert _('Invalid password') in res # Goes to homepage a second time. res = testapp.get('/') # Fills out login form, password incorrect. form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits and sees error. res = form.submit(expect_errors=True) assert res.status_code == 429 # Also traceable in Nginx logs: assert res.headers['X-Username'] == user.email FailedLoginAttempt.purge_failed_for_username_and_ip( user.email, '127.0.0.1') # Goes to homepage a third time. res = testapp.get('/') # Fills out login form, password incorrect. form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits. res = form.submit().follow() assert res.status_code == 200
def test_login_attempt_purge(superuser): """Test purging login attempts for username/IP combo.""" user = User('*****@*****.**', 'Foo fooson') user.save_as(superuser) foo_login_attempt = FailedLoginAttempt('*****@*****.**', '127.0.0.1') foo_login_attempt.save() bar_login_attempt = FailedLoginAttempt('bar', '127.0.0.1') bar_login_attempt.save() attempts = FailedLoginAttempt.query.all() assert len(attempts) == 2 user.update_last_login_internal(commit=True, remote_address='127.0.0.1') attempts = FailedLoginAttempt.query.all() assert len(attempts) == 1 assert bar_login_attempt in attempts