summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKali Kaneko (leap communications) <kali@leap.se>2016-12-07 20:46:02 +0100
committerKali Kaneko (leap communications) <kali@leap.se>2016-12-29 03:09:52 +0100
commit7c588e919e959f32b33235b4a44da257d8f4a964 (patch)
tree91db85ddbffd6989d14f7843e6ec5dd004935893
parente50a442c6f03ba09a800f9999e29e9340b1d45c7 (diff)
[refactor] move web service to its own submodule
-rw-r--r--src/leap/bitmask/core/service.py4
-rw-r--r--src/leap/bitmask/core/web/__init__.py12
-rw-r--r--src/leap/bitmask/core/web/_auth.py86
-rw-r--r--src/leap/bitmask/core/web/api.py32
-rw-r--r--src/leap/bitmask/core/web/service.py (renamed from src/leap/bitmask/core/_web.py)142
-rw-r--r--src/leap/bitmask/core/web/static/README (renamed from src/leap/bitmask/core/web/README)0
-rw-r--r--src/leap/bitmask/core/web/static/__init__.py0
-rw-r--r--src/leap/bitmask/core/web/static/index.html (renamed from src/leap/bitmask/core/web/index.html)0
8 files changed, 145 insertions, 131 deletions
diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py
index 705dcb5..9682c18 100644
--- a/src/leap/bitmask/core/service.py
+++ b/src/leap/bitmask/core/service.py
@@ -29,9 +29,9 @@ from twisted.logger import Logger
from leap.bitmask import __version__
from leap.bitmask.core import configurable
from leap.bitmask.core import _zmq
-from leap.bitmask.core import _web
from leap.bitmask.core import flags
from leap.bitmask.core import _session
+from leap.bitmask.core.web.service import HTTPDispatcherService
from leap.common.events import server as event_server
# from leap.vpn import EIPService
@@ -175,7 +175,7 @@ class BitmaskBackend(configurable.ConfigurableService):
zs.setServiceParent(self)
def _init_web(self, onion=False):
- service = _web.HTTPDispatcherService
+ service = HTTPDispatcherService
self._maybe_init_service('web', service, self, onion=onion)
def _init_websockets(self):
diff --git a/src/leap/bitmask/core/web/__init__.py b/src/leap/bitmask/core/web/__init__.py
index e69de29..ed8cc52 100644
--- a/src/leap/bitmask/core/web/__init__.py
+++ b/src/leap/bitmask/core/web/__init__.py
@@ -0,0 +1,12 @@
+try:
+ import leap.bitmask_js
+ assert leap.bitmask_js
+ HAS_WEB_UI = True
+except ImportError:
+ HAS_WEB_UI = False
+
+try:
+ import txtorcon
+ assert txtorcon
+except Exception:
+ pass
diff --git a/src/leap/bitmask/core/web/_auth.py b/src/leap/bitmask/core/web/_auth.py
new file mode 100644
index 0000000..6a5e362
--- /dev/null
+++ b/src/leap/bitmask/core/web/_auth.py
@@ -0,0 +1,86 @@
+from zope.interface import implementer
+
+from twisted.cred import portal, checkers, credentials, error as credError
+from twisted.internet import defer
+from twisted.web.guard import HTTPAuthSessionWrapper, BasicCredentialFactory
+from twisted.web.resource import IResource
+
+
+class TokenCredentialFactory(BasicCredentialFactory):
+ scheme = 'token'
+
+
+@implementer(IResource)
+class WhitelistHTTPAuthSessionWrapper(HTTPAuthSessionWrapper):
+
+ """
+ Wrap a portal, enforcing supported header-based authentication schemes.
+ It doesn't apply the enforcement to routes included in a whitelist.
+ """
+
+ # TODO extend this to inspect the data -- so that we pass a tuple
+ # with the action
+
+ whitelist = (None,)
+
+ def __init__(self, *args, **kw):
+ self.whitelist = kw.pop('whitelist', tuple())
+ super(WhitelistHTTPAuthSessionWrapper, self).__init__(
+ *args, **kw)
+
+ def getChildWithDefault(self, path, request):
+ if request.path in self.whitelist:
+ return self
+ return HTTPAuthSessionWrapper.getChildWithDefault(self, path, request)
+
+ def render(self, request):
+ if request.path in self.whitelist:
+ _res = self._portal.realm.resource
+ return _res.render(request)
+ return HTTPAuthSessionWrapper.render(self, request)
+
+
+def protectedResourceFactory(resource, session_tokens, whitelist):
+ realm = HttpPasswordRealm(resource)
+ checker = TokenDictChecker(session_tokens)
+ resource_portal = portal.Portal(realm, [checker])
+ credentialFactory = TokenCredentialFactory('localhost')
+ protected_resource = WhitelistHTTPAuthSessionWrapper(
+ resource_portal, [credentialFactory],
+ whitelist=whitelist)
+ return protected_resource
+
+
+@implementer(checkers.ICredentialsChecker)
+class TokenDictChecker:
+
+ credentialInterfaces = (credentials.IUsernamePassword,
+ credentials.IUsernameHashedPassword)
+
+ def __init__(self, tokens):
+ self.tokens = tokens
+
+ def requestAvatarId(self, credentials):
+ username = credentials.username
+ if username in self.tokens:
+ if credentials.checkPassword(self.tokens[username]):
+ return defer.succeed(username)
+ else:
+ return defer.fail(
+ credError.UnauthorizedLogin("Bad session token"))
+ else:
+ return defer.fail(
+ credError.UnauthorizedLogin("No such user"))
+
+
+@implementer(portal.IRealm)
+class HttpPasswordRealm(object):
+
+ def __init__(self, resource):
+ self.resource = resource
+
+ def requestAvatar(self, user, mind, *interfaces):
+ # the resource is passed on regardless of user
+ if IResource in interfaces:
+ return (IResource, self.resource, lambda: None)
+ raise NotImplementedError()
diff --git a/src/leap/bitmask/core/web/api.py b/src/leap/bitmask/core/web/api.py
new file mode 100644
index 0000000..e8bd21e
--- /dev/null
+++ b/src/leap/bitmask/core/web/api.py
@@ -0,0 +1,32 @@
+import json
+from twisted.web.server import NOT_DONE_YET
+
+from twisted.web.resource import Resource
+
+
+class Api(Resource):
+
+ isLeaf = True
+
+ def __init__(self, dispatcher):
+ Resource.__init__(self)
+ self.dispatcher = dispatcher
+
+ def render_POST(self, request):
+ command = request.uri.split('/')[2:]
+ params = request.content.getvalue()
+ if params:
+ # json.loads returns unicode strings and the rest of the code
+ # expects strings. This 'str(param)' conversion can be removed
+ # if we move to python3
+ for param in json.loads(params):
+ command.append(str(param))
+
+ d = self.dispatcher.dispatch(command)
+ d.addCallback(self._write_response, request)
+ return NOT_DONE_YET
+
+ def _write_response(self, response, request):
+ request.setHeader('Content-Type', 'application/json')
+ request.write(response)
+ request.finish()
diff --git a/src/leap/bitmask/core/_web.py b/src/leap/bitmask/core/web/service.py
index 11a7be1..2437d2d 100644
--- a/src/leap/bitmask/core/_web.py
+++ b/src/leap/bitmask/core/web/service.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# _web.py
-# Copyright (C) 2016 LEAP Encryption Access Project
+# service.py
+# Copyright (C) 2016 LEAP Encryption Acess Project
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,118 +19,29 @@
HTTP REST Dispatcher Service.
"""
-import json
import os
import pkg_resources
from twisted.application import service
-
-from twisted.internet import endpoints
-from twisted.cred import portal, checkers, credentials, error as credError
-from twisted.internet import reactor, defer
from twisted.logger import Logger
-from twisted.web.guard import HTTPAuthSessionWrapper, BasicCredentialFactory
-from twisted.web.resource import IResource, Resource
-from twisted.web.server import Site, NOT_DONE_YET
+from twisted.internet import endpoints
+from twisted.internet import reactor
+from twisted.web.server import Site
from twisted.web.static import File
-from zope.interface import implementer
-
-from leap.bitmask.util import here
from leap.bitmask.core.dispatcher import CommandDispatcher
-
-try:
- import leap.bitmask_js
- HAS_WEB_UI = True
-except ImportError:
- HAS_WEB_UI = False
+from leap.bitmask.core.web import HAS_WEB_UI
+from leap.bitmask.core.web.api import Api
+from leap.bitmask.core.web._auth import protectedResourceFactory
+from leap.bitmask.util import here
try:
import txtorcon
-except Exception:
+except ImportError:
pass
-log = Logger()
-
-
-class TokenCredentialFactory(BasicCredentialFactory):
- scheme = 'token'
-
-
-@implementer(checkers.ICredentialsChecker)
-class TokenDictChecker:
-
- credentialInterfaces = (credentials.IUsernamePassword,
- credentials.IUsernameHashedPassword)
-
- def __init__(self, tokens):
- self.tokens = tokens
-
- def requestAvatarId(self, credentials):
- username = credentials.username
- if username in self.tokens:
- if credentials.checkPassword(self.tokens[username]):
- return defer.succeed(username)
- else:
- return defer.fail(
- credError.UnauthorizedLogin("Bad session token"))
- else:
- return defer.fail(
- credError.UnauthorizedLogin("No such user"))
-
-
-@implementer(portal.IRealm)
-class HttpPasswordRealm(object):
- def __init__(self, resource):
- self.resource = resource
-
- def requestAvatar(self, user, mind, *interfaces):
- # the resource is passed on regardless of user
- if IResource in interfaces:
- return (IResource, self.resource, lambda: None)
- raise NotImplementedError()
-
-
-@implementer(IResource)
-class WhitelistHTTPAuthSessionWrapper(HTTPAuthSessionWrapper):
-
- """
- Wrap a portal, enforcing supported header-based authentication schemes.
- It doesn't apply the enforcement to routes included in a whitelist.
- """
-
- # TODO extend this to inspect the data -- so that we pass a tuple
- # with the action
-
- whitelist = (None,)
-
- def __init__(self, *args, **kw):
- self.whitelist = kw.pop('whitelist', tuple())
- super(WhitelistHTTPAuthSessionWrapper, self).__init__(
- *args, **kw)
-
- def getChildWithDefault(self, path, request):
- if request.path in self.whitelist:
- return self
- return HTTPAuthSessionWrapper.getChildWithDefault(self, path, request)
-
- def render(self, request):
- if request.path in self.whitelist:
- _res = self._portal.realm.resource
- return _res.render(request)
- return HTTPAuthSessionWrapper.render(self, request)
-
-
-def protectedResourceFactory(resource, session_tokens, whitelist):
- realm = HttpPasswordRealm(resource)
- checker = TokenDictChecker(session_tokens)
- resource_portal = portal.Portal(realm, [checker])
- credentialFactory = TokenCredentialFactory('localhost')
- protected_resource = WhitelistHTTPAuthSessionWrapper(
- resource_portal, [credentialFactory],
- whitelist=whitelist)
- return protected_resource
+log = Logger()
class HTTPDispatcherService(service.Service):
@@ -167,7 +78,8 @@ class HTTPDispatcherService(service.Service):
else:
log.warn('bitmask_js not found, serving bitmask.core ui')
webdir = os.path.abspath(
- pkg_resources.resource_filename('leap.bitmask.core', 'web'))
+ pkg_resources.resource_filename(
+ 'leap.bitmask.core.web', 'static'))
jspath = os.path.join(
here(), '..', '..', '..',
'ui', 'app', 'lib', 'bitmask.js')
@@ -230,34 +142,6 @@ class HTTPDispatcherService(service.Service):
return {'web': status, 'uri': self.uri}
-class Api(Resource):
-
- isLeaf = True
-
- def __init__(self, dispatcher):
- Resource.__init__(self)
- self.dispatcher = dispatcher
-
- def render_POST(self, request):
- command = request.uri.split('/')[2:]
- params = request.content.getvalue()
- if params:
- # json.loads returns unicode strings and the rest of the code
- # expects strings. This 'str(param)' conversion can be removed
- # if we move to python3
- for param in json.loads(params):
- command.append(str(param))
-
- d = self.dispatcher.dispatch(command)
- d.addCallback(self._write_response, request)
- return NOT_DONE_YET
-
- def _write_response(self, response, request):
- request.setHeader('Content-Type', 'application/json')
- request.write(response)
- request.finish()
-
-
def _has_txtorcon():
try:
import txtorcon
diff --git a/src/leap/bitmask/core/web/README b/src/leap/bitmask/core/web/static/README
index 2b99926..2b99926 100644
--- a/src/leap/bitmask/core/web/README
+++ b/src/leap/bitmask/core/web/static/README
diff --git a/src/leap/bitmask/core/web/static/__init__.py b/src/leap/bitmask/core/web/static/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/leap/bitmask/core/web/static/__init__.py
diff --git a/src/leap/bitmask/core/web/index.html b/src/leap/bitmask/core/web/static/index.html
index 9951a9b..9951a9b 100644
--- a/src/leap/bitmask/core/web/index.html
+++ b/src/leap/bitmask/core/web/static/index.html