From 91e4481c450eb7eb928debc1cb7fa59bdb63dd7b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 25 Jul 2017 11:40:11 -0400 Subject: [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. --- service/pixelated/resources/__init__.py | 148 ------------- .../resources/account_recovery_resource.py | 87 -------- .../pixelated/resources/attachments_resource.py | 110 ---------- service/pixelated/resources/auth.py | 117 ---------- .../pixelated/resources/backup_account_resource.py | 79 ------- service/pixelated/resources/contacts_resource.py | 44 ---- service/pixelated/resources/features_resource.py | 46 ---- service/pixelated/resources/feedback_resource.py | 31 --- service/pixelated/resources/keys_resource.py | 46 ---- service/pixelated/resources/login_resource.py | 173 --------------- service/pixelated/resources/logout_resource.py | 45 ---- service/pixelated/resources/mail_resource.py | 92 -------- service/pixelated/resources/mails_resource.py | 244 --------------------- service/pixelated/resources/root_resource.py | 139 ------------ service/pixelated/resources/sandbox_resource.py | 37 ---- service/pixelated/resources/session.py | 55 ----- service/pixelated/resources/tags_resource.py | 38 ---- .../pixelated/resources/user_settings_resource.py | 43 ---- service/pixelated/resources/users.py | 30 --- 19 files changed, 1604 deletions(-) delete mode 100644 service/pixelated/resources/__init__.py delete mode 100644 service/pixelated/resources/account_recovery_resource.py delete mode 100644 service/pixelated/resources/attachments_resource.py delete mode 100644 service/pixelated/resources/auth.py delete mode 100644 service/pixelated/resources/backup_account_resource.py delete mode 100644 service/pixelated/resources/contacts_resource.py delete mode 100644 service/pixelated/resources/features_resource.py delete mode 100644 service/pixelated/resources/feedback_resource.py delete mode 100644 service/pixelated/resources/keys_resource.py delete mode 100644 service/pixelated/resources/login_resource.py delete mode 100644 service/pixelated/resources/logout_resource.py delete mode 100644 service/pixelated/resources/mail_resource.py delete mode 100644 service/pixelated/resources/mails_resource.py delete mode 100644 service/pixelated/resources/root_resource.py delete mode 100644 service/pixelated/resources/sandbox_resource.py delete mode 100644 service/pixelated/resources/session.py delete mode 100644 service/pixelated/resources/tags_resource.py delete mode 100644 service/pixelated/resources/user_settings_resource.py delete mode 100644 service/pixelated/resources/users.py (limited to 'service/pixelated/resources') 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 . - -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 . - -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 . -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 . - -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 . - -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 . - -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 . - -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 . -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 . - -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 . - -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 . - -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 . - -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 . - -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 . -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 . - -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 . - -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 . - -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 . - -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 . - -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) -- cgit v1.2.3