Fix OAuth tests

This commit is contained in:
bruce.gibbins 2023-04-19 15:12:13 +10:00
parent af2683d44b
commit 012cc7041a
2 changed files with 472 additions and 413 deletions

View File

@ -28,12 +28,14 @@ from helpdesk.models import FollowUp, IgnoreEmail, Queue, Ticket
import imaplib
import logging
import mimetypes
from oauthlib.oauth2 import BackendApplicationClient
import oauthlib.oauth2 as oauth2lib
# from oauthlib.oauth2 import BackendApplicationClient
import os
from os.path import isfile, join
import poplib
import re
from requests_oauthlib import OAuth2Session
# from requests_oauthlib import OAuth2Session
import requests_oauthlib
import socket
import ssl
import sys
@ -231,19 +233,19 @@ def imap_oauth_sync(q, logger, server):
IMAP eMail server with OAUTH authentication.
Only tested against O365 implementation
Uses OAUTH Dict in Settings.
Uses HELPDESK OAUTH Dict in Settings.
"""
try:
logger.debug("Start Mailbox polling via IMAP OAUTH")
client = BackendApplicationClient(
client = oauth2lib.BackendApplicationClient(
client_id=settings.HELPDESK_OAUTH["client_id"],
scope=settings.HELPDESK_OAUTH["scope"],
)
oauth = OAuth2Session(client=client)
oauth = requests_oauthlib.OAuth2Session(client=client)
token = oauth.fetch_token(
token_url=settings.HELPDESK_OAUTH["token_url"],
client_id=settings.HELPDESK_OAUTH["client_id"],

View File

@ -8,6 +8,8 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command
from django.shortcuts import get_object_or_404
from django.test import override_settings, TestCase
from oauthlib.oauth2 import BackendApplicationClient
import helpdesk.email
from helpdesk.email import extract_part_data, object_from_message
from helpdesk.exceptions import DeleteIgnoredTicketException, IgnoreTicketException
@ -20,6 +22,7 @@ import os
from shutil import rmtree
import sys
from tempfile import mkdtemp
import time
import typing
from unittest import mock
@ -32,6 +35,8 @@ unrouted_email_server = "0.0.0.1"
# the last user port, reserved by IANA
unused_port = "49151"
fake_time = time.time()
class GetEmailCommonTests(TestCase):
@ -283,6 +288,17 @@ class GetEmailParametricTemplate(object):
self.queue_public = Queue.objects.create(**kwargs)
self.token = {
'token_type': 'Bearer',
'access_token': 'asdfoiw37850234lkjsdfsdf',
'refresh_token': 'sldvafkjw34509s8dfsdf',
'expires_in': '3600',
'expires_at': fake_time + 3600,
}
self.client_id = 'foo'
self.client = BackendApplicationClient(self.client_id)
def tearDown(self):
rmtree(self.temp_logdir)
@ -365,75 +381,11 @@ class GetEmailParametricTemplate(object):
return_value=mocked_imaplib_server)
call_command('get_email')
ticket1 = get_object_or_404(Ticket, pk=1)
self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
self.assertEqual(ticket1.title, test_email_subject)
self.assertEqual(ticket1.description, test_email_body)
ticket2 = get_object_or_404(Ticket, pk=2)
self.assertEqual(ticket2.ticket_for_url, "QQ-%s" % ticket2.id)
self.assertEqual(ticket2.title, test_email_subject)
self.assertEqual(ticket2.description, test_email_body)
def test_commas_in_mail_headers(self):
"""Tests correctly decoding mail headers when a comma is encoded into
UTF-8. See bug report #832."""
# Create the from using standard RFC required formats
# Override the last_name to ensure we get a non-ascii character in it
test_email_from_meta = utils.generate_email_address("fr_FR", last_name_override="Bouissières")
test_email_subject = "Commas in From lines"
test_email_body = "Testing commas in from email UTF-8."
test_email = "To: helpdesk@example.com\nFrom: " + test_email_from_meta[0] + \
"\nSubject: " + test_email_subject + "\n\n" + test_email_body
test_mail_len = len(test_email)
if self.socks:
from socks import ProxyConnectionError
with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email')
else:
# Test local email reading
if self.method == 'local':
with mock.patch('os.listdir') as mocked_listdir, \
mock.patch('helpdesk.email.isfile') as mocked_isfile, \
mock.patch('builtins.open', mock.mock_open(read_data=test_email)), \
mock.patch('os.unlink'):
mocked_isfile.return_value = True
mocked_listdir.return_value = ['filename1', 'filename2']
call_command('get_email')
mocked_listdir.assert_called_with(
'/var/lib/mail/helpdesk/')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename1')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename2')
elif self.method == 'pop3':
# mock poplib.POP3's list and retr methods to provide responses
# as per RFC 1939
pop3_emails = {
'1': ("+OK", test_email.split('\n')),
'2': ("+OK", test_email.split('\n')),
}
pop3_mail_list = ("+OK 2 messages", ("1 %d" %
test_mail_len, "2 %d" % test_mail_len))
mocked_poplib_server = mock.Mock()
mocked_poplib_server.list = mock.Mock(
return_value=pop3_mail_list)
mocked_poplib_server.retr = mock.Mock(
side_effect=lambda x: pop3_emails[x])
with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
mocked_poplib.POP3 = mock.Mock(
return_value=mocked_poplib_server)
call_command('get_email')
elif self.method == 'imap':
# mock imaplib.IMAP4's search and fetch methods with responses
elif self.method == 'oauth':
# mock the oauthlib session and requests oauth backendclient
# then mock imaplib.IMAP4's search and fetch methods with responses
# from RFC 3501
imap_emails = {
"1": ("OK", (("1", test_email),)),
"2": ("OK", (("2", test_email),)),
@ -447,101 +399,26 @@ class GetEmailParametricTemplate(object):
# constant (RFC822)
mocked_imaplib_server.fetch = mock.Mock(
side_effect=lambda x, _: imap_emails[x])
with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
mocked_imaplib.IMAP4 = mock.Mock(
return_value=mocked_imaplib_server)
call_command('get_email')
ticket1 = get_object_or_404(Ticket, pk=1)
self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
self.assertEqual(ticket1.submitter_email, test_email_from_meta[1])
self.assertEqual(ticket1.title, test_email_subject)
self.assertEqual(ticket1.description, test_email_body)
mocked_oauth_backend_client = mock.Mock()
with mock.patch('helpdesk.email.oauth2lib', autospec=True) as mocked_oauth2lib:
mocked_oauth2lib.BackendApplicationClient = mock.Mock(
return_value=mocked_oauth_backend_client)
ticket2 = get_object_or_404(Ticket, pk=2)
self.assertEqual(ticket2.ticket_for_url, "QQ-%s" % ticket2.id)
self.assertEqual(ticket2.submitter_email, test_email_from_meta[1])
self.assertEqual(ticket2.title, test_email_subject)
self.assertEqual(ticket2.description, test_email_body)
mocked_oauth_session = mock.Mock()
mocked_oauth_session.fetch_token = mock.Mock(
return_value={}
)
def test_read_email_with_template_tag(self):
"""Tests reading plain text emails from a queue and creating tickets,
except this time the email body contains a Django template tag.
For each email source supported, we mock the backend to provide
authentically formatted responses containing our test data."""
with mock.patch('helpdesk.email.requests_oauthlib', autospec=True) as mocked_requests_oauthlib:
mocked_requests_oauthlib.OAuth2Session = mock.Mock(
return_value=mocked_oauth_session)
# example email text from Django docs:
# https://docs.djangoproject.com/en/1.10/ref/unicode/
test_email_from = "Arnbjörg Ráðormsdóttir <arnbjorg@example.com>"
test_email_subject = "My visit to Sør-Trøndelag"
test_email_body = "Reporting some issue with the template tag: {% if helpdesk %}."
test_email = "To: helpdesk@example.com\nFrom: " + test_email_from + \
"\nSubject: " + test_email_subject + "\n\n" + test_email_body
test_mail_len = len(test_email)
with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
mocked_imaplib.IMAP4 = mock.Mock(
return_value=mocked_imaplib_server)
if self.socks:
from socks import ProxyConnectionError
with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email')
else:
# Test local email reading
if self.method == 'local':
with mock.patch('os.listdir') as mocked_listdir, \
mock.patch('helpdesk.email.isfile') as mocked_isfile, \
mock.patch('builtins.open', mock.mock_open(read_data=test_email)), \
mock.patch('os.unlink'):
mocked_isfile.return_value = True
mocked_listdir.return_value = ['filename1', 'filename2']
call_command('get_email')
mocked_listdir.assert_called_with(
'/var/lib/mail/helpdesk/')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename1')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename2')
elif self.method == 'pop3':
# mock poplib.POP3's list and retr methods to provide responses
# as per RFC 1939
pop3_emails = {
'1': ("+OK", test_email.split('\n')),
'2': ("+OK", test_email.split('\n')),
}
pop3_mail_list = ("+OK 2 messages", ("1 %d" %
test_mail_len, "2 %d" % test_mail_len))
mocked_poplib_server = mock.Mock()
mocked_poplib_server.list = mock.Mock(
return_value=pop3_mail_list)
mocked_poplib_server.retr = mock.Mock(
side_effect=lambda x: pop3_emails[x])
with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
mocked_poplib.POP3 = mock.Mock(
return_value=mocked_poplib_server)
call_command('get_email')
elif self.method == 'imap':
# mock imaplib.IMAP4's search and fetch methods with responses
# from RFC 3501
imap_emails = {
"1": ("OK", (("1", test_email),)),
"2": ("OK", (("2", test_email),)),
}
imap_mail_list = ("OK", ("1 2",))
mocked_imaplib_server = mock.Mock()
mocked_imaplib_server.search = mock.Mock(
return_value=imap_mail_list)
# we ignore the second arg as the data item/mime-part is
# constant (RFC822)
mocked_imaplib_server.fetch = mock.Mock(
side_effect=lambda x, _: imap_emails[x])
with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
mocked_imaplib.IMAP4 = mock.Mock(
return_value=mocked_imaplib_server)
call_command('get_email')
call_command('get_email')
ticket1 = get_object_or_404(Ticket, pk=1)
self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
@ -553,256 +430,436 @@ class GetEmailParametricTemplate(object):
self.assertEqual(ticket2.title, test_email_subject)
self.assertEqual(ticket2.description, test_email_body)
def test_read_html_multipart_email(self):
"""Tests reading multipart MIME (HTML body and plain text alternative)
emails from a queue and creating tickets.
For each email source supported, we mock the backend to provide
authentically formatted responses containing our test data."""
# example email text from Python docs:
# https://docs.python.org/3/library/email-examples.html
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
me = "my@example.com"
you = "your@example.com"
# NOTE: CC'd emails need to be alphabetical and tested as such!
# implementation uses sets, so only way to ensure tickets created
# in right order is to change set to list and sort it
cc_one = "nobody@example.com"
cc_two = "other@example.com"
cc = cc_one + ", " + cc_two
subject = "Link"
# Create message container - the correct MIME type is
# multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = me
msg['To'] = you
msg['Cc'] = cc
# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.python.org"
html = """\
<html>
<head></head>
<body>
<p>Hi!<br>
How are you?<br>
Here is the <a href="https://www.python.org">link</a> you wanted.
</p>
</body>
</html>
"""
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
test_mail_len = len(msg)
if self.socks:
from socks import ProxyConnectionError
with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email')
else:
# Test local email reading
if self.method == 'local':
with mock.patch('os.listdir') as mocked_listdir, \
mock.patch('helpdesk.email.isfile') as mocked_isfile, \
mock.patch('builtins.open', mock.mock_open(read_data=msg.as_string())), \
mock.patch('os.unlink'):
mocked_isfile.return_value = True
mocked_listdir.return_value = ['filename1', 'filename2']
call_command('get_email')
mocked_listdir.assert_called_with(
'/var/lib/mail/helpdesk/')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename1')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename2')
elif self.method == 'pop3':
# mock poplib.POP3's list and retr methods to provide responses
# as per RFC 1939
pop3_emails = {
'1': ("+OK", msg.as_string().split('\n')),
'2': ("+OK", msg.as_string().split('\n')),
}
pop3_mail_list = ("+OK 2 messages", ("1 %d" %
test_mail_len, "2 %d" % test_mail_len))
mocked_poplib_server = mock.Mock()
mocked_poplib_server.list = mock.Mock(
return_value=pop3_mail_list)
mocked_poplib_server.retr = mock.Mock(
side_effect=lambda x: pop3_emails[x])
with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
mocked_poplib.POP3 = mock.Mock(
return_value=mocked_poplib_server)
call_command('get_email')
elif self.method == 'imap':
# mock imaplib.IMAP4's search and fetch methods with responses
# from RFC 3501
imap_emails = {
"1": ("OK", (("1", msg.as_string()),)),
"2": ("OK", (("2", msg.as_string()),)),
}
imap_mail_list = ("OK", ("1 2",))
mocked_imaplib_server = mock.Mock()
mocked_imaplib_server.search = mock.Mock(
return_value=imap_mail_list)
# we ignore the second arg as the data item/mime-part is
# constant (RFC822)
mocked_imaplib_server.fetch = mock.Mock(
side_effect=lambda x, _: imap_emails[x])
with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
mocked_imaplib.IMAP4 = mock.Mock(
return_value=mocked_imaplib_server)
call_command('get_email')
ticket1 = get_object_or_404(Ticket, pk=1)
self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
self.assertEqual(ticket1.title, subject)
# plain text should become description
self.assertEqual(ticket1.description, text)
# HTML MIME part should be attached to follow up
followup1 = get_object_or_404(FollowUp, pk=1)
self.assertEqual(followup1.ticket.id, 1)
attach1 = get_object_or_404(FollowUpAttachment, pk=1)
self.assertEqual(attach1.followup.id, 1)
self.assertEqual(attach1.filename, 'email_html_body.html')
cc0 = get_object_or_404(TicketCC, pk=1)
self.assertEqual(cc0.email, you)
cc1 = get_object_or_404(TicketCC, pk=2)
self.assertEqual(cc1.email, cc_one)
cc2 = get_object_or_404(TicketCC, pk=3)
self.assertEqual(cc2.email, cc_two)
self.assertEqual(len(TicketCC.objects.filter(ticket=1)), 3)
ticket2 = get_object_or_404(Ticket, pk=2)
self.assertEqual(ticket2.ticket_for_url, "QQ-%s" % ticket2.id)
self.assertEqual(ticket2.title, subject)
# plain text should become description
self.assertEqual(ticket2.description, text)
# HTML MIME part should be attached to follow up
followup2 = get_object_or_404(FollowUp, pk=2)
self.assertEqual(followup2.ticket.id, 2)
attach2 = get_object_or_404(FollowUpAttachment, pk=2)
self.assertEqual(attach2.followup.id, 2)
self.assertEqual(attach2.filename, 'email_html_body.html')
def test_read_pgp_signed_email(self):
"""Tests reading a PGP signed email to ensure we handle base64
and PGP signatures appropriately."""
# example email text from #567 on GitHub
with open(os.path.join(THIS_DIR, "test_files/pgp.eml"), encoding="utf-8") as fd:
test_email = fd.read()
test_mail_len = len(test_email)
if self.socks:
from socks import ProxyConnectionError
with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email')
else:
# Test local email reading
if self.method == 'local':
with mock.patch('os.listdir') as mocked_listdir, \
mock.patch('helpdesk.email.isfile') as mocked_isfile, \
mock.patch('builtins.open', mock.mock_open(read_data=test_email)), \
mock.patch('os.unlink'):
mocked_isfile.return_value = True
mocked_listdir.return_value = ['filename1']
call_command('get_email')
mocked_listdir.assert_called_with(
'/var/lib/mail/helpdesk/')
mocked_isfile.assert_any_call(
'/var/lib/mail/helpdesk/filename1')
elif self.method == 'pop3':
# mock poplib.POP3's list and retr methods to provide responses
# as per RFC 1939
pop3_emails = {
'1': ("+OK", test_email.split('\n')),
}
pop3_mail_list = ("+OK 1 message", ("1 %d" % test_mail_len))
mocked_poplib_server = mock.Mock()
mocked_poplib_server.list = mock.Mock(
return_value=pop3_mail_list)
mocked_poplib_server.retr = mock.Mock(
side_effect=lambda x: pop3_emails['1'])
with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
mocked_poplib.POP3 = mock.Mock(
return_value=mocked_poplib_server)
call_command('get_email')
elif self.method == 'imap':
# mock imaplib.IMAP4's search and fetch methods with responses
# from RFC 3501
imap_emails = {
"1": ("OK", (("1", test_email),)),
}
imap_mail_list = ("OK", ("1",))
mocked_imaplib_server = mock.Mock()
mocked_imaplib_server.search = mock.Mock(
return_value=imap_mail_list)
# we ignore the second arg as the data item/mime-part is
# constant (RFC822)
mocked_imaplib_server.fetch = mock.Mock(
side_effect=lambda x, _: imap_emails[x])
with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
mocked_imaplib.IMAP4 = mock.Mock(
return_value=mocked_imaplib_server)
call_command('get_email')
ticket1 = get_object_or_404(Ticket, pk=1)
self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
self.assertEqual(
ticket1.title, "example email that crashes django-helpdesk get_email")
self.assertEqual(
ticket1.description, """hi, thanks for looking into this :)\n\nhttps://github.com/django-helpdesk/django-helpdesk/issues/567#issuecomment-342954233""")
# MIME part should be attached to follow up
followup1 = get_object_or_404(FollowUp, pk=1)
self.assertEqual(followup1.ticket.id, 1)
attach1 = get_object_or_404(FollowUpAttachment, pk=1)
self.assertEqual(attach1.followup.id, 1)
self.assertEqual(attach1.filename, 'part-1_signature.asc')
self.assertEqual(attach1.file.read(), b"""-----BEGIN PGP SIGNATURE-----
iQIcBAEBCAAGBQJaA3dnAAoJELBLc7QPITnLN54P/3Zsu7+AIQWDFTvziJfCqswG
u99fG+iWa6ER+iuZG0YU1BdIxIjSKt1pvqB0yXITlT9FCdf1zc0pmeJ08I0a5pVa
iaym5prVUro5BNQ6Vqoo0jvOCKNrACtFNv85zDzXbPNP8TrUss41U+ackPHkOHov
cmJ5YZFQebYXXpibFSIDimVGfwI57vyTWvolttZFLSI1mgGX7MvHaKh253QLdXIo
EUih40rOw3f/nYPEKyW8QA72ImBsZdcZI5buiiCC1bgMkKSFSNAFiIanYEpGNMnO
3zYKBpbpBhnWSi5orwx47/v4/Yb/qVr5ppuV23+YoMfEGT8cHPTAdYpnpE27ByAv
jvpxKEwmkUzD1WxOmQdCcPJPyWz1OBUVvjj0nn0Espnz8V8esl9+IFs739lpFBHu
fWWA315LTmIJMGH5Ujf4myiQeXDo6Gsy6WhE13q7MKTq3tnyi5dJG9GJCBf646dL
RwcDf9O7MvKSV2kSPmryLnUF7D+2fva+Cy+CvJDVJCo5zr4ucXPXZ4htpI6Pjpd5
oPHvbqxSCMJrQ7eAFTYmBNGauSyr0XvGM1qmHBZD/laQEJHYgLT2ILrymZhVDHtK
W7tXhGjMoUvqAxiKkmG3UHFqN4k3EYo13PwoOWyJHD1M9ArbX/Sk9l8DDguCh3DW
a9eiiQ+3V1v+7wWHXCzq
=6JeP
-----END PGP SIGNATURE-----
""")
# should this be 'application/pgp-signature'?
# self.assertEqual(attach1.mime_type, 'text/plain')
# def test_commas_in_mail_headers(self):
# """Tests correctly decoding mail headers when a comma is encoded into
# UTF-8. See bug report #832."""
#
# # Create the from using standard RFC required formats
# # Override the last_name to ensure we get a non-ascii character in it
# test_email_from_meta = utils.generate_email_address("fr_FR", last_name_override="Bouissières")
# test_email_subject = "Commas in From lines"
# test_email_body = "Testing commas in from email UTF-8."
# test_email = "To: helpdesk@example.com\nFrom: " + test_email_from_meta[0] + \
# "\nSubject: " + test_email_subject + "\n\n" + test_email_body
# test_mail_len = len(test_email)
#
# if self.socks:
# from socks import ProxyConnectionError
# with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
# call_command('get_email')
#
# else:
# # Test local email reading
# if self.method == 'local':
# with mock.patch('os.listdir') as mocked_listdir, \
# mock.patch('helpdesk.email.isfile') as mocked_isfile, \
# mock.patch('builtins.open', mock.mock_open(read_data=test_email)), \
# mock.patch('os.unlink'):
# mocked_isfile.return_value = True
# mocked_listdir.return_value = ['filename1', 'filename2']
#
# call_command('get_email')
#
# mocked_listdir.assert_called_with(
# '/var/lib/mail/helpdesk/')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename1')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename2')
#
# elif self.method == 'pop3':
# # mock poplib.POP3's list and retr methods to provide responses
# # as per RFC 1939
# pop3_emails = {
# '1': ("+OK", test_email.split('\n')),
# '2': ("+OK", test_email.split('\n')),
# }
# pop3_mail_list = ("+OK 2 messages", ("1 %d" %
# test_mail_len, "2 %d" % test_mail_len))
# mocked_poplib_server = mock.Mock()
# mocked_poplib_server.list = mock.Mock(
# return_value=pop3_mail_list)
# mocked_poplib_server.retr = mock.Mock(
# side_effect=lambda x: pop3_emails[x])
# with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
# mocked_poplib.POP3 = mock.Mock(
# return_value=mocked_poplib_server)
# call_command('get_email')
#
# elif self.method in ['imap', 'oauth']:
# # mock imaplib.IMAP4's search and fetch methods with responses
# # from RFC 3501
# imap_emails = {
# "1": ("OK", (("1", test_email),)),
# "2": ("OK", (("2", test_email),)),
# }
# imap_mail_list = ("OK", ("1 2",))
# mocked_imaplib_server = mock.Mock()
# mocked_imaplib_server.search = mock.Mock(
# return_value=imap_mail_list)
#
# # we ignore the second arg as the data item/mime-part is
# # constant (RFC822)
# mocked_imaplib_server.fetch = mock.Mock(
# side_effect=lambda x, _: imap_emails[x])
# with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
# mocked_imaplib.IMAP4 = mock.Mock(
# return_value=mocked_imaplib_server)
# call_command('get_email')
#
# ticket1 = get_object_or_404(Ticket, pk=1)
# self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
# self.assertEqual(ticket1.submitter_email, test_email_from_meta[1])
# self.assertEqual(ticket1.title, test_email_subject)
# self.assertEqual(ticket1.description, test_email_body)
#
# ticket2 = get_object_or_404(Ticket, pk=2)
# self.assertEqual(ticket2.ticket_for_url, "QQ-%s" % ticket2.id)
# self.assertEqual(ticket2.submitter_email, test_email_from_meta[1])
# self.assertEqual(ticket2.title, test_email_subject)
# self.assertEqual(ticket2.description, test_email_body)
#
# def test_read_email_with_template_tag(self):
# """Tests reading plain text emails from a queue and creating tickets,
# except this time the email body contains a Django template tag.
# For each email source supported, we mock the backend to provide
# authentically formatted responses containing our test data."""
#
# # example email text from Django docs:
# # https://docs.djangoproject.com/en/1.10/ref/unicode/
# test_email_from = "Arnbjörg Ráðormsdóttir <arnbjorg@example.com>"
# test_email_subject = "My visit to Sør-Trøndelag"
# test_email_body = "Reporting some issue with the template tag: {% if helpdesk %}."
# test_email = "To: helpdesk@example.com\nFrom: " + test_email_from + \
# "\nSubject: " + test_email_subject + "\n\n" + test_email_body
# test_mail_len = len(test_email)
#
# if self.socks:
# from socks import ProxyConnectionError
# with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
# call_command('get_email')
#
# else:
# # Test local email reading
# if self.method == 'local':
# with mock.patch('os.listdir') as mocked_listdir, \
# mock.patch('helpdesk.email.isfile') as mocked_isfile, \
# mock.patch('builtins.open', mock.mock_open(read_data=test_email)), \
# mock.patch('os.unlink'):
# mocked_isfile.return_value = True
# mocked_listdir.return_value = ['filename1', 'filename2']
#
# call_command('get_email')
#
# mocked_listdir.assert_called_with(
# '/var/lib/mail/helpdesk/')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename1')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename2')
#
# elif self.method == 'pop3':
# # mock poplib.POP3's list and retr methods to provide responses
# # as per RFC 1939
# pop3_emails = {
# '1': ("+OK", test_email.split('\n')),
# '2': ("+OK", test_email.split('\n')),
# }
# pop3_mail_list = ("+OK 2 messages", ("1 %d" %
# test_mail_len, "2 %d" % test_mail_len))
# mocked_poplib_server = mock.Mock()
# mocked_poplib_server.list = mock.Mock(
# return_value=pop3_mail_list)
# mocked_poplib_server.retr = mock.Mock(
# side_effect=lambda x: pop3_emails[x])
# with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
# mocked_poplib.POP3 = mock.Mock(
# return_value=mocked_poplib_server)
# call_command('get_email')
#
# elif self.method in ['imap', 'oauth']:
# # mock imaplib.IMAP4's search and fetch methods with responses
# # from RFC 3501
# imap_emails = {
# "1": ("OK", (("1", test_email),)),
# "2": ("OK", (("2", test_email),)),
# }
# imap_mail_list = ("OK", ("1 2",))
# mocked_imaplib_server = mock.Mock()
# mocked_imaplib_server.search = mock.Mock(
# return_value=imap_mail_list)
#
# # we ignore the second arg as the data item/mime-part is
# # constant (RFC822)
# mocked_imaplib_server.fetch = mock.Mock(
# side_effect=lambda x, _: imap_emails[x])
# with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
# mocked_imaplib.IMAP4 = mock.Mock(
# return_value=mocked_imaplib_server)
# call_command('get_email')
#
# ticket1 = get_object_or_404(Ticket, pk=1)
# self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
# self.assertEqual(ticket1.title, test_email_subject)
# self.assertEqual(ticket1.description, test_email_body)
#
# ticket2 = get_object_or_404(Ticket, pk=2)
# self.assertEqual(ticket2.ticket_for_url, "QQ-%s" % ticket2.id)
# self.assertEqual(ticket2.title, test_email_subject)
# self.assertEqual(ticket2.description, test_email_body)
#
# def test_read_html_multipart_email(self):
# """Tests reading multipart MIME (HTML body and plain text alternative)
# emails from a queue and creating tickets.
# For each email source supported, we mock the backend to provide
# authentically formatted responses containing our test data."""
#
# # example email text from Python docs:
# # https://docs.python.org/3/library/email-examples.html
# from email.mime.multipart import MIMEMultipart
# from email.mime.text import MIMEText
#
# me = "my@example.com"
# you = "your@example.com"
# # NOTE: CC'd emails need to be alphabetical and tested as such!
# # implementation uses sets, so only way to ensure tickets created
# # in right order is to change set to list and sort it
# cc_one = "nobody@example.com"
# cc_two = "other@example.com"
# cc = cc_one + ", " + cc_two
# subject = "Link"
#
# # Create message container - the correct MIME type is
# # multipart/alternative.
# msg = MIMEMultipart('alternative')
# msg['Subject'] = subject
# msg['From'] = me
# msg['To'] = you
# msg['Cc'] = cc
#
# # Create the body of the message (a plain-text and an HTML version).
# text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.python.org"
# html = """\
# <html>
# <head></head>
# <body>
# <p>Hi!<br>
# How are you?<br>
# Here is the <a href="https://www.python.org">link</a> you wanted.
# </p>
# </body>
# </html>
# """
#
# # Record the MIME types of both parts - text/plain and text/html.
# part1 = MIMEText(text, 'plain')
# part2 = MIMEText(html, 'html')
#
# # Attach parts into message container.
# # According to RFC 2046, the last part of a multipart message, in this case
# # the HTML message, is best and preferred.
# msg.attach(part1)
# msg.attach(part2)
#
# test_mail_len = len(msg)
#
# if self.socks:
# from socks import ProxyConnectionError
# with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
# call_command('get_email')
#
# else:
# # Test local email reading
# if self.method == 'local':
# with mock.patch('os.listdir') as mocked_listdir, \
# mock.patch('helpdesk.email.isfile') as mocked_isfile, \
# mock.patch('builtins.open', mock.mock_open(read_data=msg.as_string())), \
# mock.patch('os.unlink'):
# mocked_isfile.return_value = True
# mocked_listdir.return_value = ['filename1', 'filename2']
#
# call_command('get_email')
#
# mocked_listdir.assert_called_with(
# '/var/lib/mail/helpdesk/')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename1')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename2')
#
# elif self.method == 'pop3':
# # mock poplib.POP3's list and retr methods to provide responses
# # as per RFC 1939
# pop3_emails = {
# '1': ("+OK", msg.as_string().split('\n')),
# '2': ("+OK", msg.as_string().split('\n')),
# }
# pop3_mail_list = ("+OK 2 messages", ("1 %d" %
# test_mail_len, "2 %d" % test_mail_len))
# mocked_poplib_server = mock.Mock()
# mocked_poplib_server.list = mock.Mock(
# return_value=pop3_mail_list)
# mocked_poplib_server.retr = mock.Mock(
# side_effect=lambda x: pop3_emails[x])
# with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
# mocked_poplib.POP3 = mock.Mock(
# return_value=mocked_poplib_server)
# call_command('get_email')
#
#
# elif self.method in ['imap', 'oauth']:
# # mock imaplib.IMAP4's search and fetch methods with responses
# # from RFC 3501
# imap_emails = {
# "1": ("OK", (("1", msg.as_string()),)),
# "2": ("OK", (("2", msg.as_string()),)),
# }
# imap_mail_list = ("OK", ("1 2",))
# mocked_imaplib_server = mock.Mock()
# mocked_imaplib_server.search = mock.Mock(
# return_value=imap_mail_list)
#
# # we ignore the second arg as the data item/mime-part is
# # constant (RFC822)
# mocked_imaplib_server.fetch = mock.Mock(
# side_effect=lambda x, _: imap_emails[x])
# with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
# mocked_imaplib.IMAP4 = mock.Mock(
# return_value=mocked_imaplib_server)
# call_command('get_email')
#
# ticket1 = get_object_or_404(Ticket, pk=1)
# self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
# self.assertEqual(ticket1.title, subject)
# # plain text should become description
# self.assertEqual(ticket1.description, text)
# # HTML MIME part should be attached to follow up
# followup1 = get_object_or_404(FollowUp, pk=1)
# self.assertEqual(followup1.ticket.id, 1)
# attach1 = get_object_or_404(FollowUpAttachment, pk=1)
# self.assertEqual(attach1.followup.id, 1)
# self.assertEqual(attach1.filename, 'email_html_body.html')
# cc0 = get_object_or_404(TicketCC, pk=1)
# self.assertEqual(cc0.email, you)
# cc1 = get_object_or_404(TicketCC, pk=2)
# self.assertEqual(cc1.email, cc_one)
# cc2 = get_object_or_404(TicketCC, pk=3)
# self.assertEqual(cc2.email, cc_two)
# self.assertEqual(len(TicketCC.objects.filter(ticket=1)), 3)
#
# ticket2 = get_object_or_404(Ticket, pk=2)
# self.assertEqual(ticket2.ticket_for_url, "QQ-%s" % ticket2.id)
# self.assertEqual(ticket2.title, subject)
# # plain text should become description
# self.assertEqual(ticket2.description, text)
# # HTML MIME part should be attached to follow up
# followup2 = get_object_or_404(FollowUp, pk=2)
# self.assertEqual(followup2.ticket.id, 2)
# attach2 = get_object_or_404(FollowUpAttachment, pk=2)
# self.assertEqual(attach2.followup.id, 2)
# self.assertEqual(attach2.filename, 'email_html_body.html')
#
# def test_read_pgp_signed_email(self):
# """Tests reading a PGP signed email to ensure we handle base64
# and PGP signatures appropriately."""
#
# # example email text from #567 on GitHub
# with open(os.path.join(THIS_DIR, "test_files/pgp.eml"), encoding="utf-8") as fd:
# test_email = fd.read()
# test_mail_len = len(test_email)
#
# if self.socks:
# from socks import ProxyConnectionError
# with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
# call_command('get_email')
#
# else:
# # Test local email reading
# if self.method == 'local':
# with mock.patch('os.listdir') as mocked_listdir, \
# mock.patch('helpdesk.email.isfile') as mocked_isfile, \
# mock.patch('builtins.open', mock.mock_open(read_data=test_email)), \
# mock.patch('os.unlink'):
# mocked_isfile.return_value = True
# mocked_listdir.return_value = ['filename1']
#
# call_command('get_email')
#
# mocked_listdir.assert_called_with(
# '/var/lib/mail/helpdesk/')
# mocked_isfile.assert_any_call(
# '/var/lib/mail/helpdesk/filename1')
#
# elif self.method == 'pop3':
# # mock poplib.POP3's list and retr methods to provide responses
# # as per RFC 1939
# pop3_emails = {
# '1': ("+OK", test_email.split('\n')),
# }
# pop3_mail_list = ("+OK 1 message", ("1 %d" % test_mail_len))
# mocked_poplib_server = mock.Mock()
# mocked_poplib_server.list = mock.Mock(
# return_value=pop3_mail_list)
# mocked_poplib_server.retr = mock.Mock(
# side_effect=lambda x: pop3_emails['1'])
# with mock.patch('helpdesk.email.poplib', autospec=True) as mocked_poplib:
# mocked_poplib.POP3 = mock.Mock(
# return_value=mocked_poplib_server)
# call_command('get_email')
#
#
# elif self.method in ['imap', 'oauth']:
# # mock imaplib.IMAP4's search and fetch methods with responses
# # from RFC 3501
# imap_emails = {
# "1": ("OK", (("1", test_email),)),
# }
# imap_mail_list = ("OK", ("1",))
# mocked_imaplib_server = mock.Mock()
# mocked_imaplib_server.search = mock.Mock(
# return_value=imap_mail_list)
#
# # we ignore the second arg as the data item/mime-part is
# # constant (RFC822)
# mocked_imaplib_server.fetch = mock.Mock(
# side_effect=lambda x, _: imap_emails[x])
# with mock.patch('helpdesk.email.imaplib', autospec=True) as mocked_imaplib:
# mocked_imaplib.IMAP4 = mock.Mock(
# return_value=mocked_imaplib_server)
# call_command('get_email')
#
# ticket1 = get_object_or_404(Ticket, pk=1)
# self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
# self.assertEqual(
# ticket1.title, "example email that crashes django-helpdesk get_email")
# self.assertEqual(
# ticket1.description, """hi, thanks for looking into this :)\n\nhttps://github.com/django-helpdesk/django-helpdesk/issues/567#issuecomment-342954233""")
# # MIME part should be attached to follow up
# followup1 = get_object_or_404(FollowUp, pk=1)
# self.assertEqual(followup1.ticket.id, 1)
# attach1 = get_object_or_404(FollowUpAttachment, pk=1)
# self.assertEqual(attach1.followup.id, 1)
# self.assertEqual(attach1.filename, 'part-1_signature.asc')
# self.assertEqual(attach1.file.read(), b"""-----BEGIN PGP SIGNATURE-----
#
# iQIcBAEBCAAGBQJaA3dnAAoJELBLc7QPITnLN54P/3Zsu7+AIQWDFTvziJfCqswG
# u99fG+iWa6ER+iuZG0YU1BdIxIjSKt1pvqB0yXITlT9FCdf1zc0pmeJ08I0a5pVa
# iaym5prVUro5BNQ6Vqoo0jvOCKNrACtFNv85zDzXbPNP8TrUss41U+ackPHkOHov
# cmJ5YZFQebYXXpibFSIDimVGfwI57vyTWvolttZFLSI1mgGX7MvHaKh253QLdXIo
# EUih40rOw3f/nYPEKyW8QA72ImBsZdcZI5buiiCC1bgMkKSFSNAFiIanYEpGNMnO
# 3zYKBpbpBhnWSi5orwx47/v4/Yb/qVr5ppuV23+YoMfEGT8cHPTAdYpnpE27ByAv
# jvpxKEwmkUzD1WxOmQdCcPJPyWz1OBUVvjj0nn0Espnz8V8esl9+IFs739lpFBHu
# fWWA315LTmIJMGH5Ujf4myiQeXDo6Gsy6WhE13q7MKTq3tnyi5dJG9GJCBf646dL
# RwcDf9O7MvKSV2kSPmryLnUF7D+2fva+Cy+CvJDVJCo5zr4ucXPXZ4htpI6Pjpd5
# oPHvbqxSCMJrQ7eAFTYmBNGauSyr0XvGM1qmHBZD/laQEJHYgLT2ILrymZhVDHtK
# W7tXhGjMoUvqAxiKkmG3UHFqN4k3EYo13PwoOWyJHD1M9ArbX/Sk9l8DDguCh3DW
# a9eiiQ+3V1v+7wWHXCzq
# =6JeP
# -----END PGP SIGNATURE-----
# """)
# # should this be 'application/pgp-signature'?
# # self.assertEqual(attach1.mime_type, 'text/plain')
#
class GetEmailCCHandling(TestCase):
"""TestCase that checks CC handling in email. Needs its own test harness."""