summaryrefslogtreecommitdiff
path: root/src/pixelated/config
diff options
context:
space:
mode:
Diffstat (limited to 'src/pixelated/config')
-rw-r--r--src/pixelated/config/__init__.py0
-rw-r--r--src/pixelated/config/arguments.py98
-rw-r--r--src/pixelated/config/credentials.py54
-rw-r--r--src/pixelated/config/leap.py92
-rw-r--r--src/pixelated/config/logger.py37
-rw-r--r--src/pixelated/config/services.py84
-rw-r--r--src/pixelated/config/site.py32
7 files changed, 397 insertions, 0 deletions
diff --git a/src/pixelated/config/__init__.py b/src/pixelated/config/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/pixelated/config/__init__.py
diff --git a/src/pixelated/config/arguments.py b/src/pixelated/config/arguments.py
new file mode 100644
index 00000000..355a7c64
--- /dev/null
+++ b/src/pixelated/config/arguments.py
@@ -0,0 +1,98 @@
+#
+# 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/>.
+
+import os
+import argparse
+
+
+def parse_user_agent_args():
+ parser = argparse.ArgumentParser(description='Pixelated user agent.')
+
+ 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)')
+ parser.add_argument('--multi-user', help='Run user agent in multi user mode',
+ action='store_false', default=True, dest='single_user')
+ parser.add_argument('-p', '--provider', help='specify a provider for mutli-user mode',
+ metavar='<provider host>', default=None, dest='provider')
+ parser.add_argument('--banner', help='banner file to show on login screen')
+
+ args = parser.parse_args()
+
+ return args
+
+
+def parse_maintenance_args():
+ parser = argparse.ArgumentParser(description='Pixelated maintenance')
+ parser_add_default_arguments(parser)
+ subparsers = parser.add_subparsers(help='commands', dest='command')
+ subparsers.add_parser('reset', help='reset account command')
+ mails_parser = subparsers.add_parser(
+ 'load-mails', help='load mails into account')
+ mails_parser.add_argument('file', nargs='+', help='file(s) with mail data')
+
+ markov_mails_parser = subparsers.add_parser(
+ 'markov-generate', help='generate mails using markov chains')
+ markov_mails_parser.add_argument(
+ '--seed', default=None, help='Specify a seed to always generate the same output')
+ markov_mails_parser.add_argument('-l', '--limit', metavar='count',
+ default='5', help='limit number of generated mails', dest='limit')
+ markov_mails_parser.add_argument(
+ 'file', nargs='+', help='file(s) with mail data')
+
+ subparsers.add_parser('dump-soledad', help='dump the soledad database')
+ subparsers.add_parser('sync', help='sync the soledad database')
+ subparsers.add_parser('repair', help='repair database if possible')
+ subparsers.add_parser(
+ 'integrity-check', help='run integrity check on database')
+
+ return parser.parse_args()
+
+
+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('-p', '--password', metavar='password', action='store',
+ default=None, help='used just to register account automatically by scripts')
+ 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('-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=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/src/pixelated/config/credentials.py b/src/pixelated/config/credentials.py
new file mode 100644
index 00000000..8bb837dd
--- /dev/null
+++ b/src/pixelated/config/credentials.py
@@ -0,0 +1,54 @@
+#
+# Copyright (c) 2015 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/>.
+
+import os
+import getpass
+import json
+import sys
+import ConfigParser
+
+
+def read(organization_mode, credentials_file):
+ if organization_mode:
+ return read_from_dispatcher()
+ else:
+ if credentials_file:
+ return read_from_file(credentials_file)
+ return prompt_for_credentials()
+
+
+def prompt_for_credentials():
+ provider = raw_input('Which provider do you want to connect to:\n')
+ username = raw_input('What\'s your username registered on the provider:\n')
+ password = getpass.getpass('Type your password:\n')
+ return provider, username, password
+
+
+def read_from_file(credentials_file):
+ config_parser = ConfigParser.ConfigParser()
+ credentials_file_path = os.path.abspath(
+ os.path.expanduser(credentials_file))
+ config_parser.read(credentials_file_path)
+ provider, user, password = \
+ config_parser.get('pixelated', 'leap_server_name'), \
+ config_parser.get('pixelated', 'leap_username'), \
+ config_parser.get('pixelated', 'leap_password')
+ return provider, user, password
+
+
+def read_from_dispatcher():
+ config = json.loads(sys.stdin.read())
+ return config['leap_provider_hostname'], config['user'], config['password']
diff --git a/src/pixelated/config/leap.py b/src/pixelated/config/leap.py
new file mode 100644
index 00000000..dc555287
--- /dev/null
+++ b/src/pixelated/config/leap.py
@@ -0,0 +1,92 @@
+from __future__ import absolute_import
+from leap.common.events import (server as events_server)
+from leap.soledad.common.errors import InvalidAuthTokenError
+
+from pixelated.config import credentials
+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
+from twisted.internet import defer
+
+import logging
+log = logging.getLogger(__name__)
+
+
+def initialize_leap_provider(provider_hostname, provider_cert, provider_fingerprint, leap_home):
+ LeapCertificate.set_cert_and_fingerprint(provider_cert,
+ provider_fingerprint)
+
+ config = LeapConfig(leap_home=leap_home, start_background_jobs=True)
+ provider = LeapProvider(provider_hostname, config)
+ provider.download_certificate()
+ LeapCertificate(provider).setup_ca_bundle()
+
+ return config, provider
+
+
+@defer.inlineCallbacks
+def initialize_leap_multi_user(provider_hostname,
+ leap_provider_cert,
+ leap_provider_cert_fingerprint,
+ credentials_file,
+ organization_mode,
+ leap_home):
+
+ config, provider = initialize_leap_provider(
+ provider_hostname, leap_provider_cert, leap_provider_cert_fingerprint, leap_home)
+
+ defer.returnValue((config, provider))
+
+
+def _create_session(provider, username, password, auth):
+ return LeapSessionFactory(provider).create(username, password, auth)
+
+
+def _force_close_session(session):
+ try:
+ session.close()
+ except Exception, e:
+ log.error(e)
+
+
+@defer.inlineCallbacks
+def authenticate_user(provider, username, password, initial_sync=True, auth=None):
+ leap_session = _create_session(provider, username, password, auth)
+ try:
+ if initial_sync:
+ yield leap_session.initial_sync()
+ except InvalidAuthTokenError:
+ _force_close_session(leap_session)
+
+ leap_session = _create_session(provider, username, password, auth)
+ if initial_sync:
+ yield leap_session.initial_sync()
+
+ defer.returnValue(leap_session)
+
+
+@defer.inlineCallbacks
+def initialize_leap_single_user(leap_provider_cert,
+ leap_provider_cert_fingerprint,
+ credentials_file,
+ organization_mode,
+ leap_home,
+ initial_sync=True):
+
+ init_monkeypatches()
+ events_server.ensure_server()
+
+ provider, username, password = credentials.read(organization_mode,
+ credentials_file)
+
+ config, provider = initialize_leap_provider(
+ provider, leap_provider_cert, leap_provider_cert_fingerprint, leap_home)
+
+ leap_session = yield authenticate_user(provider, username, password, initial_sync=initial_sync)
+
+ defer.returnValue(leap_session)
+
+
+def init_monkeypatches():
+ import pixelated.extensions.requests_urllib3
diff --git a/src/pixelated/config/logger.py b/src/pixelated/config/logger.py
new file mode 100644
index 00000000..a8000926
--- /dev/null
+++ b/src/pixelated/config/logger.py
@@ -0,0 +1,37 @@
+#
+# 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/>.
+
+import logging
+import os
+from twisted.python import log
+
+
+def init(debug=False):
+ debug_enabled = debug or os.environ.get('DEBUG', False)
+ logging_level = logging.DEBUG if debug_enabled else logging.WARN
+ log_format = "%(asctime)s [%(name)s] %(levelname)s %(message)s"
+ date_format = '%Y-%m-%d %H:%M:%S'
+
+ logging.basicConfig(level=logging_level,
+ format=log_format,
+ datefmt=date_format,
+ filemode='a')
+
+ observer = log.PythonLoggingObserver()
+ # don't remove this line, it fix the PGP private key logged
+ logging.getLogger('gnupg').setLevel(logging.WARN)
+ logging.getLogger('pixelated').setLevel(logging.INFO)
+ observer.start()
diff --git a/src/pixelated/config/services.py b/src/pixelated/config/services.py
new file mode 100644
index 00000000..a38e7cd1
--- /dev/null
+++ b/src/pixelated/config/services.py
@@ -0,0 +1,84 @@
+import os
+import logging
+
+from pixelated.adapter.mailstore.leap_attachment_store import LeapAttachmentStore
+from pixelated.adapter.mailstore.searchable_mailstore import SearchableMailStore
+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.search import SearchEngine
+from pixelated.adapter.services.draft_service import DraftService
+from pixelated.adapter.listeners.mailbox_indexer_listener import listen_all_mailboxes
+from twisted.internet import defer
+from pixelated.adapter.search.index_storage_key import SearchIndexStorageKey
+from pixelated.adapter.services.feedback_service import FeedbackService
+
+logger = logging.getLogger(__name__)
+
+
+class Services(object):
+
+ def __init__(self, leap_session):
+ self._leap_home = leap_session.config.leap_home
+ self._leap_session = leap_session
+
+ @defer.inlineCallbacks
+ def setup(self):
+ search_index_storage_key = self._setup_search_index_storage_key(
+ self._leap_session.soledad)
+ yield self._setup_search_engine(self._leap_session.user_auth.uuid, search_index_storage_key)
+
+ self._wrap_mail_store_with_indexing_mail_store(self._leap_session)
+
+ yield listen_all_mailboxes(self._leap_session.account, self.search_engine, self._leap_session.mail_store)
+
+ self.mail_service = self._setup_mail_service(self.search_engine)
+
+ self.keymanager = self._leap_session.nicknym
+ self.draft_service = self._setup_draft_service(
+ self._leap_session.mail_store)
+ self.feedback_service = self._setup_feedback_service()
+
+ yield self._index_all_mails()
+
+ def close(self):
+ self._leap_session.close()
+
+ def _wrap_mail_store_with_indexing_mail_store(self, leap_session):
+ leap_session.mail_store = SearchableMailStore(
+ leap_session.mail_store, self.search_engine)
+
+ @defer.inlineCallbacks
+ def _index_all_mails(self):
+ all_mails = yield self.mail_service.all_mails()
+ self.search_engine.index_mails(all_mails)
+
+ @defer.inlineCallbacks
+ def _setup_search_engine(self, namespace, search_index_storage_key):
+ key_unicode = yield search_index_storage_key.get_or_create_key()
+ key = str(key_unicode)
+ logger.debug('The key len is: %s' % len(key))
+ user_id = self._leap_session.user_auth.uuid
+ user_folder = os.path.join(self._leap_home, user_id)
+ search_engine = SearchEngine(key, user_home=user_folder)
+ self.search_engine = search_engine
+
+ def _setup_mail_service(self, search_engine):
+ pixelated_mail_sender = MailSender(
+ self._leap_session.smtp_config, self._leap_session.nicknym.keymanager)
+
+ return MailService(
+ pixelated_mail_sender,
+ self._leap_session.mail_store,
+ search_engine,
+ self._leap_session.account_email(),
+ LeapAttachmentStore(self._leap_session.soledad))
+
+ def _setup_draft_service(self, mail_store):
+ return DraftService(mail_store)
+
+ def _setup_search_index_storage_key(self, soledad):
+ return SearchIndexStorageKey(soledad)
+
+ def _setup_feedback_service(self):
+ return FeedbackService(self._leap_session)
diff --git a/src/pixelated/config/site.py b/src/pixelated/config/site.py
new file mode 100644
index 00000000..1fb884b0
--- /dev/null
+++ b/src/pixelated/config/site.py
@@ -0,0 +1,32 @@
+from twisted.web.server import Site, Request
+
+
+class AddSecurityHeadersRequest(Request):
+ CSP_HEADER_VALUES = "default-src 'self'; style-src 'self' 'unsafe-inline'"
+
+ def process(self):
+ self.setHeader('Content-Security-Policy', self.CSP_HEADER_VALUES)
+ self.setHeader('X-Content-Security-Policy', self.CSP_HEADER_VALUES)
+ self.setHeader('X-Webkit-CSP', self.CSP_HEADER_VALUES)
+ self.setHeader('X-Frame-Options', 'SAMEORIGIN')
+ self.setHeader('X-XSS-Protection', '1; mode=block')
+ self.setHeader('X-Content-Type-Options', 'nosniff')
+
+ if self.isSecure():
+ self.setHeader('Strict-Transport-Security',
+ 'max-age=31536000; includeSubDomains')
+
+ Request.process(self)
+
+
+class PixelatedSite(Site):
+
+ requestFactory = AddSecurityHeadersRequest
+
+ @classmethod
+ def enable_csp_requests(cls):
+ cls.requestFactory = AddSecurityHeadersRequest
+
+ @classmethod
+ def disable_csp_requests(cls):
+ cls.requestFactory = Site.requestFactory