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

Clear clipboard after login screen #1071

Merged
merged 2 commits into from
Apr 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 9 additions & 1 deletion securedrop_client/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

from gettext import gettext as _
from typing import Dict, List, Optional # noqa: F401
from PyQt5.QtWidgets import QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QDesktopWidget
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, \
QVBoxLayout, QDesktopWidget

from securedrop_client import __version__
from securedrop_client.db import Source, User
Expand Down Expand Up @@ -197,3 +198,10 @@ def clear_error_status(self):
Clear any message currently in the error status bar.
"""
self.top_pane.clear_error_status()

def clear_clipboard(self):
"""
Purge any clipboard contents.
"""
cb = QApplication.clipboard()
cb.clear()
eloquence marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions securedrop_client/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ def on_authenticate_success(self, result):
self.api.journalist_first_name,
self.api.journalist_last_name,
self.session)
# Clear clipboard contents in case of previously pasted creds
self.gui.clear_clipboard()
self.gui.show_main_window(user)
self.update_sources()
self.api_job_queue.start(self.api)
Expand All @@ -456,6 +458,9 @@ def login_offline_mode(self):
Allow user to view in offline mode without authentication.
"""
self.gui.hide_login()
# Clear clipboard contents in case of previously pasted creds (user
# may have attempted online mode login, then switched to offline)
self.gui.clear_clipboard()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplication here is intentional; I felt that this behavior should not be a side effect of another function, but explicitly invoked so the intended business logic is clear.

self.gui.show_main_window()
storage.mark_all_pending_drafts_as_failed(self.session)
self.is_authenticated = False
Expand Down
12 changes: 12 additions & 0 deletions tests/gui/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,15 @@ def test_logout(mocker):

w.left_pane.set_logged_out.assert_called_once_with()
w.top_pane.set_logged_out.assert_called_once_with()


def test_clear_clipboard(mocker):
"""
Ensure we are clearing the system-level clipboard in the expected manner.
"""
mock_clipboard = mocker.MagicMock()
mocker.patch('securedrop_client.gui.main.QApplication.clipboard',
return_value=mock_clipboard)
w = Window()
w.clear_clipboard()
mock_clipboard.clear.assert_called_once_with()
13 changes: 8 additions & 5 deletions tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from PyQt5.QtCore import Qt
from sdclientapi import RequestTimeoutError, ServerConnectionError
from tests import factory
from unittest.mock import call

from securedrop_client import db
from securedrop_client.logic import APICallRunner, Controller, TIME_BETWEEN_SHOWING_LAST_SYNC_MS
Expand Down Expand Up @@ -157,8 +158,8 @@ def test_Controller_login(homedir, config, mocker, session_maker):

def test_Controller_login_offline_mode(homedir, config, mocker):
"""
Ensures user is not authenticated when logging in in offline mode and that the correct windows
are displayed.
Ensures user is not authenticated when logging in in offline mode, that the system
clipboard is cleared, and that the correct windows are subsequently displayed.
"""
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
co.call_api = mocker.MagicMock()
Expand All @@ -172,7 +173,7 @@ def test_Controller_login_offline_mode(homedir, config, mocker):

assert co.call_api.called is False
assert co.is_authenticated is False
co.gui.show_main_window.assert_called_once_with()
co.gui.assert_has_calls([call.clear_clipboard(), call.show_main_window()])
co.gui.hide_login.assert_called_once_with()
co.update_sources.assert_called_once_with()
co.show_last_sync_timer.start.assert_called_once_with(TIME_BETWEEN_SHOWING_LAST_SYNC_MS)
Expand Down Expand Up @@ -202,7 +203,9 @@ def test_Controller_on_authenticate_failure(homedir, config, mocker, session_mak
def test_Controller_on_authenticate_success(homedir, config, mocker, session_maker,
session):
"""
Ensure the client syncs when the user successfully logs in.
Ensure the client syncs when the user successfully logs in, and that the
system clipboard is cleared prior to display of the main window.

Using the `config` fixture to ensure the config is written to disk.
"""
user = factory.User()
Expand All @@ -221,7 +224,7 @@ def test_Controller_on_authenticate_success(homedir, config, mocker, session_mak
co.resume_queues = mocker.MagicMock()

co.on_authenticate_success(True)

co.gui.assert_has_calls([call.clear_clipboard(), call.show_main_window(user)])
co.api_sync.start.assert_called_once_with(co.api)
co.api_job_queue.start.assert_called_once_with(co.api)
assert co.is_authenticated
Expand Down