Integration Testing Django All-Auth Email Confirmation - django

I'd like to do some integration testing and I'm stuck on how I should test the email confirmation bit of the sign up process.
My user story is (basically) that a new visitor comes to the site, decides that they want to sign up, confirm their email address and then get redirected to their shiny new profile.
How would I simulate clicking on the link in the email to get redirected to the user profile?
I'm currently using Selenium to conduct my functional/integration testing and to a certain extent the django test suite.

These two documentation pages have everything you need:
https://docs.djangoproject.com/en/1.10/topics/testing/overview/
https://docs.djangoproject.com/en/1.10/topics/testing/tools/#email-services
A simple example:
from django.test import TestCase
from django.core import mail
class EmailTestCase(TestCase):
def setUp(self):
## Do something
pass
def test_email_content(self):
## Do something that triggers an email.
## Check the number of emails.
print(len(mail.outbox))
## Check the content of the first email.
first_email = mail.outbox[0]
if first_email:
print(first_email.body)

You should look up a python way to read emails.
Steps:
Find the email
Store the link
Start up your selenium test, and open up said link
Validate that you are on the page

Related

How to get "starred mails" from Gmail or other mail services using IMAP_tools in django

I am able to get inbox emails and also able to get emails from specific folder but i am unable to get "starred" emails.
I tried below code. and i am expecting emails with "starred flag" in response.
from imap_tools import MailBox, A
# Create your views here.
def temp(request):
#Get date, subject and body len of all emails from INBOX folder
with MailBox('smtp.gmail.com').login('admin#gmail.com', 'password', 'INBOX') as mailbox:
temp="empty"
for msg in mailbox.fetch():
temp = (msg.date, msg.subject, msg.html)
return HttpResponse(temp)
https://github.com/ikvk/imap_tools/blob/master/examples/search.py
mailbox.fetch(AND(flagged=True))
(A long time ago in a galaxy far far away) star looked like a flag.

Django-templated-email does not send email

I am using the following package https://github.com/vintasoftware/django-templated-email in a Django app to try and send an email. I am running it inside a django management command.
When I run the command the email part outputs to the console but no email is actually sent out. The SMTP settings and such are correct as emails work fine for other areas of the application.
from templated_email import send_templated_mail
send_templated_mail(
template_name='welcome',
from_email='from#example.com',
recipient_list=['to#example.com'],
context={
'username':"test",
'full_name':"test",
'signup_date':"test"
},
)
Any help is appreciated. I suspect I have misunderstood the syntax of the package.
Signup_date must be date and username and full_name must use single quote

Unsure of how to manage email configurations

I am attempting to create Reset Password functionality using Djoser. I am successfully hitting my API's auth/users/reset_password/ endpoint, which is then sending an email as expected. But the problem is occurring in the content of the email. It is sending a redirection link to my api, rather than to my frontend.
Please note, any <> is simply hiding a variable and is not actually displayed like that
Here is an example of what the email looks like:
You're receiving this email because you requested a password reset for your user account at <api>.
Please go to the following page and choose a new password: <api>/reset-password/confirm/<uid>/<token>
Your username, in case you've forgotten: <username>
Thanks for using our site!
The <api> team
The goal with this email is to send the user to the /reset-password/confirm/ url on my frontend, not on my api, which is currently occurring.
Here are my DJOSER settings:
DJOSER = {
'DOMAIN': '<frontend>',
'SITE_NAME': '<site-name>',
'PASSWORD_RESET_CONFIRM_URL': 'reset-password/confirm/{uid}/{token}',
}
The expected behavior is for the DOMAIN setting to alter the link that is being placed in the email, but it is not. I can't seem to find reference to this particular problem within the docs.
Any help here would be greatly appreciated, thanks.
I figured it out:
Due to Djoser extending the package django-templated-mail, the variables DOMAIN and SITE_NAME have to override django-templated-mail setting rather than Djoser's setting. So, you have to pull variables specific to django-templated-mail out of the Djoser variable.
The working setup actually looks like:
DOMAIN = '<frontend>',
SITE_NAME = '<site-name>',
DJOSER = {
'PASSWORD_RESET_CONFIRM_URL': 'reset-password/confirm/{uid}/{token}',
}

Dropbox and Django SSO using SAML

Summary
I am looking to use Dropbox SSO functionality by using the authentication from a Django site. Note that I'm not looking to use SAML as a backend for my Django site.
Resources
1) Dropbox Custom SSO help page: https://www.dropbox.com/en/help/1921#custom
2) Creating a SAML response: https://robinelvin.wordpress.com/2009/09/04/saml-with-django/
3) Struggled to find any examples from Google of people doing this kind of SSO. Lots of links about people using SAML as a Django backend.
Question
In the dropbox admin settings I can add my X509 certificate and the login link. This means that when you try to login into Dropbox using SSO it nicely forwards you to my Django site's login page using a GET request with a SAMLRequest in the querystring.
However, my understanding is that I now need to, once the user is authenticated on the Django site, fire a POST request back to Dropbox at their SAML login link with a SAMLResponse in the post data. Using the second resource above I believe I can create the SAMLResponse xml but I am unsure how to redirect the user to the dropbox SAML login link with the SAML data from my Django view.
Any help much appreciated.
Managed to get the functionality I needed using django-saml2-idp https://github.com/peopledoc/django-saml2-idp
Good documentation on installing here: https://github.com/peopledoc/django-saml2-idp/blob/master/doc/INSTALL.txt
Settings in the Dropbox Admin console required the X509 certificate and then the login url set to: https://****.com/idp/login
Note that I had issues installing the M2Crypto dependency so used an Ubuntu package via:
sudo apt-get install python-m2crypto
Additionally I'm using Django 1.9.6 so needed to make overrides to the views.py, urls.py, and registry.py files to make them compatible (various import statements needed updating and the urls changed to the new list format rather than using patterns).
Created a Dropbox Processor as follows:
import base64
import zlib
from saml2idp import base
from saml2idp.xml_render import _get_assertion_xml
def get_assertion_dropbox_xml(parameters, signed=False):
return _get_assertion_xml(ASSERTION_DROPBOX, parameters, signed)
ASSERTION_DROPBOX = (
'<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" '
'ID="${ASSERTION_ID}" '
'IssueInstant="${ISSUE_INSTANT}" '
'Version="2.0">'
'<saml:Issuer>${ISSUER}</saml:Issuer>'
'${ASSERTION_SIGNATURE}'
'${SUBJECT_STATEMENT}'
'<saml:Conditions NotBefore="${NOT_BEFORE}" NotOnOrAfter="${NOT_ON_OR_AFTER}">'
'<saml:AudienceRestriction>'
'<saml:Audience>${AUDIENCE}</saml:Audience>'
'</saml:AudienceRestriction>'
'</saml:Conditions>'
'<saml:AuthnStatement AuthnInstant="${AUTH_INSTANT}"'
'>'
'<saml:AuthnContext>'
'<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>'
'</saml:AuthnContext>'
'</saml:AuthnStatement>'
'${ATTRIBUTE_STATEMENT}'
'</saml:Assertion>'
)
class Processor(base.Processor):
def _decode_request(self):
"""
Decodes _request_xml from _saml_request.
"""
self._request_xml = zlib.decompress(base64.b64decode(self._saml_request), -15)
def _format_assertion(self):
self._assertion_xml = get_assertion_dropbox_xml(self._assertion_params, signed=False)
Which you register in your settings.py file as follows:
SAML2IDP_CONFIG = {
'autosubmit': True,
'certificate_file': '/****/certificate.pem',
'private_key_file': '/****/private-key.pem',
'issuer': 'https://www.****.com',
'signing': True,
}
sampleSpConfig = {
'acs_url': 'https://www.dropbox.com/saml_login',
'processor': 'dropbox.Processor',
}
SAML2IDP_REMOTES = {
'sample': sampleSpConfig,
}
Works like a dream. Hope this helps somebody out there.

Testing Django email backend

In my settings.py, I put:
EMAIL_BACKEND = 'mailer.backend.DbBackend'
So even when importing from from django.core.mail import send_mail, the send_mail function still queues up the email in the database instead of sending it immediately.
It works just fine when actually running the website, but when testing the website, and accessing some webpages that trigger emails, emails are no longer queued anymore:
def test_something(self):
...
# Check no emails are actually sent yet
self.assertEquals(len(mail.outbox), 0) # test fails here -- 2 != 0
# Check queued emails.
messages = Message.objects.all()
self.assertEquals(messages.count(), 2) # test would also fail here -- 0 != 2
...
How come it doesn't seem to be using the backend when it is testing? (importing send_mail from mailer itself gets the tests to pass, but I can't really change the imports of other mailing apps like django-templated-email)
According to this question django overrides the setting.EMAIL_BACKEND when testing to 'django.core.mail.backends.locmem.EmailBackend'. It's also in the django docs here.
To properly test email with django-mailer, you need to override two settings:
Make the tests to use the django-mailer backend
Make the djano-mailer backend to use the test backend
If you don't set the django-mailer backend (number 2), your tests will try to send the email for real.
You also need to simulate running django-mailer's send_mail management command so that you can check mail.outbox for the correct email.
Here's an example of how to setup a test method:
from mailer.engine import send_all
#override_settings(EMAIL_BACKEND='mailer.backend.DbBackend')
#override_settings(MAILER_EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
def test_email(self):
# Code that generates email goes here.
send_all() # Simulates running django-mailer's send_mail management command.
# Code to check the email in mail.outbox goes here.
This strategy makes your tests specific to django-mailer which you don't always want or need. I personally only use this setup when I'm testing specific functionality enabled by django-mailer. Otherwise, I use the default test email backend setup by django.
If you really want have sending of emails (like default) via SMTP in django tests use the decorator:
from django.test.utils import override_settings
#override_settings(EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend')
class TestEmailVerification(TestCase):
...
Try the following:
django.core.mail.backends.console.EmailBackend