summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/pixelated/resources/root_resource.py127
-rw-r--r--service/test/unit/resources/test_root_resource.py107
2 files changed, 127 insertions, 107 deletions
diff --git a/service/pixelated/resources/root_resource.py b/service/pixelated/resources/root_resource.py
index 5477dca8..d35147f5 100644
--- a/service/pixelated/resources/root_resource.py
+++ b/service/pixelated/resources/root_resource.py
@@ -19,6 +19,7 @@ import os
from string import Template
from pixelated.resources.users import UsersResource
+import pixelated
from pixelated.resources import BaseResource, UnAuthorizedResource, UnavailableResource
from pixelated.resources import IPixelatedSession
from pixelated.resources.attachments_resource import AttachmentsResource
@@ -47,44 +48,66 @@ MODE_STARTUP = 1
MODE_RUNNING = 2
-class PublicRootResource(BaseResource, object):
+class InboxResource(BaseResource):
+ isLeaf = True
- def __init__(self, services_factory, assets_path, **kwargs):
- super(PublicRootResource, self).__init__(services_factory)
- self._child_resources = dict(
- assets=File(assets_path),
- login=LoginResource(services_factory, **{k: kwargs[k] for k in kwargs if k in ('provider', 'disclaimer_banner', 'authenticator')})
- )
+ def __init__(self, services_factory):
+ BaseResource.__init__(self, services_factory)
+ self._templates_folder = self._get_templates_folder()
+ self._html_template = open(os.path.join(self._templates_folder, 'index.html')).read()
+ with open(os.path.join(self._templates_folder, 'Interstitial.html')) as f:
+ self.interstitial = f.read()
+ self._mode = MODE_STARTUP
+
+ def initialize(self):
+ self._mode = MODE_RUNNING
- def getChild(self, path, request):
- return self._child_resources.get(path) or NoResource()
+ def _get_templates_folder(self):
+ path = os.path.dirname(os.path.abspath(pixelated.__file__))
+ return os.path.join(path, 'assets')
+
+ def _add_csrf_cookie(self, request):
+ csrf_token = hashlib.sha256(os.urandom(CSRF_TOKEN_LENGTH)).hexdigest()
+ request.addCookie('XSRF-TOKEN', csrf_token)
+
+ def _is_starting(self):
+ return self._mode == MODE_STARTUP
+
+ def render_GET(self, request):
+ self._add_csrf_cookie(request)
+ if self._is_starting():
+ return self.interstitial
+ else:
+ account_email = self.mail_service(request).account_email
+ response = Template(self._html_template).safe_substitute(account_email=account_email)
+ return str(response)
class RootResource(BaseResource):
def __init__(self, services_factory):
BaseResource.__init__(self, services_factory)
+ self._assets_folder = self._get_assets_folder()
self._startup_assets_folder = self._get_startup_folder()
- self._public_assets_folder = self._get_public_folder()
self._static_folder = self._get_static_folder()
self._html_template = open(os.path.join(self._static_folder, 'index.html')).read()
self._services_factory = services_factory
- self._child_resources = ChildResourcesMap()
with open(os.path.join(self._startup_assets_folder, 'Interstitial.html')) as f:
self.interstitial = f.read()
+ self._inbox_resource = InboxResource(services_factory)
self._startup_mode()
def _startup_mode(self):
+ self.putChild('assets', File(self._assets_folder))
self.putChild('startup-assets', File(self._startup_assets_folder))
- self.putChild('public-assets', File(self._public_assets_folder))
self._mode = MODE_STARTUP
- def getChild(self, path, request):
+ def getChildWithDefault(self, path, request):
if path == '':
- return self
+ return self._inbox_resource
if self._mode == MODE_STARTUP:
return UnavailableResource()
if self._is_xsrf_valid(request):
- return self._child_resources.get(path)
+ return BaseResource.getChildWithDefault(self, path, request)
return UnAuthorizedResource()
def _is_xsrf_valid(self, request):
@@ -103,40 +126,33 @@ class RootResource(BaseResource):
return csrf_input and csrf_input == xsrf_token
def initialize(self, provider=None, disclaimer_banner=None, authenticator=None):
- self._child_resources.add('sandbox', SandboxResource(self._static_folder))
- self._child_resources.add('assets', File(self._static_folder))
- self._child_resources.add('keys', KeysResource(self._services_factory))
- self._child_resources.add(AttachmentsResource.BASE_URL, AttachmentsResource(self._services_factory))
- self._child_resources.add('contacts', ContactsResource(self._services_factory))
- self._child_resources.add('features', FeaturesResource(provider))
- self._child_resources.add('tags', TagsResource(self._services_factory))
- self._child_resources.add('mails', MailsResource(self._services_factory))
- self._child_resources.add('mail', MailResource(self._services_factory))
- self._child_resources.add('feedback', FeedbackResource(self._services_factory))
- self._child_resources.add('user-settings', UserSettingsResource(self._services_factory))
- self._child_resources.add('users', UsersResource(self._services_factory))
- self._child_resources.add(LoginResource.BASE_URL,
- LoginResource(self._services_factory, provider, disclaimer_banner=disclaimer_banner, authenticator=authenticator))
- self._child_resources.add(LogoutResource.BASE_URL, LogoutResource(self._services_factory))
-
+ self.putChild('sandbox', SandboxResource(self._static_folder))
+ self.putChild('keys', KeysResource(self._services_factory))
+ self.putChild(AttachmentsResource.BASE_URL, AttachmentsResource(self._services_factory))
+ self.putChild('contacts', ContactsResource(self._services_factory))
+ self.putChild('features', FeaturesResource(provider))
+ self.putChild('tags', TagsResource(self._services_factory))
+ self.putChild('mails', MailsResource(self._services_factory))
+ self.putChild('mail', MailResource(self._services_factory))
+ self.putChild('feedback', FeedbackResource(self._services_factory))
+ self.putChild('user-settings', UserSettingsResource(self._services_factory))
+ self.putChild('users', UsersResource(self._services_factory))
+ self.putChild(LoginResource.BASE_URL,
+ LoginResource(self._services_factory, provider, disclaimer_banner=disclaimer_banner, authenticator=authenticator))
+ self.putChild(LogoutResource.BASE_URL, LogoutResource(self._services_factory))
+
+ self._inbox_resource.initialize()
self._mode = MODE_RUNNING
+ def _get_assets_folder(self):
+ pixelated_path = os.path.dirname(os.path.abspath(pixelated.__file__))
+ return os.path.join(pixelated_path, '..', '..', 'web-ui', 'public')
+
# TODO: use the public folder for this
def _get_startup_folder(self):
path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(path, '..', 'assets')
- def _get_public_folder(self):
- public_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "public"))
- # this is a workaround for packaging
- if not os.path.exists(public_folder):
- public_folder = os.path.abspath(
- os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "public"))
- if not os.path.exists(public_folder):
- # TODO: how is this packaged?
- public_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
- return public_folder
-
def _get_static_folder(self):
static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app"))
# this is a workaround for packaging
@@ -146,30 +162,3 @@ class RootResource(BaseResource):
if not os.path.exists(static_folder):
static_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
return static_folder
-
- def _is_starting(self):
- return self._mode == MODE_STARTUP
-
- def _add_csrf_cookie(self, request):
- csrf_token = hashlib.sha256(os.urandom(CSRF_TOKEN_LENGTH)).hexdigest()
- request.addCookie('XSRF-TOKEN', csrf_token)
-
- def render_GET(self, request):
- self._add_csrf_cookie(request)
- if self._is_starting():
- return self.interstitial
- else:
- account_email = self.mail_service(request).account_email
- response = Template(self._html_template).safe_substitute(account_email=account_email)
- return str(response)
-
-
-class ChildResourcesMap(object):
- def __init__(self):
- self._registry = {}
-
- def add(self, path, resource):
- self._registry[path] = resource
-
- def get(self, path):
- return self._registry.get(path) or NoResource()
diff --git a/service/test/unit/resources/test_root_resource.py b/service/test/unit/resources/test_root_resource.py
index 9b3042a8..2c74d7b9 100644
--- a/service/test/unit/resources/test_root_resource.py
+++ b/service/test/unit/resources/test_root_resource.py
@@ -14,39 +14,7 @@ from twisted.trial import unittest
from twisted.web.resource import IResource
from twisted.web.static import File
from twisted.web.test.requesthelper import DummyRequest
-from pixelated.resources.root_resource import PublicRootResource, RootResource, MODE_STARTUP, MODE_RUNNING
-
-
-class TestPublicRootResource(unittest.TestCase):
-
- def setUp(self):
- self.portal_mock = mock()
- assets_path = os.path.abspath(
- os.path.join(os.path.abspath(pixelated.__file__), '..', '..', '..', 'web-ui', 'public')
- )
- services_factory = mock()
- self.public_root_resource = PublicRootResource(services_factory, assets_path=assets_path, provider=mock())
- self.web = DummySite(self.public_root_resource)
-
- def test_assets_should_be_available(self):
- request = DummyRequest(['assets', 'dummy.json'])
- d = self.web.get(request)
-
- def assert_response(_):
- self.assertEqual(200, request.responseCode)
-
- d.addCallback(assert_response)
- return d
-
- def test_login_should_be_available(self):
- request = DummyRequest(['login'])
- d = self.web.get(request)
-
- def assert_response(_):
- self.assertEqual(200, request.responseCode)
-
- d.addCallback(assert_response)
- return d
+from pixelated.resources.root_resource import InboxResource, RootResource, MODE_STARTUP, MODE_RUNNING
class TestRootResource(unittest.TestCase):
@@ -63,12 +31,13 @@ class TestRootResource(unittest.TestCase):
self.mail_service.account_email = self.MAIL_ADDRESS
root_resource = RootResource(self.services_factory)
- root_resource._html_template = "<html><head><title>$account_email</title></head></html>"
- root_resource._mode = root_resource
self.web = DummySite(root_resource)
self.root_resource = root_resource
def test_render_GET_should_template_account_email(self):
+ self.root_resource._inbox_resource._html_template = "<html><head><title>$account_email</title></head></html>"
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
request = DummyRequest([''])
request.addCookie = lambda key, value: 'stubbed'
@@ -126,6 +95,8 @@ class TestRootResource(unittest.TestCase):
request.requestHeaders.setRawHeaders('x-xsrf-token', [csrf_token])
def test_should_unauthorize_child_resource_ajax_requests_when_csrf_mismatch(self):
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
request = DummyRequest(['/child'])
request.method = 'POST'
self._mock_ajax_csrf(request, 'stubbed csrf token')
@@ -141,10 +112,25 @@ class TestRootResource(unittest.TestCase):
d.addCallback(assert_unauthorized)
return d
+ def test_GET_should_return_503_for_uninitialized_resource(self):
+ request = DummyRequest(['/sandbox/'])
+ request.method = 'GET'
+
+ request.getCookie = MagicMock(return_value='stubbed csrf token')
+
+ d = self.web.get(request)
+
+ def assert_unavailable(_):
+ self.assertEqual(503, request.responseCode)
+
+ d.addCallback(assert_unavailable)
+ return d
+
def test_GET_should_return_404_for_non_existing_resource(self):
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
request = DummyRequest(['/non-existing-child'])
request.method = 'GET'
-
request.getCookie = MagicMock(return_value='stubbed csrf token')
d = self.web.get(request)
@@ -156,10 +142,11 @@ class TestRootResource(unittest.TestCase):
return d
def test_should_404_non_existing_resource_with_valid_csrf(self):
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
request = DummyRequest(['/non-existing-child'])
request.method = 'POST'
self._mock_ajax_csrf(request, 'stubbed csrf token')
-
request.getCookie = MagicMock(return_value='stubbed csrf token')
d = self.web.get(request)
@@ -175,7 +162,7 @@ class TestRootResource(unittest.TestCase):
request = DummyRequest(['features'])
request.getCookie = MagicMock(return_value='irrelevant -- stubbed')
- self.root_resource._child_resources.add('features', FeaturesResource())
+ self.root_resource.putChild('features', FeaturesResource())
self.root_resource._mode = MODE_RUNNING
d = self.web.get(request)
@@ -187,6 +174,8 @@ class TestRootResource(unittest.TestCase):
return d
def test_should_unauthorize_child_resource_non_ajax_POST_requests_when_csrf_input_mismatch(self):
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
request = DummyRequest(['mails'])
request.method = 'POST'
request.addArg('csrftoken', 'some csrf token')
@@ -204,3 +193,45 @@ class TestRootResource(unittest.TestCase):
d.addCallback(assert_unauthorized)
return d
+
+ def test_assets_should_be_publicly_available(self):
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
+ request = DummyRequest(['assets', 'dummy.json'])
+ d = self.web.get(request)
+
+ def assert_response(_):
+ self.assertEqual(200, request.responseCode)
+
+ d.addCallback(assert_response)
+ return d
+
+ def test_login_should_be_publicly_available(self):
+ self.root_resource.initialize(provider=mock(), authenticator=mock())
+
+ request = DummyRequest(['login'])
+ d = self.web.get(request)
+
+ def assert_response(_):
+ self.assertEqual(200, request.responseCode)
+
+ d.addCallback(assert_response)
+ return d
+
+ def test_root_should_be_handled_by_inbox_resource(self):
+ request = DummyRequest([])
+ request.prepath = ['']
+ request.path = '/'
+ # TODO: setup mocked portal
+
+ resource = self.root_resource.getChildWithDefault(request.prepath[-1], request)
+ self.assertIsInstance(resource, InboxResource)
+
+ def test_inbox_should_not_be_public(self):
+ request = DummyRequest([])
+ request.prepath = ['']
+ request.path = '/'
+ # TODO: setup mocked portal
+
+ resource = self.root_resource.getChildWithDefault(request.prepath[-1], request)
+ self.assertIsInstance(resource, InboxResource)