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

Add EmailStore plugin #55

Merged
merged 1 commit into from
Apr 3, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 77 additions & 0 deletions cloudmarker/alerts/emailalert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Email alert plugin."""


import email
import logging
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# Define module-level logger.
_log = logging.getLogger(__name__)


class EmailAlert:
"""A plugin to send email notification for anomalies found."""

def __init__(self, host, port, subject, to, sender, body):
"""Create an instance of :class:`EmailAlert` plugin.

Arguments:
host (string): Server hostname for SMTP.
port (int): Server port for SMTP.
subject (string): Subject line for email.
to (list): List of recipients (string).
sender (string): From field for email.
body (string): Email body.

"""
self.host = host
self.port = port
self.subject = subject
self.to = to
self.sender = sender
self.body = body
self.stringbuffer = []

def write(self, record):
"""Write JSON records to the file system.

This method is called once for every ``record`` read from a
cloud. In this example implementation of a alert, we simply
send the ``record`` in JSON format via email to the recipient.
The records keeps appending to a stringbuffer which then is
written in the email body

Arguments:
record (dict): Data to send via email.
"""
for _, value in record.items():
self.stringbuffer.append(repr(value))

def done(self):
"""Perform final cleanup tasks.

This method is called after all records have been written. In
this example implementation, we properly terminate the JSON
array in the email body.

"""
message = MIMEMultipart()
message['Date'] = email.utils.formatdate(localtime=True)
message['Subject'] = self.subject
message['From'] = self.sender
message['To'] = email.utils.COMMASPACE.join(self.to)

# Incase if the buffer is empty then the default message will be sent
if self.stringbuffer:
self.body = ''.join(self.stringbuffer)
message.attach(MIMEText(self.body))

smtp_connection = smtplib.SMTP(host=self.host, port=self.port)
try:
smtp_connection.sendmail(self.sender, self.to, message.as_string())
except smtplib.SMTPException as e:
_log.error('Failed to send email: %s', e)
finally:
smtp_connection.quit()
13 changes: 11 additions & 2 deletions config.base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ alerts:
to:
- foo@example.com
text: foo
emailalert:
plugin: cloudmarker.alerts.emailalert.EmailAlert
params:
host: smtp.example.com
port: 25
sender: CloudMarker Alert <no-reply@example.com>
to:
- recipient@example.com
subject: Anomaly notification
body: default text

audits:
mockaudit:
Expand All @@ -35,11 +45,11 @@ audits:
- mockevent
alerts:
- filestore
- emailalert
Copy link
Member

Choose a reason for hiding this comment

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

This line should be removed from config.base.yaml. Any enabled audit config in config.base.yaml should run smoothly without any errors right after a user or developer clones this project's GitHub repository. In other words, the following should run fine without errors without requiring any additional steps:

# Ensure there is no user config to mimic a freshly cloned repo.
mv config.yaml config.bkp.yaml

# Run the project with the base config.
. ./venv
python3 -m cloudmarker -f

Or equivalently, this should run without errors:

. ./venv
python3 -m cloudmarker -c config.base.yaml -f

Since there is no way to configure the EmailAlert such that it works out of the box right after cloning the repository, we should not enable EmailAlert by default. The sample EmailAlert configuration at line 20 can still be there. Only its usage at line 41 here should be removed.


run:
- mockaudit


logger:

version: 1
Expand Down Expand Up @@ -74,5 +84,4 @@ logger:
- console
- file_handler


schedule: "00:00"
8 changes: 8 additions & 0 deletions docs/api/cloudmarker.stores.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ cloudmarker.stores package
Submodules
----------

cloudmarker.stores.emailalert module
Copy link
Member

Choose a reason for hiding this comment

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

I think this needs to be corrected.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, there is issue #72 open to track this.

------------------------------------

.. automodule:: cloudmarker.stores.emailalert
:members:
:undoc-members:
:show-inheritance:

cloudmarker.stores.filestore module
-----------------------------------

Expand Down
5 changes: 4 additions & 1 deletion pylama.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ ignore = E1101
# E1101 Instance of 'Resource' has no 'instances' member [pylint]

[pylama:cloudmarker/stores/mongodbstore.py]
ignore = R0913
ignore = R0913
# R0913 Too many arguments (7/5)

[pylama:cloudmarker/alerts/emailalert.py]
ignore = R0913,C0412

[pylama:cloudmarker/stores/splunkhecstore.py]
ignore = R0913,W0703
# R0913 Too many arguments (6/5) [pylint]
Expand Down