예제 #1
0
    def __init__(self, hs: "HomeServer", pusher_config: PusherConfig, mailer: Mailer):
        super().__init__(hs, pusher_config)
        self.mailer = mailer

        self.store = self.hs.get_datastore()
        self.email = pusher_config.pushkey
        self.timed_call: Optional[IDelayedCall] = None
        self.throttle_params: Dict[str, ThrottleParams] = {}
        self._inited = False

        self._is_processing = False

        # Make sure that the email is valid.
        try:
            validate_email(self.email)
        except ValueError:
            raise PusherConfigException("Invalid email")
예제 #2
0
파일: register.py 프로젝트: Fnux/synapse
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        if self.hs.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
            if (self.hs.config.email.
                    local_threepid_handling_disabled_due_to_email_config):
                logger.warning(
                    "Email registration has been disabled due to lack of email config"
                )
            raise SynapseError(
                400,
                "Email-based registration has been disabled on this server")
        body = parse_json_object_from_request(request)

        assert_params_in_dict(body, ["client_secret", "email", "send_attempt"])

        # Extract params from body
        client_secret = body["client_secret"]
        assert_valid_client_secret(client_secret)

        # For emails, canonicalise the address.
        # We store all email addresses canonicalised in the DB.
        # (See on_POST in EmailThreepidRequestTokenRestServlet
        # in synapse/rest/client/account.py)
        try:
            email = validate_email(body["email"])
        except ValueError as e:
            raise SynapseError(400, str(e))
        send_attempt = body["send_attempt"]
        next_link = body.get("next_link")  # Optional param

        if not check_3pid_allowed(self.hs, "email", email):
            raise SynapseError(
                403,
                "Your email domain is not authorized to register on this server",
                Codes.THREEPID_DENIED,
            )

        await self.identity_handler.ratelimit_request_token_requests(
            request, "email", email)

        existing_user_id = await self.hs.get_datastore(
        ).get_user_id_by_threepid("email", email)

        if existing_user_id is not None:
            if self.hs.config.server.request_token_inhibit_3pid_errors:
                # Make the client think the operation succeeded. See the rationale in the
                # comments for request_token_inhibit_3pid_errors.
                # Also wait for some random amount of time between 100ms and 1s to make it
                # look like we did something.
                await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
                return 200, {"sid": random_string(16)}

            raise SynapseError(400, "Email is already in use",
                               Codes.THREEPID_IN_USE)

        if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
            assert self.hs.config.registration.account_threepid_delegate_email

            # Have the configured identity server handle the request
            ret = await self.identity_handler.requestEmailToken(
                self.hs.config.registration.account_threepid_delegate_email,
                email,
                client_secret,
                send_attempt,
                next_link,
            )
        else:
            # Send registration emails from Synapse
            sid = await self.identity_handler.send_threepid_validation(
                email,
                client_secret,
                send_attempt,
                self.mailer.send_registration_mail,
                next_link,
            )

            # Wrap the session id in a JSON object
            ret = {"sid": sid}

        threepid_send_requests.labels(type="email",
                                      reason="register").observe(send_attempt)

        return 200, ret
예제 #3
0
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
            if self.config.email.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")

        body = parse_json_object_from_request(request)
        assert_params_in_dict(body, ["client_secret", "email", "send_attempt"])
        client_secret = body["client_secret"]
        assert_valid_client_secret(client_secret)

        # Canonicalise the email address. The addresses are all stored canonicalised
        # in the database.
        # This ensures that the validation email is sent to the canonicalised address
        # as it will later be entered into the database.
        # Otherwise the email will be sent to "*****@*****.**" and stored as
        # "*****@*****.**" in database.
        try:
            email = validate_email(body["email"])
        except ValueError as e:
            raise SynapseError(400, str(e))
        send_attempt = body["send_attempt"]
        next_link = body.get("next_link")  # Optional param

        if not await check_3pid_allowed(self.hs, "email", email):
            raise SynapseError(
                403,
                "Your email is not authorized on this server",
                Codes.THREEPID_DENIED,
            )

        await self.identity_handler.ratelimit_request_token_requests(
            request, "email", email)

        if next_link:
            # Raise if the provided next_link value isn't valid
            assert_valid_next_link(self.hs, next_link)

        existing_user_id = await self.store.get_user_id_by_threepid(
            "email", email)

        if existing_user_id is not None:
            if self.config.server.request_token_inhibit_3pid_errors:
                # Make the client think the operation succeeded. See the rationale in the
                # comments for request_token_inhibit_3pid_errors.
                # Also wait for some random amount of time between 100ms and 1s to make it
                # look like we did something.
                await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
                return 200, {"sid": random_string(16)}

            raise SynapseError(400, "Email is already in use",
                               Codes.THREEPID_IN_USE)

        if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
            assert self.hs.config.registration.account_threepid_delegate_email

            # Have the configured identity server handle the request
            ret = await self.identity_handler.requestEmailToken(
                self.hs.config.registration.account_threepid_delegate_email,
                email,
                client_secret,
                send_attempt,
                next_link,
            )
        else:
            # Send threepid validation emails from Synapse
            sid = await self.identity_handler.send_threepid_validation(
                email,
                client_secret,
                send_attempt,
                self.mailer.send_add_threepid_mail,
                next_link,
            )

            # Wrap the session id in a JSON object
            ret = {"sid": sid}

        threepid_send_requests.labels(
            type="email", reason="add_threepid").observe(send_attempt)

        return 200, ret
예제 #4
0
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
            if self.config.email.local_threepid_handling_disabled_due_to_email_config:
                logger.warning(
                    "User password resets have been disabled due to lack of email config"
                )
            raise SynapseError(
                400,
                "Email-based password resets have been disabled on this server"
            )

        body = parse_json_object_from_request(request)

        assert_params_in_dict(body, ["client_secret", "email", "send_attempt"])

        # Extract params from body
        client_secret = body["client_secret"]
        assert_valid_client_secret(client_secret)

        # Canonicalise the email address. The addresses are all stored canonicalised
        # in the database. This allows the user to reset his password without having to
        # know the exact spelling (eg. upper and lower case) of address in the database.
        # Stored in the database "*****@*****.**"
        # User requests with "*****@*****.**" would raise a Not Found error
        try:
            email = validate_email(body["email"])
        except ValueError as e:
            raise SynapseError(400, str(e))
        send_attempt = body["send_attempt"]
        next_link = body.get("next_link")  # Optional param

        if next_link:
            # Raise if the provided next_link value isn't valid
            assert_valid_next_link(self.hs, next_link)

        await self.identity_handler.ratelimit_request_token_requests(
            request, "email", email)

        # The email will be sent to the stored address.
        # This avoids a potential account hijack by requesting a password reset to
        # an email address which is controlled by the attacker but which, after
        # canonicalisation, matches the one in our database.
        existing_user_id = await self.hs.get_datastore(
        ).get_user_id_by_threepid("email", email)

        if existing_user_id is None:
            if self.config.server.request_token_inhibit_3pid_errors:
                # Make the client think the operation succeeded. See the rationale in the
                # comments for request_token_inhibit_3pid_errors.
                # Also wait for some random amount of time between 100ms and 1s to make it
                # look like we did something.
                await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
                return 200, {"sid": random_string(16)}

            raise SynapseError(400, "Email not found",
                               Codes.THREEPID_NOT_FOUND)

        if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
            assert self.hs.config.registration.account_threepid_delegate_email

            # Have the configured identity server handle the request
            ret = await self.identity_handler.requestEmailToken(
                self.hs.config.registration.account_threepid_delegate_email,
                email,
                client_secret,
                send_attempt,
                next_link,
            )
        else:
            # Send password reset emails from Synapse
            sid = await self.identity_handler.send_threepid_validation(
                email,
                client_secret,
                send_attempt,
                self.mailer.send_password_reset_mail,
                next_link,
            )

            # Wrap the session id in a JSON object
            ret = {"sid": sid}

        threepid_send_requests.labels(
            type="email", reason="password_reset").observe(send_attempt)

        return 200, ret
예제 #5
0
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        body = parse_json_object_from_request(request)

        # we do basic sanity checks here because the auth layer will store these
        # in sessions. Pull out the new password provided to us.
        new_password = body.pop("new_password", None)
        if new_password is not None:
            if not isinstance(new_password, str) or len(new_password) > 512:
                raise SynapseError(400, "Invalid password")
            self.password_policy_handler.validate_password(new_password)

        # there are two possibilities here. Either the user does not have an
        # access token, and needs to do a password reset; or they have one and
        # need to validate their identity.
        #
        # In the first case, we offer a couple of means of identifying
        # themselves (email and msisdn, though it's unclear if msisdn actually
        # works).
        #
        # In the second case, we require a password to confirm their identity.

        requester = None
        if self.auth.has_access_token(request):
            requester = await self.auth.get_user_by_req(request)
            # blindly trust ASes without UI-authing them
            try:
                (
                    params,
                    session_id,
                ) = await self.auth_handler.validate_user_via_ui_auth(
                    requester,
                    request,
                    body,
                    "modify your account password",
                )
            except InteractiveAuthIncompleteError as e:
                # The user needs to provide more steps to complete auth, but
                # they're not required to provide the password again.
                #
                # If a password is available now, hash the provided password and
                # store it for later.
                if new_password:
                    new_password_hash = await self.auth_handler.hash(
                        new_password)
                    await self.auth_handler.set_session_data(
                        e.session_id,
                        UIAuthSessionDataConstants.PASSWORD_HASH,
                        new_password_hash,
                    )
                raise
            user_id = requester.user.to_string()
        else:
            try:
                result, params, session_id = await self.auth_handler.check_ui_auth(
                    [[LoginType.EMAIL_IDENTITY]],
                    request,
                    body,
                    "modify your account password",
                )
            except InteractiveAuthIncompleteError as e:
                # The user needs to provide more steps to complete auth, but
                # they're not required to provide the password again.
                #
                # If a password is available now, hash the provided password and
                # store it for later.
                if new_password:
                    new_password_hash = await self.auth_handler.hash(
                        new_password)
                    await self.auth_handler.set_session_data(
                        e.session_id,
                        UIAuthSessionDataConstants.PASSWORD_HASH,
                        new_password_hash,
                    )
                raise

            if LoginType.EMAIL_IDENTITY in result:
                threepid = result[LoginType.EMAIL_IDENTITY]
                if "medium" not in threepid or "address" not in threepid:
                    raise SynapseError(500, "Malformed threepid")
                if threepid["medium"] == "email":
                    # For emails, canonicalise the address.
                    # We store all email addresses canonicalised in the DB.
                    # (See add_threepid in synapse/handlers/auth.py)
                    try:
                        threepid["address"] = validate_email(
                            threepid["address"])
                    except ValueError as e:
                        raise SynapseError(400, str(e))
                # if using email, we must know about the email they're authing with!
                threepid_user_id = await self.datastore.get_user_id_by_threepid(
                    threepid["medium"], threepid["address"])
                if not threepid_user_id:
                    raise SynapseError(404, "Email address not found",
                                       Codes.NOT_FOUND)
                user_id = threepid_user_id
            else:
                logger.error("Auth succeeded but no known type! %r",
                             result.keys())
                raise SynapseError(500, "", Codes.UNKNOWN)

        # If we have a password in this request, prefer it. Otherwise, use the
        # password hash from an earlier request.
        if new_password:
            password_hash: Optional[str] = await self.auth_handler.hash(
                new_password)
        elif session_id is not None:
            password_hash = await self.auth_handler.get_session_data(
                session_id, UIAuthSessionDataConstants.PASSWORD_HASH, None)
        else:
            # UI validation was skipped, but the request did not include a new
            # password.
            password_hash = None
        if not password_hash:
            raise SynapseError(400, "Missing params: password",
                               Codes.MISSING_PARAM)

        logout_devices = params.get("logout_devices", True)

        await self._set_password_handler.set_password(user_id, password_hash,
                                                      logout_devices,
                                                      requester)

        return 200, {}