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
|
#
# 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_startup_folder, get_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.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 = get_startup_folder()
self._static_folder = 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._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('recovery', AccountRecoveryResource(self._services_factory))
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
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()
|