1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
#
# 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 IPixelatedSession
from pixelated.resources.attachments_resource import AttachmentsResource
from pixelated.resources.sandbox_resource import SandboxResource
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
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):
BaseResource.__init__(self, services_factory)
self._startup_assets_folder = self._get_startup_folder()
self._public_assets_folder = self._get_public_folder()
self._static_folder = self._get_static_folder()
self._html_template = open(os.path.join(self._static_folder, 'index.html')).read()
self._services_factory = services_factory
self._child_resources = ChildResourcesMap()
with open(os.path.join(self._startup_assets_folder, 'Interstitial.html')) as f:
self.interstitial = f.read()
self._startup_mode()
def _startup_mode(self):
self.putChild('startup-assets', File(self._startup_assets_folder))
self.putChild('public-assets', File(self._public_assets_folder))
self._mode = MODE_STARTUP
def getChild(self, path, request):
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('sandbox', SandboxResource(self._static_folder))
self._child_resources.add('assets', File(self._static_folder))
self._child_resources.add('keys', KeysResource(self._services_factory))
self._child_resources.add(AttachmentsResource.BASE_URL, AttachmentsResource(self._services_factory))
self._child_resources.add('contacts', ContactsResource(self._services_factory))
self._child_resources.add('features', FeaturesResource(provider))
self._child_resources.add('tags', TagsResource(self._services_factory))
self._child_resources.add('mails', MailsResource(self._services_factory))
self._child_resources.add('mail', MailResource(self._services_factory))
self._child_resources.add('feedback', FeedbackResource(self._services_factory))
self._child_resources.add('user-settings', UserSettingsResource(self._services_factory))
self._child_resources.add('users', UsersResource(self._services_factory))
self._child_resources.add(LoginResource.BASE_URL,
LoginResource(self._services_factory, provider, disclaimer_banner=disclaimer_banner, authenticator=authenticator))
self._child_resources.add(LogoutResource.BASE_URL, LogoutResource(self._services_factory))
self._mode = MODE_RUNNING
# TODO: use the public folder for this
def _get_startup_folder(self):
path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(path, '..', 'assets')
def _get_public_folder(self):
public_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "public"))
# this is a workaround for packaging
if not os.path.exists(public_folder):
public_folder = os.path.abspath(
os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "public"))
if not os.path.exists(public_folder):
# TODO: how is this packaged?
public_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
return public_folder
def _get_static_folder(self):
static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app"))
# this is a workaround for packaging
if not os.path.exists(static_folder):
static_folder = os.path.abspath(
os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "app"))
if not os.path.exists(static_folder):
static_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
return static_folder
def _is_starting(self):
return self._mode == MODE_STARTUP
def _add_csrf_cookie(self, request):
csrf_token = hashlib.sha256(os.urandom(CSRF_TOKEN_LENGTH)).hexdigest()
request.addCookie('XSRF-TOKEN', csrf_token)
def render_GET(self, request):
self._add_csrf_cookie(request)
if self._is_starting():
return self.interstitial
else:
account_email = self.mail_service(request).account_email
response = Template(self._html_template).safe_substitute(account_email=account_email)
return str(response)
class ChildResourcesMap(object):
def __init__(self):
self._registry = {}
def add(self, path, resource):
self._registry[path] = resource
def get(self, path):
return self._registry.get(path) or NoResource()
|