summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeonardo Schäffer <leonardo.schaffer@gmail.com>2015-06-25 11:20:44 -0300
committerLeonardo Schäffer <leonardo.schaffer@gmail.com>2015-06-25 11:20:44 -0300
commit42f31aa607750eb7c00224e7c1c353b27cbd7519 (patch)
tree4bbfae731c8427c526f22bacb9136e5ed3e69d8c
parent7f349abe67b8e1a71798478f6c26b08773f2d869 (diff)
parent0b179c61043a9854fa23b8fd40fd252864ecd021 (diff)
Merge branch 'master' into floatlabel_for_compose
-rw-r--r--provisioning/Dockerfile4
-rw-r--r--service/pixelated/adapter/model/mail.py16
-rw-r--r--service/pixelated/adapter/search/__init__.py7
-rw-r--r--service/pixelated/adapter/services/mail_service.py3
-rw-r--r--service/pixelated/application.py20
-rw-r--r--service/pixelated/bitmask_libraries/certs.py104
-rw-r--r--service/pixelated/bitmask_libraries/config.py48
-rw-r--r--service/pixelated/bitmask_libraries/nicknym.py10
-rw-r--r--service/pixelated/bitmask_libraries/provider.py14
-rw-r--r--service/pixelated/bitmask_libraries/session.py56
-rw-r--r--service/pixelated/bitmask_libraries/smtp.py19
-rw-r--r--service/pixelated/bitmask_libraries/soledad.py14
-rw-r--r--service/pixelated/config/app_factory.py52
-rw-r--r--service/pixelated/config/arguments.py9
-rw-r--r--service/pixelated/config/leap.py33
-rw-r--r--service/pixelated/config/services.py65
-rw-r--r--service/pixelated/maintenance.py7
-rw-r--r--service/pixelated/register.py31
-rw-r--r--service/pixelated/resources/attachments_resource.py12
-rw-r--r--service/pixelated/resources/root_resource.py5
-rw-r--r--service/pixelated/runserver.py21
-rw-r--r--service/setup.py3
-rw-r--r--service/test/functional/features/environment.py2
-rw-r--r--service/test/functional/features/steps/data_setup.py1
-rw-r--r--service/test/support/dispatcher/proxy.py2
-rw-r--r--service/test/support/integration/app_test_client.py4
-rw-r--r--service/test/unit/adapter/search/test_search.py6
-rw-r--r--service/test/unit/bitmask_libraries/test_certs.py51
-rw-r--r--service/test/unit/bitmask_libraries/test_nicknym.py25
-rw-r--r--service/test/unit/bitmask_libraries/test_provider.py29
-rw-r--r--service/test/unit/bitmask_libraries/test_session.py4
-rw-r--r--service/test/unit/bitmask_libraries/test_smtp.py8
-rw-r--r--service/test/unit/config/test_app_factory.py41
-rw-r--r--service/test/unit/maintenance/test_commands.py2
-rw-r--r--service/test/unit/resources/test_keys_resources.py1
-rw-r--r--service/test/unit/test_application.py53
36 files changed, 347 insertions, 435 deletions
diff --git a/provisioning/Dockerfile b/provisioning/Dockerfile
index 32da36d0..930ed875 100644
--- a/provisioning/Dockerfile
+++ b/provisioning/Dockerfile
@@ -32,6 +32,10 @@ RUN echo "deb http://deb.leap.se/experimental wheezy main" >> /etc/apt/sources.l
RUN apt-key adv --keyserver pool.sks-keyservers.net --recv-key 1E34A1828E207901 && \
apt-key adv --keyserver pool.sks-keyservers.net --recv-key 287A1542472DC0E3
+RUN echo "Package: python-sqlcipher" > /etc/apt/preferences.d/python-sqlcipher
+RUN echo "Pin: version 2.6.3.3+0~20141111222659.14+wheezy~1.gbp2d164a+pix1" >> /etc/apt/preferences.d/python-sqlcipher
+RUN echo "Pin-Priority: 1000" >> /etc/apt/preferences.d/python-sqlcipher
+
# Update packages lists
RUN apt-get update -y --force-yes
diff --git a/service/pixelated/adapter/model/mail.py b/service/pixelated/adapter/model/mail.py
index 7c4223de..464e0343 100644
--- a/service/pixelated/adapter/model/mail.py
+++ b/service/pixelated/adapter/model/mail.py
@@ -393,6 +393,10 @@ class PixelatedMail(Mail):
def uid(self):
return self.fdoc.content['uid']
+ @property
+ def flags(self):
+ return self.fdoc.content['flags']
+
def save(self):
return self.querier.save_mail(self)
@@ -407,21 +411,21 @@ class PixelatedMail(Mail):
return self.tags
def mark_as_read(self):
- if Status.SEEN in self.fdoc.content['flags']:
+ if Status.SEEN in self.flags:
return self
- self.fdoc.content['flags'].append(Status.SEEN)
+ self.flags.append(Status.SEEN)
self.save()
return self
def mark_as_unread(self):
- if Status.SEEN in self.fdoc.content['flags']:
- self.fdoc.content['flags'].remove(Status.SEEN)
+ if Status.SEEN in self.flags:
+ self.flags.remove(Status.SEEN)
self.save()
return self
def mark_as_not_recent(self):
- if Status.RECENT in self.fdoc.content['flags']:
- self.fdoc.content['flags'].remove(Status.RECENT)
+ if Status.RECENT in self.flags:
+ self.flags.remove(Status.RECENT)
self.save()
return self
diff --git a/service/pixelated/adapter/search/__init__.py b/service/pixelated/adapter/search/__init__.py
index da8845cc..b8d3e7ca 100644
--- a/service/pixelated/adapter/search/__init__.py
+++ b/service/pixelated/adapter/search/__init__.py
@@ -35,8 +35,8 @@ class SearchEngine(object):
DEFAULT_INDEX_HOME = os.path.join(os.environ['HOME'], '.leap')
DEFAULT_TAGS = ['inbox', 'sent', 'drafts', 'trash']
- def __init__(self, soledad_querier, agent_home=DEFAULT_INDEX_HOME):
- self.soledad_querier = soledad_querier
+ def __init__(self, key, agent_home=DEFAULT_INDEX_HOME):
+ self.key = key
self.index_folder = os.path.join(agent_home, 'search_index')
if not os.path.exists(self.index_folder):
os.makedirs(self.index_folder)
@@ -111,8 +111,7 @@ class SearchEngine(object):
raw=TEXT(stored=False))
def _create_index(self):
- masterkey = self.soledad_querier.get_index_masterkey()
- storage = EncryptedFileStorage(self.index_folder, masterkey)
+ storage = EncryptedFileStorage(self.index_folder, self.key)
return FileIndex.create(storage, self._mail_schema(), indexname='mails')
def index_mail(self, mail):
diff --git a/service/pixelated/adapter/services/mail_service.py b/service/pixelated/adapter/services/mail_service.py
index 4e6b6aa8..233d4d4a 100644
--- a/service/pixelated/adapter/services/mail_service.py
+++ b/service/pixelated/adapter/services/mail_service.py
@@ -60,6 +60,9 @@ class MailService(object):
def mail(self, mail_id):
return self.querier.mail(mail_id)
+ def attachment(self, attachment_id, encoding):
+ return self.querier.attachment(attachment_id, encoding)
+
def mail_exists(self, mail_id):
return not(not(self.querier.get_header_by_chash(mail_id)))
diff --git a/service/pixelated/application.py b/service/pixelated/application.py
index 55946a5e..6d83c6f7 100644
--- a/service/pixelated/application.py
+++ b/service/pixelated/application.py
@@ -14,8 +14,6 @@
# 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 sys
-
from twisted.internet import reactor
from twisted.internet.threads import deferToThread
from twisted.internet import defer
@@ -25,16 +23,26 @@ from OpenSSL import SSL
from OpenSSL import crypto
from pixelated.config import arguments
-from pixelated.resources import loading_page
+from pixelated.config.services import Services
from pixelated.config.leap import initialize_leap
-from pixelated.config import logger, app_factory
+from pixelated.config import logger
+from pixelated.resources.loading_page import LoadingResource
+from pixelated.resources.root_resource import RootResource
@defer.inlineCallbacks
def start_user_agent(loading_app, host, port, sslkey, sslcert, leap_home, leap_session):
yield loading_app.stopListening()
- resource = app_factory.init_app(leap_home, leap_session)
+ services = Services(leap_home, leap_session)
+
+ resource = RootResource()
+
+ resource.initialize(
+ services.keymanager,
+ services.search_engine,
+ services.mail_service,
+ services.draft_service)
if sslkey and sslcert:
reactor.listenSSL(port, Site(resource), _ssl_options(sslkey, sslcert), interface=host)
@@ -64,7 +72,7 @@ def initialize():
args = arguments.parse_user_agent_args()
logger.init(debug=args.debug)
- loading_app = reactor.listenTCP(args.port, Site(loading_page.LoadingResource()), interface=args.host)
+ loading_app = reactor.listenTCP(args.port, Site(LoadingResource()), interface=args.host)
deferred = deferToThread(
lambda: initialize_leap(
diff --git a/service/pixelated/bitmask_libraries/certs.py b/service/pixelated/bitmask_libraries/certs.py
index a321e00e..9d543672 100644
--- a/service/pixelated/bitmask_libraries/certs.py
+++ b/service/pixelated/bitmask_libraries/certs.py
@@ -14,108 +14,42 @@
# 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):
- return str(LeapCertificate(provider).api_ca_bundle())
-
-
-def which_bootstrap_cert_fingerprint():
- return LEAP_FINGERPRINT
-
-
-def which_bootstrap_CA_bundle(provider):
- if LEAP_CERT is not None:
- return LEAP_CERT
- return str(LeapCertificate(provider).auto_detect_bootstrap_ca_bundle())
-
-
-def refresh_ca_bundle(provider):
- LeapCertificate(provider).refresh_ca_bundle()
+class LeapCertificate(object):
+ LEAP_CERT = None
+ LEAP_FINGERPRINT = None
-class LeapCertificate(object):
def __init__(self, provider):
self._config = provider.config
self._server_name = provider.server_name
self._provider = provider
- def auto_detect_bootstrap_ca_bundle(self):
- if self._config.bootstrap_ca_cert_bundle == AUTO_DETECT_CA_BUNDLE:
- local_cert = self._local_bootstrap_server_cert()
- if local_cert:
- return local_cert
- else:
- return ca_bundle.where()
+ @staticmethod
+ def set_cert_and_fingerprint(cert_file=None, cert_fingerprint=None):
+ if cert_fingerprint is None:
+ LeapCertificate.LEAP_CERT = str(cert_file) if cert_file else True
+ LeapCertificate.LEAP_FINGERPRINT = None
else:
- return self._config.bootstrap_ca_cert_bundle
-
- def api_ca_bundle(self):
- if self._provider.config.ca_cert_bundle:
- return self._provider.config.ca_cert_bundle
-
- cert_file = self._api_cert_file()
-
- if not os.path.isfile(cert_file):
- self._download_server_cert(cert_file)
+ LeapCertificate.LEAP_FINGERPRINT = cert_fingerprint
+ LeapCertificate.LEAP_CERT = False
- return cert_file
+ @property
+ def provider_web_cert(self):
+ return self.LEAP_CERT
- def refresh_ca_bundle(self):
- cert_file = self._api_cert_file()
- self._download_server_cert(cert_file)
+ @property
+ def provider_api_cert(self):
+ return str(os.path.join(self._provider.config.leap_home, 'providers', self._server_name, 'keys', 'client', 'api.pem'))
- def _api_cert_file(self):
- certs_root = self._api_certs_root_path()
- return os.path.join(certs_root, 'api.pem')
-
- def _api_certs_root_path(self):
+ def setup_ca_bundle(self):
path = os.path.join(self._provider.config.leap_home, 'providers', self._server_name, 'keys', 'client')
if not os.path.isdir(path):
os.makedirs(path, 0700)
- return path
-
- def _local_bootstrap_server_cert(self):
- 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)
+ self._download_cert(self.provider_api_cert)
- file_path = os.path.join(path, '%s.ca.crt' % self._server_name)
-
- return file_path
-
- def _download_server_cert(self, cert_file_name):
+ def _download_cert(self, cert_file_name):
cert = self._provider.fetch_valid_certificate()
-
with open(cert_file_name, 'w') as file:
file.write(cert)
diff --git a/service/pixelated/bitmask_libraries/config.py b/service/pixelated/bitmask_libraries/config.py
index 8c862d0a..efb43411 100644
--- a/service/pixelated/bitmask_libraries/config.py
+++ b/service/pixelated/bitmask_libraries/config.py
@@ -13,10 +13,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from distutils.spawn import find_executable
import os
-from os.path import expanduser
+from distutils.spawn import find_executable
def discover_gpg_binary():
@@ -30,54 +29,19 @@ def discover_gpg_binary():
return path
-DEFAULT_LEAP_HOME = os.path.join(expanduser("~"), '.leap')
-
SYSTEM_CA_BUNDLE = True
-AUTO_DETECT_CA_BUNDLE = None
class LeapConfig(object):
- """
- LEAP client configuration
- """
-
- 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,
+ def __init__(self,
+ leap_home=None,
fetch_interval_in_s=30,
- timeout_in_s=15, start_background_jobs=False, gpg_binary=discover_gpg_binary()):
- """
- Constructor.
-
- :param server_name: The LEAP server name, e.g. demo.leap.se
- :type server_name: str
-
- :param user_name: The LEAP account user name, normally the first part of your email, e.g. foobar for foobar@demo.leap.se
- :type user_name: str
-
- :param user_password: The LEAP account password
- :type user_password: str
-
- :param db_passphrase: The passphrase used to encrypt the local soledad database
- :type db_passphrase: str
-
- :param verify_ssl: Set to false to disable strict SSL certificate validation
- :type verify_ssl: bool
-
- :param fetch_interval_in_s: Polling interval for fetching incoming mail from LEAP server
- :type fetch_interval_in_s: int
-
- :param timeout_in_s: Timeout for network operations, e.g. HTTP calls
- :type timeout_in_s: int
-
- :param gpg_binary: Path to the GPG binary (must not be a symlink)
- :type gpg_binary: str
+ timeout_in_s=15,
+ start_background_jobs=False,
+ gpg_binary=discover_gpg_binary()):
- """
self.leap_home = leap_home
- self.bootstrap_ca_cert_bundle = bootstrap_ca_cert_bundle
- self.ca_cert_bundle = ca_cert_bundle
- self.verify_ssl = verify_ssl
self.timeout_in_s = timeout_in_s
self.start_background_jobs = start_background_jobs
self.gpg_binary = gpg_binary
diff --git a/service/pixelated/bitmask_libraries/nicknym.py b/service/pixelated/bitmask_libraries/nicknym.py
index bee90897..220d75e5 100644
--- a/service/pixelated/bitmask_libraries/nicknym.py
+++ b/service/pixelated/bitmask_libraries/nicknym.py
@@ -14,16 +14,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
from leap.keymanager import KeyManager, openpgp, KeyNotFound
-from .certs import which_api_CA_bundle
+from .certs import LeapCertificate
class NickNym(object):
- def __init__(self, provider, config, soledad_session, username, token, uuid):
+ def __init__(self, provider, config, soledad_session, email_address, token, uuid):
nicknym_url = _discover_nicknym_server(provider)
- self._email = '%s@%s' % (username, provider.domain)
- self.keymanager = KeyManager('%s@%s' % (username, provider.domain), nicknym_url,
+ self._email = email_address
+ self.keymanager = KeyManager(self._email, nicknym_url,
soledad_session.soledad,
- token, which_api_CA_bundle(provider), provider.api_uri,
+ token, LeapCertificate(provider).provider_api_cert, provider.api_uri,
provider.api_version,
uuid, config.gpg_binary)
diff --git a/service/pixelated/bitmask_libraries/provider.py b/service/pixelated/bitmask_libraries/provider.py
index 1564c974..315ea7f1 100644
--- a/service/pixelated/bitmask_libraries/provider.py
+++ b/service/pixelated/bitmask_libraries/provider.py
@@ -17,7 +17,7 @@ import json
from leap.common.certs import get_digest
import requests
-from .certs import which_bootstrap_CA_bundle, which_api_CA_bundle, which_bootstrap_cert_fingerprint
+from .certs import LeapCertificate
from pixelated.support.tls_adapter import EnforceTLSv1Adapter
@@ -26,7 +26,6 @@ class LeapProvider(object):
self.server_name = server_name
self.config = config
self.local_ca_crt = '%s/ca.crt' % self.config.leap_home
-
self.provider_json = self.fetch_provider_json()
@property
@@ -99,8 +98,8 @@ class LeapProvider(object):
def _validated_get(self, url):
session = requests.session()
try:
- session.mount('https://', EnforceTLSv1Adapter(assert_fingerprint=which_bootstrap_cert_fingerprint()))
- response = session.get(url, verify=which_bootstrap_CA_bundle(self), timeout=self.config.timeout_in_s)
+ session.mount('https://', EnforceTLSv1Adapter(assert_fingerprint=LeapCertificate.LEAP_FINGERPRINT))
+ response = session.get(url, verify=LeapCertificate(self).provider_web_cert, timeout=self.config.timeout_in_s)
response.raise_for_status()
return response
finally:
@@ -115,16 +114,19 @@ class LeapProvider(object):
def fetch_soledad_json(self):
service_url = "%s/%s/config/soledad-service.json" % (
self.api_uri, self.api_version)
- response = requests.get(service_url, verify=which_api_CA_bundle(self), timeout=self.config.timeout_in_s)
+ response = requests.get(service_url, verify=LeapCertificate(self).provider_api_cert, timeout=self.config.timeout_in_s)
response.raise_for_status()
return json.loads(response.content)
def fetch_smtp_json(self):
service_url = '%s/%s/config/smtp-service.json' % (
self.api_uri, self.api_version)
- response = requests.get(service_url, verify=which_api_CA_bundle(self), timeout=self.config.timeout_in_s)
+ response = requests.get(service_url, verify=LeapCertificate(self).provider_api_cert, timeout=self.config.timeout_in_s)
response.raise_for_status()
return json.loads(response.content)
def _provider_base_url(self):
return 'https://%s' % self.server_name
+
+ def address_for(self, username):
+ return '%s@%s' % (username, self.domain)
diff --git a/service/pixelated/bitmask_libraries/session.py b/service/pixelated/bitmask_libraries/session.py
index 12cbd91b..a9cb15f2 100644
--- a/service/pixelated/bitmask_libraries/session.py
+++ b/service/pixelated/bitmask_libraries/session.py
@@ -22,29 +22,16 @@ from leap.mail.imap.fetch import LeapIncomingMail
from leap.mail.imap.account import SoledadBackedAccount
from leap.mail.imap.memorystore import MemoryStore
from leap.mail.imap.soledadstore import SoledadStore
-from pixelated.bitmask_libraries.config import LeapConfig
-from pixelated.bitmask_libraries.provider import LeapProvider
-from pixelated.bitmask_libraries.certs import refresh_ca_bundle
from twisted.internet import reactor
from .nicknym import NickNym
from leap.auth import SRPAuth
from .soledad import SoledadSessionFactory
from .smtp import LeapSmtp
-from .config import DEFAULT_LEAP_HOME
SESSIONS = {}
-def open_leap_session(username, password, server_name, leap_home=DEFAULT_LEAP_HOME):
- config = LeapConfig(leap_home=leap_home)
- provider = LeapProvider(server_name, config)
- refresh_ca_bundle(provider)
- session = LeapSessionFactory(provider).create(username, password)
-
- return session
-
-
class LeapSession(object):
"""
A LEAP session.
@@ -52,9 +39,13 @@ class LeapSession(object):
Properties:
- - ``leap_config`` the configuration for this session (LeapClientConfig).
+ - ``smtp`` the smtp gateway instance (LeapSmtp).
+
+ - ``config`` the configuration for this session (LeapClientConfig).
+
+ - ``provider`` the responsible for interacting with provider.json (LeapProvider).
- - ``srp_session`` the secure remote password session to authenticate with LEAP. See http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol (LeapSecureRemotePassword)
+ - ``user_auth`` the secure remote password session data after authenticating with LEAP. See http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol (SRPSession)
- ``soledad_session`` the soledad session. See https://leap.se/soledad (LeapSecureRemotePassword)
@@ -66,13 +57,6 @@ class LeapSession(object):
"""
def __init__(self, provider, user_auth, soledad_session, nicknym, soledad_account, incoming_mail_fetcher, smtp):
- """
- Constructor.
-
- :param leap_config: The config for this LEAP session
- :type leap_config: LeapConfig
-
- """
self.smtp = smtp
self.config = provider.config
self.provider = provider
@@ -81,22 +65,25 @@ class LeapSession(object):
self.nicknym = nicknym
self.account = soledad_account
self.incoming_mail_fetcher = incoming_mail_fetcher
+ self.soledad_session.soledad.sync(defer_decryption=False)
+ self.nicknym.generate_openpgp_key()
if self.config.start_background_jobs:
self.start_background_jobs()
def account_email(self):
- domain = self.provider.domain
name = self.user_auth.username
- return '%s@%s' % (name, domain)
+ return self.provider.address_for(name)
def close(self):
self.stop_background_jobs()
def start_background_jobs(self):
+ self.smtp.ensure_running()
reactor.callFromThread(self.incoming_mail_fetcher.start_loop)
def stop_background_jobs(self):
+ self.smtp.stop()
reactor.callFromThread(self.incoming_mail_fetcher.stop)
def sync(self):
@@ -127,16 +114,15 @@ class LeapSessionFactory(object):
srp_auth = SRPAuth(self._provider.api_uri, self._provider.local_ca_crt)
auth = srp_auth.authenticate(username, password)
+ account_email = self._provider.address_for(username)
soledad = SoledadSessionFactory.create(self._provider, auth.token, auth.uuid, password)
- nicknym = self._create_nicknym(auth.username, auth.token, auth.uuid, soledad)
+ nicknym = self._create_nicknym(account_email, auth.token, auth.uuid, soledad)
account = self._create_account(auth.uuid, soledad)
- incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad, account, auth.username)
+ incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad, account, account_email)
- smtp = LeapSmtp(self._provider, auth.username, auth.session_id, nicknym.keymanager)
-
- smtp.ensure_running()
+ smtp = LeapSmtp(self._provider, auth, nicknym.keymanager)
return LeapSession(self._provider, auth, soledad, nicknym, account, incoming_mail_fetcher, smtp)
@@ -163,17 +149,13 @@ class LeapSessionFactory(object):
else:
raise
- def _create_nicknym(self, username, token, uuid, soledad_session):
- return NickNym(self._provider, self._config, soledad_session, username, token, uuid)
+ def _create_nicknym(self, email_address, token, uuid, soledad_session):
+ return NickNym(self._provider, self._config, soledad_session, email_address, token, uuid)
def _create_account(self, uuid, soledad_session):
memstore = MemoryStore(permanent_store=SoledadStore(soledad_session.soledad))
return SoledadBackedAccount(uuid, soledad_session.soledad, memstore)
- def _create_incoming_mail_fetcher(self, nicknym, soledad_session, account, username):
+ def _create_incoming_mail_fetcher(self, nicknym, soledad_session, account, email_address):
return LeapIncomingMail(nicknym.keymanager, soledad_session.soledad, account,
- self._config.fetch_interval_in_s, self._account_email(username))
-
- def _account_email(self, username):
- domain = self._provider.domain
- return '%s@%s' % (username, domain)
+ self._config.fetch_interval_in_s, email_address)
diff --git a/service/pixelated/bitmask_libraries/smtp.py b/service/pixelated/bitmask_libraries/smtp.py
index c22601d2..31e56995 100644
--- a/service/pixelated/bitmask_libraries/smtp.py
+++ b/service/pixelated/bitmask_libraries/smtp.py
@@ -17,8 +17,8 @@ import logging
import os
import requests
import random
-from .certs import which_api_CA_bundle
from leap.mail.smtp import setup_smtp_gateway
+from pixelated.bitmask_libraries.certs import LeapCertificate
logger = logging.getLogger(__name__)
@@ -26,11 +26,12 @@ logger = logging.getLogger(__name__)
class LeapSmtp(object):
- def __init__(self, provider, username, session_id, keymanager=None):
+ def __init__(self, provider, auth, keymanager=None):
self.local_smtp_port_number = random.randrange(12000, 16000)
self._provider = provider
- self.username = username
- self.session_id = session_id
+ self.username = auth.username
+ self.session_id = auth.session_id
+ self.user_token = auth.token
self._keymanager = keymanager
self._remote_hostname, self._remote_port = self._discover_remote_smtp_server()
self._local_smtp_service_socket = None
@@ -58,8 +59,14 @@ class LeapSmtp(object):
cert_url = '%s/%s/cert' % (self._provider.api_uri, self._provider.api_version)
cookies = {"_session_id": self.session_id}
-
- response = requests.get(cert_url, verify=which_api_CA_bundle(self._provider), cookies=cookies, timeout=self._provider.config.timeout_in_s)
+ headers = {}
+ headers["Authorization"] = 'Token token="{0}"'.format(self.user_token)
+ response = requests.get(
+ cert_url,
+ verify=LeapCertificate(self._provider).provider_api_cert,
+ cookies=cookies,
+ timeout=self._provider.config.timeout_in_s,
+ headers=headers)
response.raise_for_status()
client_cert = response.content
diff --git a/service/pixelated/bitmask_libraries/soledad.py b/service/pixelated/bitmask_libraries/soledad.py
index f3fca95a..f0cd9f2f 100644
--- a/service/pixelated/bitmask_libraries/soledad.py
+++ b/service/pixelated/bitmask_libraries/soledad.py
@@ -16,11 +16,9 @@
import errno
import os
-from leap.keymanager import KeyManager
from leap.soledad.client import Soledad
from leap.soledad.common.crypto import WrongMac, UnknownMacMethod
-from .certs import which_api_CA_bundle
-
+from pixelated.bitmask_libraries.certs import LeapCertificate
SOLEDAD_TIMEOUT = 120
SOLEDAD_CERT = '/tmp/ca.crt'
@@ -36,14 +34,6 @@ class SoledadWrongPassphraseException(Exception):
super(SoledadWrongPassphraseException, self).__init__(*args, **kwargs)
-class LeapKeyManager(object):
- def __init__(self, soledad, leap_session, nicknym_url):
- provider = leap_session.provider
- self.keymanager = KeyManager(leap_session.account_email(), nicknym_url, soledad,
- leap_session.session_id, leap_session.leap_home + '/ca.crt', provider.api_uri, leap_session.api_version,
- leap_session.uuid, leap_session.leap_config.gpg_binary)
-
-
class SoledadSessionFactory(object):
@classmethod
def create(cls, provider, user_token, user_uuid, encryption_passphrase):
@@ -68,7 +58,7 @@ class SoledadSession(object):
local_db = self._local_db_path()
return Soledad(self.user_uuid, unicode(encryption_passphrase), secrets,
- local_db, server_url, which_api_CA_bundle(self.provider), self.user_token, defer_encryption=False)
+ local_db, server_url, LeapCertificate(self.provider).provider_api_cert, self.user_token, defer_encryption=False)
except (WrongMac, UnknownMacMethod), e:
raise SoledadWrongPassphraseException(e)
diff --git a/service/pixelated/config/app_factory.py b/service/pixelated/config/app_factory.py
deleted file mode 100644
index 1c7fb8a1..00000000
--- a/service/pixelated/config/app_factory.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Copyright (c) 2014 ThoughtWorks, Inc.
-#
-# Pixelated is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Pixelated is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-
-from pixelated.resources.root_resource import RootResource
-from pixelated.adapter.services.mail_service import MailService
-from pixelated.adapter.model.mail import InputMail
-from pixelated.adapter.services.mail_sender import MailSender
-from pixelated.adapter.services.mailboxes import Mailboxes
-from pixelated.adapter.soledad.soledad_querier import SoledadQuerier
-from pixelated.adapter.search import SearchEngine
-from pixelated.adapter.services.draft_service import DraftService
-from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerListener
-
-
-def init_app(leap_home, leap_session):
- keymanager = leap_session.nicknym.keymanager
-
- soledad_querier = SoledadQuerier(soledad=leap_session.account._soledad)
-
- search_engine = SearchEngine(soledad_querier, agent_home=leap_home)
- pixelated_mail_sender = MailSender(leap_session.account_email(), leap_session.smtp)
-
- pixelated_mailboxes = Mailboxes(leap_session.account, soledad_querier, search_engine)
-
- pixelated_mailboxes.add_welcome_mail_for_fresh_user()
-
- draft_service = DraftService(pixelated_mailboxes)
- mail_service = MailService(pixelated_mailboxes, pixelated_mail_sender, soledad_querier, search_engine)
- soledad_querier.remove_duplicates()
- search_engine.index_mails(mails=mail_service.all_mails(),
- callback=soledad_querier.mark_all_as_not_recent)
-
- MailboxIndexerListener.SEARCH_ENGINE = search_engine
- InputMail.FROM_EMAIL_ADDRESS = leap_session.account_email()
-
- resource = RootResource()
- resource.initialize(soledad_querier, keymanager, search_engine, mail_service, draft_service)
-
- return resource
diff --git a/service/pixelated/config/arguments.py b/service/pixelated/config/arguments.py
index 8899f9a8..56f83421 100644
--- a/service/pixelated/config/arguments.py
+++ b/service/pixelated/config/arguments.py
@@ -14,8 +14,8 @@
# 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 argparse
-from pixelated.bitmask_libraries.config import DEFAULT_LEAP_HOME
def parse_user_agent_args():
@@ -24,6 +24,7 @@ def parse_user_agent_args():
parser_add_default_arguments(parser)
parser.add_argument('--host', default='127.0.0.1', help='the host to run the user agent on')
+ parser.add_argument('--organization-mode', help='Runs the user agent in organization mode, the credentials will be received from the stdin', default=False, action='store_true', dest='organization_mode')
parser.add_argument('--port', type=int, default=3333, help='the port to run the user agent on')
parser.add_argument('-sk', '--sslkey', metavar='<server.key>', default=None, help='use specified file as web server\'s SSL key (when using the user-agent together with the pixelated-dispatcher)')
parser.add_argument('-sc', '--sslcert', metavar='<server.crt>', default=None, help='use specified file as web server\'s SSL certificate (when using the user-agent together with the pixelated-dispatcher)')
@@ -50,13 +51,15 @@ def parse_register_args():
parser = argparse.ArgumentParser(description='Pixelated register')
parser.add_argument('provider', metavar='provider', action='store')
parser.add_argument('username', metavar='username', action='store')
+ parser.add_argument('-lc', '--leap-provider-cert', metavar='<leap-provider.crt>', default=None, help='use specified file for LEAP provider cert authority certificate (url https://<LEAP-provider-domain>/ca.crt)')
+ parser.add_argument('-lf', '--leap-provider-cert-fingerprint', metavar='<leap provider certificate fingerprint>', default=None, help='use specified fingerprint to validate connection with LEAP provider', dest='leap_provider_cert_fingerprint')
+ parser.add_argument('--leap-home', help='The folder where the user agent stores its data. Defaults to ~/.leap', dest='leap_home', default=os.path.join(os.path.expanduser("~"), '.leap'))
return parser.parse_args()
def parser_add_default_arguments(parser):
parser.add_argument('--debug', action='store_true', help='DEBUG mode.')
- parser.add_argument('--organization-mode', help='Runs the user agent in organization mode, the credentials will be received from the stdin', default=False, action='store_true', dest='organization_mode')
parser.add_argument('-c', '--config', dest='credentials_file', metavar='<credentials_file>', default=None, help='use specified file for credentials (for test purposes only)')
- parser.add_argument('--leap-home', help='The folder where the user agent stores its data. Defaults to ~/.leap', dest='leap_home', default=DEFAULT_LEAP_HOME)
+ parser.add_argument('--leap-home', help='The folder where the user agent stores its data. Defaults to ~/.leap', dest='leap_home', default=os.path.join(os.path.expanduser("~"), '.leap'))
parser.add_argument('-lc', '--leap-provider-cert', metavar='<leap-provider.crt>', default=None, help='use specified file for LEAP provider cert authority certificate (url https://<LEAP-provider-domain>/ca.crt)')
parser.add_argument('-lf', '--leap-provider-cert-fingerprint', metavar='<leap provider certificate fingerprint>', default=None, help='use specified fingerprint to validate connection with LEAP provider', dest='leap_provider_cert_fingerprint')
diff --git a/service/pixelated/config/leap.py b/service/pixelated/config/leap.py
index f13a3fc7..52cd4c8f 100644
--- a/service/pixelated/config/leap.py
+++ b/service/pixelated/config/leap.py
@@ -2,8 +2,10 @@ from __future__ import absolute_import
import random
from pixelated.config import credentials
from leap.common.events import server as events_server
-import pixelated.bitmask_libraries.certs as certs
-from pixelated.bitmask_libraries.session import open_leap_session
+from pixelated.bitmask_libraries.config import LeapConfig
+from pixelated.bitmask_libraries.certs import LeapCertificate
+from pixelated.bitmask_libraries.provider import LeapProvider
+from pixelated.bitmask_libraries.session import LeapSessionFactory
def initialize_leap(leap_provider_cert,
@@ -12,33 +14,18 @@ def initialize_leap(leap_provider_cert,
organization_mode,
leap_home):
init_monkeypatches()
- provider, user, password = credentials.read(organization_mode, credentials_file)
- init_leap_cert(leap_provider_cert, leap_provider_cert_fingerprint)
events_server.ensure_server(random.randrange(8000, 11999))
- leap_session = create_leap_session(provider, user, password, leap_home)
- leap_session.start_background_jobs()
- return leap_session
+ provider, username, password = credentials.read(organization_mode, credentials_file)
+ LeapCertificate.set_cert_and_fingerprint(leap_provider_cert, leap_provider_cert_fingerprint)
+ config = LeapConfig(leap_home=leap_home, start_background_jobs=True)
+ provider = LeapProvider(provider, config)
+ LeapCertificate(provider).setup_ca_bundle()
+ leap_session = LeapSessionFactory(provider).create(username, password)
-def create_leap_session(provider, username, password, leap_home):
- leap_session = open_leap_session(username,
- password,
- provider,
- leap_home)
- leap_session.soledad_session.soledad.sync(defer_decryption=False)
- leap_session.nicknym.generate_openpgp_key()
return leap_session
-def init_leap_cert(leap_provider_cert, leap_provider_cert_fingerprint):
- if leap_provider_cert_fingerprint is None:
- certs.LEAP_CERT = leap_provider_cert or True
- certs.LEAP_FINGERPRINT = None
- else:
- certs.LEAP_FINGERPRINT = leap_provider_cert_fingerprint
- certs.LEAP_CERT = False
-
-
def init_monkeypatches():
import pixelated.extensions.protobuf_socket
import pixelated.extensions.sqlcipher_wal
diff --git a/service/pixelated/config/services.py b/service/pixelated/config/services.py
new file mode 100644
index 00000000..f1c7a540
--- /dev/null
+++ b/service/pixelated/config/services.py
@@ -0,0 +1,65 @@
+from pixelated.adapter.services.mail_service import MailService
+from pixelated.adapter.model.mail import InputMail
+from pixelated.adapter.services.mail_sender import MailSender
+from pixelated.adapter.services.mailboxes import Mailboxes
+from pixelated.adapter.soledad.soledad_querier import SoledadQuerier
+from pixelated.adapter.search import SearchEngine
+from pixelated.adapter.services.draft_service import DraftService
+from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerListener
+
+
+class Services(object):
+
+ def __init__(self, leap_home, leap_session):
+
+ soledad_querier = SoledadQuerier(soledad=leap_session.soledad_session.soledad)
+
+ self.search_engine = self.setup_search_engine(
+ leap_home,
+ soledad_querier)
+
+ pixelated_mailboxes = Mailboxes(
+ leap_session.account,
+ soledad_querier,
+ self.search_engine)
+
+ self.mail_service = self.setup_mail_service(
+ leap_session,
+ soledad_querier,
+ self.search_engine,
+ pixelated_mailboxes)
+
+ self.keymanager = self.setup_keymanager(leap_session)
+ self.draft_service = self.setup_draft_service(pixelated_mailboxes)
+
+ self.post_setup(soledad_querier, leap_session)
+
+ def post_setup(self, soledad_querier, leap_session):
+ self.search_engine.index_mails(
+ mails=self.mail_service.all_mails(),
+ callback=soledad_querier.mark_all_as_not_recent)
+ soledad_querier.remove_duplicates()
+ InputMail.FROM_EMAIL_ADDRESS = leap_session.account_email()
+
+ def setup_keymanager(self, leap_session):
+ return leap_session.nicknym.keymanager
+
+ def setup_search_engine(self, leap_home, soledad_querier):
+ key = soledad_querier.get_index_masterkey()
+ search_engine = SearchEngine(key, agent_home=leap_home)
+ MailboxIndexerListener.SEARCH_ENGINE = search_engine
+ return search_engine
+
+ def setup_mail_service(self, leap_session, soledad_querier, search_engine, pixelated_mailboxes):
+ pixelated_mailboxes.add_welcome_mail_for_fresh_user()
+ pixelated_mail_sender = MailSender(
+ leap_session.account_email(),
+ leap_session.smtp)
+ return MailService(
+ pixelated_mailboxes,
+ pixelated_mail_sender,
+ soledad_querier,
+ search_engine)
+
+ def setup_draft_service(self, pixelated_mailboxes):
+ return DraftService(pixelated_mailboxes)
diff --git a/service/pixelated/maintenance.py b/service/pixelated/maintenance.py
index cd55e6f6..7170055c 100644
--- a/service/pixelated/maintenance.py
+++ b/service/pixelated/maintenance.py
@@ -21,9 +21,11 @@ from pixelated.config.leap import initialize_leap
from pixelated.config import logger, arguments
from leap.mail.imap.fields import WithMsgFields
+import time
def initialize():
+ import time
args = arguments.parse_maintenance_args()
logger.init(debug=args.debug)
@@ -32,8 +34,8 @@ def initialize():
args.leap_provider_cert,
args.leap_provider_cert_fingerprint,
args.credentials_file,
- args.organization_mode,
- args.leap_home)
+ organization_mode=False,
+ leap_home=args.leap_home)
execute_command = create_execute_command(args, leap_session)
@@ -175,6 +177,7 @@ def dump_soledad(args):
def shutdown(args):
+ time.sleep(30)
reactor.stop()
diff --git a/service/pixelated/register.py b/service/pixelated/register.py
index 0eac97a7..f917840b 100644
--- a/service/pixelated/register.py
+++ b/service/pixelated/register.py
@@ -17,31 +17,39 @@ import re
import getpass
import logging
-from pixelated.bitmask_libraries import session as leap_session
from pixelated.config import arguments
from pixelated.config import logger as logger_config
-from pixelated.bitmask_libraries.certs import which_api_CA_bundle
+from pixelated.bitmask_libraries.certs import LeapCertificate
from pixelated.bitmask_libraries.config import LeapConfig
from pixelated.bitmask_libraries.provider import LeapProvider
+from pixelated.bitmask_libraries.session import LeapSessionFactory
from leap.auth import SRPAuth
logger = logging.getLogger(__name__)
-def register(server_name, username):
+def register(
+ server_name,
+ username,
+ leap_home,
+ provider_cert,
+ provider_cert_fingerprint):
+
try:
validate_username(username)
except ValueError:
print('Only lowercase letters, digits, . - and _ allowed.')
- config = LeapConfig()
- provider = LeapProvider(server_name, config)
password = getpass.getpass('Please enter password for %s: ' % username)
- srp_auth = SRPAuth(provider.api_uri, which_api_CA_bundle(provider))
+
+ LeapCertificate.set_cert_and_fingerprint(provider_cert, provider_cert_fingerprint)
+ config = LeapConfig(leap_home=leap_home)
+ provider = LeapProvider(server_name, config)
+ LeapCertificate(provider).setup_ca_bundle()
+ srp_auth = SRPAuth(provider.api_uri, LeapCertificate(provider).provider_api_cert)
if srp_auth.register(username, password):
- session = leap_session.open_leap_session(username, password, server_name)
- session.nicknym.generate_openpgp_key()
+ LeapSessionFactory(provider).create(username, password)
else:
logger.error("Register failed")
@@ -55,4 +63,9 @@ def validate_username(username):
def initialize():
logger_config.init(debug=False)
args = arguments.parse_register_args()
- register(args.provider, args.username)
+ register(
+ args.provider,
+ args.username,
+ args.leap_home,
+ args.leap_provider_cert,
+ args.leap_provider_cert_fingerprint)
diff --git a/service/pixelated/resources/attachments_resource.py b/service/pixelated/resources/attachments_resource.py
index e0ba1bd1..83c7156d 100644
--- a/service/pixelated/resources/attachments_resource.py
+++ b/service/pixelated/resources/attachments_resource.py
@@ -27,15 +27,15 @@ class AttachmentResource(Resource):
isLeaf = True
- def __init__(self, attachment_id, querier):
+ def __init__(self, mail_service, attachment_id):
Resource.__init__(self)
self.attachment_id = attachment_id
- self.querier = querier
+ self.mail_service = mail_service
def render_GET(self, request):
encoding = request.args.get('encoding', [None])[0]
filename = request.args.get('filename', [self.attachment_id])[0]
- attachment = self.querier.attachment(self.attachment_id, encoding)
+ attachment = self.mail_service.attachment(self.attachment_id, encoding)
request.setHeader(b'Content-Type', b'application/force-download')
request.setHeader(b'Content-Disposition', bytes('attachment; filename=' + filename))
@@ -57,9 +57,9 @@ class AttachmentResource(Resource):
class AttachmentsResource(Resource):
- def __init__(self, querier):
+ def __init__(self, mail_service):
Resource.__init__(self)
- self.querier = querier
+ self.mail_service = mail_service
def getChild(self, attachment_id, request):
- return AttachmentResource(attachment_id, self.querier)
+ return AttachmentResource(self.mail_service, attachment_id)
diff --git a/service/pixelated/resources/root_resource.py b/service/pixelated/resources/root_resource.py
index 4c0c47ac..c1111269 100644
--- a/service/pixelated/resources/root_resource.py
+++ b/service/pixelated/resources/root_resource.py
@@ -21,10 +21,10 @@ class RootResource(Resource):
return self
return Resource.getChild(self, path, request)
- def initialize(self, querier, keymanager, search_engine, mail_service, draft_service):
+ def initialize(self, keymanager, search_engine, mail_service, draft_service):
self.putChild('assets', File(self._static_folder))
self.putChild('keys', KeysResource(keymanager))
- self.putChild('attachment', AttachmentsResource(querier))
+ self.putChild('attachment', AttachmentsResource(mail_service))
self.putChild('contacts', ContactsResource(search_engine))
self.putChild('features', FeaturesResource())
self.putChild('tags', TagsResource(search_engine))
@@ -32,7 +32,6 @@ class RootResource(Resource):
self.putChild('mail', MailResource(mail_service))
def _get_static_folder(self):
-
static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app"))
# this is a workaround for packaging
if not os.path.exists(static_folder):
diff --git a/service/pixelated/runserver.py b/service/pixelated/runserver.py
deleted file mode 100644
index 7b0a3b4f..00000000
--- a/service/pixelated/runserver.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Copyright (c) 2014 ThoughtWorks, Inc.
-#
-# Pixelated is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Pixelated is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-
-from config import initialize
-
-
-if __name__ == '__main__':
- initialize()
diff --git a/service/setup.py b/service/setup.py
index a894dab7..d0cb967f 100644
--- a/service/setup.py
+++ b/service/setup.py
@@ -43,7 +43,8 @@ setup(name='pixelated-user-agent',
'pixelated.assets',
'pixelated.certificates',
'pixelated.support',
- 'pixelated.resources'
+ 'pixelated.resources',
+ 'pixelated.extensions'
],
install_requires=[
'pyasn1==0.1.3',
diff --git a/service/test/functional/features/environment.py b/service/test/functional/features/environment.py
index 5dab437f..437529b8 100644
--- a/service/test/functional/features/environment.py
+++ b/service/test/functional/features/environment.py
@@ -17,7 +17,7 @@ import logging
import uuid
from test.support.dispatcher.proxy import Proxy
-from test.support.integration import AppTestClient, MailBuilder
+from test.support.integration import AppTestClient
from selenium import webdriver
from pixelated.resources.features_resource import FeaturesResource
diff --git a/service/test/functional/features/steps/data_setup.py b/service/test/functional/features/steps/data_setup.py
index 4e349f05..2a3876fc 100644
--- a/service/test/functional/features/steps/data_setup.py
+++ b/service/test/functional/features/steps/data_setup.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
from test.support.integration import MailBuilder
+from behave import given
@given('I have a mail in my inbox')
diff --git a/service/test/support/dispatcher/proxy.py b/service/test/support/dispatcher/proxy.py
index d4cb95ee..ca3bbc41 100644
--- a/service/test/support/dispatcher/proxy.py
+++ b/service/test/support/dispatcher/proxy.py
@@ -87,7 +87,7 @@ class MainHandler(tornado.web.RequestHandler):
except tornado.httpclient.HTTPError, x:
if hasattr(x, 'response') and x.response:
self.handle_response(x.response)
- except Exception, e:
+ except Exception:
self.set_status(500)
self.write("Internal server error:\n" + ''.join(traceback.format_exception(*sys.exc_info())))
self.finish()
diff --git a/service/test/support/integration/app_test_client.py b/service/test/support/integration/app_test_client.py
index a1b0c3b8..52372507 100644
--- a/service/test/support/integration/app_test_client.py
+++ b/service/test/support/integration/app_test_client.py
@@ -62,7 +62,7 @@ class AppTestClient(object):
self.soledad_querier = self._create_soledad_querier(self.soledad, self.INDEX_KEY)
self.keymanager = mock()
- self.search_engine = SearchEngine(self.soledad_querier, agent_home=soledad_test_folder)
+ self.search_engine = SearchEngine(self.INDEX_KEY, agent_home=soledad_test_folder)
self.mail_sender = self._create_mail_sender()
self.account = SoledadBackedAccount(self.ACCOUNT, self.soledad, MagicMock())
@@ -73,7 +73,7 @@ class AppTestClient(object):
self.search_engine.index_mails(self.mail_service.all_mails())
self.resource = RootResource()
- self.resource.initialize(self.soledad_querier, self.keymanager, self.search_engine, self.mail_service, self.draft_service)
+ self.resource.initialize(self.keymanager, self.search_engine, self.mail_service, self.draft_service)
def _render(self, request, as_json=True):
def get_str(_str):
diff --git a/service/test/unit/adapter/search/test_search.py b/service/test/unit/adapter/search/test_search.py
index 491cb2fc..1d9076a2 100644
--- a/service/test/unit/adapter/search/test_search.py
+++ b/service/test/unit/adapter/search/test_search.py
@@ -16,7 +16,6 @@
import unittest
-from mockito import mock, when
from pixelated.adapter.search import SearchEngine
from tempdir import TempDir
from test.support import test_helper
@@ -47,10 +46,7 @@ class SearchEngineTest(unittest.TestCase):
def test_encoding(self):
# given
- soledad_querier = mock()
- when(soledad_querier).get_index_masterkey().thenReturn(INDEX_KEY)
-
- se = SearchEngine(soledad_querier, self.agent_home)
+ se = SearchEngine(INDEX_KEY, self.agent_home)
headers = {
'From': 'foo@bar.tld',
diff --git a/service/test/unit/bitmask_libraries/test_certs.py b/service/test/unit/bitmask_libraries/test_certs.py
index 4a06649d..5d447537 100644
--- a/service/test/unit/bitmask_libraries/test_certs.py
+++ b/service/test/unit/bitmask_libraries/test_certs.py
@@ -1,35 +1,40 @@
import unittest
-from pixelated.bitmask_libraries.certs import which_bootstrap_CA_bundle, which_api_CA_bundle
-from pixelated.bitmask_libraries.config import AUTO_DETECT_CA_BUNDLE
-from mock import MagicMock, patch
+from pixelated.bitmask_libraries.certs import LeapCertificate
+from mock import MagicMock
class CertsTest(unittest.TestCase):
- @patch('pixelated.bitmask_libraries.certs.os.path.isfile')
- @patch('pixelated.bitmask_libraries.certs.os.path.isdir')
- 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, leap_home='/leap/home')
- provider = MagicMock(server_name=u'test.leap.net', config=config)
+ def setUp(self):
+ config = MagicMock(leap_home='/some/leap/home')
+ self.provider = MagicMock(server_name=u'test.leap.net', config=config)
- bundle = which_bootstrap_CA_bundle(provider)
+ def test_set_cert_and_fingerprint_sets_cert(self):
+ LeapCertificate.set_cert_and_fingerprint('some cert', None)
- self.assertEqual('/leap/home/providers/test.leap.net/test.leap.net.ca.crt', bundle)
- self.assertEqual(str, type(bundle))
+ certs = LeapCertificate(self.provider)
- @patch('pixelated.bitmask_libraries.certs.os.path.isfile')
- @patch('pixelated.bitmask_libraries.certs.os.path.isdir')
- def test_that_which_bundle_returns_byte_string(self, mock_isdir, mock_isfile):
- mock_isfile.return_value = True
- mock_isdir.return_value = True
+ self.assertIsNone(certs.LEAP_FINGERPRINT)
+ self.assertEqual('some cert', certs.provider_web_cert)
- 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)
+ def test_set_cert_and_fingerprint_sets_fingerprint(self):
+ LeapCertificate.set_cert_and_fingerprint(None, 'fingerprint')
- bundle = which_api_CA_bundle(provider)
+ certs = LeapCertificate(self.provider)
- self.assertEqual('/some/leap/home/providers/test.leap.net/keys/client/api.pem', bundle)
- self.assertEqual(str, type(bundle))
+ self.assertEqual('fingerprint', LeapCertificate.LEAP_FINGERPRINT)
+ self.assertFalse(certs.provider_web_cert)
+
+ def test_set_cert_and_fingerprint_when_none_are_passed(self):
+ LeapCertificate.set_cert_and_fingerprint(None, None)
+
+ certs = LeapCertificate(self.provider)
+
+ self.assertIsNone(certs.LEAP_FINGERPRINT)
+ self.assertEqual(True, certs.provider_web_cert)
+
+ def test_provider_api_cert(self):
+ certs = LeapCertificate(self.provider).provider_api_cert
+
+ self.assertEqual('/some/leap/home/providers/test.leap.net/keys/client/api.pem', certs)
diff --git a/service/test/unit/bitmask_libraries/test_nicknym.py b/service/test/unit/bitmask_libraries/test_nicknym.py
index b892c22c..ca3b348d 100644
--- a/service/test/unit/bitmask_libraries/test_nicknym.py
+++ b/service/test/unit/bitmask_libraries/test_nicknym.py
@@ -15,29 +15,36 @@
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
from mock import patch
+from test_abstract_leap import AbstractLeapTest
from leap.keymanager import openpgp, KeyNotFound
from pixelated.bitmask_libraries.nicknym import NickNym
-from test_abstract_leap import AbstractLeapTest
+from pixelated.bitmask_libraries.certs import LeapCertificate
class NickNymTest(AbstractLeapTest):
@patch('pixelated.bitmask_libraries.nicknym.KeyManager.__init__', return_value=None)
- def test_that_keymanager_is_created(self, init_mock):
+ def test_that_keymanager_is_created(self, keymanager_init_mock):
# given
-
+ LeapCertificate.provider_api_cert = '/some/path/to/provider_ca_cert'
# when
NickNym(self.provider,
self.config,
self.soledad_session,
- self.auth.username,
+ 'test_user@some-server.test',
self.auth.token,
self.auth.uuid)
# then
- init_mock.assert_called_with('test_user@some-server.test', 'https://nicknym.some-server.test:6425/',
- self.soledad, self.auth.token, '/some/path/to/provider_ca_cert',
- 'https://api.some-server.test:4430', '1', self.auth.uuid,
- '/path/to/gpg')
+ keymanager_init_mock.assert_called_with(
+ 'test_user@some-server.test',
+ 'https://nicknym.some-server.test:6425/',
+ self.soledad,
+ self.auth.token,
+ '/some/path/to/provider_ca_cert',
+ 'https://api.some-server.test:4430',
+ '1',
+ self.auth.uuid,
+ '/path/to/gpg')
@patch('pixelated.bitmask_libraries.nicknym.KeyManager')
def test_gen_key(self, keymanager_mock):
@@ -47,7 +54,7 @@ class NickNymTest(AbstractLeapTest):
nicknym = NickNym(self.provider,
self.config,
self.soledad_session,
- self.auth.username,
+ 'test_user@some-server.test',
self.auth.token,
self.auth.uuid)
diff --git a/service/test/unit/bitmask_libraries/test_provider.py b/service/test/unit/bitmask_libraries/test_provider.py
index 0771c7cc..1fe5a66d 100644
--- a/service/test/unit/bitmask_libraries/test_provider.py
+++ b/service/test/unit/bitmask_libraries/test_provider.py
@@ -20,8 +20,8 @@ from httmock import all_requests, HTTMock, urlmatch
from requests import HTTPError
from pixelated.bitmask_libraries.config import LeapConfig
from pixelated.bitmask_libraries.provider import LeapProvider
+from pixelated.bitmask_libraries.certs import LeapCertificate
from test_abstract_leap import AbstractLeapTest
-from requests import Session
import requests
@@ -133,13 +133,14 @@ VeJ6
"""
-CA_CERT = '/tmp/ca.crt'
-BOOTSTRAP_CA_CERT = '/tmp/bootstrap-ca.crt'
+PROVIDER_API_CERT = '/tmp/ca.crt'
+PROVIDER_WEB_CERT = '/tmp/bootstrap-ca.crt'
class LeapProviderTest(AbstractLeapTest):
def setUp(self):
- self.config = LeapConfig(verify_ssl=False, leap_home='/tmp/foobar', bootstrap_ca_cert_bundle=BOOTSTRAP_CA_CERT, ca_cert_bundle=CA_CERT)
+ self.config = LeapConfig(leap_home='/tmp/foobar')
+ LeapCertificate.set_cert_and_fingerprint(PROVIDER_WEB_CERT, None)
def test_provider_fetches_provider_json(self):
with HTTMock(provider_json_mock):
@@ -195,6 +196,7 @@ class LeapProviderTest(AbstractLeapTest):
session = MagicMock(wraps=requests.session())
session_func = MagicMock(return_value=session)
get_func = MagicMock(wraps=requests.get)
+ LeapCertificate.LEAP_CERT = PROVIDER_WEB_CERT
with patch('pixelated.bitmask_libraries.provider.requests.session', new=session_func):
with patch('pixelated.bitmask_libraries.provider.requests.get', new=get_func):
@@ -202,29 +204,28 @@ class LeapProviderTest(AbstractLeapTest):
provider = LeapProvider('some-provider.test', self.config)
provider.fetch_valid_certificate()
- session.get.assert_any_call('https://some-provider.test/ca.crt', verify=BOOTSTRAP_CA_CERT, timeout=15)
- session.get.assert_any_call('https://some-provider.test/provider.json', verify=BOOTSTRAP_CA_CERT, timeout=15)
+ session.get.assert_any_call('https://some-provider.test/ca.crt', verify=PROVIDER_WEB_CERT, timeout=15)
+ session.get.assert_any_call('https://some-provider.test/provider.json', verify=PROVIDER_WEB_CERT, timeout=15)
def test_that_provider_cert_is_used_to_fetch_soledad_json(self):
get_func = MagicMock(wraps=requests.get)
+ LeapCertificate.provider_api_cert = PROVIDER_API_CERT
with patch('pixelated.bitmask_libraries.provider.requests.get', new=get_func):
with HTTMock(provider_json_mock, soledad_json_mock, not_found_mock):
provider = LeapProvider('some-provider.test', self.config)
provider.fetch_soledad_json()
-
- get_func.assert_called_with('https://api.some-provider.test:4430/1/config/soledad-service.json', verify=CA_CERT, timeout=15)
+ get_func.assert_called_with('https://api.some-provider.test:4430/1/config/soledad-service.json', verify=PROVIDER_API_CERT, timeout=15)
def test_that_leap_fingerprint_is_validated(self):
session = MagicMock(wraps=requests.session())
session_func = MagicMock(return_value=session)
+ LeapCertificate.set_cert_and_fingerprint(None, 'some fingerprint')
- with patch('pixelated.bitmask_libraries.provider.which_bootstrap_cert_fingerprint', return_value='some fingerprint'):
- with patch('pixelated.bitmask_libraries.provider.which_bootstrap_CA_bundle', return_value=False):
- with patch('pixelated.bitmask_libraries.provider.requests.session', new=session_func):
- with HTTMock(provider_json_mock, ca_cert_mock, not_found_mock):
- provider = LeapProvider('some-provider.test', self.config)
- provider.fetch_valid_certificate()
+ with patch('pixelated.bitmask_libraries.provider.requests.session', new=session_func):
+ with HTTMock(provider_json_mock, ca_cert_mock, not_found_mock):
+ provider = LeapProvider('some-provider.test', self.config)
+ provider.fetch_valid_certificate()
session.get.assert_any_call('https://some-provider.test/ca.crt', verify=False, timeout=15)
session.mount.assert_called_with('https://', ANY)
diff --git a/service/test/unit/bitmask_libraries/test_session.py b/service/test/unit/bitmask_libraries/test_session.py
index 62330481..0c662ecb 100644
--- a/service/test/unit/bitmask_libraries/test_session.py
+++ b/service/test/unit/bitmask_libraries/test_session.py
@@ -60,10 +60,6 @@ class SessionTest(AbstractLeapTest):
self.soledad_session.sync.assert_called_once_with()
- def test_account_email(self):
- session = self._create_session()
- self.assertEqual('test_user@some-server.test', session.account_email())
-
def _create_session(self):
return LeapSession(self.provider, self.auth, self.soledad_session, self.nicknym, self.soledad_account,
self.mail_fetcher_mock, self.smtp_mock)
diff --git a/service/test/unit/bitmask_libraries/test_smtp.py b/service/test/unit/bitmask_libraries/test_smtp.py
index 5987415c..ec51c56b 100644
--- a/service/test/unit/bitmask_libraries/test_smtp.py
+++ b/service/test/unit/bitmask_libraries/test_smtp.py
@@ -53,7 +53,7 @@ class LeapSmtpTest(AbstractLeapTest):
self.config.timeout_in_s = 15
def test_that_client_cert_gets_downloaded(self):
- smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager)
+ smtp = LeapSmtp(self.provider, self.auth, self.keymanager)
with HTTMock(ca_cert_mock, not_found_mock):
smtp._download_client_certificates()
@@ -66,7 +66,7 @@ class LeapSmtpTest(AbstractLeapTest):
@patch('pixelated.bitmask_libraries.smtp.setup_smtp_gateway')
def test_that_start_calls_setup_smtp_gateway(self, gateway_mock):
- smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager)
+ smtp = LeapSmtp(self.provider, self.auth, self.keymanager)
port = 500
smtp.local_smtp_port_number = port
@@ -78,14 +78,14 @@ class LeapSmtpTest(AbstractLeapTest):
gateway_mock.assert_called_with(keymanager=self.keymanager, smtp_cert=cert_path, smtp_key=cert_path, userid='test_user@some-server.test', smtp_port='1234', encrypted_only=False, smtp_host='smtp.some-sever.test', port=port)
def test_that_client_stop_does_nothing_if_not_started(self):
- smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager)
+ smtp = LeapSmtp(self.provider, self.auth, self.keymanager)
with HTTMock(not_found_mock):
smtp.stop()
@patch('pixelated.bitmask_libraries.smtp.setup_smtp_gateway')
def test_that_running_smtp_sevice_is_stopped(self, gateway_mock):
- smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager)
+ smtp = LeapSmtp(self.provider, self.auth, self.keymanager)
smtp_service = MagicMock()
smtp_port = MagicMock()
diff --git a/service/test/unit/config/test_app_factory.py b/service/test/unit/config/test_app_factory.py
deleted file mode 100644
index a43273c3..00000000
--- a/service/test/unit/config/test_app_factory.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import unittest
-
-from mock import patch, MagicMock, ANY
-import pixelated
-
-
-class AppFactoryTest(unittest.TestCase):
-
- class MockConfig:
- def __init__(self, port, host, sslkey=None, sslcert=None):
- self.port = port
- self.host = host
- self.sslkey = sslkey
- self.sslcert = sslcert
- self.home = 'leap_home'
-
- @patch('pixelated.config.app_factory.init_app')
- @patch('pixelated.config.app_factory.reactor')
- @unittest.skip("refactoring startup, need to define a better place to put this")
- def test_that_create_app_binds_to_tcp_port_if_no_ssl_options(self, reactor_mock, init_app_mock):
- app_mock = MagicMock()
- leap_session = MagicMock()
- config = AppFactoryTest.MockConfig(12345, '127.0.0.1', leap_session)
-
- create_app(config.home, leap_session, config.host, config.port, config.sslkey, config.sslcert)
-
- reactor_mock.listenTCP.assert_called_once_with(12345, ANY, interface='127.0.0.1')
-
- @patch('pixelated.config.app_factory.init_app')
- @patch('pixelated.config.app_factory.reactor')
- @unittest.skip("refactoring startup, need to define a better place to put this")
- def test_that_create_app_binds_to_ssl_if_ssl_options(self, reactor_mock, init_app_mock):
- app_mock = MagicMock()
- leap_session = MagicMock()
- pixelated.config.app_factory._ssl_options = lambda x, y: 'options'
-
- config = AppFactoryTest.MockConfig(12345, '127.0.0.1', sslkey="sslkey", sslcert="sslcert")
-
- create_app(config.home, leap_session, config.host, config.port, config.sslkey, config.sslcert)
-
- reactor_mock.listenSSL.assert_called_once_with(12345, ANY, 'options', interface='127.0.0.1')
diff --git a/service/test/unit/maintenance/test_commands.py b/service/test/unit/maintenance/test_commands.py
index 6f993106..f1bf6e45 100644
--- a/service/test/unit/maintenance/test_commands.py
+++ b/service/test/unit/maintenance/test_commands.py
@@ -22,7 +22,7 @@ from leap.mail.imap.account import SoledadBackedAccount
from leap.mail.imap.fields import WithMsgFields
from leap.soledad.client import Soledad
from leap.soledad.common.document import SoledadDocument
-from mock import MagicMock, ANY
+from mock import MagicMock
from os.path import join, dirname
from twisted.internet import defer, reactor
diff --git a/service/test/unit/resources/test_keys_resources.py b/service/test/unit/resources/test_keys_resources.py
index fb085cad..be79424b 100644
--- a/service/test/unit/resources/test_keys_resources.py
+++ b/service/test/unit/resources/test_keys_resources.py
@@ -1,4 +1,3 @@
-import json
from mockito import mock, when
from leap.keymanager import OpenPGPKey, KeyNotFound
from pixelated.resources.keys_resource import KeysResource
diff --git a/service/test/unit/test_application.py b/service/test/unit/test_application.py
new file mode 100644
index 00000000..b2799d4c
--- /dev/null
+++ b/service/test/unit/test_application.py
@@ -0,0 +1,53 @@
+import unittest
+
+from mock import patch, MagicMock, ANY
+import pixelated
+
+
+class ApplicationTest(unittest.TestCase):
+
+ class MockConfig:
+ def __init__(self, port, host, sslkey=None, sslcert=None):
+ self.port = port
+ self.host = host
+ self.sslkey = sslkey
+ self.sslcert = sslcert
+ self.home = 'leap_home'
+
+ @patch('pixelated.application.reactor')
+ @patch('pixelated.application.Services')
+ def test_that_create_app_binds_to_tcp_port_if_no_ssl_options(self, services_mock, reactor_mock):
+ app_mock = MagicMock()
+ leap_session = MagicMock()
+ config = ApplicationTest.MockConfig(12345, '127.0.0.1', leap_session)
+
+ d = pixelated.application.start_user_agent(app_mock, config.host, config.port, config.sslkey, config.sslcert, config.home, leap_session)
+
+ def _assert(_):
+ services_mock.assert_called_once_with(config.home, leap_session)
+
+ reactor_mock.listenTCP.assert_called_once_with(12345, ANY, interface='127.0.0.1')
+ app_mock.stopListening.assert_called()
+
+ d.addCallback(_assert)
+ return d
+
+ @patch('pixelated.application.reactor')
+ @patch('pixelated.application.Services')
+ def test_that_create_app_binds_to_ssl_if_ssl_options(self, services_mock, reactor_mock):
+ app_mock = MagicMock()
+ leap_session = MagicMock()
+ pixelated.application._ssl_options = lambda x, y: 'options'
+
+ config = ApplicationTest.MockConfig(12345, '127.0.0.1', sslkey="sslkey", sslcert="sslcert")
+
+ d = pixelated.application.start_user_agent(app_mock, config.host, config.port, config.sslkey, config.sslcert, config.home, leap_session)
+
+ def _assert(_):
+ services_mock.assert_called_once_with(config.home, leap_session)
+
+ reactor_mock.listenSSL.assert_called_once_with(12345, ANY, 'options', interface='127.0.0.1')
+ app_mock.stopListening.assert_called()
+
+ d.addCallback(_assert)
+ return d