summaryrefslogtreecommitdiff
path: root/service/pixelated/resources
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2017-07-25 11:40:11 -0400
committerKali Kaneko <kali@leap.se>2017-07-25 11:40:29 -0400
commit91e4481c450eb7eb928debc1cb7fa59bdb63dd7b (patch)
tree8fd7e6e77b6df669c33d96b7edad6db3cbe14dfe /service/pixelated/resources
parente4f755309d4cf5cfb6b0bcc62ed73d6070956ab5 (diff)
[pkg] packaging and path changes
- move all the pixelated python package under src/ - move the pixelated_www package under the leap namespace - allow to set globally the static folder - add hours and minutes to the timestamp in package version, to allow for several releases a day.
Diffstat (limited to 'service/pixelated/resources')
-rw-r--r--service/pixelated/resources/__init__.py148
-rw-r--r--service/pixelated/resources/account_recovery_resource.py87
-rw-r--r--service/pixelated/resources/attachments_resource.py110
-rw-r--r--service/pixelated/resources/auth.py117
-rw-r--r--service/pixelated/resources/backup_account_resource.py79
-rw-r--r--service/pixelated/resources/contacts_resource.py44
-rw-r--r--service/pixelated/resources/features_resource.py46
-rw-r--r--service/pixelated/resources/feedback_resource.py31
-rw-r--r--service/pixelated/resources/keys_resource.py46
-rw-r--r--service/pixelated/resources/login_resource.py173
-rw-r--r--service/pixelated/resources/logout_resource.py45
-rw-r--r--service/pixelated/resources/mail_resource.py92
-rw-r--r--service/pixelated/resources/mails_resource.py244
-rw-r--r--service/pixelated/resources/root_resource.py139
-rw-r--r--service/pixelated/resources/sandbox_resource.py37
-rw-r--r--service/pixelated/resources/session.py55
-rw-r--r--service/pixelated/resources/tags_resource.py38
-rw-r--r--service/pixelated/resources/user_settings_resource.py43
-rw-r--r--service/pixelated/resources/users.py30
19 files changed, 0 insertions, 1604 deletions
diff --git a/service/pixelated/resources/__init__.py b/service/pixelated/resources/__init__.py
deleted file mode 100644
index 9dae4a61..00000000
--- a/service/pixelated/resources/__init__.py
+++ /dev/null
@@ -1,148 +0,0 @@
-#
-# 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 json
-import os
-
-from twisted.web.http import UNAUTHORIZED
-from twisted.web.resource import Resource
-from twisted.logger import Logger
-
-from pixelated.resources.session import IPixelatedSession
-
-from twisted.web.http import INTERNAL_SERVER_ERROR, SERVICE_UNAVAILABLE
-
-log = Logger()
-
-
-class SetEncoder(json.JSONEncoder):
- def default(self, obj):
- if isinstance(obj, set):
- return list(obj)
- return super(SetEncoder, self).default(obj)
-
-
-def respond_json(entity, request, status_code=200):
- json_response = json.dumps(entity, cls=SetEncoder)
- request.responseHeaders.setRawHeaders(b"content-type", [b"application/json"])
- request.code = status_code
- return json_response
-
-
-def respond_json_deferred(entity, request, status_code=200):
- json_response = json.dumps(entity, cls=SetEncoder)
- request.responseHeaders.setRawHeaders(b"content-type", [b"application/json"])
- request.code = status_code
- request.write(json_response)
- request.finish()
-
-
-def handle_error_deferred(e, request):
- log.error(e)
- request.setResponseCode(INTERNAL_SERVER_ERROR)
- request.write('Something went wrong!')
- request.finish()
-
-
-def get_protected_static_folder(static_folder=None):
- static = static_folder or _get_static_folder()
- return os.path.join(static, 'protected')
-
-
-def get_public_static_folder(static_folder=None):
- static = static_folder or _get_static_folder()
- return os.path.join(static, 'public')
-
-
-def _get_static_folder():
- static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "dist"))
- if not os.path.exists(static_folder):
- static_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
- return static_folder
-
-
-class BaseResource(Resource):
-
- def __init__(self, services_factory):
- Resource.__init__(self)
- self._services_factory = services_factory
-
- def _get_user_id_from_request(self, request):
- if self._services_factory.mode.is_single_user:
- return None # it doesn't matter
- session = self.get_session(request)
- if session.is_logged_in():
- return session.user_uuid
- raise ValueError('Not logged in')
-
- def is_logged_in(self, request):
- session = self.get_session(request)
- return session.is_logged_in() and self._services_factory.has_session(session.user_uuid)
-
- def get_session(self, request):
- return IPixelatedSession(request.getSession())
-
- def is_admin(self, request):
- services = self._services(request)
- return services._leap_session.user_auth.is_admin()
-
- def _services(self, request):
- user_id = self._get_user_id_from_request(request)
- return self._services_factory.services(user_id)
-
- def _service(self, request, attribute):
- return getattr(self._services(request), attribute)
-
- def keymanager(self, request):
- return self._service(request, 'keymanager')
-
- def mail_service(self, request):
- return self._service(request, 'mail_service')
-
- def search_engine(self, request):
- return self._service(request, 'search_engine')
-
- def draft_service(self, request):
- return self._service(request, 'draft_service')
-
- def feedback_service(self, request):
- return self._service(request, 'feedback_service')
-
- def soledad(self, request):
- return self._service(request, '_leap_session').soledad
-
-
-class UnAuthorizedResource(Resource):
-
- def __init__(self):
- Resource.__init__(self)
-
- def render_GET(self, request):
- request.setResponseCode(UNAUTHORIZED)
- return "Unauthorized!"
-
- def render_POST(self, request):
- request.setResponseCode(UNAUTHORIZED)
- return "Unauthorized!"
-
-
-class UnavailableResource(Resource):
- def __init__(self):
- Resource.__init__(self)
-
- def render(self, request):
- request.setResponseCode(SERVICE_UNAVAILABLE)
- return "Service Unavailable"
diff --git a/service/pixelated/resources/account_recovery_resource.py b/service/pixelated/resources/account_recovery_resource.py
deleted file mode 100644
index 209a7693..00000000
--- a/service/pixelated/resources/account_recovery_resource.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#
-# Copyright (c) 2017 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 os
-import json
-
-from twisted.python.filepath import FilePath
-from twisted.web.http import OK, INTERNAL_SERVER_ERROR
-from twisted.web.template import Element, XMLFile, renderElement
-from twisted.web.server import NOT_DONE_YET
-from twisted.internet import defer
-from twisted.logger import Logger
-
-from pixelated.resources import BaseResource
-from pixelated.resources import get_public_static_folder
-
-log = Logger()
-
-
-class InvalidPasswordError(Exception):
- pass
-
-
-class AccountRecoveryPage(Element):
- loader = XMLFile(FilePath(os.path.join(get_public_static_folder(), 'account_recovery.html')))
-
- def __init__(self):
- super(AccountRecoveryPage, self).__init__()
-
-
-class AccountRecoveryResource(BaseResource):
- BASE_URL = 'account-recovery'
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- request.setResponseCode(OK)
- return self._render_template(request)
-
- def _render_template(self, request):
- site = AccountRecoveryPage()
- return renderElement(request, site)
-
- def render_POST(self, request):
- def success_response(response):
- request.setResponseCode(OK)
- request.finish()
-
- def error_response(failure):
- log.warn(failure)
- request.setResponseCode(INTERNAL_SERVER_ERROR)
- request.finish()
-
- d = self._handle_post(request)
- d.addCallbacks(success_response, error_response)
- return NOT_DONE_YET
-
- def _get_post_form(self, request):
- return json.loads(request.content.getvalue())
-
- def _validate_password(self, password, confirm_password):
- return password == confirm_password and len(password) >= 8 and len(password) <= 9999
-
- def _handle_post(self, request):
- form = self._get_post_form(request)
- password = form.get('password')
- confirm_password = form.get('confirmPassword')
-
- if not self._validate_password(password, confirm_password):
- return defer.fail(InvalidPasswordError('The user entered an invalid password or confirmation'))
-
- return defer.succeed('Done!')
diff --git a/service/pixelated/resources/attachments_resource.py b/service/pixelated/resources/attachments_resource.py
deleted file mode 100644
index 1081b4b8..00000000
--- a/service/pixelated/resources/attachments_resource.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# 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 cgi
-import io
-import re
-
-from twisted.internet import defer
-from twisted.protocols.basic import FileSender
-from twisted.python.log import msg
-from twisted.web import server
-from twisted.web.resource import Resource
-from twisted.web.server import NOT_DONE_YET
-from twisted.logger import Logger
-
-from pixelated.resources import respond_json_deferred, BaseResource
-
-
-logger = Logger()
-
-
-class AttachmentResource(Resource):
- isLeaf = True
-
- def __init__(self, mail_service, attachment_id):
- Resource.__init__(self)
- self.attachment_id = attachment_id
- self.mail_service = mail_service
-
- def render_GET(self, request):
- def error_handler(failure):
- msg(failure, 'attachment not found')
- request.code = 404
- request.finish()
-
- encoding = request.args.get('encoding', [None])[0]
- filename = request.args.get('filename', [self.attachment_id])[0]
- content_type = request.args.get('content_type', ['application/octet-stream'])[0]
- request.setHeader(b'Content-Type', content_type)
- request.setHeader(b'Content-Disposition', bytes('attachment; filename="' + filename + '"'))
-
- d = self._send_attachment(encoding, filename, request)
- d.addErrback(error_handler)
-
- return server.NOT_DONE_YET
-
- @defer.inlineCallbacks
- def _send_attachment(self, encoding, filename, request):
- attachment = yield self.mail_service.attachment(self.attachment_id)
-
- bytes_io = io.BytesIO(attachment['content'])
-
- try:
- request.code = 200
- yield FileSender().beginFileTransfer(bytes_io, request)
- finally:
- bytes_io.close()
- request.finish()
-
- def _extract_mimetype(self, content_type):
- match = re.compile('([A-Za-z-]+\/[A-Za-z-]+)').search(content_type)
- return match.group(1)
-
-
-class AttachmentsResource(BaseResource):
- BASE_URL = 'attachment'
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def getChild(self, attachment_id, request):
- _mail_service = self.mail_service(request)
- return AttachmentResource(_mail_service, attachment_id)
-
- def render_POST(self, request):
- _mail_service = self.mail_service(request)
- fields = cgi.FieldStorage(fp=request.content, headers=(request.getAllHeaders()),
- environ={'REQUEST_METHOD': 'POST'})
- _file = fields['attachment']
- deferred = _mail_service.save_attachment(_file.value, _file.type)
-
- def send_location(attachment_id):
- request.setHeader('Location', '/%s/%s' % (self.BASE_URL, attachment_id))
- response_json = {"ident": attachment_id,
- "content-type": _file.type,
- "encoding": "base64", # hard coded for now -- not really used
- "name": _file.filename,
- "size": len(_file.value)}
- respond_json_deferred(response_json, request, status_code=201)
-
- def error_handler(error):
- logger.error(error)
- respond_json_deferred({"message": "Something went wrong. Attachment not saved."}, request, status_code=500)
-
- deferred.addCallback(send_location)
- deferred.addErrback(error_handler)
-
- return NOT_DONE_YET
diff --git a/service/pixelated/resources/auth.py b/service/pixelated/resources/auth.py
deleted file mode 100644
index adac985f..00000000
--- a/service/pixelated/resources/auth.py
+++ /dev/null
@@ -1,117 +0,0 @@
-#
-# Copyright (c) 2016 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 re
-
-from pixelated.resources import IPixelatedSession
-from twisted.cred import error
-from twisted.cred import portal, checkers
-from twisted.cred.checkers import ANONYMOUS
-from twisted.cred.credentials import ICredentials
-from twisted.internet import defer
-from twisted.logger import Logger
-from twisted.web import util
-from twisted.web._auth.wrapper import UnauthorizedResource
-from twisted.web.error import UnsupportedMethod
-from twisted.web.resource import IResource, ErrorPage
-from zope.interface import implements, implementer, Attribute
-
-
-log = Logger()
-
-
-class ISessionCredential(ICredentials):
-
- request = Attribute('the current request')
-
-
-@implementer(ISessionCredential)
-class SessionCredential(object):
- def __init__(self, request):
- self.request = request
-
-
-@implementer(checkers.ICredentialsChecker)
-class SessionChecker(object):
- credentialInterfaces = (ISessionCredential,)
-
- def __init__(self, services_factory):
- self._services_factory = services_factory
-
- def requestAvatarId(self, credentials):
- session = self.get_session(credentials.request)
- if session.is_logged_in() and self._services_factory.has_session(session.user_uuid):
- return defer.succeed(session.user_uuid)
- return defer.succeed(ANONYMOUS)
-
- def get_session(self, request):
- return IPixelatedSession(request.getSession())
-
-
-class PixelatedRealm(object):
- implements(portal.IRealm)
-
- def requestAvatar(self, avatarId, mind, *interfaces):
- if IResource in interfaces:
- return IResource, avatarId, lambda: None
- raise NotImplementedError()
-
-
-@implementer(IResource)
-class PixelatedAuthSessionWrapper(object):
-
- isLeaf = False
-
- def __init__(self, portal, root_resource, anonymous_resource, credentialFactories):
- self._portal = portal
- self._credentialFactories = credentialFactories
- self._root_resource = root_resource
- self._anonymous_resource = anonymous_resource
-
- def render(self, request):
- raise UnsupportedMethod(())
-
- def getChildWithDefault(self, path, request):
- request.postpath.insert(0, request.prepath.pop())
- return self._authorizedResource(request)
-
- def _authorizedResource(self, request):
- creds = SessionCredential(request)
- return util.DeferredResource(self._login(creds, request))
-
- def _login(self, credentials, request):
- pattern = re.compile("^/sandbox/")
-
- def loginSucceeded(args):
- interface, avatar, logout = args
- if avatar == checkers.ANONYMOUS and not pattern.match(request.path):
- return self._anonymous_resource
- else:
- return self._root_resource
-
- def loginFailed(result):
- if result.check(error.Unauthorized, error.LoginFailed):
- return UnauthorizedResource(self._credentialFactories)
- else:
- log.err(
- result,
- "HTTPAuthSessionWrapper.getChildWithDefault encountered "
- "unexpected error")
- return ErrorPage(500, None, None)
-
- d = self._portal.login(credentials, None, IResource)
- d.addCallbacks(loginSucceeded, loginFailed)
- return d
diff --git a/service/pixelated/resources/backup_account_resource.py b/service/pixelated/resources/backup_account_resource.py
deleted file mode 100644
index 94129122..00000000
--- a/service/pixelated/resources/backup_account_resource.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#
-# Copyright (c) 2017 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 os
-import json
-
-from twisted.python.filepath import FilePath
-from twisted.web.http import OK, NO_CONTENT, INTERNAL_SERVER_ERROR
-from twisted.web.server import NOT_DONE_YET
-from twisted.web.template import Element, XMLFile, renderElement
-
-from pixelated.resources import BaseResource
-from pixelated.resources import get_protected_static_folder
-from pixelated.account_recovery import AccountRecovery
-from pixelated.support.language import parse_accept_language
-
-
-class BackupAccountPage(Element):
- loader = XMLFile(FilePath(os.path.join(get_protected_static_folder(), 'backup_account.html')))
-
- def __init__(self):
- super(BackupAccountPage, self).__init__()
-
-
-class BackupAccountResource(BaseResource):
- isLeaf = True
-
- def __init__(self, services_factory, authenticator, leap_provider):
- BaseResource.__init__(self, services_factory)
- self._authenticator = authenticator
- self._leap_provider = leap_provider
-
- def render_GET(self, request):
- request.setResponseCode(OK)
- return self._render_template(request)
-
- def _render_template(self, request):
- site = BackupAccountPage()
- return renderElement(request, site)
-
- def render_POST(self, request):
- account_recovery = AccountRecovery(
- self._authenticator.bonafide_session,
- self.soledad(request),
- self._service(request, '_leap_session').smtp_config,
- self._get_backup_email(request),
- self._leap_provider.server_name,
- language=self._get_language(request))
-
- def update_response(response):
- request.setResponseCode(NO_CONTENT)
- request.finish()
-
- def error_response(response):
- request.setResponseCode(INTERNAL_SERVER_ERROR)
- request.finish()
-
- d = account_recovery.update_recovery_code()
- d.addCallbacks(update_response, error_response)
- return NOT_DONE_YET
-
- def _get_backup_email(self, request):
- return json.loads(request.content.getvalue()).get('backupEmail')
-
- def _get_language(self, request):
- return parse_accept_language(request.getAllHeaders())
diff --git a/service/pixelated/resources/contacts_resource.py b/service/pixelated/resources/contacts_resource.py
deleted file mode 100644
index dc17d1ac..00000000
--- a/service/pixelated/resources/contacts_resource.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# 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 pixelated.resources import respond_json_deferred, BaseResource
-from twisted.internet.threads import deferToThread
-from twisted.web import server
-from twisted.web.resource import Resource
-
-
-class ContactsResource(BaseResource):
-
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- _search_engine = self.search_engine(request)
- query = request.args.get('q', [''])[-1]
- d = deferToThread(lambda: _search_engine.contacts(query))
- d.addCallback(lambda tags: respond_json_deferred(tags, request))
-
- def handle_error(error):
- print 'Something went wrong'
- import traceback
- traceback.print_exc()
- print error
-
- d.addErrback(handle_error)
-
- return server.NOT_DONE_YET
diff --git a/service/pixelated/resources/features_resource.py b/service/pixelated/resources/features_resource.py
deleted file mode 100644
index c1b61f12..00000000
--- a/service/pixelated/resources/features_resource.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# 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 pixelated.resources import respond_json
-import os
-from twisted.web.resource import Resource
-
-from pixelated.resources.logout_resource import LogoutResource
-
-
-class FeaturesResource(Resource):
- DISABLED_FEATURES = ['draftReply']
- isLeaf = True
-
- def __init__(self, multi_user=False):
- Resource.__init__(self)
- self._multi_user = multi_user
-
- def render_GET(self, request):
- disabled_features = self._disabled_features()
- features = {'disabled_features': disabled_features}
- self._add_multi_user_to(features)
- return respond_json(features, request)
-
- def _disabled_features(self):
- disabled_features = [default_disabled_feature for default_disabled_feature in self.DISABLED_FEATURES]
- if not os.environ.get('FEEDBACK_URL'):
- disabled_features.append('feedback')
- return disabled_features
-
- def _add_multi_user_to(self, features):
- if self._multi_user:
- features.update({'multi_user': {'logout': LogoutResource.BASE_URL}})
diff --git a/service/pixelated/resources/feedback_resource.py b/service/pixelated/resources/feedback_resource.py
deleted file mode 100644
index aeead401..00000000
--- a/service/pixelated/resources/feedback_resource.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (c) 2015 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 json
-
-from pixelated.resources import respond_json, BaseResource
-
-
-class FeedbackResource(BaseResource):
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_POST(self, request):
- _feedback_service = self.feedback_service(request)
- feedback = json.loads(request.content.read()).get('feedback')
- _feedback_service.open_ticket(feedback)
- return respond_json({}, request)
diff --git a/service/pixelated/resources/keys_resource.py b/service/pixelated/resources/keys_resource.py
deleted file mode 100644
index 091c27d0..00000000
--- a/service/pixelated/resources/keys_resource.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Copyright (c) 2015 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 email.utils import parseaddr
-from pixelated.resources import respond_json_deferred, BaseResource
-from twisted.web import server
-
-
-class KeysResource(BaseResource):
-
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- _keymanager = self.keymanager(request)
-
- def finish_request(key):
- if key.private:
- respond_json_deferred(None, request, status_code=401)
- else:
- respond_json_deferred(key.get_active_json(), request)
-
- def key_not_found(_):
- respond_json_deferred(None, request, status_code=404)
-
- _, key_to_find = parseaddr(request.args.get('search')[0])
- d = _keymanager.get_key(key_to_find)
- d.addCallback(finish_request)
- d.addErrback(key_not_found)
-
- return server.NOT_DONE_YET
diff --git a/service/pixelated/resources/login_resource.py b/service/pixelated/resources/login_resource.py
deleted file mode 100644
index 5b0b70d0..00000000
--- a/service/pixelated/resources/login_resource.py
+++ /dev/null
@@ -1,173 +0,0 @@
-#
-# Copyright (c) 2016 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 os
-from xml.sax import SAXParseException
-
-from pixelated.authentication import Authenticator
-from pixelated.config.leap import BootstrapUserServices
-from pixelated.resources import BaseResource, UnAuthorizedResource, IPixelatedSession
-from pixelated.resources.account_recovery_resource import AccountRecoveryResource
-from pixelated.resources import get_public_static_folder, respond_json
-from pixelated.support.language import parse_accept_language
-
-from twisted.cred.error import UnauthorizedLogin
-from twisted.internet import defer
-from twisted.logger import Logger
-from twisted.python.filepath import FilePath
-from twisted.web import util
-from twisted.web.http import UNAUTHORIZED, OK
-from twisted.web.resource import NoResource
-from twisted.web.server import NOT_DONE_YET
-from twisted.web.static import File
-from twisted.web.template import Element, XMLFile, renderElement, renderer
-
-log = Logger()
-
-
-class DisclaimerElement(Element):
- loader = XMLFile(FilePath(os.path.join(get_public_static_folder(), '_login_disclaimer_banner.html')))
-
- def __init__(self, banner):
- super(DisclaimerElement, self).__init__()
- self._set_loader(banner)
- self._banner_filename = banner or "_login_disclaimer_banner.html"
-
- def _set_loader(self, banner):
- if banner:
- current_path = os.path.dirname(os.path.abspath(__file__))
- banner_file_path = os.path.join(current_path, "..", "..", "..", banner)
- self.loader = XMLFile(FilePath(banner_file_path))
-
- def render(self, request):
- try:
- return super(DisclaimerElement, self).render(request)
- except SAXParseException:
- return ["Invalid XML template format for %s." % self._banner_filename]
- except IOError:
- return ["Disclaimer banner file %s could not be read or does not exit." % self._banner_filename]
-
-
-class LoginWebSite(Element):
- loader = XMLFile(FilePath(os.path.join(get_public_static_folder(), 'login.html')))
-
- def __init__(self, disclaimer_banner_file=None):
- super(LoginWebSite, self).__init__()
- self.disclaimer_banner_file = disclaimer_banner_file
-
- @renderer
- def disclaimer(self, request, tag):
- return DisclaimerElement(self.disclaimer_banner_file).render(request)
-
-
-class LoginResource(BaseResource):
- BASE_URL = 'login'
-
- def __init__(self, services_factory, provider=None, disclaimer_banner=None, authenticator=None):
- BaseResource.__init__(self, services_factory)
- self._disclaimer_banner = disclaimer_banner
- self._provider = provider
- self._authenticator = authenticator
- self._bootstrap_user_services = BootstrapUserServices(services_factory, provider)
-
- static_folder = get_public_static_folder()
- self.putChild('public', File(static_folder))
- with open(os.path.join(static_folder, 'interstitial.html')) as f:
- self.interstitial = f.read()
-
- def getChild(self, path, request):
- if path == '':
- return self
- if path == 'login':
- return self
- if path == 'status':
- return LoginStatusResource(self._services_factory)
- if path == AccountRecoveryResource.BASE_URL:
- return AccountRecoveryResource(self._services_factory)
- if not self.is_logged_in(request):
- return UnAuthorizedResource()
- return NoResource()
-
- def render_GET(self, request):
- request.setResponseCode(OK)
- return self._render_template(request)
-
- def _render_template(self, request):
- site = LoginWebSite(disclaimer_banner_file=self._disclaimer_banner)
- return renderElement(request, site)
-
- def render_POST(self, request):
- if self.is_logged_in(request):
- return util.redirectTo("/", request)
-
- def render_response(user_auth):
- request.setResponseCode(OK)
- request.write(self.interstitial)
- request.finish()
- self._complete_bootstrap(user_auth, request)
-
- def render_error(error):
- if error.type is UnauthorizedLogin:
- log.info('Unauthorized login for %s. User typed wrong username/password combination.' % request.args['username'][0])
- else:
- log.error('Authentication error for %s' % request.args['username'][0])
- log.error('%s' % error)
- request.setResponseCode(UNAUTHORIZED)
- content = util.redirectTo("/login?auth-error", request)
- request.write(content)
- request.finish()
-
- d = self._handle_login(request)
- d.addCallbacks(render_response, render_error)
- return NOT_DONE_YET
-
- @defer.inlineCallbacks
- def _handle_login(self, request):
- username = request.args['username'][0]
- password = request.args['password'][0]
- user_auth = yield self._authenticator.authenticate(username, password)
- defer.returnValue(user_auth)
-
- def _complete_bootstrap(self, user_auth, request):
- def login_error(error, session):
- log.error('Login error during %s services setup: %s \n %s' % (user_auth.username, error.getErrorMessage(), error.getTraceback()))
- session.login_error()
-
- def login_successful(_, session):
- session.login_successful(user_auth.uuid)
-
- language = parse_accept_language(request.getAllHeaders())
- password = request.args['password'][0]
- session = IPixelatedSession(request.getSession())
- session.login_started()
-
- d = self._bootstrap_user_services.setup(user_auth, password, language)
- d.addCallback(login_successful, session)
- d.addErrback(login_error, session)
-
-
-class LoginStatusResource(BaseResource):
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- session = IPixelatedSession(request.getSession())
- status = 'completed' if self._services_factory.mode.is_single_user else str(session.check_login_status())
-
- response = {'status': status}
- return respond_json(response, request)
diff --git a/service/pixelated/resources/logout_resource.py b/service/pixelated/resources/logout_resource.py
deleted file mode 100644
index a4fe584f..00000000
--- a/service/pixelated/resources/logout_resource.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# 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 twisted.internet import defer
-from twisted.web import util
-from twisted.web.server import NOT_DONE_YET
-
-from pixelated.resources import BaseResource, handle_error_deferred
-from pixelated.resources.login_resource import LoginResource
-
-
-class LogoutResource(BaseResource):
- BASE_URL = "logout"
- isLeaf = True
-
- @defer.inlineCallbacks
- def _execute_logout(self, request):
- http_session = self.get_session(request)
- yield self._services_factory.destroy_session(http_session.user_uuid)
- http_session.expire()
-
- def render_POST(self, request):
- def _redirect_to_login(_):
- content = util.redirectTo("/%s" % LoginResource.BASE_URL, request)
- request.write(content)
- request.finish()
-
- d = self._execute_logout(request)
- d.addCallback(_redirect_to_login)
- d.addErrback(handle_error_deferred, request)
-
- return NOT_DONE_YET
diff --git a/service/pixelated/resources/mail_resource.py b/service/pixelated/resources/mail_resource.py
deleted file mode 100644
index e1ba6087..00000000
--- a/service/pixelated/resources/mail_resource.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#
-# Copyright (c) 2015 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 json
-
-from twisted.python.log import err
-from twisted.web.resource import Resource
-from twisted.web.server import NOT_DONE_YET
-
-from pixelated.resources import respond_json_deferred, BaseResource, handle_error_deferred
-from pixelated.support import replier
-
-
-class MailTags(Resource):
-
- isLeaf = True
-
- def __init__(self, mail_id, mail_service):
- Resource.__init__(self)
- self._mail_service = mail_service
- self._mail_id = mail_id
-
- def render_POST(self, request):
- new_tags = json.loads(request.content.read()).get('newtags')
-
- d = self._mail_service.update_tags(self._mail_id, new_tags)
- d.addCallback(lambda mail: respond_json_deferred(mail.as_dict(), request))
-
- def handle403(failure):
- failure.trap(ValueError)
- return respond_json_deferred(failure.getErrorMessage(), request, 403)
- d.addErrback(handle403)
- return NOT_DONE_YET
-
-
-class Mail(Resource):
-
- def __init__(self, mail_id, mail_service):
- Resource.__init__(self)
- self.putChild('tags', MailTags(mail_id, mail_service))
- self._mail_id = mail_id
- self._mail_service = mail_service
-
- def render_GET(self, request):
- def populate_reply(mail):
- mail_dict = mail.as_dict()
- current_user = self._mail_service.account_email
- sender = mail.headers.get('Reply-to', mail.headers.get('From'))
- to = mail.headers.get('To', [])
- ccs = mail.headers.get('Cc', [])
- mail_dict['replying'] = replier.generate_recipients(sender, to, ccs, current_user)
- return mail_dict
-
- d = self._mail_service.mail(self._mail_id)
- d.addCallback(lambda mail: populate_reply(mail))
- d.addCallback(lambda mail_dict: respond_json_deferred(mail_dict, request))
- d.addErrback(handle_error_deferred, request)
-
- return NOT_DONE_YET
-
- def render_DELETE(self, request):
- def response_failed(failure):
- err(failure, 'something failed')
- request.finish()
-
- d = self._mail_service.delete_mail(self._mail_id)
- d.addCallback(lambda _: respond_json_deferred(None, request))
- d.addErrback(response_failed)
- return NOT_DONE_YET
-
-
-class MailResource(BaseResource):
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def getChild(self, mail_id, request):
- _mail_service = self.mail_service(request)
- return Mail(mail_id, _mail_service)
diff --git a/service/pixelated/resources/mails_resource.py b/service/pixelated/resources/mails_resource.py
deleted file mode 100644
index d911e0d2..00000000
--- a/service/pixelated/resources/mails_resource.py
+++ /dev/null
@@ -1,244 +0,0 @@
-#
-# Copyright (c) 2015 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 time
-import json
-
-from twisted.internet import defer
-from twisted.logger import Logger
-from twisted.web.server import NOT_DONE_YET
-from twisted.web.resource import Resource
-from twisted.web import server
-
-from leap.common import events
-
-from pixelated.adapter.model.mail import InputMail
-from pixelated.resources import respond_json_deferred, BaseResource
-from pixelated.adapter.services.mail_sender import SMTPDownException
-from pixelated.support.functional import to_unicode
-
-
-log = Logger()
-
-
-class MailsUnreadResource(Resource):
- isLeaf = True
-
- def __init__(self, mail_service):
- Resource.__init__(self)
- self._mail_service = mail_service
-
- def render_POST(self, request):
- idents = json.load(request.content).get('idents')
- deferreds = []
- for ident in idents:
- deferreds.append(self._mail_service.mark_as_unread(ident))
-
- d = defer.gatherResults(deferreds, consumeErrors=True)
- d.addCallback(lambda _: respond_json_deferred(None, request))
- d.addErrback(lambda _: respond_json_deferred(None, request, status_code=500))
-
- return NOT_DONE_YET
-
-
-class MailsReadResource(Resource):
- isLeaf = True
-
- def __init__(self, mail_service):
- Resource.__init__(self)
- self._mail_service = mail_service
-
- def render_POST(self, request):
- idents = json.load(request.content).get('idents')
- deferreds = []
- for ident in idents:
- deferreds.append(self._mail_service.mark_as_read(ident))
-
- d = defer.gatherResults(deferreds, consumeErrors=True)
- d.addCallback(lambda _: respond_json_deferred(None, request))
- d.addErrback(lambda _: respond_json_deferred(None, request, status_code=500))
-
- return NOT_DONE_YET
-
-
-class MailsDeleteResource(Resource):
- isLeaf = True
-
- def __init__(self, mail_service):
- Resource.__init__(self)
- self._mail_service = mail_service
-
- def render_POST(self, request):
- def response_failed(failure):
- log.error('something failed: %s' % failure.getErrorMessage())
- request.finish()
-
- idents = json.loads(request.content.read())['idents']
- deferreds = []
- for ident in idents:
- deferreds.append(self._mail_service.delete_mail(ident))
-
- d = defer.gatherResults(deferreds, consumeErrors=True)
- d.addCallback(lambda _: respond_json_deferred(None, request))
- d.addErrback(response_failed)
- return NOT_DONE_YET
-
-
-class MailsRecoverResource(Resource):
- isLeaf = True
-
- def __init__(self, mail_service):
- Resource.__init__(self)
- self._mail_service = mail_service
-
- def render_POST(self, request):
- idents = json.loads(request.content.read())['idents']
- deferreds = []
- for ident in idents:
- deferreds.append(self._mail_service.recover_mail(ident))
- d = defer.gatherResults(deferreds, consumeErrors=True)
- d.addCallback(lambda _: respond_json_deferred(None, request))
- d.addErrback(lambda _: respond_json_deferred(None, request, status_code=500))
- return NOT_DONE_YET
-
-
-class MailsArchiveResource(Resource):
- isLeaf = True
-
- def __init__(self, mail_service):
- Resource.__init__(self)
- self._mail_service = mail_service
-
- def render_POST(self, request):
- idents = json.loads(request.content.read())['idents']
- deferreds = []
- for ident in idents:
- deferreds.append(self._mail_service.archive_mail(ident))
- d = defer.gatherResults(deferreds, consumeErrors=True)
- d.addCallback(lambda _: respond_json_deferred({'successMessage': 'your-message-was-archived'}, request))
- d.addErrback(lambda _: respond_json_deferred(None, request, status_code=500))
- return NOT_DONE_YET
-
-
-class MailsResource(BaseResource):
-
- def _register_smtp_error_handler(self):
-
- def on_error(event, content):
- delivery_error_mail = InputMail.delivery_error_template(delivery_address=event.content)
- self._mail_service.mailboxes.inbox.add(delivery_error_mail)
-
- events.register(events.catalog.SMTP_SEND_MESSAGE_ERROR, callback=on_error)
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
- self._register_smtp_error_handler()
-
- def getChild(self, action, request):
- _mail_service = self.mail_service(request)
-
- if action == 'delete':
- return MailsDeleteResource(_mail_service)
- if action == 'recover':
- return MailsRecoverResource(_mail_service)
- if action == 'archive':
- return MailsArchiveResource(_mail_service)
- if action == 'read':
- return MailsReadResource(_mail_service)
- if action == 'unread':
- return MailsUnreadResource(_mail_service)
-
- def _build_mails_response(self, (mails, total)):
- return {
- "stats": {
- "total": total,
- },
- "mails": [mail.as_dict() for mail in mails]
- }
-
- def render_GET(self, request):
-
- _mail_service = self.mail_service(request)
- query, window_size, page = request.args.get('q')[0], request.args.get('w')[0], request.args.get('p')[0]
- unicode_query = to_unicode(query)
- d = _mail_service.mails(unicode_query, window_size, page)
-
- d.addCallback(self._build_mails_response)
- d.addCallback(lambda res: respond_json_deferred(res, request))
-
- def error_handler(error):
- print error
-
- d.addErrback(error_handler)
-
- return NOT_DONE_YET
-
- def render_POST(self, request):
- def onError(error):
- if isinstance(error.value, SMTPDownException):
- respond_json_deferred({'message': str(error.value)}, request, status_code=503)
- else:
- log.error('error occurred while sending: %s' % error.getErrorMessage())
- respond_json_deferred({'message': 'an error occurred while sending'}, request, status_code=422)
-
- deferred = self._handle_post(request)
- deferred.addErrback(onError)
-
- return server.NOT_DONE_YET
-
- def render_PUT(self, request):
- def onError(error):
- log.error('error saving draft: %s' % error.getErrorMessage())
- respond_json_deferred("", request, status_code=422)
-
- deferred = self._handle_put(request)
- deferred.addErrback(onError)
-
- return server.NOT_DONE_YET
-
- @defer.inlineCallbacks
- def _fetch_attachment_contents(self, content_dict, _mail_service):
- attachments = content_dict.get('attachments', []) if content_dict else []
- for attachment in attachments:
- retrieved_attachment = yield _mail_service.attachment(attachment['ident'])
- attachment['raw'] = retrieved_attachment['content']
- content_dict['attachments'] = attachments
- defer.returnValue(content_dict)
-
- @defer.inlineCallbacks
- def _handle_post(self, request):
- _mail_service = self.mail_service(request)
- content_dict = json.loads(request.content.read())
- with_attachment_content = yield self._fetch_attachment_contents(content_dict, _mail_service)
-
- sent_mail = yield _mail_service.send_mail(with_attachment_content)
- respond_json_deferred(sent_mail.as_dict(), request, status_code=201)
-
- @defer.inlineCallbacks
- def _handle_put(self, request):
- _draft_service = self.draft_service(request)
- _mail_service = self.mail_service(request)
- content_dict = json.loads(request.content.read())
- with_attachment_content = yield self._fetch_attachment_contents(content_dict, _mail_service)
-
- _mail = InputMail.from_dict(with_attachment_content, from_address=_mail_service.account_email)
- draft_id = content_dict.get('ident')
- pixelated_mail = yield _draft_service.process_draft(draft_id, _mail)
-
- if not pixelated_mail:
- respond_json_deferred("", request, status_code=422)
- else:
- respond_json_deferred({'ident': pixelated_mail.ident}, request)
diff --git a/service/pixelated/resources/root_resource.py b/service/pixelated/resources/root_resource.py
deleted file mode 100644
index b014a590..00000000
--- a/service/pixelated/resources/root_resource.py
+++ /dev/null
@@ -1,139 +0,0 @@
-#
-# Copyright (c) 2016 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 hashlib
-import json
-import os
-from string import Template
-from pixelated.resources.users import UsersResource
-
-from pixelated.resources import BaseResource, UnAuthorizedResource, UnavailableResource
-from pixelated.resources import get_public_static_folder, get_protected_static_folder
-from pixelated.resources.attachments_resource import AttachmentsResource
-from pixelated.resources.sandbox_resource import SandboxResource
-from pixelated.resources.account_recovery_resource import AccountRecoveryResource
-from pixelated.resources.backup_account_resource import BackupAccountResource
-from pixelated.resources.contacts_resource import ContactsResource
-from pixelated.resources.features_resource import FeaturesResource
-from pixelated.resources.feedback_resource import FeedbackResource
-from pixelated.resources.login_resource import LoginResource, LoginStatusResource
-from pixelated.resources.logout_resource import LogoutResource
-from pixelated.resources.user_settings_resource import UserSettingsResource
-from pixelated.resources.mail_resource import MailResource
-from pixelated.resources.mails_resource import MailsResource
-from pixelated.resources.tags_resource import TagsResource
-from pixelated.resources.keys_resource import KeysResource
-from twisted.web.resource import NoResource
-from twisted.web.static import File
-
-from twisted.logger import Logger
-
-log = Logger()
-
-
-CSRF_TOKEN_LENGTH = 32
-
-MODE_STARTUP = 1
-MODE_RUNNING = 2
-
-
-class RootResource(BaseResource):
- def __init__(self, services_factory, static_folder=None):
- BaseResource.__init__(self, services_factory)
- self._public_static_folder = get_public_static_folder(static_folder)
- self._protected_static_folder = get_protected_static_folder(static_folder)
- self._html_template = open(os.path.join(self._protected_static_folder, 'index.html')).read()
- self._services_factory = services_factory
- self._child_resources = ChildResourcesMap()
- with open(os.path.join(self._public_static_folder, 'interstitial.html')) as f:
- self.interstitial = f.read()
- self._startup_mode()
-
- def _startup_mode(self):
- self.putChild('public', File(self._public_static_folder))
- self.putChild('status', LoginStatusResource(self._services_factory))
- self._mode = MODE_STARTUP
-
- def getChild(self, path, request):
- if path == '':
- return self
- if self._mode == MODE_STARTUP:
- return UnavailableResource()
- if self._is_xsrf_valid(request):
- return self._child_resources.get(path)
- return UnAuthorizedResource()
-
- def _is_xsrf_valid(self, request):
- get_request = (request.method == 'GET')
- if get_request:
- return True
-
- xsrf_token = request.getCookie('XSRF-TOKEN')
-
- ajax_request = (request.getHeader('x-requested-with') == 'XMLHttpRequest')
- if ajax_request:
- xsrf_header = request.getHeader('x-xsrf-token')
- return xsrf_header and xsrf_header == xsrf_token
-
- csrf_input = request.args.get('csrftoken', [None])[0] or json.loads(request.content.read()).get('csrftoken', [None])[0]
- return csrf_input and csrf_input == xsrf_token
-
- def initialize(self, provider=None, disclaimer_banner=None, authenticator=None):
- self._child_resources.add('assets', File(self._protected_static_folder))
- self._child_resources.add(AccountRecoveryResource.BASE_URL, AccountRecoveryResource(self._services_factory))
- self._child_resources.add('backup-account', BackupAccountResource(self._services_factory, authenticator, provider))
- self._child_resources.add('sandbox', SandboxResource(self._protected_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._mode = MODE_RUNNING
-
- 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/pixelated/resources/sandbox_resource.py b/service/pixelated/resources/sandbox_resource.py
deleted file mode 100644
index 35f99774..00000000
--- a/service/pixelated/resources/sandbox_resource.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright (c) 2016 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 twisted.web.static import File
-
-
-class SandboxResource(File):
- CSP_HEADER_VALUES = "sandbox allow-popups allow-scripts;" \
- "default-src 'self';" \
- "style-src *;" \
- "script-src *;" \
- "font-src *;" \
- "img-src *;" \
- "object-src 'none';" \
- "connect-src 'none';"
-
- def render_GET(self, request):
- request.setHeader('Content-Security-Policy', self.CSP_HEADER_VALUES)
- request.setHeader('X-Content-Security-Policy', self.CSP_HEADER_VALUES)
- request.setHeader('X-Webkit-CSP', self.CSP_HEADER_VALUES)
- request.setHeader('Access-Control-Allow-Origin', '*')
- request.setHeader('Access-Control-Allow-Methods', 'GET')
-
- return super(SandboxResource, self).render_GET(request)
diff --git a/service/pixelated/resources/session.py b/service/pixelated/resources/session.py
deleted file mode 100644
index 5dfa52e6..00000000
--- a/service/pixelated/resources/session.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# Copyright (c) 2016 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 zope.interface import Interface, Attribute, implements
-from twisted.python.components import registerAdapter
-from twisted.web.server import Session
-
-
-class IPixelatedSession(Interface):
- user_uuid = Attribute('The uuid of the currently logged in user')
- login_status = Attribute('The status during user login')
-
-
-class PixelatedSession(object):
- implements(IPixelatedSession)
-
- def __init__(self, session):
- self.user_uuid = None
- self.login_status = None
-
- def is_logged_in(self):
- return self.user_uuid is not None
-
- def expire(self):
- self.user_uuid = None
- self.login_status = None
-
- def login_started(self):
- self.login_status = 'started'
-
- def login_successful(self, user_uuid):
- self.user_uuid = user_uuid
- self.login_status = 'completed'
-
- def login_error(self):
- self.login_status = 'error'
-
- def check_login_status(self):
- return self.login_status
-
-
-registerAdapter(PixelatedSession, Session, IPixelatedSession)
diff --git a/service/pixelated/resources/tags_resource.py b/service/pixelated/resources/tags_resource.py
deleted file mode 100644
index 4cea4ca7..00000000
--- a/service/pixelated/resources/tags_resource.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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 pixelated.resources import respond_json_deferred, BaseResource, handle_error_deferred
-from twisted.internet.threads import deferToThread
-from twisted.web.server import NOT_DONE_YET
-
-
-class TagsResource(BaseResource):
-
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- _search_engine = self.search_engine(request)
- query = request.args.get('q', [''])[0]
- skip_default_tags = request.args.get('skipDefaultTags', [False])[0]
-
- d = deferToThread(lambda: _search_engine.tags(query=query, skip_default_tags=skip_default_tags))
- d.addCallback(lambda tags: respond_json_deferred(tags, request))
- d.addErrback(handle_error_deferred, request)
-
- return NOT_DONE_YET
diff --git a/service/pixelated/resources/user_settings_resource.py b/service/pixelated/resources/user_settings_resource.py
deleted file mode 100644
index 04b434bd..00000000
--- a/service/pixelated/resources/user_settings_resource.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Copyright (c) 2015 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 pixelated.resources import respond_json_deferred, BaseResource
-from twisted.web import server
-
-FINGERPRINT_NOT_FOUND = 'Fingerprint not found'
-
-
-class UserSettingsResource(BaseResource):
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- _account_email = self.mail_service(request).account_email
-
- def finish_request(key):
- _fingerprint = key.fingerprint
- respond_json_deferred({'account_email': _account_email, 'fingerprint': _fingerprint}, request)
-
- def key_not_found(_):
- respond_json_deferred({'account_email': _account_email, 'fingerprint': FINGERPRINT_NOT_FOUND}, request)
-
- d = self.keymanager(request).get_key(_account_email)
- d.addCallback(finish_request)
- d.addErrback(key_not_found)
-
- return server.NOT_DONE_YET
diff --git a/service/pixelated/resources/users.py b/service/pixelated/resources/users.py
deleted file mode 100644
index a3e6118e..00000000
--- a/service/pixelated/resources/users.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (c) 2016 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 pixelated.resources import respond_json_deferred, BaseResource, respond_json, UnAuthorizedResource
-from twisted.web import server
-
-
-class UsersResource(BaseResource):
- isLeaf = True
-
- def __init__(self, services_factory):
- BaseResource.__init__(self, services_factory)
-
- def render_GET(self, request):
- if self.is_admin(request):
- return respond_json({"count": self._services_factory.online_sessions()}, request)
- return UnAuthorizedResource().render_GET(request)