Mercurial > libervia-backend
comparison sat/plugins/plugin_comp_file_sharing.py @ 3296:da443cf946ad
comp file sharing: CORS:
- added CORS headers to allow using the HTTP server from an other domain
- added `Content-Security-Policy`
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 09 Jun 2020 06:21:23 +0200 |
parents | 449dfbfcdbcc |
children | 91b5ae058c66 |
comparison
equal
deleted
inserted
replaced
3295:9bc3fca290ab | 3296:da443cf946ad |
---|---|
34 from sat.tools.common import files_utils | 34 from sat.tools.common import files_utils |
35 from sat.tools.common import tls | 35 from sat.tools.common import tls |
36 from sat.tools import stream | 36 from sat.tools import stream |
37 from twisted.internet import defer, reactor | 37 from twisted.internet import defer, reactor |
38 from twisted.words.protocols.jabber import error | 38 from twisted.words.protocols.jabber import error |
39 from twisted.web import server, resource, static | 39 from twisted.web import server, resource, static, http |
40 from wokkel import pubsub | 40 from wokkel import pubsub |
41 from wokkel import generic | 41 from wokkel import generic |
42 | 42 |
43 | 43 |
44 log = getLogger(__name__) | 44 log = getLogger(__name__) |
83 class HTTPFileServer(resource.Resource): | 83 class HTTPFileServer(resource.Resource): |
84 isLeaf = True | 84 isLeaf = True |
85 | 85 |
86 def errorPage(self, request, code): | 86 def errorPage(self, request, code): |
87 request.setResponseCode(code) | 87 request.setResponseCode(code) |
88 if code == 400: | 88 if code == http.BAD_REQUEST: |
89 brief = 'Bad Request' | 89 brief = 'Bad Request' |
90 details = "Your request is invalid" | 90 details = "Your request is invalid" |
91 elif code == 403: | 91 elif code == http.FORBIDDEN: |
92 brief = 'Forbidden' | 92 brief = 'Forbidden' |
93 details = "You're not allowed to use this resource" | 93 details = "You're not allowed to use this resource" |
94 elif code == 404: | 94 elif code == http.NOT_FOUND: |
95 brief = 'Not Found' | 95 brief = 'Not Found' |
96 details = "No resource found at this URL" | 96 details = "No resource found at this URL" |
97 else: | 97 else: |
98 brief = 'Error' | 98 brief = 'Error' |
99 details = "This resource can't be used" | 99 details = "This resource can't be used" |
111 elif media_type == 'application' and media_subtype == 'pdf': | 111 elif media_type == 'application' and media_subtype == 'pdf': |
112 return 'inline' | 112 return 'inline' |
113 else: | 113 else: |
114 return 'attachment' | 114 return 'attachment' |
115 | 115 |
116 def render(self, request): | |
117 request.setHeader("Access-Control-Allow-Origin", "*") | |
118 request.setHeader("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, PUT") | |
119 request.setHeader("Access-Control-Allow-Headers", "Content-Type, Xmpp-File-Path, Xmpp-File-No-Http") | |
120 request.setHeader("Access-Control-Allow-Credentials", "true") | |
121 return super().render(request) | |
122 | |
123 def render_OPTIONS(self, request): | |
124 request.setResponseCode(http.OK) | |
125 return b"" | |
126 | |
116 def render_GET(self, request): | 127 def render_GET(self, request): |
117 try: | 128 try: |
118 request.upload_data | 129 request.upload_data |
119 except exceptions.DataError: | 130 except exceptions.DataError: |
120 return self.errorPage(request, 404) | 131 return self.errorPage(request, http.NOT_FOUND) |
121 | 132 |
122 defer.ensureDeferred(self.renderGet(request)) | 133 defer.ensureDeferred(self.renderGet(request)) |
123 return server.NOT_DONE_YET | 134 return server.NOT_DONE_YET |
124 | 135 |
125 async def renderGet(self, request): | 136 async def renderGet(self, request): |
126 try: | 137 try: |
127 upload_id, filename = request.upload_data | 138 upload_id, filename = request.upload_data |
128 except exceptions.DataError: | 139 except exceptions.DataError: |
129 request.write(self.errorPage(request, 403)) | 140 request.write(self.errorPage(request, http.FORBIDDEN)) |
130 request.finish() | 141 request.finish() |
131 return | 142 return |
132 found_files = await request.file_sharing.host.memory.getFiles( | 143 found_files = await request.file_sharing.host.memory.getFiles( |
133 client=None, peer_jid=None, perms_to_check=None, public_id=upload_id) | 144 client=None, peer_jid=None, perms_to_check=None, public_id=upload_id) |
134 if not found_files: | 145 if not found_files: |
135 request.write(self.errorPage(request, 404)) | 146 request.write(self.errorPage(request, http.NOT_FOUND)) |
136 request.finish() | 147 request.finish() |
137 return | 148 return |
138 if len(found_files) > 1: | 149 if len(found_files) > 1: |
139 log.error(f"more that one files found for public id {upload_id!r}") | 150 log.error(f"more that one files found for public id {upload_id!r}") |
140 | 151 |
149 # thus we add a content disposition header | 160 # thus we add a content disposition header |
150 request.setHeader( | 161 request.setHeader( |
151 'Content-Disposition', | 162 'Content-Disposition', |
152 f"{disp_type}; filename*=UTF-8''{quote(found_file['name'])}" | 163 f"{disp_type}; filename*=UTF-8''{quote(found_file['name'])}" |
153 ) | 164 ) |
165 # cf. https://xmpp.org/extensions/xep-0363.html#server | |
166 request.setHeader( | |
167 'Content-Security-Policy', | |
168 "default-src 'none'; frame-ancestors 'none';" | |
169 ) | |
154 ret = file_res.render(request) | 170 ret = file_res.render(request) |
155 if ret != server.NOT_DONE_YET: | 171 if ret != server.NOT_DONE_YET: |
156 # HEAD returns directly the result (while GET use a produced) | 172 # HEAD returns directly the result (while GET use a produced) |
157 request.write(ret) | 173 request.write(ret) |
158 request.finish() | 174 request.finish() |
164 async def renderPut(self, request): | 180 async def renderPut(self, request): |
165 try: | 181 try: |
166 client, upload_request = request.upload_request_data | 182 client, upload_request = request.upload_request_data |
167 upload_id, filename = request.upload_data | 183 upload_id, filename = request.upload_data |
168 except AttributeError: | 184 except AttributeError: |
169 request.write(self.errorPage(request, 400)) | 185 request.write(self.errorPage(request, http.BAD_REQUEST)) |
170 request.finish() | 186 request.finish() |
171 return | 187 return |
172 | 188 |
173 # at this point request is checked and file is buffered, we can store it | 189 # at this point request is checked and file is buffered, we can store it |
174 # we close the content here, before registering the file | 190 # we close the content here, before registering the file |
186 await request.file_sharing.registerReceivedFile( | 202 await request.file_sharing.registerReceivedFile( |
187 client, upload_request.from_, file_data, tmp_file_path, | 203 client, upload_request.from_, file_data, tmp_file_path, |
188 public_id=upload_id, | 204 public_id=upload_id, |
189 ) | 205 ) |
190 | 206 |
191 request.setResponseCode(201) | 207 request.setResponseCode(http.CREATED) |
192 request.finish() | 208 request.finish() |
193 | 209 |
194 | 210 |
195 class FileSharingRequest(server.Request): | 211 class FileSharingRequest(server.Request): |
196 | 212 |