summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFolker Bernitt <fbernitt@thoughtworks.com>2015-04-15 11:11:55 +0200
committerFolker Bernitt <fbernitt@thoughtworks.com>2015-04-15 11:53:51 +0200
commitc7cddf5ccda132b2babc76ab310840cf232b2950 (patch)
tree74c9fd883b5e1021ea9ac328ce4a0495a71f4d6b
parent9521b46ea1c9e6c596be1fbe397d9bd9126a1912 (diff)
Introduced maintenance command and added reset behaviour.
- Call with: pixelated_maintenance reset - Issues #335, #345
-rw-r--r--service/pixelated/config/args.py18
-rw-r--r--service/pixelated/maintenance.py112
-rw-r--r--service/setup.py3
-rw-r--r--service/test/unit/maintenance/test_commands.py65
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