diff options
| author | Folker Bernitt <fbernitt@thoughtworks.com> | 2015-04-15 11:11:55 +0200 | 
|---|---|---|
| committer | Folker Bernitt <fbernitt@thoughtworks.com> | 2015-04-15 11:53:51 +0200 | 
| commit | c7cddf5ccda132b2babc76ab310840cf232b2950 (patch) | |
| tree | 74c9fd883b5e1021ea9ac328ce4a0495a71f4d6b | |
| parent | 9521b46ea1c9e6c596be1fbe397d9bd9126a1912 (diff) | |
Introduced maintenance command and added reset behaviour.
- Call with: pixelated_maintenance reset
- Issues #335, #345
| -rw-r--r-- | service/pixelated/config/args.py | 18 | ||||
| -rw-r--r-- | service/pixelated/maintenance.py | 112 | ||||
| -rw-r--r-- | service/setup.py | 3 | ||||
| -rw-r--r-- | service/test/unit/maintenance/test_commands.py | 65 | 
4 files changed, 191 insertions, 7 deletions
| diff --git a/service/pixelated/config/args.py b/service/pixelated/config/args.py index d3284fab..7c4f02dc 100644 --- a/service/pixelated/config/args.py +++ b/service/pixelated/config/args.py @@ -20,18 +20,24 @@ from pixelated.bitmask_libraries.config import DEFAULT_LEAP_HOME  def parse():      parser = argparse.ArgumentParser(description='Pixelated user agent.') -    parser.add_argument('--debug', action='store_true', help='DEBUG mode.') -    parser.add_argument('--dispatcher', help='run in organization mode, the credentials will be read from specified file', metavar='file') -    parser.add_argument('--dispatcher-stdin', help='run in organization mode, the credentials will be read from stdin', default=False, action='store_true', dest='dispatcher_stdin') + +    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('--port', type=int, default=3333, help='the port to run the user agent on') -    parser.add_argument('--home', help='The folder where the user agent stores its data. Defaults to ~/.leap', default=DEFAULT_LEAP_HOME)      parser.add_argument('-c', '--config', metavar='<configfile>', default=None, help='use specified file for credentials (for test purposes only)')      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('-lc', '--leap-cert', metavar='<leap.crt>', default=None, help='use specified file for LEAP cert authority certificate (url https://<provider-domain>/ca.crt)') -    parser.add_argument('--leap-cert-fingerprint', metavar='<leap certificate fingerprint>', default=None, help='use specified fingerprint to validate connection with leap provider', dest='leap_cert_fingerprint')      parser.add_argument('--register', metavar=('provider', 'username'),                          nargs=2, help='register a new username on the desired provider')      args = parser.parse_args()      return args + + +def parser_add_default_arguments(parser): +    parser.add_argument('--debug', action='store_true', help='DEBUG mode.') +    parser.add_argument('--dispatcher', help='run in organization mode, the credentials will be read from specified file', metavar='file') +    parser.add_argument('--dispatcher-stdin', help='run in organization mode, the credentials will be read from stdin', default=False, action='store_true', dest='dispatcher_stdin') +    parser.add_argument('--home', help='The folder where the user agent stores its data. Defaults to ~/.leap', default=DEFAULT_LEAP_HOME) +    parser.add_argument('-lc', '--leap-cert', metavar='<leap.crt>', default=None, help='use specified file for LEAP cert authority certificate (url https://<provider-domain>/ca.crt)') +    parser.add_argument('--leap-cert-fingerprint', metavar='<leap certificate fingerprint>', default=None, help='use specified fingerprint to validate connection with leap provider', dest='leap_cert_fingerprint') diff --git a/service/pixelated/maintenance.py b/service/pixelated/maintenance.py new file mode 100644 index 00000000..6beeb2fd --- /dev/null +++ b/service/pixelated/maintenance.py @@ -0,0 +1,112 @@ +# +# 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 functools import partial +import sys +import json +import argparse + +from pixelated.config.app import App +from pixelated.config import app_factory +from pixelated.config.args import parser_add_default_arguments +from pixelated.config.config_ua import config_user_agent +from pixelated.config.dispatcher import config_dispatcher +from pixelated.config.events_server import init_events_server +from pixelated.config.loading_page import loading +from pixelated.config.register import register +from pixelated.config.logging_setup import init_logging +from pixelated.config.leap_cert import init_leap_cert +from pixelated.config.soledad import init_soledad_and_user_key +from twisted.internet import reactor, defer +from twisted.internet.threads import deferToThread + +# monkey patching some specifics +import pixelated.support.ext_protobuf +import pixelated.support.ext_sqlcipher +import pixelated.support.ext_esmtp_sender_factory +import pixelated.support.ext_fetch +import pixelated.support.ext_keymanager_fetch_key +import pixelated.support.ext_requests_urllib3 + + +def delete_all_mails(args): +    leap_session, soledad = args +    generation, docs = soledad.get_all_docs() + +    for doc in docs: +        if doc.content.get('type', None) in ['head', 'cnt', 'flags']: +            soledad.delete_doc(doc) + +    return args + + +def initialize(): +    parser = argparse.ArgumentParser(description='pixelated maintenance') +    parser_add_default_arguments(parser) +    subparsers = parser.add_subparsers(help='commands') +    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') + +    args = parser.parse_args() +    app = App() + +    init_logging(args) +    init_leap_cert(args) + +    init_events_server() + +    config_dispatcher(app, args) + +    def execute_command(): + +        def init_soledad(): +            return init_soledad_and_user_key(app, args.home) + +        def get_soledad_handle(leap_session): +            soledad = leap_session.soledad_session.soledad + +            return leap_session, soledad + +        def soledad_sync(args): +            leap_session, soledad = args + +            soledad.sync() + +            return args + +        d = deferToThread(init_soledad) +        d.addCallback(get_soledad_handle) +        d.addCallback(soledad_sync) +        d.addCallback(delete_all_mails) +        d.addCallback(soledad_sync) +        d.addCallback(shutdown) +        d.addErrback(shutdown_on_error) + +    reactor.callWhenRunning(execute_command) +    reactor.run() + + +def shutdown(args): +    reactor.stop() + + +def shutdown_on_error(error): +    print error +    reactor.stop() + +if __name__ == 'main': +    initialize() diff --git a/service/setup.py b/service/setup.py index ed6be9c0..973cac47 100644 --- a/service/setup.py +++ b/service/setup.py @@ -70,7 +70,8 @@ setup(name='pixelated-user-agent',        ],        entry_points={            'console_scripts': [ -              'pixelated-user-agent = pixelated.config:initialize' +              'pixelated-user-agent = pixelated.config:initialize', +              'pixelated-maintenance = pixelated.maintenance:initialize'            ]        },        include_package_data=True diff --git a/service/test/unit/maintenance/test_commands.py b/service/test/unit/maintenance/test_commands.py new file mode 100644 index 00000000..a20b0383 --- /dev/null +++ b/service/test/unit/maintenance/test_commands.py @@ -0,0 +1,65 @@ +# +# 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 unittest + +from pixelated.maintenance import delete_all_mails +from pixelated.bitmask_libraries.session import LeapSession +from leap.soledad.client import Soledad +from leap.soledad.common.document import SoledadDocument +from mock import MagicMock + + +class TestCommands(unittest.TestCase): + +        def setUp(self): +            self.leap_session = MagicMock(spec=LeapSession) +            self.soledad = MagicMock(spec=Soledad) +            self.args = (self.leap_session, self.soledad) + +        def test_delete_all_mails_supports_empty_doclist(self): +            self.soledad.get_all_docs.return_value = (1, []) + +            delete_all_mails(self.args) + +            self.assertFalse(self.soledad.delete_doc.called) + +        def test_delete_all_mails(self): +            doc = MagicMock(spec=SoledadDocument) +            doc.content = {'type': 'head'} +            self.soledad.get_all_docs.return_value = (1, [doc]) + +            delete_all_mails(self.args) + +            self.soledad.delete_doc.assert_called_once_with(doc) + +        def test_only_mail_documents_are_deleted(self): +            docs = self._create_docs_of_type(['head', 'cnt', 'flags', 'mbx', 'foo', None]) +            self.soledad.get_all_docs.return_value = (1, docs) + +            delete_all_mails(self.args) + +            for doc in docs: +                if doc.content['type'] in ['head', 'cnt', 'flags']: +                    self.soledad.delete_doc.assert_any_call(doc) +            self.assertEqual(3, len(self.soledad.delete_doc.mock_calls)) + +        def _create_docs_of_type(self, type_list): +            return [self._create_doc_type(t) for t in type_list] + +        def _create_doc_type(self, doc_type): +            doc = MagicMock(spec=SoledadDocument) +            doc.content = {'type': doc_type} +            return doc | 
