From 3b3731d873664db00c02603363f61d34c41a3990 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 25 Apr 2016 22:13:19 -0400 Subject: embed pixelated --- src/pixelated/config/__init__.py | 0 src/pixelated/config/arguments.py | 98 +++++++++++++++++++++++++++++++++++++ src/pixelated/config/credentials.py | 54 ++++++++++++++++++++ src/pixelated/config/leap.py | 92 ++++++++++++++++++++++++++++++++++ src/pixelated/config/logger.py | 37 ++++++++++++++ src/pixelated/config/services.py | 84 +++++++++++++++++++++++++++++++ src/pixelated/config/site.py | 32 ++++++++++++ 7 files changed, 397 insertions(+) create mode 100644 src/pixelated/config/__init__.py create mode 100644 src/pixelated/config/arguments.py create mode 100644 src/pixelated/config/credentials.py create mode 100644 src/pixelated/config/leap.py create mode 100644 src/pixelated/config/logger.py create mode 100644 src/pixelated/config/services.py create mode 100644 src/pixelated/config/site.py (limited to 'src/pixelated/config') diff --git a/src/pixelated/config/__init__.py b/src/pixelated/config/__init__.py new file mode 100644 index 00000000..e69de29b 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 . + +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='', 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='', 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='', 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='', default=None, + help='use specified file for LEAP provider cert authority certificate (url https:///ca.crt)') + parser.add_argument('-lf', '--leap-provider-cert-fingerprint', metavar='', 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='', + 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='', default=None, + help='use specified file for LEAP provider cert authority certificate (url https:///ca.crt)') + parser.add_argument('-lf', '--leap-provider-cert-fingerprint', metavar='', 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 . + +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 . + +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 -- cgit v1.2.3