def getClientIP(self): "If there's an X-Forwarded-For, treat that as the client IP." forwarded_for = self.getHeader('x-forwarded-for') if forwarded_for is not None: return forwarded_for else: return Request.getClientIP(self)
async def _get_appservice_user_id( self, request: Request ) -> Tuple[Optional[str], Optional[ApplicationService]]: app_service = self.store.get_app_service_by_token( self.get_access_token_from_request(request)) if app_service is None: return None, None if app_service.ip_range_whitelist: ip_address = IPAddress(request.getClientIP()) if ip_address not in app_service.ip_range_whitelist: return None, None # This will always be set by the time Twisted calls us. assert request.args is not None if b"user_id" not in request.args: return app_service.sender, app_service user_id = request.args[b"user_id"][0].decode("utf8") await self.validate_appservice_can_control_user_id( app_service, user_id) if app_service.sender == user_id: return app_service.sender, app_service return user_id, app_service
async def _get_appservice_user_id( self, request: Request ) -> Tuple[Optional[str], Optional[ApplicationService]]: app_service = self.store.get_app_service_by_token( self.get_access_token_from_request(request)) if app_service is None: return None, None if app_service.ip_range_whitelist: ip_address = IPAddress(request.getClientIP()) if ip_address not in app_service.ip_range_whitelist: return None, None # This will always be set by the time Twisted calls us. assert request.args is not None if b"user_id" not in request.args: return app_service.sender, app_service user_id = request.args[b"user_id"][0].decode("utf8") if app_service.sender == user_id: return app_service.sender, app_service if not app_service.is_interested_in_user(user_id): raise AuthError( 403, "Application service cannot masquerade as this user.") if not (await self.store.get_user_by_id(user_id)): raise AuthError( 403, "Application service has not registered this user") return user_id, app_service
async def on_GET(self, request: Request) -> Tuple[int, JsonDict]: await self.ratelimiter.ratelimit(None, (request.getClientIP(), )) if not self.hs.config.registration.enable_registration: raise SynapseError(403, "Registration has been disabled", errcode=Codes.FORBIDDEN) token = parse_string(request, "token", required=True) valid = await self.store.registration_token_is_valid(token) return 200, {"valid": valid}
async def on_GET(self, request: Request) -> Tuple[int, JsonDict]: if not self.hs.config.registration.enable_registration: raise SynapseError(403, "Registration has been disabled", errcode=Codes.FORBIDDEN) ip = request.getClientIP() with self.ratelimiter.ratelimit(ip) as wait_deferred: await wait_deferred username = parse_string(request, "username", required=True) await self.registration_handler.check_username(username) return 200, {"available": True}
def getClientIP(self, **kwargs): """Override base getClientIP to be X-Real-IP aware. Arguments: honor_xrealip (bool)(optional) - (If present, overrides the default for honor_xrealip specified in the config file.) Whether or not to prefer the value of the X-Real-IP header if present. """ if kwargs.has_key("honor_xrealip"): honor_xrealip = kwargs["honor_xrealip"] else: honor_xrealip = self.site.honor_xrealip if honor_xrealip and self.getHeader("X-Real-IP"): return self.getHeader("X-Real-IP") return Request.getClientIP(self)
async def register_sso_user(self, request: Request, session_id: str) -> None: """Called once we have all the info we need to register a new user. Does so and serves an HTTP response Args: request: HTTP request session_id: ID of the username mapping session, extracted from a cookie """ try: session = self.get_mapping_session(session_id) except SynapseError as e: self.render_error(request, "bad_session", e.msg, code=e.code) return logger.info( "[session %s] Registering localpart %s", session_id, session.chosen_localpart, ) attributes = UserAttributes( localpart=session.chosen_localpart, emails=session.emails_to_use, ) if session.use_display_name: attributes.display_name = session.display_name # the following will raise a 400 error if the username has been taken in the # meantime. user_id = await self._register_mapped_user( attributes, session.auth_provider_id, session.remote_user_id, get_request_user_agent(request), request.getClientIP(), ) logger.info( "[session %s] Registered userid %s with attributes %s", session_id, user_id, attributes, ) # delete the mapping session and the cookie del self._username_mapping_sessions[session_id] # delete the cookie request.addCookie( USERNAME_MAPPING_SESSION_COOKIE_NAME, b"", expires=b"Thu, 01 Jan 1970 00:00:00 GMT", path=b"/", ) auth_result = {} if session.terms_accepted_version: # TODO: make this less awful. auth_result[LoginType.TERMS] = True await self._registration_handler.post_registration_actions( user_id, auth_result, access_token=None) await self._auth_handler.complete_sso_login( user_id, session.auth_provider_id, request, session.client_redirect_url, session.extra_login_attributes, new_user=True, )
def getClientIP(self): """Get the clients IP address. Evaluates allowed proxys (from localhost)""" ip = Request.getClientIP(self) if ip in ('::1', '127.0.0.1'): return self.requestHeaders.getRawHeaders('X-Forwarded-For', [ip])[-1] return ip
def getClientIP(self): if self.requestHeaders.hasHeader('x-forwarded-for'): return self.requestHeaders.getRawHeaders(b"x-forwarded-for")[0].split(b",")[0].strip() if self.requestHeaders.hasHeader('x-real-ip'): return self.requestHeaders.getRawHeaders(b"x-real-ip")[0].split(b",")[0].strip() return Request.getClientIP(self)
async def _get_appservice_user_id_and_device_id( self, request: Request ) -> Tuple[Optional[str], Optional[str], Optional[ApplicationService]]: """ Given a request, reads the request parameters to determine: - whether it's an application service that's making this request - what user the application service should be treated as controlling (the user_id URI parameter allows an application service to masquerade any applicable user in its namespace) - what device the application service should be treated as controlling (the device_id[^1] URI parameter allows an application service to masquerade as any device that exists for the relevant user) [^1] Unstable and provided by MSC3202. Must use `org.matrix.msc3202.device_id` in place of `device_id` for now. Returns: 3-tuple of (user ID?, device ID?, application service?) Postconditions: - If an application service is returned, so is a user ID - A user ID is never returned without an application service - A device ID is never returned without a user ID or an application service - The returned application service, if present, is permitted to control the returned user ID. - The returned device ID, if present, has been checked to be a valid device ID for the returned user ID. """ DEVICE_ID_ARG_NAME = b"org.matrix.msc3202.device_id" app_service = self.store.get_app_service_by_token( self.get_access_token_from_request(request)) if app_service is None: return None, None, None if app_service.ip_range_whitelist: ip_address = IPAddress(request.getClientIP()) if ip_address not in app_service.ip_range_whitelist: return None, None, None # This will always be set by the time Twisted calls us. assert request.args is not None if b"user_id" in request.args: effective_user_id = request.args[b"user_id"][0].decode("utf8") await self.validate_appservice_can_control_user_id( app_service, effective_user_id) else: effective_user_id = app_service.sender effective_device_id: Optional[str] = None if (self.hs.config.experimental.msc3202_device_masquerading_enabled and DEVICE_ID_ARG_NAME in request.args): effective_device_id = request.args[DEVICE_ID_ARG_NAME][0].decode( "utf8") # We only just set this so it can't be None! assert effective_device_id is not None device_opt = await self.store.get_device(effective_user_id, effective_device_id) if device_opt is None: # For now, use 400 M_EXCLUSIVE if the device doesn't exist. # This is an open thread of discussion on MSC3202 as of 2021-12-09. raise AuthError( 400, f"Application service trying to use a device that doesn't exist ('{effective_device_id}' for {effective_user_id})", Codes.EXCLUSIVE, ) return effective_user_id, effective_device_id, app_service
async def get_user_by_req( self, request: Request, allow_guest: bool = False, rights: str = "access", allow_expired: bool = False, ) -> synapse.types.Requester: """Get a registered user's ID. Args: request: An HTTP request with an access_token query parameter. allow_guest: If False, will raise an AuthError if the user making the request is a guest. rights: The operation being performed; the access token must allow this allow_expired: If True, allow the request through even if the account is expired, or session token lifetime has ended. Note that /login will deliver access tokens regardless of expiration. Returns: Resolves to the requester Raises: InvalidClientCredentialsError if no user by that token exists or the token is invalid. AuthError if access is denied for the user in the access token """ try: ip_addr = request.getClientIP() user_agent = get_request_user_agent(request) access_token = self.get_access_token_from_request(request) user_id, app_service = await self._get_appservice_user_id(request) if user_id: if ip_addr and self._track_appservice_user_ips: await self.store.insert_client_ip( user_id=user_id, access_token=access_token, ip=ip_addr, user_agent=user_agent, device_id="dummy-device", # stubbed ) requester = synapse.types.create_requester( user_id, app_service=app_service) request.requester = user_id opentracing.set_tag("authenticated_entity", user_id) opentracing.set_tag("user_id", user_id) opentracing.set_tag("appservice_id", app_service.id) return requester user_info = await self.get_user_by_access_token( access_token, rights, allow_expired=allow_expired) token_id = user_info.token_id is_guest = user_info.is_guest shadow_banned = user_info.shadow_banned # Deny the request if the user account has expired. if self._account_validity.enabled and not allow_expired: if await self.store.is_account_expired(user_info.user_id, self.clock.time_msec()): raise AuthError(403, "User account has expired", errcode=Codes.EXPIRED_ACCOUNT) device_id = user_info.device_id if access_token and ip_addr: await self.store.insert_client_ip( user_id=user_info.token_owner, access_token=access_token, ip=ip_addr, user_agent=user_agent, device_id=device_id, ) if is_guest and not allow_guest: raise AuthError( 403, "Guest access not allowed", errcode=Codes.GUEST_ACCESS_FORBIDDEN, ) requester = synapse.types.create_requester( user_info.user_id, token_id, is_guest, shadow_banned, device_id, app_service=app_service, authenticated_entity=user_info.token_owner, ) request.requester = requester opentracing.set_tag("authenticated_entity", user_info.token_owner) opentracing.set_tag("user_id", user_info.user_id) if device_id: opentracing.set_tag("device_id", device_id) return requester except KeyError: raise MissingClientTokenError()
def getClientIP(self): real_ip = self.getHeader("X-Real-IP") if real_ip: return real_ip else: Request.getClientIP(self)
def fix_twisted_web_http_Request(): """Add ipv6 support to Request.getClientIP() Specifically, IPv6 IP addresses need to be wrapped in [], and return address.IPv6Address when needed. See https://bugs.launchpad.net/ubuntu/+source/twisted/+bug/1604608 """ from netaddr import IPAddress from netaddr.core import AddrFormatError from twisted.internet import address from twisted.python.compat import ( intToBytes, networkString, ) import twisted.web.http from twisted.web.server import Request from twisted.web.test.requesthelper import DummyChannel def new_getClientIP(self): from twisted.internet import address # upstream doesn't check for address.IPv6Address if isinstance(self.client, address.IPv4Address): return self.client.host elif isinstance(self.client, address.IPv6Address): return self.client.host else: return None def new_getRequestHostname(self): # Unlike upstream, support/require IPv6 addresses to be # [ip:v6:add:ress]:port, with :port being optional. # IPv6 IP addresses are wrapped in [], to disambigate port numbers. host = self.getHeader(b'host') if host: if host.startswith(b'[') and b']' in host: if host.find(b']') < host.rfind(b':'): # The format is: [ip:add:ress]:port. return host[:host.rfind(b':')] else: # no :port after [...] return host # No brackets, so it must be host:port or IPv4:port. return host.split(b':', 1)[0] host = self.getHost().host try: if isinstance(host, str): ip = IPAddress(host) else: ip = IPAddress(host.decode("idna")) except AddrFormatError: # If we could not convert the hostname to an IPAddress, assume that # it is a hostname. return networkString(host) if ip.version == 4: return networkString(host) else: return networkString('[' + host + ']') def new_setHost(self, host, port, ssl=0): try: ip = IPAddress(host.decode("idna")) except AddrFormatError: ip = None # `host` is a host or domain name. self._forceSSL = ssl # set first so isSecure will work if self.isSecure(): default = 443 else: default = 80 if ip is None: hostHeader = host elif ip.version == 4: hostHeader = host else: hostHeader = b"[" + host + b"]" if port != default: hostHeader += b":" + intToBytes(port) self.requestHeaders.setRawHeaders(b"host", [hostHeader]) if ip is None: # Pretend that a host or domain name is an IPv4 address. self.host = address.IPv4Address("TCP", host, port) elif ip.version == 4: self.host = address.IPv4Address("TCP", host, port) else: self.host = address.IPv6Address("TCP", host, port) request = Request(DummyChannel(), False) request.client = address.IPv6Address('TCP', 'fe80::1', '80') request.setHost(b"fe80::1", 1234) if request.getClientIP() is None: # Buggy code returns None for IPv6 addresses. twisted.web.http.Request.getClientIP = new_getClientIP if isinstance(request.host, address.IPv4Address): # Buggy code calls fe80::1 an IPv4Address. twisted.web.http.Request.setHost = new_setHost if request.getRequestHostname() == b'fe80': # The fe80::1 test address above was incorrectly interpreted as # address='fe80', port = ':1', because it does host.split(':', 1)[0]. twisted.web.http.Request.getRequestHostname = new_getRequestHostname
async def on_POST(self, request: Request, stagetype: str) -> None: session = parse_string(request, "session") if not session: raise SynapseError(400, "No session supplied") if stagetype == LoginType.RECAPTCHA: response = parse_string(request, "g-recaptcha-response") if not response: raise SynapseError(400, "No captcha response supplied") authdict = {"response": response, "session": session} try: await self.auth_handler.add_oob_auth(LoginType.RECAPTCHA, authdict, request.getClientIP()) except LoginError as e: # Authentication failed, let user try again html = self.recaptcha_template.render( session=session, myurl="%s/r0/auth/%s/fallback/web" % (CLIENT_API_PREFIX, LoginType.RECAPTCHA), sitekey=self.hs.config.recaptcha_public_key, error=e.msg, ) else: # No LoginError was raised, so authentication was successful html = self.success_template.render() elif stagetype == LoginType.TERMS: authdict = {"session": session} try: await self.auth_handler.add_oob_auth(LoginType.TERMS, authdict, request.getClientIP()) except LoginError as e: # Authentication failed, let user try again html = self.terms_template.render( session=session, terms_url="%s_matrix/consent?v=%s" % ( self.hs.config.public_baseurl, self.hs.config.user_consent_version, ), myurl="%s/r0/auth/%s/fallback/web" % (CLIENT_API_PREFIX, LoginType.TERMS), error=e.msg, ) else: # No LoginError was raised, so authentication was successful html = self.success_template.render() elif stagetype == LoginType.SSO: # The SSO fallback workflow should not post here, raise SynapseError( 404, "Fallback SSO auth does not support POST requests.") elif stagetype == LoginType.REGISTRATION_TOKEN: token = parse_string(request, "token", required=True) authdict = {"session": session, "token": token} try: await self.auth_handler.add_oob_auth( LoginType.REGISTRATION_TOKEN, authdict, request.getClientIP()) except LoginError as e: html = self.registration_token_template.render( session=session, myurl= f"{CLIENT_API_PREFIX}/r0/auth/{LoginType.REGISTRATION_TOKEN}/fallback/web", error=e.msg, ) else: html = self.success_template.render() else: raise SynapseError(404, "Unknown auth stage type") # Render the HTML and return. respond_with_html(request, 200, html) return None