async def _async_render_GET(self, request: SynapseRequest) -> None:
        client_redirect_url = parse_string(request,
                                           "redirectUrl",
                                           required=True,
                                           encoding="utf-8")
        idp = parse_string(request, "idp", required=False)

        # if we need to pick an IdP, do so
        if not idp:
            return await self._serve_id_picker(request, client_redirect_url)

        # otherwise, redirect to the IdP's redirect URI
        providers = self._sso_handler.get_identity_providers()
        auth_provider = providers.get(idp)
        if not auth_provider:
            logger.info("Unknown idp %r", idp)
            self._sso_handler.render_error(request, "unknown_idp",
                                           "Unknown identity provider ID")
            return

        sso_url = await auth_provider.handle_redirect_request(
            request, client_redirect_url.encode("utf8"))
        logger.info("Redirecting to %s", sso_url)
        request.redirect(sso_url)
        finish_request(request)
Exemple #2
0
    async def on_POST(self, request: SynapseRequest):
        login_submission = parse_json_object_from_request(request)

        try:
            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    self._address_ratelimiter.ratelimit(request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission, appservice)
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_jwt_login(login_submission)
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_token_login(login_submission)
            else:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_other_login(login_submission)
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
Exemple #3
0
    async def on_POST(self,
                      request: SynapseRequest) -> Tuple[int, LoginResponse]:
        login_submission = parse_json_object_from_request(request)

        # Check to see if the client requested a refresh token.
        client_requested_refresh_token = login_submission.get(
            LoginRestServlet.REFRESH_TOKEN_PARAM, False)
        if not isinstance(client_requested_refresh_token, bool):
            raise SynapseError(400, "`refresh_token` should be true or false.")

        should_issue_refresh_token = (self._refresh_tokens_enabled
                                      and client_requested_refresh_token)

        try:
            if login_submission["type"] in (
                    LoginRestServlet.APPSERVICE_TYPE,
                    LoginRestServlet.APPSERVICE_TYPE_UNSTABLE,
            ):
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    await self._address_ratelimiter.ratelimit(
                        None, request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission,
                    appservice,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_jwt_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_token_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            else:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_other_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
Exemple #4
0
 async def on_GET(self, request: SynapseRequest):
     args = request.args
     if b"redirectUrl" not in args:
         return 400, "Redirect URL not specified for SSO auth"
     client_redirect_url = args[b"redirectUrl"][0]
     sso_url = await self.get_sso_url(request, client_redirect_url)
     request.redirect(sso_url)
     finish_request(request)
Exemple #5
0
    async def on_POST(self,
                      request: SynapseRequest) -> Tuple[int, LoginResponse]:
        login_submission = parse_json_object_from_request(request)

        if self._msc2918_enabled:
            # Check if this login should also issue a refresh token, as per
            # MSC2918
            should_issue_refresh_token = parse_boolean(
                request,
                name=LoginRestServlet.REFRESH_TOKEN_PARAM,
                default=False)
        else:
            should_issue_refresh_token = False

        try:
            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    await self._address_ratelimiter.ratelimit(
                        None, request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission,
                    appservice,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_jwt_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_token_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            else:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_other_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
Exemple #6
0
    async def handle_redirect_request(
        self, request: SynapseRequest, client_redirect_url: bytes
    ) -> None:
        """Handle an incoming request to /login/sso/redirect

        It redirects the browser to the authorization endpoint with a few
        parameters:

          - ``client_id``: the client ID set in ``oidc_config.client_id``
          - ``response_type``: ``code``
          - ``redirect_uri``: the callback URL ; ``{base url}/_synapse/oidc/callback``
          - ``scope``: the list of scopes set in ``oidc_config.scopes``
          - ``state``: a random string
          - ``nonce``: a random string

        In addition to redirecting the client, we are setting a cookie with
        a signed macaroon token containing the state, the nonce and the
        client_redirect_url params. Those are then checked when the client
        comes back from the provider.


        Args:
            request: the incoming request from the browser.
                We'll respond to it with a redirect and a cookie.
            client_redirect_url: the URL that we should redirect the client to
                when everything is done
        """

        state = generate_token()
        nonce = generate_token()

        cookie = self._generate_oidc_session_token(
            state=state, nonce=nonce, client_redirect_url=client_redirect_url.decode(),
        )
        request.addCookie(
            SESSION_COOKIE_NAME,
            cookie,
            path="/_synapse/oidc",
            max_age="3600",
            httpOnly=True,
            sameSite="lax",
        )

        metadata = await self.load_metadata()
        authorization_endpoint = metadata.get("authorization_endpoint")
        uri = prepare_grant_uri(
            authorization_endpoint,
            client_id=self._client_auth.client_id,
            response_type="code",
            redirect_uri=self._callback_url,
            scope=self._scopes,
            state=state,
            nonce=nonce,
        )
        request.redirect(uri)
        finish_request(request)
Exemple #7
0
    async def handle_submit_username_request(self, request: SynapseRequest,
                                             localpart: str,
                                             session_id: str) -> None:
        """Handle a request to the username-picker 'submit' endpoint

        Will serve an HTTP response to the request.

        Args:
            request: HTTP request
            localpart: localpart requested by the user
            session_id: ID of the username mapping session, extracted from a cookie
        """
        self._expire_old_sessions()
        session = self._username_mapping_sessions.get(session_id)
        if not session:
            logger.info("Couldn't find session id %s", session_id)
            raise SynapseError(400, "unknown session")

        logger.info("[session %s] Registering localpart %s", session_id,
                    localpart)

        attributes = UserAttributes(
            localpart=localpart,
            display_name=session.display_name,
            emails=session.emails,
        )

        # 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", session_id, user_id)

        # 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"/",
        )

        await self._auth_handler.complete_sso_login(
            user_id,
            request,
            session.client_redirect_url,
            session.extra_login_attributes,
        )
Exemple #8
0
 async def on_GET(self, request: SynapseRequest):
     client_redirect_url = parse_string(request,
                                        "redirectUrl",
                                        required=True,
                                        encoding=None)
     sso_url = await self._sso_handler.handle_redirect_request(
         request, client_redirect_url)
     logger.info("Redirecting to %s", sso_url)
     request.redirect(sso_url)
     finish_request(request)
    def test_with_request_context(self):
        """
        Information from the logging context request should be added to the JSON response.
        """
        handler = logging.StreamHandler(self.output)
        handler.setFormatter(JsonFormatter())
        handler.addFilter(LoggingContextFilter())
        logger = self.get_logger(handler)

        # A full request isn't needed here.
        site = Mock(spec=["site_tag", "server_version_string", "getResourceFor"])
        site.site_tag = "test-site"
        site.server_version_string = "Server v1"
        site.reactor = Mock()
        request = SynapseRequest(FakeChannel(site, None), site)
        # Call requestReceived to finish instantiating the object.
        request.content = BytesIO()
        # Partially skip some of the internal processing of SynapseRequest.
        request._started_processing = Mock()
        request.request_metrics = Mock(spec=["name"])
        with patch.object(Request, "render"):
            request.requestReceived(b"POST", b"/_matrix/client/versions", b"1.1")

        # Also set the requester to ensure the processing works.
        request.requester = "@foo:test"

        with LoggingContext(
            request.get_request_id(), parent_context=request.logcontext
        ):
            logger.info("Hello there, %s!", "wally")

        log = self.get_log_line()

        # The terse logger includes additional request information, if possible.
        expected_log_keys = [
            "log",
            "level",
            "namespace",
            "request",
            "ip_address",
            "site_tag",
            "requester",
            "authenticated_entity",
            "method",
            "url",
            "protocol",
            "user_agent",
        ]
        self.assertCountEqual(log.keys(), expected_log_keys)
        self.assertEqual(log["log"], "Hello there, wally!")
        self.assertTrue(log["request"].startswith("POST-"))
        self.assertEqual(log["ip_address"], "127.0.0.1")
        self.assertEqual(log["site_tag"], "test-site")
        self.assertEqual(log["requester"], "@foo:test")
        self.assertEqual(log["authenticated_entity"], "@foo:test")
        self.assertEqual(log["method"], "POST")
        self.assertEqual(log["url"], "/_matrix/client/versions")
        self.assertEqual(log["protocol"], "1.1")
        self.assertEqual(log["user_agent"], "")
Exemple #10
0
    def complete_sso_login(
        self,
        registered_user_id: str,
        request: SynapseRequest,
        client_redirect_url: str,
    ):
        """Having figured out a mxid for this user, complete the HTTP request

        Args:
            registered_user_id: The registered user ID to complete SSO login for.
            request: The request to complete.
            client_redirect_url: The URL to which to redirect the user at the end of the
                process.
        """
        # Create a login token
        login_token = self.macaroon_gen.generate_short_term_login_token(
            registered_user_id
        )

        # Append the login token to the original redirect URL (i.e. with its query
        # parameters kept intact) to build the URL to which the template needs to
        # redirect the users once they have clicked on the confirmation link.
        redirect_url = self.add_query_param_to_url(
            client_redirect_url, "loginToken", login_token
        )

        # if the client is whitelisted, we can redirect straight to it
        if client_redirect_url.startswith(self._whitelisted_sso_clients):
            request.redirect(redirect_url)
            finish_request(request)
            return

        # Otherwise, serve the redirect confirmation page.

        # Remove the query parameters from the redirect URL to get a shorter version of
        # it. This is only to display a human-readable URL in the template, but not the
        # URL we redirect users to.
        redirect_url_no_params = client_redirect_url.split("?")[0]

        html = self._sso_redirect_confirm_template.render(
            display_url=redirect_url_no_params,
            redirect_url=redirect_url,
            server_name=self._server_name,
        ).encode("utf-8")

        request.setResponseCode(200)
        request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
        request.setHeader(b"Content-Length", b"%d" % (len(html),))
        request.write(html)
        finish_request(request)
Exemple #11
0
    async def complete_sso_ui_auth(
        self,
        registered_user_id: str,
        session_id: str,
        request: SynapseRequest,
    ):
        """Having figured out a mxid for this user, complete the HTTP request

        Args:
            registered_user_id: The registered user ID to complete SSO login for.
            request: The request to complete.
            client_redirect_url: The URL to which to redirect the user at the end of the
                process.
        """
        # Mark the stage of the authentication as successful.
        # Save the user who authenticated with SSO, this will be used to ensure
        # that the account be modified is also the person who logged in.
        await self.store.mark_ui_auth_stage_complete(session_id, LoginType.SSO,
                                                     registered_user_id)

        # Render the HTML and return.
        html_bytes = self._sso_auth_success_template.encode("utf-8")
        request.setResponseCode(200)
        request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
        request.setHeader(b"Content-Length", b"%d" % (len(html_bytes), ))

        request.write(html_bytes)
        finish_request(request)
Exemple #12
0
    async def complete_sso_login(
        self,
        registered_user_id: str,
        request: SynapseRequest,
        client_redirect_url: str,
    ):
        """Having figured out a mxid for this user, complete the HTTP request

        Args:
            registered_user_id: The registered user ID to complete SSO login for.
            request: The request to complete.
            client_redirect_url: The URL to which to redirect the user at the end of the
                process.
        """
        # If the account has been deactivated, do not proceed with the login
        # flow.
        deactivated = await self.store.get_user_deactivated_status(
            registered_user_id)
        if deactivated:
            html_bytes = self._sso_account_deactivated_template.encode("utf-8")

            request.setResponseCode(403)
            request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
            request.setHeader(b"Content-Length", b"%d" % (len(html_bytes), ))
            request.write(html_bytes)
            finish_request(request)
            return

        self._complete_sso_login(registered_user_id, request,
                                 client_redirect_url)
Exemple #13
0
    async def _async_render(self, request: SynapseRequest) -> Tuple[int, Any]:
        callback, servlet_classname, group_dict = self._get_handler_for_request(
            request)

        request.is_render_cancellable = is_method_cancellable(callback)

        # Make sure we have an appropriate name for this handler in prometheus
        # (rather than the default of JsonResource).
        request.request_metrics.name = servlet_classname

        # Now trigger the callback. If it returns a response, we send it
        # here. If it throws an exception, that is handled by the wrapper
        # installed by @request_handler.
        kwargs = intern_dict({
            name: urllib.parse.unquote(value) if value else value
            for name, value in group_dict.items()
        })

        raw_callback_return = callback(request, **kwargs)

        # Is it synchronous? We'll allow this for now.
        if isinstance(raw_callback_return,
                      (defer.Deferred, types.CoroutineType)):
            callback_return = await raw_callback_return
        else:
            callback_return = raw_callback_return

        return callback_return
Exemple #14
0
    async def _async_render(
            self, request: SynapseRequest) -> Optional[Tuple[int, Any]]:
        """Delegates to `_async_render_<METHOD>` methods, or returns a 400 if
        no appropriate method exists. Can be overridden in sub classes for
        different routing.
        """
        # Treat HEAD requests as GET requests.
        request_method = request.method.decode("ascii")
        if request_method == "HEAD":
            request_method = "GET"

        method_handler = getattr(self, "_async_render_%s" % (request_method, ),
                                 None)
        if method_handler:
            request.is_render_cancellable = is_method_cancellable(
                method_handler)

            raw_callback_return = method_handler(request)

            # Is it synchronous? We'll allow this for now.
            if isawaitable(raw_callback_return):
                callback_return = await raw_callback_return
            else:
                callback_return = raw_callback_return

            return callback_return

        _unrecognised_request_handler(request)
Exemple #15
0
    async def on_POST(self, request: SynapseRequest):
        self._address_ratelimiter.ratelimit(request.getClientIP())

        params = parse_json_object_from_request(request)
        logger.info("----lookup bind--------param:%s" % (str(params)))

        bind_type = params["bind_type"]
        if bind_type is None:
            raise LoginError(410,
                             "bind_type field for bind openid is missing",
                             errcode=Codes.FORBIDDEN)

        requester = await self.auth.get_user_by_req(request)
        logger.info('------requester: %s' % (requester, ))
        user_id = requester.user
        logger.info('------user: %s' % (user_id, ))

        #complete unbind
        openid = await self.hs.get_datastore(
        ).get_external_id_for_user_provider(
            bind_type,
            str(user_id),
        )
        if openid is None:
            raise LoginError(400,
                             "openid not bind",
                             errcode=Codes.OPENID_NOT_BIND)

        return 200, {}
Exemple #16
0
    async def _check_auth_and_handle(self, request: SynapseRequest,
                                     **kwargs: Any) -> Tuple[int, JsonDict]:
        """Called on new incoming requests when caching is enabled. Checks
        if there is a cached response for the request and returns that,
        otherwise calls `_handle_request` and caches its response.
        """
        # We just use the txn_id here, but we probably also want to use the
        # other PATH_ARGS as well.

        # Check the authorization headers before handling the request.
        if self._replication_secret:
            self._check_auth(request)

        if self.CACHE:
            txn_id = kwargs.pop("txn_id")

            # We ignore the `@cancellable` flag, since cancellation wouldn't interupt
            # `_handle_request` and `ResponseCache` does not handle cancellation
            # correctly yet. In particular, there may be issues to do with logging
            # context lifetimes.

            return await self.response_cache.wrap(txn_id, self._handle_request,
                                                  request, **kwargs)

        # The `@cancellable` decorator may be applied to `_handle_request`. But we
        # told `HttpServer.register_paths` that our handler is `_check_auth_and_handle`,
        # so we have to set up the cancellable flag ourselves.
        request.is_render_cancellable = is_method_cancellable(
            self._handle_request)

        return await self._handle_request(request, **kwargs)
Exemple #17
0
    def complete_sso_login(self, registered_user_id: str,
                           request: SynapseRequest, client_redirect_url: str):
        """Having figured out a mxid for this user, complete the HTTP request

        Args:
            registered_user_id:
            request:
            client_redirect_url:
        """

        login_token = self._macaroon_gen.generate_short_term_login_token(
            registered_user_id)
        redirect_url = self._add_login_token_to_redirect_url(
            client_redirect_url, login_token)
        request.redirect(redirect_url)
        finish_request(request)
Exemple #18
0
        def post_json(destination, path, data):
            self.assertEqual(destination, self.hs.hostname)
            self.assertEqual(
                path, "/_matrix/key/v2/query",
            )

            channel = FakeChannel(self.site, self.reactor)
            req = SynapseRequest(channel)
            req.content = BytesIO(encode_canonical_json(data))

            req.requestReceived(
                b"POST", path.encode("utf-8"), b"1.1",
            )
            wait_until_result(self.reactor, req)
            self.assertEqual(channel.code, 200)
            resp = channel.json_body
            return resp
Exemple #19
0
    async def on_POST(self, request: SynapseRequest):
        login_submission = parse_json_object_from_request(request)
        logger.info('login type------%s' % (login_submission["type"], ))
        try:
            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    self._address_ratelimiter.ratelimit(request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission, appservice)
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_jwt_login(login_submission)
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_token_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.VER_CODE_EMAIL_TYPE:  # Email verification code to login
                result = await self._do_ver_code_email_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.VER_CODE_MSISDN_TYPE:  # Msisdn verification code to login
                result = await self._do_ver_code_msisdn_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.OAUTH2_ALIPAY_TYPE:  # OAuth2 of alipay login
                result = await self._do_oauth2_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.OAUTH2_WEIXIN_TYPE:  # OAuth2 of weixin login
                result = await self._do_oauth2_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.SSO_LDAP_TYPE:  # SSO of ldap login
                result = await self._do_sso_ldap_login(login_submission)
            else:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_other_login(login_submission)
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
Exemple #20
0
 def get_appservice_by_req(self, request: SynapseRequest) -> ApplicationService:
     token = self.get_access_token_from_request(request)
     service = self.store.get_app_service_by_token(token)
     if not service:
         logger.warning("Unrecognised appservice access token.")
         raise InvalidClientTokenError()
     request.requester = create_requester(service.sender, app_service=service)
     return service
Exemple #21
0
    def _complete_sso_login(
        self,
        registered_user_id: str,
        request: SynapseRequest,
        client_redirect_url: str,
    ):
        """
        The synchronous portion of complete_sso_login.

        This exists purely for backwards compatibility of synapse.module_api.ModuleApi.
        """
        # Create a login token
        login_token = self.macaroon_gen.generate_short_term_login_token(
            registered_user_id)

        # Append the login token to the original redirect URL (i.e. with its query
        # parameters kept intact) to build the URL to which the template needs to
        # redirect the users once they have clicked on the confirmation link.
        redirect_url = self.add_query_param_to_url(client_redirect_url,
                                                   "loginToken", login_token)

        # if the client is whitelisted, we can redirect straight to it
        if client_redirect_url.startswith(self._whitelisted_sso_clients):
            request.redirect(redirect_url)
            finish_request(request)
            return

        # Otherwise, serve the redirect confirmation page.

        # Remove the query parameters from the redirect URL to get a shorter version of
        # it. This is only to display a human-readable URL in the template, but not the
        # URL we redirect users to.
        redirect_url_no_params = client_redirect_url.split("?")[0]

        html_bytes = self._sso_redirect_confirm_template.render(
            display_url=redirect_url_no_params,
            redirect_url=redirect_url,
            server_name=self._server_name,
        ).encode("utf-8")

        request.setResponseCode(200)
        request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
        request.setHeader(b"Content-Length", b"%d" % (len(html_bytes), ))
        request.write(html_bytes)
        finish_request(request)
Exemple #22
0
    def make_notary_request(self, server_name: str, key_id: str) -> dict:
        """Send a GET request to the test server requesting the given key.

        Checks that the response is a 200 and returns the decoded json body.
        """
        channel = FakeChannel(self.site, self.reactor)
        req = SynapseRequest(channel)
        req.content = BytesIO(b"")
        req.requestReceived(
            b"GET",
            b"/_matrix/key/v2/query/%s/%s" %
            (server_name.encode("utf-8"), key_id.encode("utf-8")),
            b"1.1",
        )
        channel.await_result()
        self.assertEqual(channel.code, 200)
        resp = channel.json_body
        return resp
Exemple #23
0
    async def handle_ticket(
        self,
        request: SynapseRequest,
        ticket: str,
        client_redirect_url: Optional[str],
        session: Optional[str],
    ) -> None:
        """
        Called once the user has successfully authenticated with the SSO.
        Validates a CAS ticket sent by the client and completes the auth process.

        If the user interactive authentication session is provided, marks the
        UI Auth session as complete, then returns an HTML page notifying the
        user they are done.

        Otherwise, this registers the user if necessary, and then returns a
        redirect (with a login token) to the client.

        Args:
            request: the incoming request from the browser. We'll
                respond to it with a redirect or an HTML page.

            ticket: The CAS ticket provided by the client.

            client_redirect_url: the redirectUrl parameter from the `/cas/ticket` HTTP request, if given.
                This should be the same as the redirectUrl from the original `/login/sso/redirect` request.

            session: The session parameter from the `/cas/ticket` HTTP request, if given.
                This should be the UI Auth session id.
        """
        args = {}
        if client_redirect_url:
            args["redirectUrl"] = client_redirect_url
        if session:
            args["session"] = session
        username, user_display_name = await self._validate_ticket(ticket, args)

        # Pull out the user-agent and IP from the request.
        user_agent = request.get_user_agent("")
        ip_address = self.hs.get_ip_from_request(request)

        # Get the matrix ID from the CAS username.
        user_id = await self._map_cas_user_to_matrix_user(
            username, user_display_name, user_agent, ip_address)

        if session:
            await self._auth_handler.complete_sso_ui_auth(
                user_id,
                session,
                request,
            )
        else:
            # If this not a UI auth request than there must be a redirect URL.
            assert client_redirect_url

            await self._auth_handler.complete_sso_login(
                user_id, request, client_redirect_url)
Exemple #24
0
    async def _async_render_POST(self, request: SynapseRequest) -> None:
        requester = await self.auth.get_user_by_req(request)
        # TODO: The checks here are a bit late. The content will have
        # already been uploaded to a tmp file at this point
        content_length = request.getHeader("Content-Length")
        if content_length is None:
            raise SynapseError(msg="Request must specify a Content-Length",
                               code=400)
        if int(content_length) > self.max_upload_size:
            raise SynapseError(
                msg="Upload request body is too large",
                code=413,
                errcode=Codes.TOO_LARGE,
            )

        upload_name = parse_string(request, b"filename", encoding=None)
        if upload_name:
            try:
                upload_name = upload_name.decode("utf8")
            except UnicodeDecodeError:
                raise SynapseError(msg="Invalid UTF-8 filename parameter: %r" %
                                   (upload_name),
                                   code=400)

        # If the name is falsey (e.g. an empty byte string) ensure it is None.
        else:
            upload_name = None

        headers = request.requestHeaders

        if headers.hasHeader(b"Content-Type"):
            content_type_headers = headers.getRawHeaders(b"Content-Type")
            assert content_type_headers  # for mypy
            media_type = content_type_headers[0].decode("ascii")
        else:
            raise SynapseError(msg="Upload request missing 'Content-Type'",
                               code=400)

        # if headers.hasHeader(b"Content-Disposition"):
        #     disposition = headers.getRawHeaders(b"Content-Disposition")[0]
        # TODO(markjh): parse content-dispostion

        try:
            content = request.content  # type: IO  # type: ignore
            content_uri = await self.media_repo.create_content(
                media_type, upload_name, content, content_length,
                requester.user)
        except SpamMediaException:
            # For uploading of media we want to respond with a 400, instead of
            # the default 404, as that would just be confusing.
            raise SynapseError(400, "Bad content")

        logger.info("Uploaded content with URI %r", content_uri)

        respond_with_json(request,
                          200, {"content_uri": content_uri},
                          send_cors=True)
Exemple #25
0
    async def _async_render_POST(self, request: SynapseRequest):
        localpart = parse_string(request, "username", required=True)

        session_id = request.getCookie(USERNAME_MAPPING_SESSION_COOKIE_NAME)
        if not session_id:
            raise SynapseError(code=400, msg="missing session_id")

        await self._sso_handler.handle_submit_username_request(
            request, localpart, session_id.decode("ascii", errors="replace")
        )
Exemple #26
0
async def respond_with_responder(
    request: SynapseRequest,
    responder: "Optional[Responder]",
    media_type: str,
    file_size: Optional[int],
    upload_name: Optional[str] = None,
) -> None:
    """Responds to the request with given responder. If responder is None then
    returns 404.

    Args:
        request
        responder
        media_type: The media/content type.
        file_size: Size in bytes of the media. If not known it should be None
        upload_name: The name of the requested file, if any.
    """
    if request._disconnected:
        logger.warning(
            "Not sending response to request %s, already disconnected.",
            request)
        return

    if not responder:
        respond_404(request)
        return

    logger.debug("Responding to media request with responder %s", responder)
    add_file_headers(request, media_type, file_size, upload_name)
    try:
        with responder:
            await responder.write_to_consumer(request)
    except Exception as e:
        # The majority of the time this will be due to the client having gone
        # away. Unfortunately, Twisted simply throws a generic exception at us
        # in that case.
        logger.warning("Failed to write to consumer: %s %s", type(e), e)

        # Unregister the producer, if it has one, so Twisted doesn't complain
        if request.producer:
            request.unregisterProducer()

    finish_request(request)
Exemple #27
0
    async def on_POST(self, request: SynapseRequest):
        self._address_ratelimiter.ratelimit(request.getClientIP())

        params = parse_json_object_from_request(request)
        medium = params["medium"]
        address = params["address"]
        if medium is None:
            raise LoginError(410,
                             "medium field for get_ver_code is missing",
                             errcode=Codes.FORBIDDEN)
        if address is None:
            raise LoginError(410,
                             "address field for get_ver_code is missing",
                             errcode=Codes.FORBIDDEN)
        if medium not in ("email", "msisdn"):
            raise LoginError(411, "no support medium", errcode=Codes.FORBIDDEN)
        # verify medium and send to email
        existing_user_id = await self.hs.get_datastore(
        ).get_user_id_by_threepid(medium, address)
        if existing_user_id is None:
            if medium == "msisdn":
                raise SynapseError(400, "msisdn not bind",
                                   Codes.TEMPORARY_NOT_BIND_MSISDN)
            else:
                raise SynapseError(400, "email not bind", Codes.EMAIL_NOT_BIND)

        # call IS for verify email ver_code
        # identity_handler = self.hs.get_identity_handler()
        # result = await identity_handler.request_get_threepid_ver_code(self.hs.config.account_threepid_delegate_email, "email", email)
        # logger.info("result:%s" % (str(result)))
        # self.get_ver_code_cache.setdefault(email, result["verCode"])
        # ver_code_service_host = "192.168.15.4"
        # ver_code_service_port = "8080"
        # ver_code_service_send_api = "/api/services/auth/v1/code"
        sendSmsType = medium
        if sendSmsType == "msisdn":
            sendSmsType = "mobile"
        params = {"value": address, "type": sendSmsType}
        try:
            result = await self.http_client.post_json_get_json(
                self.hs.config.auth_baseurl + self.hs.config.auth_get_vercode,
                params,
            )
            logger.info("result: %s" % (str(result)))
            if result["code"] != 200:
                raise SynapseError(500, result["message"])
        except HttpResponseException as e:
            logger.info("Proxied getvercode failed: %r", e)
            raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(
                500,
                "Timed out contacting extral server:ver_code_send_service")
        return 200, {}
Exemple #28
0
    async def on_GET(self,
                     request: SynapseRequest,
                     idp_id: Optional[str] = None) -> None:
        if not self._public_baseurl:
            raise SynapseError(400, "SSO requires a valid public_baseurl")

        # if this isn't the expected hostname, redirect to the right one, so that we
        # get our cookies back.
        requested_uri = get_request_uri(request)
        baseurl_bytes = self._public_baseurl.encode("utf-8")
        if not requested_uri.startswith(baseurl_bytes):
            # swap out the incorrect base URL for the right one.
            #
            # The idea here is to redirect from
            #    https://foo.bar/whatever/_matrix/...
            # to
            #    https://public.baseurl/_matrix/...
            #
            i = requested_uri.index(b"/_matrix")
            new_uri = baseurl_bytes[:-1] + requested_uri[i:]
            logger.info(
                "Requested URI %s is not canonical: redirecting to %s",
                requested_uri.decode("utf-8", errors="replace"),
                new_uri.decode("utf-8", errors="replace"),
            )
            request.redirect(new_uri)
            finish_request(request)
            return

        client_redirect_url = parse_string(request,
                                           "redirectUrl",
                                           required=True,
                                           encoding=None)
        sso_url = await self._sso_handler.handle_redirect_request(
            request,
            client_redirect_url,
            idp_id,
        )
        logger.info("Redirecting to %s", sso_url)
        request.redirect(sso_url)
        finish_request(request)
Exemple #29
0
    async def _async_render_GET(self, request: SynapseRequest) -> None:
        set_cors_headers(request)
        set_corp_headers(request)
        request.setHeader(
            b"Content-Security-Policy",
            b"sandbox;"
            b" default-src 'none';"
            b" script-src 'none';"
            b" plugin-types application/pdf;"
            b" style-src 'unsafe-inline';"
            b" media-src 'self';"
            b" object-src 'self';",
        )
        # Limited non-standard form of CSP for IE11
        request.setHeader(b"X-Content-Security-Policy", b"sandbox;")
        request.setHeader(
            b"Referrer-Policy",
            b"no-referrer",
        )
        server_name, media_id, name = parse_media_id(request)
        if server_name == self.server_name:
            await self.media_repo.get_local_media(request, media_id, name)
        else:
            allow_remote = parse_boolean(request, "allow_remote", default=True)
            if not allow_remote:
                logger.info(
                    "Rejecting request for remote media %s/%s due to allow_remote",
                    server_name,
                    media_id,
                )
                respond_404(request)
                return

            await self.media_repo.get_remote_media(request, server_name,
                                                   media_id, name)
Exemple #30
0
def make_request(method, path, content=b""):
    """
    Make a web request using the given method and path, feed it the
    content, and return the Request and the Channel underneath.
    """
    if not isinstance(method, bytes):
        method = method.encode('ascii')

    if not isinstance(path, bytes):
        path = path.encode('ascii')

    # Decorate it to be the full path
    if not path.startswith(b"/_matrix"):
        path = b"/_matrix/client/r0/" + path
        path = path.replace(b"//", b"/")

    if isinstance(content, text_type):
        content = content.encode('utf8')

    site = FakeSite()
    channel = FakeChannel()

    req = SynapseRequest(site, channel)
    req.process = lambda: b""
    req.content = BytesIO(content)
    req.requestReceived(method, path, b"1.1")

    return req, channel