Repository: great-expectations/great_expectations

Issue:
Cannot send Email via EmailAction, when (internal) Email server does not need authentication
**Describe the bug**
One is not able to send an Email via an internal server that does not require authentication (see below).

I would be willing do create a PR to fix this, if it is fine, that GX supports sending such emails.

**To Reproduce**
I am using the EmailAction class to send a summary validation result to an email address

```
from great_expectations.checkpoint import EmailAction
action = EmailAction(
            data_context=None,
            renderer={
                "module_name": "great_expectations.render.renderer.email_renderer",
                "class_name": "EmailRenderer",
            },
            smtp_address=smtp_address,
            smtp_port=smtp_port,
            sender_login=sender_login,
            sender_password=sender_password,
            receiver_emails=",".join(recipients),
            sender_alias=sender_alias,
            use_ssl=security_protocol.lower() == "ssl",
            use_tls=security_protocol.lower() == "tls",
            notify_on=self._notify_on,
            notify_with=None,
        )
```
When I try to run the action via `action.run(...)`
I get 

> ┌───────────────────── Traceback (most recent call last) ─────────────────────┐
> │ C:\XXXXX\scratches\scra │
> │ tch_5.py:19 in <module>                                                     │
> │                                                                             │
> │   16 │   "recipients": ["XXXXX"]                         │
> │   17 }                                                                      │
> │   18                                                                        │
> │ > 19 notifier = EmailNotifier(**email_notifier)                             │
> │   20                                                                        │
> │   21 summary_validation_result = ExpectationSuiteValidationResult(          │
> │   22 │   success=True,                                                      │
> │                                                                             │
> │ XXXXX\venv\lib\site-packages\kedro_expectat │
> │ ions\notification.py:85 in __init__                                         │
> │                                                                             │
> │    82 │   │   │   notify_on=notify_on,                                      │
> │    83 │   │   │   subject=subject,                                          │
> │    84 │   │   )                                                             │
> │ >  85 │   │   self._action = EmailAction(                                   │
> │    86 │   │   │   data_context=None,                                        │
> │    87 │   │   │   renderer={                                                │
> │    88 │   │   │   │   "module_name": "great_expectations.render.renderer.em │
> │                                                                             │
> │ XXXXXXX\venv\lib\site-packages\great_expectat │
> │ ions\checkpoint\actions.py:760 in __init__                                  │
> │                                                                             │
> │    757 │   │   self.use_ssl = use_ssl                                       │
> │    758 │   │   assert smtp_address, "No SMTP server address found in action │
> │    759 │   │   assert smtp_port, "No SMTP server port found in action confi │
> │ >  760 │   │   assert sender_login, "No login found for sending the email i │
> │    761 │   │   assert (                                                     │
> │    762 │   │   │   sender_password                                          │
> │    763 │   │   ), "No password found for sending the email in action config │
> └─────────────────────────────────────────────────────────────────────────────┘
> AssertionError: No login found for sending the email in action config.
> 

However I must not provide a login as the server does not require one (it is an internal server and only allow specific recipients).
When I provide None or empty strings as login and password, it won't work, as the great expectation utils methos `send_email` always tries to perform a login with the provided login/password, which will fail.


**Expected behavior**
Should be able to send an email to an internal email server that does not require authentication, by not defining `sender_login` and `sender_password` (or define it as None).

**Environment (please complete the following information):**
 - Operating System: Windows
 - Great Expectations Version: 0.18.8
 - Data Source: None
 - Cloud environment: None

**Additional context**
Add any other context about the problem here.



Discussion:

Resolving patch:
diff --git a/great_expectations/checkpoint/actions.py b/great_expectations/checkpoint/actions.py
--- a/great_expectations/checkpoint/actions.py
+++ b/great_expectations/checkpoint/actions.py
@@ -740,9 +740,9 @@ def __init__(  # noqa: PLR0913
         renderer: dict,
         smtp_address: str,
         smtp_port: str,
-        sender_login: str,
-        sender_password: str,
         receiver_emails: str,
+        sender_login: Optional[str] = None,
+        sender_password: Optional[str] = None,
         sender_alias: Optional[str] = None,
         use_tls: Optional[bool] = None,
         use_ssl: Optional[bool] = None,
@@ -778,13 +778,19 @@ def __init__(  # noqa: PLR0913
         self.use_ssl = use_ssl
         assert smtp_address, "No SMTP server address found in action config."
         assert smtp_port, "No SMTP server port found in action config."
-        assert sender_login, "No login found for sending the email in action config."
-        assert (
-            sender_password
-        ), "No password found for sending the email in action config."
         assert (
             receiver_emails
         ), "No email addresses to send the email to in action config."
+        if not sender_login:
+            logger.warning(
+                "No login found for sending the email in action config. "
+                "This will only work for email server that does not require authentication."
+            )
+        if not sender_password:
+            logger.warning(
+                "No password found for sending the email in action config."
+                "This will only work for email server that does not require authentication."
+            )
         self.notify_on = notify_on
         self.notify_with = notify_with
 
diff --git a/great_expectations/checkpoint/util.py b/great_expectations/checkpoint/util.py
--- a/great_expectations/checkpoint/util.py
+++ b/great_expectations/checkpoint/util.py
@@ -171,7 +171,12 @@ def send_email(  # noqa: PLR0913
         else:
             logger.warning("Not using TLS or SSL to send an email is not secure")
             mailserver = smtplib.SMTP(smtp_address, smtp_port)
-        mailserver.login(sender_login, sender_password)
+        if sender_login is not None and sender_password is not None:
+            mailserver.login(sender_login, sender_password)
+        elif not (sender_login is None and sender_password is None):
+            logger.error(
+                "Please specify both sender_login and sender_password or specify both as None"
+            )
         mailserver.sendmail(sender_alias, receiver_emails_list, msg.as_string())
         mailserver.quit()
     except smtplib.SMTPConnectError:


Test patch:
diff --git a/tests/actions/test_core_actions.py b/tests/actions/test_core_actions.py
--- a/tests/actions/test_core_actions.py
+++ b/tests/actions/test_core_actions.py
@@ -501,19 +501,33 @@ def quit(self, *args, **kwargs):
 
 @pytest.mark.parametrize(
     (
-        "class_to_patch,use_tls,use_ssl,raise_on,exception,expected,"
+        "class_to_patch,use_tls,use_ssl,sender_login,sender_password,raise_on,exception,expected,"
         "data_context_parameterized_expectation_suite,"
         "validation_result_suite,validation_result_suite_id"
     ),
     [
-        ("SMTP", False, False, None, None, "success", None, None, None),
-        ("SMTP", True, False, None, None, "success", None, None, None),
-        ("SMTP", False, False, None, None, "success", None, None, None),
-        ("SMTP_SSL", False, True, None, None, "success", None, None, None),
+        ("SMTP", False, False, "test", "test", None, None, "success", None, None, None),
+        ("SMTP", True, False, "test", "test", None, None, "success", None, None, None),
+        ("SMTP", False, False, "test", "test", None, None, "success", None, None, None),
         (
             "SMTP_SSL",
             False,
             True,
+            "test",
+            "test",
+            None,
+            None,
+            "success",
+            None,
+            None,
+            None,
+        ),
+        (
+            "SMTP_SSL",
+            False,
+            True,
+            "test",
+            "test",
             "__init__",
             smtplib.SMTPConnectError(421, "Can't connect"),
             None,
@@ -525,6 +539,8 @@ def quit(self, *args, **kwargs):
             "SMTP",
             True,
             False,
+            "test",
+            "test",
             "starttls",
             smtplib.SMTPConnectError(421, "Can't connect"),
             None,
@@ -536,6 +552,8 @@ def quit(self, *args, **kwargs):
             "SMTP",
             True,
             False,
+            "test",
+            "test",
             "login",
             smtplib.SMTPAuthenticationError(534, "Can't authenticate"),
             None,
@@ -543,6 +561,19 @@ def quit(self, *args, **kwargs):
             None,
             None,
         ),
+        (
+            "SMTP",
+            False,
+            False,
+            None,
+            None,
+            "login",
+            smtplib.SMTPAuthenticationError(534, "Can't authenticate"),
+            "success",
+            None,
+            None,
+            None,
+        ),
     ],
     indirect=[
         "data_context_parameterized_expectation_suite",
@@ -556,6 +587,8 @@ def test_EmailAction(
     class_to_patch,
     use_tls,
     use_ssl,
+    sender_login,
+    sender_password,
     raise_on,
     exception,
     expected,
@@ -574,8 +607,6 @@ def test_EmailAction(
         }
         smtp_address = "test"
         smtp_port = 999
-        sender_login = "test"
-        sender_password = "test"
         sender_alias = "other"
         receiver_emails = "test"
         notify_on = "all"