diff --git a/lib/galaxy/app_unittest_utils/galaxy_mock.py b/lib/galaxy/app_unittest_utils/galaxy_mock.py
index d50842158020..3de37061ebf8 100644
--- a/lib/galaxy/app_unittest_utils/galaxy_mock.py
+++ b/lib/galaxy/app_unittest_utils/galaxy_mock.py
@@ -213,7 +213,7 @@ def __init__(self, **kwargs):
self.password_expiration_period = 0
self.pretty_datetime_format = "pretty_datetime_format"
self.redact_username_in_logs = False
- self.smtp_server = True
+ self.smtp_server = None
self.terms_url = "terms_url"
self.templates_dir = "templates"
diff --git a/lib/galaxy/tools/error_reports/plugins/email.py b/lib/galaxy/tools/error_reports/plugins/email.py
index 42446c2ecb3e..2db91db9a2b5 100644
--- a/lib/galaxy/tools/error_reports/plugins/email.py
+++ b/lib/galaxy/tools/error_reports/plugins/email.py
@@ -35,7 +35,9 @@ def submit_report(self, dataset, job, tool, **kwargs):
)
return ("Your error report has been sent", "success")
except Exception as e:
- return (f"An error occurred sending the report by email: {unicodify(e)}", "danger")
+ msg = f"An error occurred sending the report by email: {unicodify(e)}"
+ log.exception(msg)
+ return (msg, "danger")
__all__ = ("EmailPlugin",)
diff --git a/lib/galaxy/util/__init__.py b/lib/galaxy/util/__init__.py
index b688d633c9b8..a43da79a0802 100644
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -1628,6 +1628,7 @@ def send_mail(frm, to, subject, body, config, html=None, reply_to=None):
email_json = json.to_json_string(email_dict)
with open(path, "w") as f:
f.write(email_json)
+ return
to = listify(to)
if html:
diff --git a/test/unit/app/tools/test_error_reporting.py b/test/unit/app/tools/test_error_reporting.py
new file mode 100644
index 000000000000..611e298a9f4f
--- /dev/null
+++ b/test/unit/app/tools/test_error_reporting.py
@@ -0,0 +1,141 @@
+import json
+
+from galaxy import model
+from galaxy.app_unittest_utils.tools_support import UsesApp
+from galaxy.model.base import transaction
+from galaxy.tools.errors import EmailErrorReporter
+from galaxy.util.unittest import TestCase
+
+# The email the user created their account with.
+TEST_USER_EMAIL = "mockgalaxyuser@galaxyproject.org"
+# The email the user supplied when submitting the error
+TEST_USER_SUPPLIED_EMAIL = "fake@example.org"
+TEST_SERVER_EMAIL_FROM = "email_from@galaxyproject.org"
+TEST_SERVER_ERROR_EMAIL_TO = "admin@email.to" # setup in mock config
+
+
+class TestErrorReporter(TestCase, UsesApp):
+
+ def setUp(self):
+ self.setup_app()
+ self.app.config.email_from = TEST_SERVER_EMAIL_FROM
+
+ def test_basic(self, tmp_path):
+ email_path = tmp_path / "moo.json"
+ self.app.config.smtp_server = f"mock_emails_to_path://{email_path}"
+ user, hda = self._setup_model_objects()
+
+ assert not email_path.exists()
+ error_report = EmailErrorReporter(hda, self.app)
+ error_report.send_report(user, email=TEST_USER_SUPPLIED_EMAIL, message="My custom message")
+ assert email_path.exists()
+ text = email_path.read_text()
+ email_json = json.loads(text)
+ assert email_json["from"] == TEST_SERVER_EMAIL_FROM
+ assert email_json["to"] == f"{TEST_SERVER_ERROR_EMAIL_TO}, {TEST_USER_SUPPLIED_EMAIL}"
+ assert f"Galaxy tool error report from {TEST_USER_SUPPLIED_EMAIL}" == email_json["subject"]
+ assert "cat1" in email_json["body"]
+ assert "cat1" in email_json["html"]
+ assert TEST_USER_EMAIL == email_json["reply_to"]
+
+ def test_hda_security(self, tmp_path):
+ email_path = tmp_path / "moo.json"
+ self.app.config.smtp_server = f"mock_emails_to_path://{email_path}"
+ user, hda = self._setup_model_objects()
+ error_report = EmailErrorReporter(hda, self.app)
+ security_agent = self.app.security_agent
+ private_role = security_agent.create_private_user_role(user)
+ access_action = security_agent.permitted_actions.DATASET_ACCESS.action
+ manage_action = security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action
+ permissions = {access_action: [private_role], manage_action: [private_role]}
+ security_agent.set_all_dataset_permissions(hda.dataset, permissions)
+
+ other_user = model.User(email="otheruser@galaxyproject.org", password="mockpass2")
+ self._commit_objects([other_user])
+ security_agent = self.app.security_agent
+ assert not email_path.exists()
+ error_report.send_report(other_user, email=TEST_USER_SUPPLIED_EMAIL, message="My custom message")
+ # Without permissions, the email still gets sent but the supplied email is ignored
+ # I'm not saying this is the right behavior but it is what the code does at the time of test
+ # writing -John
+ assert email_path.exists()
+ text = email_path.read_text()
+ email_json = json.loads(text)
+ assert "otheruser@galaxyproject.org" not in email_json["to"]
+
+ def test_html_sanitization(self, tmp_path):
+ email_path = tmp_path / "moo.json"
+ self.app.config.smtp_server = f"mock_emails_to_path://{email_path}"
+ user, hda = self._setup_model_objects()
+
+ assert not email_path.exists()
+ error_report = EmailErrorReporter(hda, self.app)
+ error_report.send_report(
+ user, email=TEST_USER_SUPPLIED_EMAIL, message='My custom message'
+ )
+ assert email_path.exists()
+ text = email_path.read_text()
+ email_json = json.loads(text)
+ html = email_json["html"]
+ assert "<a href="http://sneaky.com/">message</a>" in html
+
+ def test_redact_user_details_in_bugreport(self, tmp_path):
+ email_path = tmp_path / "moo.json"
+ self.app.config.smtp_server = f"mock_emails_to_path://{email_path}"
+ user, hda = self._setup_model_objects()
+
+ assert not email_path.exists()
+ error_report = EmailErrorReporter(hda, self.app)
+ error_report.send_report(
+ user, email=TEST_USER_SUPPLIED_EMAIL, message="My custom message", redact_user_details_in_bugreport=True
+ )
+ assert email_path.exists()
+ text = email_path.read_text()
+ email_json = json.loads(text)
+ assert "The user redacted (user: 1) provided the following information:" in email_json["body"]
+ assert (
+ """The user redacted (user: 1) provided the following information:"""
+ in email_json["html"]
+ )
+
+ def test_no_redact_user_details_in_bugreport(self, tmp_path):
+ email_path = tmp_path / "moo.json"
+ self.app.config.smtp_server = f"mock_emails_to_path://{email_path}"
+ user, hda = self._setup_model_objects()
+
+ assert not email_path.exists()
+ error_report = EmailErrorReporter(hda, self.app)
+ error_report.send_report(
+ user, email=TEST_USER_SUPPLIED_EMAIL, message="My custom message", redact_user_details_in_bugreport=False
+ )
+ assert email_path.exists()
+ text = email_path.read_text()
+ email_json = json.loads(text)
+ assert (
+ f"The user '{TEST_USER_EMAIL}' (providing preferred contact email '{TEST_USER_SUPPLIED_EMAIL}') provided the following information:"
+ in email_json["body"]
+ )
+ assert (
+ f"""The user '{TEST_USER_EMAIL}' (providing preferred contact email '{TEST_USER_SUPPLIED_EMAIL}') provided the following information:"""
+ in email_json["html"]
+ )
+
+ def _setup_model_objects(self):
+ user = model.User(email=TEST_USER_EMAIL, password="mockpass")
+ job = model.Job()
+ job.tool_id = "cat1"
+ job.history = model.History()
+ job.user = user
+ job.history.user = user
+ hda = model.HistoryDatasetAssociation(history=job.history)
+ hda.dataset = model.Dataset()
+ hda.dataset.state = "ok"
+ job.add_output_dataset("out1", hda)
+ self._commit_objects([job, hda, user])
+ return user, hda
+
+ def _commit_objects(self, objects):
+ session = self.app.model.context
+ session.add_all(objects)
+ with transaction(session):
+ session.commit()