|
14 | 14 | # limitations under the License. |
15 | 15 | import logging |
16 | 16 | import sys |
17 | | -from typing import Dict, List, Optional, Tuple |
| 17 | +from typing import Dict, List |
18 | 18 |
|
19 | | -from twisted.internet import address |
20 | 19 | from twisted.web.resource import Resource |
21 | 20 |
|
22 | 21 | import synapse |
23 | 22 | import synapse.events |
24 | | -from synapse.api.errors import HttpResponseException, RequestSendFailed, SynapseError |
25 | 23 | from synapse.api.urls import ( |
26 | 24 | CLIENT_API_PREFIX, |
27 | 25 | FEDERATION_PREFIX, |
|
43 | 41 | from synapse.config.server import ListenerConfig |
44 | 42 | from synapse.federation.transport.server import TransportLayerServer |
45 | 43 | from synapse.http.server import JsonResource, OptionsResource |
46 | | -from synapse.http.servlet import RestServlet, parse_json_object_from_request |
47 | | -from synapse.http.site import SynapseRequest |
48 | 44 | from synapse.logging.context import LoggingContext |
49 | 45 | from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy |
50 | 46 | from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource |
|
70 | 66 | versions, |
71 | 67 | voip, |
72 | 68 | ) |
73 | | -from synapse.rest.client._base import client_patterns |
74 | 69 | from synapse.rest.client.account import ThreepidRestServlet, WhoamiRestServlet |
75 | 70 | from synapse.rest.client.devices import DevicesRestServlet |
76 | 71 | from synapse.rest.client.keys import ( |
77 | 72 | KeyChangesServlet, |
78 | 73 | KeyQueryServlet, |
| 74 | + KeyUploadServlet, |
79 | 75 | OneTimeKeyServlet, |
80 | 76 | ) |
81 | 77 | from synapse.rest.client.register import ( |
|
132 | 128 | from synapse.storage.databases.main.ui_auth import UIAuthWorkerStore |
133 | 129 | from synapse.storage.databases.main.user_directory import UserDirectoryStore |
134 | 130 | from synapse.storage.databases.main.user_erasure_store import UserErasureWorkerStore |
135 | | -from synapse.types import JsonDict |
136 | 131 | from synapse.util import SYNAPSE_VERSION |
137 | 132 | from synapse.util.httpresourcetree import create_resource_tree |
138 | 133 |
|
139 | 134 | logger = logging.getLogger("synapse.app.generic_worker") |
140 | 135 |
|
141 | 136 |
|
142 | | -class KeyUploadServlet(RestServlet): |
143 | | - """An implementation of the `KeyUploadServlet` that responds to read only |
144 | | - requests, but otherwise proxies through to the master instance. |
145 | | - """ |
146 | | - |
147 | | - PATTERNS = client_patterns("/keys/upload(/(?P<device_id>[^/]+))?$") |
148 | | - |
149 | | - def __init__(self, hs: HomeServer): |
150 | | - """ |
151 | | - Args: |
152 | | - hs: server |
153 | | - """ |
154 | | - super().__init__() |
155 | | - self.auth = hs.get_auth() |
156 | | - self.store = hs.get_datastores().main |
157 | | - self.http_client = hs.get_simple_http_client() |
158 | | - self.main_uri = hs.config.worker.worker_main_http_uri |
159 | | - |
160 | | - async def on_POST( |
161 | | - self, request: SynapseRequest, device_id: Optional[str] |
162 | | - ) -> Tuple[int, JsonDict]: |
163 | | - requester = await self.auth.get_user_by_req(request, allow_guest=True) |
164 | | - user_id = requester.user.to_string() |
165 | | - body = parse_json_object_from_request(request) |
166 | | - |
167 | | - if device_id is not None: |
168 | | - # passing the device_id here is deprecated; however, we allow it |
169 | | - # for now for compatibility with older clients. |
170 | | - if requester.device_id is not None and device_id != requester.device_id: |
171 | | - logger.warning( |
172 | | - "Client uploading keys for a different device " |
173 | | - "(logged in as %s, uploading for %s)", |
174 | | - requester.device_id, |
175 | | - device_id, |
176 | | - ) |
177 | | - else: |
178 | | - device_id = requester.device_id |
179 | | - |
180 | | - if device_id is None: |
181 | | - raise SynapseError( |
182 | | - 400, "To upload keys, you must pass device_id when authenticating" |
183 | | - ) |
184 | | - |
185 | | - if body: |
186 | | - # They're actually trying to upload something, proxy to main synapse. |
187 | | - |
188 | | - # Proxy headers from the original request, such as the auth headers |
189 | | - # (in case the access token is there) and the original IP / |
190 | | - # User-Agent of the request. |
191 | | - headers: Dict[bytes, List[bytes]] = { |
192 | | - header: list(request.requestHeaders.getRawHeaders(header, [])) |
193 | | - for header in (b"Authorization", b"User-Agent") |
194 | | - } |
195 | | - # Add the previous hop to the X-Forwarded-For header. |
196 | | - x_forwarded_for = list( |
197 | | - request.requestHeaders.getRawHeaders(b"X-Forwarded-For", []) |
198 | | - ) |
199 | | - # we use request.client here, since we want the previous hop, not the |
200 | | - # original client (as returned by request.getClientAddress()). |
201 | | - if isinstance(request.client, (address.IPv4Address, address.IPv6Address)): |
202 | | - previous_host = request.client.host.encode("ascii") |
203 | | - # If the header exists, add to the comma-separated list of the first |
204 | | - # instance of the header. Otherwise, generate a new header. |
205 | | - if x_forwarded_for: |
206 | | - x_forwarded_for = [x_forwarded_for[0] + b", " + previous_host] |
207 | | - x_forwarded_for.extend(x_forwarded_for[1:]) |
208 | | - else: |
209 | | - x_forwarded_for = [previous_host] |
210 | | - headers[b"X-Forwarded-For"] = x_forwarded_for |
211 | | - |
212 | | - # Replicate the original X-Forwarded-Proto header. Note that |
213 | | - # XForwardedForRequest overrides isSecure() to give us the original protocol |
214 | | - # used by the client, as opposed to the protocol used by our upstream proxy |
215 | | - # - which is what we want here. |
216 | | - headers[b"X-Forwarded-Proto"] = [ |
217 | | - b"https" if request.isSecure() else b"http" |
218 | | - ] |
219 | | - |
220 | | - try: |
221 | | - result = await self.http_client.post_json_get_json( |
222 | | - self.main_uri + request.uri.decode("ascii"), body, headers=headers |
223 | | - ) |
224 | | - except HttpResponseException as e: |
225 | | - raise e.to_synapse_error() from e |
226 | | - except RequestSendFailed as e: |
227 | | - raise SynapseError(502, "Failed to talk to master") from e |
228 | | - |
229 | | - return 200, result |
230 | | - else: |
231 | | - # Just interested in counts. |
232 | | - result = await self.store.count_e2e_one_time_keys(user_id, device_id) |
233 | | - return 200, {"one_time_key_counts": result} |
234 | | - |
235 | | - |
236 | 137 | class GenericWorkerSlavedStore( |
237 | 138 | # FIXME(#3714): We need to add UserDirectoryStore as we write directly |
238 | 139 | # rather than going via the correct worker. |
|
0 commit comments