From 1173e77cb8d635936c9730ba4ad8b88b24ad1be2 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Sun, 1 May 2016 11:09:07 -0400 Subject: [feature] pluggable backends and api registry the idea behind this mechanism (partially implemented for that) is to be able to check the backend output against some type annotations. We want to be able to detect if a given backend (real services or authoritative mocks) have diverged from what's specified in the API annotations. --- src/leap/bitmask/core/dispatcher.py | 286 +++++++++++++++++++++++------------- 1 file changed, 181 insertions(+), 105 deletions(-) (limited to 'src/leap/bitmask/core/dispatcher.py') diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index 648cbe9b..e7c961fd 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -22,154 +22,214 @@ import json from twisted.internet import defer from twisted.python import failure, log +from .api import APICommand, register_method -# TODO implement sub-classes to dispatch subcommands (user, mail). +class SubCommand(object): -class CommandDispatcher(object): + __metaclass__ = APICommand - def __init__(self, core): + def dispatch(self, service, *parts, **kw): + subcmd = parts[1] - self.core = core + _method = getattr(self, 'do_' + subcmd.upper(), None) + if not _method: + raise RuntimeError('No such subcommand') + return _method(service, *parts, **kw) - def _get_service(self, name): - try: - return self.core.getServiceNamed(name) - except KeyError: - return None +class UserCmd(SubCommand): - def dispatch(self, msg): - cmd = msg[0] + label = 'user' - _method = getattr(self, 'do_' + cmd.upper(), None) + @register_method("{'srp_token': unicode, 'uuid': unicode}") + def do_AUTHENTICATE(self, bonafide, *parts): + user, password = parts[2], parts[3] + d = defer.maybeDeferred(bonafide.do_authenticate, user, password) + return d - if not _method: - return defer.fail(failure.Failure(RuntimeError('No such command'))) + @register_method("{'signup': 'ok', 'user': str}") + def do_SIGNUP(self, bonafide, *parts): + user, password = parts[2], parts[3] + d = defer.maybeDeferred(bonafide.do_signup, user, password) + return d - return defer.maybeDeferred(_method, *msg) + @register_method("{'logout': 'ok'}") + def do_LOGOUT(self, bonafide, *parts): + user, password = parts[2], parts[3] + d = defer.maybeDeferred(bonafide.do_logout, user, password) + return d - def do_STATS(self, *parts): - return _format_result(self.core.do_stats()) + @register_method('str') + def do_ACTIVE(self, bonafide, *parts): + d = defer.maybeDeferred(bonafide.do_get_active_user) + return d - def do_VERSION(self, *parts): - return _format_result(self.core.do_version()) - def do_STATUS(self, *parts): - return _format_result(self.core.do_status()) +class EIPCmd(SubCommand): - def do_SHUTDOWN(self, *parts): - return _format_result(self.core.do_shutdown()) + label = 'eip' - def do_USER(self, *parts): + @register_method('dict') + def do_ENABLE(self, service, *parts): + d = service.do_enable_service(self.label) + return d - subcmd = parts[1] - user, password = parts[2], parts[3] + @register_method('dict') + def do_DISABLE(self, service, *parts): + d = service.do_disable_service(self.label) + return d - bf = self._get_service('bonafide') + @register_method('dict') + def do_STATUS(self, eip, *parts): + d = eip.do_status() + return d - if subcmd == 'authenticate': - d = bf.do_authenticate(user, password) + @register_method('dict') + def do_START(self, eip, *parts): + # TODO --- attempt to get active provider + # TODO or catch the exception and send error + provider = parts[2] + d = eip.do_start(provider) + return d - elif subcmd == 'signup': - d = bf.do_signup(user, password) + @register_method('dict') + def do_STOP(self, eip, *parts): + d = eip.do_stop() + return d - elif subcmd == 'logout': - d = bf.do_logout(user, password) - elif subcmd == 'active': - d = bf.do_get_active_user() +class MailCmd(SubCommand): - d.addCallbacks(_format_result, _format_error) + label = 'mail' + + @register_method('dict') + def do_ENABLE(self, service, *parts): + d = service.do_enable_service(self.label) return d - def do_EIP(self, *parts): + @register_method('dict') + def do_DISABLE(self, service, *parts): + d = service.do_disable_service(self.label) + return d - subcmd = parts[1] - eip_label = 'eip' + @register_method('dict') + def do_STATUS(self, mail, *parts): + d = mail.do_status() + return d - if subcmd == 'enable': - return _format_result( - self.core.do_enable_service(eip_label)) + @register_method('dict') + def do_GET_IMAP_TOKEN(self, mail, *parts): + d = mail.get_imap_token() + return d - eip = self._get_service(eip_label) - if not eip: - return _format_result('eip: disabled') + @register_method('dict') + def do_GET_SMTP_TOKEN(self, mail, *parts): + d = mail.get_smtp_token() + return d - if subcmd == 'status': - return _format_result(eip.do_status()) + @register_method('dict') + def do_GET_SMTP_CERTIFICATE(self, mail, *parts, **kw): + # TODO move to mail service + # TODO should ask for confirmation? like --force or something, + # if we already have a valid one. or better just refuse if cert + # exists. + # TODO how should we pass the userid?? + # - Keep an 'active' user in bonafide (last authenticated) + # (doing it now) + # - Get active user from Mail Service (maybe preferred?) + # - Have a command/method to set 'active' user. + + @defer.inlineCallbacks + def save_cert(cert_data): + userid, cert_str = cert_data + cert_path = yield mail.do_get_smtp_cert_path(userid) + with open(cert_path, 'w') as outf: + outf.write(cert_str) + defer.returnValue('certificate saved to %s' % cert_path) + + bonafide = kw['bonafide'] + d = bonafide.do_get_smtp_cert() + d.addCallback(save_cert) + return d - elif subcmd == 'disable': - return _format_result( - self.core.do_disable_service(eip_label)) - elif subcmd == 'start': - # TODO --- attempt to get active provider - # TODO or catch the exception and send error - provider = parts[2] - d = eip.do_start(provider) - d.addCallbacks(_format_result, _format_error) - return d +class CommandDispatcher(object): - elif subcmd == 'stop': - d = eip.do_stop() - d.addCallbacks(_format_result, _format_error) - return d + __metaclass__ = APICommand - def do_MAIL(self, *parts): + label = 'core' + def __init__(self, core): + + self.core = core + self.subcommand_user = UserCmd() + self.subcommand_eip = EIPCmd() + self.subcommand_mail = MailCmd() + + # XXX -------------------------------------------- + # TODO move general services to another subclass + + @register_method("{'mem_usage': str}") + def do_STATS(self, *parts): + return _format_result(self.core.do_stats()) + + @register_method("{version_core': '0.0.0'}") + def do_VERSION(self, *parts): + return _format_result(self.core.do_version()) + + @register_method("{'mail': 'running'}") + def do_STATUS(self, *parts): + return _format_result(self.core.do_status()) + + @register_method("{'shutdown': 'ok'}") + def do_SHUTDOWN(self, *parts): + return _format_result(self.core.do_shutdown()) + + # ----------------------------------------------- + + def do_USER(self, *parts): + bonafide = self._get_service('bonafide') + d = self.subcommand_user.dispatch(bonafide, *parts) + d.addCallbacks(_format_result, _format_error) + return d + + def do_EIP(self, *parts): + eip = self._get_service(self.subcommand_eip.label) + if not eip: + return _format_result('eip: disabled') subcmd = parts[1] - mail_label = 'mail' - if subcmd == 'enable': - return _format_result( - self.core.do_enable_service(mail_label)) + dispatch = self._subcommand_eip.dispatch + if subcmd in ('enable', 'disable'): + d = dispatch(self.core, *parts) + else: + d = dispatch(eip, *parts) - m = self._get_service(mail_label) - bf = self._get_service('bonafide') + d.addCallbacks(_format_result, _format_error) + return d - if not m: - return _format_result('mail: disabled') + def do_MAIL(self, *parts): + subcmd = parts[1] + dispatch = self.subcommand_mail.dispatch - if subcmd == 'status': - return _format_result(m.do_status()) + if subcmd == 'enable': + d = dispatch(self.core, *parts) - elif subcmd == 'disable': - return _format_result(self.core.do_disable_service(mail_label)) + mail = self._get_service(self.subcommand_mail.label) + bonafide = self._get_service('bonafide') + kw = {'bonafide': bonafide} - elif subcmd == 'get_imap_token': - d = m.get_imap_token() - d.addCallbacks(_format_result, _format_error) - return d + if not mail: + return _format_result('mail: disabled') - elif subcmd == 'get_smtp_token': - d = m.get_smtp_token() - d.addCallbacks(_format_result, _format_error) - return d + if subcmd == 'disable': + d = dispatch(self.core) + else: + d = dispatch(mail, *parts, **kw) - elif subcmd == 'get_smtp_certificate': - # TODO move to mail service - # TODO should ask for confirmation? like --force or something, - # if we already have a valid one. or better just refuse if cert - # exists. - # TODO how should we pass the userid?? - # - Keep an 'active' user in bonafide (last authenticated) - # (doing it now) - # - Get active user from Mail Service (maybe preferred?) - # - Have a command/method to set 'active' user. - - @defer.inlineCallbacks - def save_cert(cert_data): - userid, cert_str = cert_data - cert_path = yield m.do_get_smtp_cert_path(userid) - with open(cert_path, 'w') as outf: - outf.write(cert_str) - defer.returnValue('certificate saved to %s' % cert_path) - - d = bf.do_get_smtp_cert() - d.addCallback(save_cert) - d.addCallbacks(_format_result, _format_error) - return d + d.addCallbacks(_format_result, _format_error) + return d def do_KEYS(self, *parts): subcmd = parts[1] @@ -187,6 +247,22 @@ class CommandDispatcher(object): d.addCallbacks(_format_result, _format_error) return d + def dispatch(self, msg): + cmd = msg[0] + + _method = getattr(self, 'do_' + cmd.upper(), None) + + if not _method: + return defer.fail(failure.Failure(RuntimeError('No such command'))) + + return defer.maybeDeferred(_method, *msg) + + def _get_service(self, name): + try: + return self.core.getServiceNamed(name) + except KeyError: + return None + def _format_result(result): return json.dumps({'error': None, 'result': result}) -- cgit v1.2.3