summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
Diffstat (limited to 'service')
-rw-r--r--service/pixelated/adapter/model/mail.py38
-rw-r--r--service/pixelated/bitmask_libraries/certs.py34
-rw-r--r--service/pixelated/bitmask_libraries/config.py3
-rw-r--r--service/pixelated/bitmask_libraries/register.py3
-rw-r--r--service/pixelated/bitmask_libraries/session.py4
-rw-r--r--service/pixelated/config/leap_cert.py2
-rw-r--r--service/pixelated/support/ext_requests_urllib3.py107
-rw-r--r--service/pixelated/support/tls_adapter.py2
-rw-r--r--service/test/unit/adapter/test_mail.py34
-rw-r--r--service/test/unit/bitmask_libraries/test_certs.py6
10 files changed, 152 insertions, 81 deletions
diff --git a/service/pixelated/adapter/model/mail.py b/service/pixelated/adapter/model/mail.py
index f23c2708..618b980a 100644
--- a/service/pixelated/adapter/model/mail.py
+++ b/service/pixelated/adapter/model/mail.py
@@ -21,6 +21,7 @@ from email.header import decode_header
from leap.mail.imap.fields import fields
import leap.mail.walk as walk
import dateutil.parser as dateparser
+from datetime import datetime
from pixelated.adapter.model.status import Status
import pixelated.support.date
from email.MIMEMultipart import MIMEMultipart
@@ -291,7 +292,10 @@ class PixelatedMail(Mail):
for header in ['From', 'Subject']:
_headers[header] = self._decode_header(hdoc_headers.get(header))
- _headers['Date'] = self._get_date()
+ try:
+ _headers['Date'] = self._get_date()
+ except Exception, e:
+ _headers['Date'] = pixelated.support.date.iso_now()
if self.parts and len(self.parts['alternatives']) > 1:
_headers['content_type'] = 'multipart/alternative; boundary="%s"' % self.boundary
@@ -303,25 +307,35 @@ class PixelatedMail(Mail):
return _headers
+ def _decode_header_with_fallback(self, entry):
+ try:
+ return decode_header(entry)[0][0]
+ except Exception, e:
+ return entry.encode('ascii', 'ignore')
+
def _decode_header(self, header):
if not header:
return None
if isinstance(header, list):
- return [decode_header(entry)[0][0] for entry in header]
+ return [self._decode_header_with_fallback(entry) for entry in header]
else:
- return decode_header(header)[0][0]
+ return self._decode_header_with_fallback(header)
def _get_date(self):
date = self.hdoc.content.get('date', None)
- if not date:
- received = self.hdoc.content.get('received', None)
- if received:
- date = received.split(";")[-1].strip()
- else:
- # we can't get a date for this mail, so lets just use now
- logger.warning('Encountered a mail with missing date and received header fields. Subject %s' % self.hdoc.content.get('subject', None))
- date = pixelated.support.date.iso_now()
- return dateparser.parse(date).isoformat()
+ try:
+ if not date:
+ received = self.hdoc.content.get('received', None)
+ if received:
+ date = received.split(";")[-1].strip()
+ else:
+ # we can't get a date for this mail, so lets just use now
+ logger.warning('Encountered a mail with missing date and received header fields. Subject %s' % self.hdoc.content.get('subject', None))
+ date = pixelated.support.date.iso_now()
+ return dateparser.parse(date).isoformat()
+ except (ValueError, TypeError) as e:
+ date = pixelated.support.date.iso_now()
+ return dateparser.parse(date).isoformat()
@property
def security_casing(self):
diff --git a/service/pixelated/bitmask_libraries/certs.py b/service/pixelated/bitmask_libraries/certs.py
index 31e68d1c..a321e00e 100644
--- a/service/pixelated/bitmask_libraries/certs.py
+++ b/service/pixelated/bitmask_libraries/certs.py
@@ -14,13 +14,15 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
import os
-
+import requests
+import json
from leap.common import ca_bundle
from .config import AUTO_DETECT_CA_BUNDLE
LEAP_CERT = None
LEAP_FINGERPRINT = None
+PACKAGED_CERTS_HOME = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates"))
def which_api_CA_bundle(provider):
@@ -45,7 +47,6 @@ class LeapCertificate(object):
def __init__(self, provider):
self._config = provider.config
self._server_name = provider.server_name
- self._certs_home = self._config.certs_home
self._provider = provider
def auto_detect_bootstrap_ca_bundle(self):
@@ -84,12 +85,35 @@ class LeapCertificate(object):
return path
def _local_bootstrap_server_cert(self):
- cert_file = os.path.join(self._certs_home, '%s.ca.crt' % self._server_name)
- if not os.path.isfile(cert_file):
- self._download_server_cert(cert_file)
+ cert_file = self._bootstrap_certs_cert_file()
+ if os.path.isfile(cert_file):
+ return cert_file
+
+ cert_file = os.path.join(PACKAGED_CERTS_HOME, '%s.ca.crt' % self._server_name)
+ if os.path.exists(cert_file):
+ return cert_file
+
+ # else download the file
+ cert_file = self._bootstrap_certs_cert_file()
+ response = requests.get('https://%s/provider.json' % self._server_name)
+ provider_data = json.loads(response.content)
+ ca_cert_uri = str(provider_data['ca_cert_uri'])
+
+ response = requests.get(ca_cert_uri)
+ with open(cert_file, 'w') as file:
+ file.write(response.content)
return cert_file
+ def _bootstrap_certs_cert_file(self):
+ path = os.path.join(self._provider.config.leap_home, 'providers', self._server_name)
+ if not os.path.isdir(path):
+ os.makedirs(path, 0700)
+
+ file_path = os.path.join(path, '%s.ca.crt' % self._server_name)
+
+ return file_path
+
def _download_server_cert(self, cert_file_name):
cert = self._provider.fetch_valid_certificate()
diff --git a/service/pixelated/bitmask_libraries/config.py b/service/pixelated/bitmask_libraries/config.py
index 56f28706..8c862d0a 100644
--- a/service/pixelated/bitmask_libraries/config.py
+++ b/service/pixelated/bitmask_libraries/config.py
@@ -45,7 +45,7 @@ class LeapConfig(object):
def __init__(self, leap_home=DEFAULT_LEAP_HOME, bootstrap_ca_cert_bundle=AUTO_DETECT_CA_BUNDLE,
ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, verify_ssl=True,
fetch_interval_in_s=30,
- timeout_in_s=15, start_background_jobs=False, gpg_binary=discover_gpg_binary(), certs_home=None):
+ timeout_in_s=15, start_background_jobs=False, gpg_binary=discover_gpg_binary()):
"""
Constructor.
@@ -75,7 +75,6 @@ class LeapConfig(object):
"""
self.leap_home = leap_home
- self.certs_home = certs_home
self.bootstrap_ca_cert_bundle = bootstrap_ca_cert_bundle
self.ca_cert_bundle = ca_cert_bundle
self.verify_ssl = verify_ssl
diff --git a/service/pixelated/bitmask_libraries/register.py b/service/pixelated/bitmask_libraries/register.py
index 258394da..bb54477f 100644
--- a/service/pixelated/bitmask_libraries/register.py
+++ b/service/pixelated/bitmask_libraries/register.py
@@ -25,8 +25,7 @@ from pixelated.bitmask_libraries.auth import LeapAuthenticator, LeapCredentials
def register_new_user(username, server_name):
- certs_home = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates"))
- config = LeapConfig(certs_home=certs_home)
+ config = LeapConfig()
provider = LeapProvider(server_name, config)
password = getpass.getpass('Please enter password for %s: ' % username)
LeapAuthenticator(provider).register(LeapCredentials(username, password))
diff --git a/service/pixelated/bitmask_libraries/session.py b/service/pixelated/bitmask_libraries/session.py
index c0c8f712..eb6f39d7 100644
--- a/service/pixelated/bitmask_libraries/session.py
+++ b/service/pixelated/bitmask_libraries/session.py
@@ -37,9 +37,7 @@ SESSIONS = {}
def open(username, password, server_name, leap_home=DEFAULT_LEAP_HOME):
- certs_home = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates"))
-
- config = LeapConfig(leap_home=leap_home, certs_home=certs_home)
+ config = LeapConfig(leap_home=leap_home)
provider = LeapProvider(server_name, config)
refresh_ca_bundle(provider)
session = LeapSessionFactory(provider).create(LeapCredentials(username, password))
diff --git a/service/pixelated/config/leap_cert.py b/service/pixelated/config/leap_cert.py
index 3172c953..22f73720 100644
--- a/service/pixelated/config/leap_cert.py
+++ b/service/pixelated/config/leap_cert.py
@@ -19,7 +19,7 @@ import pixelated.bitmask_libraries.certs as certs
def init_leap_cert(args):
if args.leap_cert_fingerprint is None:
- certs.LEAP_CERT = args.leap_cert
+ certs.LEAP_CERT = args.leap_cert or True
certs.LEAP_FINGERPRINT = None
else:
certs.LEAP_FINGERPRINT = args.leap_cert_fingerprint
diff --git a/service/pixelated/support/ext_requests_urllib3.py b/service/pixelated/support/ext_requests_urllib3.py
index a836d6fd..c4ec2438 100644
--- a/service/pixelated/support/ext_requests_urllib3.py
+++ b/service/pixelated/support/ext_requests_urllib3.py
@@ -15,65 +15,68 @@
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
import requests
-import requests.packages.urllib3.connectionpool
-from socket import error as SocketError, timeout as SocketTimeout
-from requests.packages.urllib3.packages.ssl_match_hostname import CertificateError, match_hostname
-import socket
-import ssl
-from requests.packages.urllib3.exceptions import (
- ClosedPoolError,
- ConnectTimeoutError,
- EmptyPoolError,
- HostChangedError,
- MaxRetryError,
- SSLError,
- ReadTimeoutError,
- ProxyError,
-)
-from requests.packages.urllib3.util import (
- assert_fingerprint,
- get_host,
- is_connection_dropped,
- resolve_cert_reqs,
- resolve_ssl_version,
- ssl_wrap_socket,
- Timeout,
-)
+if requests.__version__ == '2.0.0':
+ try:
+ import requests.packages.urllib3.connectionpool
+ from socket import error as SocketError, timeout as SocketTimeout
+ from requests.packages.urllib3.packages.ssl_match_hostname import CertificateError, match_hostname
+ import socket
+ import ssl
+ from requests.packages.urllib3.exceptions import (
+ ClosedPoolError,
+ ConnectTimeoutError,
+ EmptyPoolError,
+ HostChangedError,
+ MaxRetryError,
+ SSLError,
+ ReadTimeoutError,
+ ProxyError,
+ )
-def patched_connect(self):
- # Add certificate verification
- try:
- sock = socket.create_connection(address=(self.host, self.port), timeout=self.timeout)
- except SocketTimeout:
- raise ConnectTimeoutError(self, "Connection to %s timed out. (connect timeout=%s)" % (self.host, self.timeout))
+ from requests.packages.urllib3.util import (
+ assert_fingerprint,
+ get_host,
+ is_connection_dropped,
+ resolve_cert_reqs,
+ resolve_ssl_version,
+ ssl_wrap_socket,
+ Timeout,
+ )
- resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
- resolved_ssl_version = resolve_ssl_version(self.ssl_version)
+ def patched_connect(self):
+ # Add certificate verification
+ try:
+ sock = socket.create_connection(address=(self.host, self.port), timeout=self.timeout)
+ except SocketTimeout:
+ raise ConnectTimeoutError(self, "Connection to %s timed out. (connect timeout=%s)" % (self.host, self.timeout))
- if self._tunnel_host:
- self.sock = sock
- # Calls self._set_hostport(), so self.host is
- # self._tunnel_host below.
- self._tunnel()
+ resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
+ resolved_ssl_version = resolve_ssl_version(self.ssl_version)
- # Wrap socket using verification with the root certs in
- # trusted_root_certs
- self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
- cert_reqs=resolved_cert_reqs,
- ca_certs=self.ca_certs,
- server_hostname=self.host,
- ssl_version=resolved_ssl_version)
+ if self._tunnel_host:
+ self.sock = sock
+ # Calls self._set_hostport(), so self.host is
+ # self._tunnel_host below.
+ self._tunnel()
- if self.assert_fingerprint:
- assert_fingerprint(self.sock.getpeercert(binary_form=True),
- self.assert_fingerprint)
- elif resolved_cert_reqs != ssl.CERT_NONE and self.assert_hostname is not False:
- match_hostname(self.sock.getpeercert(),
- self.assert_hostname or self.host)
+ # Wrap socket using verification with the root certs in
+ # trusted_root_certs
+ self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
+ cert_reqs=resolved_cert_reqs,
+ ca_certs=self.ca_certs,
+ server_hostname=self.host,
+ ssl_version=resolved_ssl_version)
+ if self.assert_fingerprint:
+ assert_fingerprint(self.sock.getpeercert(binary_form=True),
+ self.assert_fingerprint)
+ elif resolved_cert_reqs != ssl.CERT_NONE and self.assert_hostname is not False:
+ match_hostname(self.sock.getpeercert(),
+ self.assert_hostname or self.host)
-if requests.__version__ == '2.0.0':
- requests.packages.urllib3.connectionpool.VerifiedHTTPSConnection.connect = patched_connect
+ requests.packages.urllib3.connectionpool.VerifiedHTTPSConnection.connect = patched_connect
+ except ImportError:
+ pass # The patch is specific for the debian package. Ignore it if it can't be found
diff --git a/service/pixelated/support/tls_adapter.py b/service/pixelated/support/tls_adapter.py
index f543bf4d..301a2123 100644
--- a/service/pixelated/support/tls_adapter.py
+++ b/service/pixelated/support/tls_adapter.py
@@ -41,7 +41,7 @@ class EnforceTLSv1Adapter(HTTPAdapter):
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize,
- block=block, ssl_version=latest_available_ssl_version(),
+ block=block,
assert_hostname=self._assert_hostname,
assert_fingerprint=self._assert_fingerprint,
cert_reqs=ssl.CERT_REQUIRED)
diff --git a/service/test/unit/adapter/test_mail.py b/service/test/unit/adapter/test_mail.py
index c7910b7f..d77816cd 100644
--- a/service/test/unit/adapter/test_mail.py
+++ b/service/test/unit/adapter/test_mail.py
@@ -1,3 +1,4 @@
+# -*- coding: UTF-8 -*-
#
# Copyright (c) 2014 ThoughtWorks, Inc.
#
@@ -66,6 +67,28 @@ class TestPixelatedMail(unittest.TestCase):
self.assertEqual(str(mail.headers['Date']), leap_mail_date_in_iso_format)
+ def test_use_datetime_now_as_fallback_for_invalid_date(self):
+ leap_mail_date = u'söme däte'
+ date_expected = "2014-09-03T13:11:15-03:00"
+
+ when(pixelated.support.date).iso_now().thenReturn(date_expected)
+ leap_mail = test_helper.leap_mail(headers={'date': leap_mail_date})
+
+ mail = PixelatedMail.from_soledad(*leap_mail, soledad_querier=self.querier)
+
+ self.assertEqual(str(mail.headers['Date']), date_expected)
+
+ def test_fall_back_to_ascii_if_invalid_received_header(self):
+ leap_mail_received_header = u"söme invalid received heäder\n"
+ date_expected = "2014-09-03T13:11:15-03:00"
+
+ when(pixelated.support.date).iso_now().thenReturn(date_expected)
+ leap_mail = test_helper.leap_mail(headers={'received': leap_mail_received_header})
+
+ mail = PixelatedMail.from_soledad(*leap_mail, soledad_querier=self.querier)
+
+ self.assertEqual(mail.headers['Date'], date_expected)
+
def test_update_tags_return_a_set_with_the_current_tags(self):
soledad_docs = test_helper.leap_mail(extra_headers={'X-tags': '["custom_1", "custom_2"]'})
pixelated_mail = PixelatedMail.from_soledad(*soledad_docs, soledad_querier=self.querier)
@@ -334,6 +357,17 @@ class TestPixelatedMail(unittest.TestCase):
mail.as_dict()
+ def test_parse_UTF8_headers_with_CharsetAscii(self):
+ leap_mail_from = u'"söme ümläuds" <lisa5@dev.pixelated-project.org>'
+ leap_mail_to = u'"söme ümläuds" <lisa5@dev.pixelated-project.org>,\n"söme ümläuds" <lisa5@dev.pixelated-project.org>'
+
+ leap_mail = test_helper.leap_mail(extra_headers={'From': leap_mail_from, 'Subject': "some subject", 'To': leap_mail_to})
+
+ mail = PixelatedMail.from_soledad(*leap_mail, soledad_querier=self.querier)
+
+ mail.headers['From'].encode('ascii')
+ self.assertEqual(mail.headers['To'], ['"sme mluds" <lisa5@dev.pixelated-project.org>', '"sme mluds" <lisa5@dev.pixelated-project.org>'])
+
def simple_mail_dict():
return {
diff --git a/service/test/unit/bitmask_libraries/test_certs.py b/service/test/unit/bitmask_libraries/test_certs.py
index ba56d5c2..4a06649d 100644
--- a/service/test/unit/bitmask_libraries/test_certs.py
+++ b/service/test/unit/bitmask_libraries/test_certs.py
@@ -12,12 +12,12 @@ class CertsTest(unittest.TestCase):
def test_that_which_bootstrap_cert_bundle_returns_byte_string(self, mock_isdir, mock_isfile):
mock_isfile.return_value = True
mock_isdir.return_value = True
- config = MagicMock(bootstrap_ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, certs_home='/some/path')
+ config = MagicMock(bootstrap_ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, leap_home='/leap/home')
provider = MagicMock(server_name=u'test.leap.net', config=config)
bundle = which_bootstrap_CA_bundle(provider)
- self.assertEqual('/some/path/test.leap.net.ca.crt', bundle)
+ self.assertEqual('/leap/home/providers/test.leap.net/test.leap.net.ca.crt', bundle)
self.assertEqual(str, type(bundle))
@patch('pixelated.bitmask_libraries.certs.os.path.isfile')
@@ -26,7 +26,7 @@ class CertsTest(unittest.TestCase):
mock_isfile.return_value = True
mock_isdir.return_value = True
- config = MagicMock(bootstrap_ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, ca_cert_bundle=None, leap_home='/some/leap/home', certs_home='/some/path')
+ config = MagicMock(bootstrap_ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, ca_cert_bundle=None, leap_home='/some/leap/home')
provider = MagicMock(server_name=u'test.leap.net', config=config)
bundle = which_api_CA_bundle(provider)