diff options
| author | Kali Kaneko (leap communications) <kali@leap.se> | 2017-02-23 00:35:33 +0100 | 
|---|---|---|
| committer | Kali Kaneko (leap communications) <kali@leap.se> | 2017-02-24 16:20:52 +0100 | 
| commit | e3999c4906348dadcc85eec1df9a48e776deccd5 (patch) | |
| tree | 7f8156ba80f367df22c4e823c301360706e06e8d /src | |
| parent | 6b3ea883a62d40f8e2d68ce95bbefa2ac64b95de (diff) | |
[feature] require authentication token for api
implements a global auth token for the app.
this token is written to .config/leap/authtoken, and passed to the
anchor part of the landing URI when opening the index resource by the
browser.
- Resolves: #8765
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/core/service.py | 18 | ||||
| -rw-r--r-- | src/leap/bitmask/core/web/_auth.py | 7 | ||||
| -rw-r--r-- | src/leap/bitmask/core/web/api.py | 11 | ||||
| -rw-r--r-- | src/leap/bitmask/core/web/service.py | 20 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/app.py | 10 | 
5 files changed, 45 insertions, 21 deletions
| diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index 902bfa6b..c06a5343 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -18,6 +18,8 @@  Bitmask-core Service.  """  import json +import os +import uuid  try:      import resource  except ImportError: @@ -62,6 +64,16 @@ class BitmaskBackend(configurable.ConfigurableService):          configurable.ConfigurableService.__init__(self, basedir)          self.core_commands = BackendCommands(self) + +        # The global token is used for authenticating some of the channels that +        # expose the dispatcher. For the moment being, this is the REST API. +        self.global_tokens = [uuid.uuid4().hex] +        logger.info('Global token: {0}'.format(self.global_tokens[0])) +        self._touch_token_file() + +        # These tokens are user-session tokens. Implemented and rolled back, +        # unused for now. If we don't move forward with user-session tokens on +        # top of the global app token, this should be removed.          self.tokens = {}          def enabled(service): @@ -89,6 +101,12 @@ class BitmaskBackend(configurable.ConfigurableService):          if enabled('websockets'):              on_start(self._init_websockets) +    def _touch_token_file(self): +        path = os.path.join(self.basedir, 'authtoken') +        with open(path, 'w') as f: +            f.write(self.global_tokens[0]) +        os.chmod(path, 0600) +      def init_events(self):          event_server.ensure_server() diff --git a/src/leap/bitmask/core/web/_auth.py b/src/leap/bitmask/core/web/_auth.py index 2747fae8..aa6aeb9b 100644 --- a/src/leap/bitmask/core/web/_auth.py +++ b/src/leap/bitmask/core/web/_auth.py @@ -6,6 +6,7 @@ from twisted.web.guard import HTTPAuthSessionWrapper, BasicCredentialFactory  from twisted.web.resource import IResource +# Deprecate if the user-session tokens are finally not used.  class TokenCredentialFactory(BasicCredentialFactory):      scheme = 'token' @@ -37,11 +38,11 @@ class WhitelistHTTPAuthSessionWrapper(HTTPAuthSessionWrapper):          return HTTPAuthSessionWrapper.render(self, request) -def protectedResourceFactory(resource, session_tokens, whitelist): +def protectedResourceFactory(resource, tokens, whitelist):      realm = HttpPasswordRealm(resource) -    checker = TokenDictChecker(session_tokens) -    resource_portal = portal.Portal(realm, [checker]) +    checker = TokenDictChecker(tokens)      credentialFactory = TokenCredentialFactory('localhost') +    resource_portal = portal.Portal(realm, [checker])      protected_resource = WhitelistHTTPAuthSessionWrapper(          resource_portal, [credentialFactory],          whitelist=whitelist) diff --git a/src/leap/bitmask/core/web/api.py b/src/leap/bitmask/core/web/api.py index d31afa50..01c65bae 100644 --- a/src/leap/bitmask/core/web/api.py +++ b/src/leap/bitmask/core/web/api.py @@ -11,11 +11,20 @@ class Api(Resource):      isLeaf = True -    def __init__(self, dispatcher): +    def __init__(self, dispatcher, global_tokens):          Resource.__init__(self)          self.dispatcher = dispatcher +        self.global_tokens = global_tokens      def render_POST(self, request): +        token = request.getHeader('x-bitmask-auth') +        if not token: +            request.setResponseCode(401) +            return 'unauthorized: no app token' +        elif token.strip() not in self.global_tokens: +            request.setResponseCode(401) +            return 'unauthorized: bad app token' +          command = request.uri.split('/')[2:]          params = request.content.getvalue()          if params: diff --git a/src/leap/bitmask/core/web/service.py b/src/leap/bitmask/core/web/service.py index c1d839e8..2b8a7343 100644 --- a/src/leap/bitmask/core/web/service.py +++ b/src/leap/bitmask/core/web/service.py @@ -61,11 +61,6 @@ class HTTPDispatcherService(service.Service):      API_WHITELIST = (          '/API/core/version',          '/API/core/stats', -        '/API/bonafide/user/create', -        '/API/bonafide/user/authenticate', -        '/API/bonafide/provider/list', -        '/API/bonafide/provider/create', -        '/API/bonafide/provider/read',      )      def __init__(self, core, port=7070, debug=False, onion=False): @@ -76,7 +71,6 @@ class HTTPDispatcherService(service.Service):          self.uri = ''      def startService(self): -        # TODO refactor this, too long----------------------------------------          if HAS_WEB_UI:              webdir = os.path.abspath(                  pkg_resources.resource_filename('leap.bitmask_js', 'public')) @@ -91,18 +85,16 @@ class HTTPDispatcherService(service.Service):                  'ui', 'app', 'lib', 'bitmask.js')              jsapi = File(os.path.abspath(jspath)) -        api = Api(CommandDispatcher(self._core)) -        # protected_api = protectedResourceFactory( -        #    api, self._core.tokens, self.API_WHITELIST) +        api = Api(CommandDispatcher(self._core), self._core.global_tokens)          root = File(webdir) +        root.putChild(u'API', api) -        # FIXME -- switching off the protected api, due to -        # https://0xacab.org/leap/bitmask-dev/issues/9 +        # XXX remove it we don't bring session tokens again +        # protected_api = protectedResourceFactory( +        # api, self._core.global_tokens, self.API_WHITELIST)          # root.putChild(u'API', protected_api) -        # ------------------------------------------------- -        root.putChild(u'API', api)          if not HAS_WEB_UI:              root.putChild('bitmask.js', jsapi) @@ -110,7 +102,7 @@ class HTTPDispatcherService(service.Service):          self.site = factory          if self.onion and _has_txtorcon(): -                self._start_onion_service(factory) +            self._start_onion_service(factory)          else:              interface = '127.0.0.1'              endpoint = endpoints.TCP4ServerEndpoint( diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py index ce9fc880..14025afc 100644 --- a/src/leap/bitmask/gui/app.py +++ b/src/leap/bitmask/gui/app.py @@ -30,8 +30,8 @@ from functools import partial  from multiprocessing import Process  from leap.bitmask.core.launcher import run_bitmaskd, pid -  from leap.bitmask.gui import app_rc +from leap.common.config import get_path_prefix  if platform.system() == 'Windows': @@ -51,7 +51,7 @@ else:      from PyQt5.QtCore import QSize -BITMASK_URI = 'http://localhost:7070' +BITMASK_URI = 'http://localhost:7070/'  IS_WIN = platform.system() == "Windows"  DEBUG = os.environ.get("DEBUG", False) @@ -100,7 +100,11 @@ class BrowserWindow(QDialog):          self.closing = False      def load_app(self): -        self.view.load(QtCore.QUrl(BITMASK_URI)) +        path = os.path.join(get_path_prefix(), 'leap', 'authtoken') +        global_token = open(path).read().strip() +        anchored_uri = BITMASK_URI + 'index.html#' + global_token +        print('[bitmask] opening Browser with {0}'.format(anchored_uri)) +        self.view.load(QtCore.QUrl(anchored_uri))      def shutdown(self, *args):          if self.closing: | 
