示例#1
0
    async def _async_render_GET(self, request):
        """On GET requests on /renew, retrieve the given renewal token from the request
        query parameters and, if it matches with an account, renew the account.
        """
        if b"token" not in request.args:
            raise SynapseError(400, "Missing renewal token")

        renewal_token = request.args[b"token"][0].decode("utf-8")

        (
            token_valid,
            token_stale,
            expiration_ts,
        ) = await self.renew_account(renewal_token)

        if token_valid:
            status_code = 200
            response = self._account_renewed_template.render(
                expiration_ts=expiration_ts)
        elif token_stale:
            status_code = 200
            response = self._account_previously_renewed_template.render(
                expiration_ts=expiration_ts)
        else:
            status_code = 404
            response = self._invalid_token_template.render()

        respond_with_html(request, status_code, response)
示例#2
0
文件: auth.py 项目: yvwvnacb/synapse
    async def on_GET(self, request, stagetype):
        session = parse_string(request, "session")
        if not session:
            raise SynapseError(400, "No session supplied")

        if stagetype == LoginType.RECAPTCHA:
            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,
            )
        elif stagetype == LoginType.TERMS:
            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),
            )

        elif stagetype == LoginType.SSO:
            # Display a confirmation page which prompts the user to
            # re-authenticate with their SSO provider.
            html = await self.auth_handler.start_sso_ui_auth(request, session)

        else:
            raise SynapseError(404, "Unknown auth stage type")

        # Render the HTML and return.
        respond_with_html(request, 200, html)
        return None
示例#3
0
    async def _async_render_GET(self, request: Request) -> None:
        try:
            session_id = get_username_mapping_session_cookie_from_request(
                request)
            session = self._sso_handler.get_mapping_session(session_id)
        except SynapseError as e:
            logger.warning("Error fetching session: %s", e)
            self._sso_handler.render_error(request,
                                           "bad_session",
                                           e.msg,
                                           code=e.code)
            return

        user_id = UserID(session.chosen_localpart, self._server_name)
        user_profile = {
            "display_name": session.display_name,
        }

        template_params = {
            "user_id": user_id.to_string(),
            "user_profile": user_profile,
            "consent_version": self._consent_version,
            "terms_url": "/_matrix/consent?v=%s" % (self._consent_version, ),
        }

        template = self._jinja_env.get_template("sso_new_user_consent.html")
        html = template.render(template_params)
        respond_with_html(request, 200, html)
示例#4
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:
            respond_with_html(request, 403,
                              self._sso_account_deactivated_template)
            return

        self._complete_sso_login(registered_user_id, request,
                                 client_redirect_url)
示例#5
0
    async def on_GET(self, request):
        if b"token" not in request.args:
            raise SynapseError(400, "Missing renewal token")
        renewal_token = request.args[b"token"][0]

        (
            token_valid,
            token_stale,
            expiration_ts,
        ) = await self.account_activity_handler.renew_account(
            renewal_token.decode("utf8"))

        if token_valid:
            status_code = 200
            response = self.account_renewed_template.render(
                expiration_ts=expiration_ts)
        elif token_stale:
            status_code = 200
            response = self.account_previously_renewed_template.render(
                expiration_ts=expiration_ts)
        else:
            status_code = 404
            response = self.invalid_token_template.render(
                expiration_ts=expiration_ts)

        respond_with_html(request, status_code, response)
示例#6
0
    async def _async_render_GET(self, request: Request) -> None:
        try:
            session_id = get_username_mapping_session_cookie_from_request(
                request)
            session = self._sso_handler.get_mapping_session(session_id)
        except SynapseError as e:
            logger.warning("Error fetching session: %s", e)
            self._sso_handler.render_error(request,
                                           "bad_session",
                                           e.msg,
                                           code=e.code)
            return

        # The configuration might mandate going through this step to validate an
        # automatically generated localpart, so session.chosen_localpart might already
        # be set.
        localpart = ""
        if session.chosen_localpart is not None:
            localpart = session.chosen_localpart

        idp_id = session.auth_provider_id
        template_params = {
            "idp": self._sso_handler.get_identity_providers()[idp_id],
            "user_attributes": {
                "display_name": session.display_name,
                "emails": session.emails,
                "localpart": localpart,
            },
        }

        template = self._jinja_env.get_template(
            "sso_auth_account_details.html")
        html = template.render(template_params)
        respond_with_html(request, 200, html)
示例#7
0
    async def _async_render_GET(self, request: Request) -> None:
        try:
            session_id = get_username_mapping_session_cookie_from_request(
                request)
            session = self._sso_handler.get_mapping_session(session_id)
        except SynapseError as e:
            logger.warning("Error fetching session: %s", e)
            self._sso_handler.render_error(request,
                                           "bad_session",
                                           e.msg,
                                           code=e.code)
            return

        idp_id = session.auth_provider_id
        template_params = {
            "idp": self._sso_handler.get_identity_providers()[idp_id],
            "user_attributes": {
                "display_name": session.display_name,
                "emails": session.emails,
            },
        }

        template = self._jinja_env.get_template(
            "sso_auth_account_details.html")
        html = template.render(template_params)
        respond_with_html(request, 200, html)
示例#8
0
 def _render_template(self, request, template_name, **template_args):
     # get_template checks for ".." so we don't need to worry too much
     # about path traversal here.
     template_html = self._jinja_env.get_template(
         path.join(TEMPLATE_LANGUAGE, template_name))
     html = template_html.render(**template_args)
     respond_with_html(request, 200, html)
示例#9
0
文件: auth.py 项目: waylon531/synapse
    async def on_GET(self, request, stagetype):
        session = parse_string(request, "session")
        if not session:
            raise SynapseError(400, "No session supplied")

        if stagetype == LoginType.RECAPTCHA:
            html = RECAPTCHA_TEMPLATE % {
                "session": session,
                "myurl": "%s/r0/auth/%s/fallback/web"
                % (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
                "sitekey": self.hs.config.recaptcha_public_key,
            }
        elif stagetype == LoginType.TERMS:
            html = TERMS_TEMPLATE % {
                "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),
            }

        elif stagetype == LoginType.SSO:
            # Display a confirmation page which prompts the user to
            # re-authenticate with their SSO provider.
            if self._cas_enabled:
                # Generate a request to CAS that redirects back to an endpoint
                # to verify the successful authentication.
                sso_redirect_url = self._cas_handler.get_redirect_url(
                    {"session": session},
                )

            elif self._saml_enabled:
                # Some SAML identity providers (e.g. Google) require a
                # RelayState parameter on requests. It is not necessary here, so
                # pass in a dummy redirect URL (which will never get used).
                client_redirect_url = b"unused"
                sso_redirect_url = self._saml_handler.handle_redirect_request(
                    client_redirect_url, session
                )

            elif self._oidc_enabled:
                client_redirect_url = b""
                sso_redirect_url = await self._oidc_handler.handle_redirect_request(
                    request, client_redirect_url, session
                )

            else:
                raise SynapseError(400, "Homeserver not configured for SSO.")

            html = await self.auth_handler.start_sso_ui_auth(sso_redirect_url, session)

        else:
            raise SynapseError(404, "Unknown auth stage type")

        # Render the HTML and return.
        respond_with_html(request, 200, html)
        return None
示例#10
0
文件: auth.py 项目: xiu/synapse
    async def on_POST(self, request, stagetype):

        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}

            success = await self.auth_handler.add_oob_auth(
                LoginType.RECAPTCHA, authdict,
                self.hs.get_ip_from_request(request))

            if success:
                html = self.success_template.render()
            else:
                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,
                )
        elif stagetype == LoginType.TERMS:
            authdict = {"session": session}

            success = await self.auth_handler.add_oob_auth(
                LoginType.TERMS, authdict,
                self.hs.get_ip_from_request(request))

            if success:
                html = self.success_template.render()
            else:
                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),
                )
        elif stagetype == LoginType.SSO:
            # The SSO fallback workflow should not post here,
            raise SynapseError(
                404, "Fallback SSO auth does not support POST requests.")
        else:
            raise SynapseError(404, "Unknown auth stage type")

        # Render the HTML and return.
        respond_with_html(request, 200, html)
        return None
示例#11
0
 async def _serve_id_picker(self, request: SynapseRequest,
                            client_redirect_url: str) -> None:
     # otherwise, serve up the IdP picker
     providers = self._sso_handler.get_identity_providers()
     html = self._sso_login_idp_picker_template.render(
         redirect_url=client_redirect_url,
         server_name=self._server_name,
         providers=providers.values(),
     )
     respond_with_html(request, 200, html)
示例#12
0
    async def on_GET(self, request):
        if self.config.threepid_behaviour_email == ThreepidBehaviour.OFF:
            if self.config.local_threepid_handling_disabled_due_to_email_config:
                logger.warning(
                    "Adding emails have been disabled due to lack of an email config"
                )
            raise SynapseError(
                400, "Adding an email to your account is disabled on this server"
            )
        elif self.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
            raise SynapseError(
                400,
                "This homeserver is not validating threepids. Use an identity server "
                "instead.",
            )

        sid = parse_string(request, "sid", required=True)
        token = parse_string(request, "token", required=True)
        client_secret = parse_string(request, "client_secret", required=True)
        assert_valid_client_secret(client_secret)

        # Attempt to validate a 3PID session
        try:
            # Mark the session as valid
            next_link = await self.store.validate_threepid_session(
                sid, client_secret, token, self.clock.time_msec()
            )

            # Perform a 302 redirect if next_link is set
            if next_link:
                if next_link.startswith("file:///"):
                    logger.warning(
                        "Not redirecting to next_link as it is a local file: address"
                    )
                else:
                    request.setResponseCode(302)
                    request.setHeader("Location", next_link)
                    finish_request(request)
                    return None

            # Otherwise show the success template
            html = self.config.email_add_threepid_template_success_html_content
            status_code = 200
        except ThreepidValidationError as e:
            status_code = e.code

            # Show a failure page with a reason
            template_vars = {"failure_reason": e.msg}
            html = self._failure_email_template.render(**template_vars)

        respond_with_html(request, status_code, html)
示例#13
0
    async def on_GET(self, request, medium):
        # We currently only handle threepid token submissions for email
        if medium != "email":
            raise SynapseError(
                400, "This medium is currently not supported for password resets"
            )
        if self.config.threepid_behaviour_email == ThreepidBehaviour.OFF:
            if self.config.local_threepid_handling_disabled_due_to_email_config:
                logger.warning(
                    "Password reset emails have been disabled due to lack of an email config"
                )
            raise SynapseError(
                400, "Email-based password resets are disabled on this server"
            )

        sid = parse_string(request, "sid", required=True)
        token = parse_string(request, "token", required=True)
        client_secret = parse_string(request, "client_secret", required=True)
        assert_valid_client_secret(client_secret)

        # Attempt to validate a 3PID session
        try:
            # Mark the session as valid
            next_link = await self.store.validate_threepid_session(
                sid, client_secret, token, self.clock.time_msec()
            )

            # Perform a 302 redirect if next_link is set
            if next_link:
                if next_link.startswith("file:///"):
                    logger.warning(
                        "Not redirecting to next_link as it is a local file: address"
                    )
                else:
                    request.setResponseCode(302)
                    request.setHeader("Location", next_link)
                    finish_request(request)
                    return None

            # Otherwise show the success template
            html = self.config.email_password_reset_template_success_html_content
            status_code = 200
        except ThreepidValidationError as e:
            status_code = e.code

            # Show a failure page with a reason
            template_vars = {"failure_reason": e.msg}
            html = self._failure_email_template.render(**template_vars)

        respond_with_html(request, status_code, html)
示例#14
0
    async def on_GET(self, request):
        if b"token" not in request.args:
            raise SynapseError(400, "Missing renewal token")
        renewal_token = request.args[b"token"][0]

        token_valid = await self.account_activity_handler.renew_account(
            renewal_token.decode("utf8"))

        if token_valid:
            status_code = 200
            response = self.success_html
        else:
            status_code = 404
            response = self.failure_html

        respond_with_html(request, status_code, response)
示例#15
0
    def _render_error(self,
                      request,
                      error: str,
                      error_description: Optional[str] = None) -> None:
        """Render the error template and respond to the request with it.

        This is used to show errors to the user. The template of this page can
        be found under `synapse/res/templates/sso_error.html`.

        Args:
            request: The incoming request from the browser.
                We'll respond with an HTML page describing the error.
            error: A technical identifier for this error.
            error_description: A human-readable description of the error.
        """
        html = self._error_template.render(error=error,
                                           error_description=error_description)
        respond_with_html(request, 400, html)
示例#16
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 = self._sso_redirect_confirm_template.render(
            display_url=redirect_url_no_params,
            redirect_url=redirect_url,
            server_name=self._server_name,
        )
        respond_with_html(request, 200, html)
示例#17
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 = self._sso_auth_success_template
        respond_with_html(request, 200, html)
    async def on_GET(self, request: Request) -> None:
        renewal_token = parse_string(request, "token", required=True)

        (
            token_valid,
            token_stale,
            expiration_ts,
        ) = await self.account_activity_handler.renew_account(renewal_token)

        if token_valid:
            status_code = 200
            response = self.account_renewed_template.render(expiration_ts=expiration_ts)
        elif token_stale:
            status_code = 200
            response = self.account_previously_renewed_template.render(
                expiration_ts=expiration_ts
            )
        else:
            status_code = 404
            response = self.invalid_token_template.render(expiration_ts=expiration_ts)

        respond_with_html(request, status_code, response)
示例#19
0
    async def _async_render_GET(self, request: Request) -> None:
        try:
            session_id = get_username_mapping_session_cookie_from_request(
                request)
            session = self._sso_handler.get_mapping_session(session_id)
        except SynapseError as e:
            logger.warning("Error fetching session: %s", e)
            self._sso_handler.render_error(request,
                                           "bad_session",
                                           e.msg,
                                           code=e.code)
            return

        # It should be impossible to get here without having first been through
        # the pick-a-username step, which ensures chosen_localpart gets set.
        if not session.chosen_localpart:
            logger.warning("Session has no user name selected")
            self._sso_handler.render_error(request,
                                           "no_user",
                                           "No user name has been selected.",
                                           code=400)
            return

        user_id = UserID(session.chosen_localpart, self._server_name)
        user_profile = {
            "display_name": session.display_name,
        }

        template_params = {
            "user_id": user_id.to_string(),
            "user_profile": user_profile,
            "consent_version": self._consent_version,
            "terms_url": "/_matrix/consent?v=%s" % (self._consent_version, ),
        }

        template = self._jinja_env.get_template("sso_new_user_consent.html")
        html = template.render(template_params)
        respond_with_html(request, 200, html)
示例#20
0
文件: sso.py 项目: yvwvnacb/synapse
    async def complete_sso_ui_auth_request(
        self,
        auth_provider_id: str,
        remote_user_id: str,
        ui_auth_session_id: str,
        request: Request,
    ) -> None:
        """
        Given an SSO ID, retrieve the user ID for it and complete UIA.

        Note that this requires that the user is mapped in the "user_external_ids"
        table. This will be the case if they have ever logged in via SAML or OIDC in
        recentish synapse versions, but may not be for older users.

        Args:
            auth_provider_id: A unique identifier for this SSO provider, e.g.
                "oidc" or "saml".
            remote_user_id: The unique identifier from the SSO provider.
            ui_auth_session_id: The ID of the user-interactive auth session.
            request: The request to complete.
        """

        user_id = await self.get_sso_user_by_remote_user_id(
            auth_provider_id,
            remote_user_id,
        )

        user_id_to_verify = await self._auth_handler.get_session_data(
            ui_auth_session_id,
            UIAuthSessionDataConstants.REQUEST_USER_ID)  # type: str

        if not user_id:
            logger.warning(
                "Remote user %s/%s has not previously logged in here: UIA will fail",
                auth_provider_id,
                remote_user_id,
            )
        elif user_id != user_id_to_verify:
            logger.warning(
                "Remote user %s/%s mapped onto incorrect user %s: UIA will fail",
                auth_provider_id,
                remote_user_id,
                user_id,
            )
        else:
            # success!
            # Mark the stage of the authentication as successful.
            await self._store.mark_ui_auth_stage_complete(
                ui_auth_session_id, LoginType.SSO, user_id)

            # Render the HTML confirmation page and return.
            html = self._sso_auth_success_template
            respond_with_html(request, 200, html)
            return

        # the user_id didn't match: mark the stage of the authentication as unsuccessful
        await self._store.mark_ui_auth_stage_complete(ui_auth_session_id,
                                                      LoginType.SSO, "")

        # render an error page.
        html = self._bad_user_template.render(
            server_name=self._server_name,
            user_id_to_verify=user_id_to_verify,
        )
        respond_with_html(request, 200, html)
示例#21
0
文件: auth.py 项目: samuelyi/synapse
    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.getClientAddress().host)
            except LoginError as e:
                # Authentication failed, let user try again
                html = self.recaptcha_template.render(
                    session=session,
                    myurl="%s/v3/auth/%s/fallback/web" %
                    (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
                    sitekey=self.hs.config.captcha.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.getClientAddress().host)
            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.server.public_baseurl,
                        self.hs.config.consent.user_consent_version,
                    ),
                    myurl="%s/v3/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.getClientAddress().host,
                )
            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