Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve login validation input #169

Merged
merged 2 commits into from
Nov 14, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions securedrop_client/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,27 @@ def validate(self):
password = self.password_field.text()
tfa_token = self.tfa_field.text().replace(' ', '')
if username and password and tfa_token:
# Validate username
min_journalist_username = 3 # Journalist.MIN_USERNAME_LEN on server
if len(username) < min_journalist_username:
self.setDisabled(False)
self.error(_('Your username should be at least 3 characters. '))
return

# Validate password
min_password_len = 14 # Journalist.MIN_PASSWORD_LEN on server
max_password_len = 128 # Journalist.MAX_PASSWORD_LEN on server
if len(password) < min_password_len or len(password) > max_password_len:
self.setDisabled(False)
self.error(_('Your password should be between 14 and 128 characters. '))
return

# Validate 2FA token
try:
int(tfa_token)
except ValueError:
self.setDisabled(False)
self.error(_('Please use only numerals for the '
'two factor number.'))
self.error(_('Please use only numerals for the two factor number.'))
return
self.controller.login(username, password, tfa_token)
else:
Expand Down
63 changes: 60 additions & 3 deletions tests/gui/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def test_LoginDialog_validate_input_non_numeric_2fa():
ld = LoginDialog(None)
ld.setup(mock_controller)
ld.username_field.text = mock.MagicMock(return_value='foo')
ld.password_field.text = mock.MagicMock(return_value='bar')
ld.password_field.text = mock.MagicMock(return_value='nicelongpassword')
ld.tfa_field.text = mock.MagicMock(return_value='baz')
ld.setDisabled = mock.MagicMock()
ld.error = mock.MagicMock()
Expand All @@ -360,6 +360,63 @@ def test_LoginDialog_validate_input_non_numeric_2fa():
assert mock_controller.login.call_count == 0


def test_LoginDialog_validate_too_short_username():
"""
If the username is too small, we show an informative error message.
"""
mock_controller = mock.MagicMock()
ld = LoginDialog(None)
ld.setup(mock_controller)
ld.username_field.text = mock.MagicMock(return_value='he')
ld.password_field.text = mock.MagicMock(return_value='nicelongpassword')
ld.tfa_field.text = mock.MagicMock(return_value='123456')
ld.setDisabled = mock.MagicMock()
ld.error = mock.MagicMock()
ld.validate()
assert ld.setDisabled.call_count == 2
assert ld.error.call_count == 1
assert mock_controller.login.call_count == 0


def test_LoginDialog_validate_too_short_password():
"""
If the password is too small, we show an informative error message.
"""
mock_controller = mock.MagicMock()
ld = LoginDialog(None)
ld.setup(mock_controller)
ld.username_field.text = mock.MagicMock(return_value='foo')
ld.password_field.text = mock.MagicMock(return_value='bar')
ld.tfa_field.text = mock.MagicMock(return_value='123456')
ld.setDisabled = mock.MagicMock()
ld.error = mock.MagicMock()
ld.validate()
assert ld.setDisabled.call_count == 2
assert ld.error.call_count == 1
assert mock_controller.login.call_count == 0


def test_LoginDialog_validate_too_long_password():
"""
If the password is too long, we show an informative error message.
"""
mock_controller = mock.MagicMock()
ld = LoginDialog(None)
ld.setup(mock_controller)

max_password_len = 128
too_long_password = 'a' * (max_password_len + 1)
ld.username_field.text = mock.MagicMock(return_value='foo')
ld.password_field.text = mock.MagicMock(return_value=too_long_password)
ld.tfa_field.text = mock.MagicMock(return_value='123456')
ld.setDisabled = mock.MagicMock()
ld.error = mock.MagicMock()
ld.validate()
assert ld.setDisabled.call_count == 2
assert ld.error.call_count == 1
assert mock_controller.login.call_count == 0


def test_LoginDialog_validate_input_ok():
"""
Valid input from the user causes a call to the controller's login method.
Expand All @@ -368,14 +425,14 @@ def test_LoginDialog_validate_input_ok():
ld = LoginDialog(None)
ld.setup(mock_controller)
ld.username_field.text = mock.MagicMock(return_value='foo')
ld.password_field.text = mock.MagicMock(return_value='bar')
ld.password_field.text = mock.MagicMock(return_value='nicelongpassword')
ld.tfa_field.text = mock.MagicMock(return_value='123456')
ld.setDisabled = mock.MagicMock()
ld.error = mock.MagicMock()
ld.validate()
assert ld.setDisabled.call_count == 1
assert ld.error.call_count == 0
mock_controller.login.assert_called_once_with('foo', 'bar', '123456')
mock_controller.login.assert_called_once_with('foo', 'nicelongpassword', '123456')


def test_SpeechBubble_init():
Expand Down