I have installed a Sentry instance on my server, and I want to configure it to send alert emails using Mandrill and its Django integration djrill. Here's the relevant settings in the sentry.conf.py file I'm using:
EXTRA_INSTALLED_APPS = (
'djrill',
)
EMAIL_BACKEND = 'djrill.mail.backends.djrill.DjrillBackend'
MANDRILL_API_KEY = '[... Mandril API key ...]'
DEFAULT_FROM_EMAIL = 'my-mandrill-allowed#email.address'
SERVER_EMAIL = 'my-mandrill-allowed#email.address'
And this setup works, except for the part that for some reason Mandrill doesn't allow setting the Message-Id header:
NotSupportedByMandrillError: Invalid message header 'Message-Id' - Mandrill only allows Reply-To and X-* headers
(This exception is raised by djrill, is not a response from Mandrill)
Which is set by Sentry here:
class MessageBuilder(object):
# ...
#cached_property
def message_id(self):
if self.reference is not None:
return email_id_for_model(self.reference)
I have managed to make it work by editing that method and make it always return None, so no Message-Id header is set in the email. But I don't like to edit/patch 3rd party code and I have no idea if that header is needed elsewhere.
How to accomplish this correctly? Switching from Mandrill is not an option right now.
Thanks!
As you can't easily change Sentry's behavior, as far as I can tell, I'd suggest implementing a subclass of DjrillBackend that removes the Message-Id header before the messages are sent. Something like (untested):
class HeaderRemovingBackend(DjrillBackend):
def send_messages(self, email_messages):
for message in email_messages:
if 'Message-Id' in message.extra_headers:
del message.extra_headers['Message-Id']
super(HeaderRemovingBackend, self).send_messages(email_messages)
Related
I'm writing tests for an application that uses the django-post_office package for most of its email functionality.
The default django.core.mail library contains plenty of useful tools for testing whether or not there are actually emails being sent. (Without actually sending any during the tests)
class TestFunctionThatSendsEmail(Testcase):
#override_settings(
EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend'
)
def test_function_sends_email(self):
self.assertEqual(len(outbox), 0)
run_function_that_calls_email()
self.assertEqual(len(outbox), 1)
...
# other tests
However, the emails in our function are being sent with the django-post_office mail.send() function
# priority now to make sure that they are being sent right away
mail.send(
sender=sender,
recipients=to,
context=context,
template=template_name,
priority='now',
)
Which causes the test above to fail as for some reason the emails do not end up in the outbox.
The strange thing is that if I change the EMAIL_BACKEND to django.core.mail.backends.console.EmailBackend the emails do show up in my terminal, so it is listening to the EMAIL_BACKEND settings.
I've tried finding alternative methods / functions to test this functionality in the django-post_office github, but all I could find was the advice to check to see if the emails are saved to the database and verify their status. (Which I did and works) but the fact that django seems to be unable to detect any emails actually being sent is making me a little bit nervous.
Does anyone know of a way to make emails sent by django-post_office appear in the outbox or, if that is not possible, a way to make sure that they are actually being sent? (beyond checking the database)
The issue is that django-post_office stores the mail backend in the Email object:
class Email(models.Model):
backend_alias = models.CharField(_("Backend alias"), blank=True, default='',
max_length=64)
When dispatch() is called, it uses this backend.
When creating an email, DPO overrides create() to set the backend from def get_available_backends(), which looks up the backend in the settings conf.
This means that using #override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend') won't set the backend_alias in the Email object correctly.
Instead you need to do this manually when you create the object, as per the dispatch test:
def test_dispatch(self):
"""
Ensure that email.dispatch() actually sends out the email
"""
email = Email.objects.create(to=['to#example.com'], from_email='from#example.com',
subject='Test dispatch', message='Message', backend_alias='locmem')
email.dispatch()
self.assertEqual(mail.outbox[0].subject, 'Test dispatch')
If you are using mail.send() you can simply pass the mail.send(backend='locmem') as an argument; make sure you have locmem': 'django.core.mail.backends.locmem.EmailBackend' in your POST_OFFICE settings
I was having the same issue as you described and fixed it by setting the DEFAULT_PRIORITY to 'now':
class TestFunctionThatSendsEmail(Testcase):
#override_settings(
POST_OFFICE={
'BACKENDS': {'default': 'django.core.mail.backends.locmem.EmailBackend'},
'DEFAULT_PRIORITY': 'now',
}
)
def test_function_sends_email(self):
self.assertEqual(len(outbox), 0)
run_function_that_calls_email()
self.assertEqual(len(outbox), 1)
...
# other tests
So I've got redis feature and tornado running on my server and whenever I open my websocket chat through a login, the terminal displays the following message
Error: Authentication missing
I'm not sure why this is happening because there are cookies in the authentication part of the app,
# Save user when authentication was successful.
def on_user_find(result, user=user):
##todo: We should check if email is given even though we can assume.
if result == "null" or not result:
# If user does not exist, create a new entry.
self.application.client.set("user:" + user["email"], tornado.escape.json_encode(user))
else:
# Update existing user.
# #todo: Should use $set to update only needed attributes?
dbuser = tornado.escape.json_decode(result)
dbuser.update(user)
user = dbuser
self.application.client.set("user:" + user["email"], tornado.escape.json_encode(user))
# Save user id in cookie.
self.set_secure_cookie("user", user["email"])
self.application.usernames[user["email"]] = user.get("name") or user["email"]
And in the websocket.py (where I run the script) I've made it so that the websocket handle checks if there are cookies available first before user access the app,
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
def open(self, user):
self.login = self.get_secure_cookie("user")
if not self.login:
# self.login = "anonymous"
print "Not authorized"
self.disconnect()
return
Yet it's still displaying the error, I've searched online and checked several SO answers but they don't show any solid solution in regards to this question. So far the most I've gotten is that I have to access the websocket header to put the above code inside, but I have no clue how I would do that. Help?
def send_one_mail(request):
send_mail('The subject', 'The body to testing', 'userone#example.com',['usertwo#gmail.com'], fail_silently=False)
return HttpResponse("This mail has sent successfull")
And my settings.py is
EMAIL_USE_TLS = True
EMAIL_HOST = 'mail.example.com'
EMAIL_HOST_USER = 'userone#example.com'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = 'userone#example.com'
SERVER_EMAIL = 'userone#example.com'
I have a view (which works) as shown above, here userone#example.com is sender and usertwo#gmail.com
is the receiver.
How can i check and act according to errors in sending mail?
(I want to show "Some error occured" in such errors)
How can i determine if the usertwo#gmail.com(reciver) exists or not?
(if email can't be reached, i want to show the user that the email doesn't exist)
I am using postfix,dovecot etc. in server.
You can pick up some errors when you use fail_silently = False. Just wrap send_mail in try/except.
When i wanted more control over e-mail sending, then i stopped using django mailing completely and installed lamson instead (lamsonproject.org). You can basically create your own mail server with it, attach your django orm to it and provide detailed feedback about what has happened to your e-mails. If you insert some kind of downloadable content into those e-mails (like images), then you can even give hashes to images and verify this way if e-mail has been opened too. You could do that with django based email sending too. Lamson just gives bit more control over the what and how that goes on after you hit send button.
django-mail-queue
Anyway, i finally ended-up with using the below package.
pip install django-mail-queue
And here is the docs
https://django-mail-queue.readthedocs.org/en/latest/
This is a very specific question, but I cannot find any documentation on how I can do it. The Facebook Documentation is pretty vague with some horrible and useless PHP examples (really, it's code like the Facebook PHP Sample Code that make people think PHP sucks) but I cannot find anything around for Python.
I can't even work out how to apply the same principles from the PHP sample code into a Python world. The xmpppy and SleekXMPP docs are a bit bare (or broken) and Google only shows examples of people using passwords.
I have the access tokens coming from the database, I have no interest in spawning a browser to find stuff, or doing anything else to find a token. I have them, consider it a hardcoded string. I want to pass that string to XMPP and send a message, that is the whole scope of things.
Any suggestions?
The below code worked, but only after some modifications mentioned in this thread
I answered this with a link to a blog I wrote because it described the solution perfectly, but apparently that annoyed some moderators.
While that's clearly ridiculous, here is the answer reposted.
import sleekxmpp
import logging
logging.basicConfig(level=logging.DEBUG)
class SendMsgBot(sleekxmpp.ClientXMPP):
def init(self, jid, recipient, message):
sleekxmpp.ClientXMPP.__init__(self, jid, 'ignore')
# The message we wish to send, and the JID that
# will receive it.
self.recipient = recipient
self.msg = message
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start, threaded=True)
def start(self, event):
self.send_presence()
self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg, mtype='chat')
# Using wait=True ensures that the send queue will be
#emptied before ending the session.
self.disconnect(wait=True)
I shoved that in a file called fbxmpp.py, then in another file (your worker, your command line app, your Flask controller, whatever) you'll need something like the following:
from fbxmpp import SendMsgBot
# The "From" Facebook ID
jid = '511501255#chat.facebook.com'
# The "Recipient" Facebook ID, with a hyphen for some reason
to = '-1000023894758#chat.facebook.com'
# Whatever you're sending
msg = 'Hey Other Phil, how is it going?'
xmpp = SendMsgBot(jid, to, unicode(msg))
xmpp.credentials['api_key'] = '123456'
xmpp.credentials['access_token'] = 'your-access-token'
if xmpp.connect(('chat.facebook.com', 5222)):
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
The problem is simple, we would like CherryPy to not log access log for a particular exposed method/API that gets called.
Basically when this API gets called, there are some parameters in the query string of the URL which are very sensitive and if leaked, would expose potential security. Naturally this is a /GET request and unfortunately it is the only way the parameters could be passed, since its a redirect(302) from an external service to this web server.
If it would not log the URL, that would serve the purpose as well.
So, is there a way that we can filter logging messages in access log by API's, URL's etc?
Thanks in advance for the help.
cherrypy uses Python's standard logging module by default, so you can just add a custom filter. This example will ignore any GET request with /foo as the path prefix:
import logging
class IgnoreURLFilter(logging.Filter):
# simple example of log message filtering
def __init__(self, ignore):
self.ignore = 'GET /' + ignore
def filter(self, record):
return self.ignore not in record.getMessage()
app = cherrypy.tree.mount( YourApplication() )
app.log.access_log.addFilter( IgnoreURLFilter('foo') )
cherrypy.engine.start()