Exemplo n.º 1
0
    def requestEmailToken(self, id_server, email, client_secret, send_attempt, **kwargs):
        yield run_on_reactor()

        if not self._should_trust_id_server(id_server):
            raise SynapseError(
                400, "Untrusted ID server '%s'" % id_server,
                Codes.SERVER_NOT_TRUSTED
            )

        params = {
            'email': email,
            'client_secret': client_secret,
            'send_attempt': send_attempt,
        }
        params.update(kwargs)

        try:
            data = yield self.http_client.post_urlencoded_get_json(
                "https://%s%s" % (
                    id_server,
                    "/_matrix/identity/api/v1/validate/email/requestToken"
                ),
                params
            )
            defer.returnValue(data)
        except CodeMessageException as e:
            logger.info("Proxied requestToken failed: %r", e)
            raise e
Exemplo n.º 2
0
    def on_new_receipts(self, min_stream_id, max_stream_id, affected_room_ids):
        yield run_on_reactor()
        try:
            # Need to subtract 1 from the minimum because the lower bound here
            # is not inclusive
            updated_receipts = yield self.store.get_all_updated_receipts(
                min_stream_id - 1, max_stream_id
            )
            # This returns a tuple, user_id is at index 3
            users_affected = set([r[3] for r in updated_receipts])

            deferreds = []

            for u in users_affected:
                if u in self.pushers:
                    for p in self.pushers[u].values():
                        deferreds.append(
                            run_in_background(
                                p.on_new_receipts,
                                min_stream_id, max_stream_id,
                            )
                        )

            yield make_deferred_yieldable(
                defer.gatherResults(deferreds, consumeErrors=True),
            )
        except Exception:
            logger.exception("Exception in pusher on_new_receipts")
Exemplo n.º 3
0
    def check_username(self, localpart, guest_access_token=None):
        yield run_on_reactor()

        if urllib.quote(localpart.encode('utf-8')) != localpart:
            raise SynapseError(
                400,
                "User ID can only contain characters a-z, 0-9, or '_-./'",
                Codes.INVALID_USERNAME
            )

        user = UserID(localpart, self.hs.hostname)
        user_id = user.to_string()

        yield self.check_user_id_is_valid(user_id)

        users = yield self.store.get_users_by_id_case_insensitive(user_id)
        if users:
            if not guest_access_token:
                raise SynapseError(
                    400,
                    "User ID already taken.",
                    errcode=Codes.USER_IN_USE,
                )
            user_data = yield self.auth.get_user_from_macaroon(guest_access_token)
            if not user_data["is_guest"] or user_data["user"].localpart != localpart:
                raise AuthError(
                    403,
                    "Cannot register taken user ID without valid guest "
                    "credentials for that user.",
                    errcode=Codes.FORBIDDEN,
                )
Exemplo n.º 4
0
    def on_POST(self, request):
        yield run_on_reactor()

        body = parse_json_object_from_request(request)

        threePidCreds = body.get("threePidCreds")
        threePidCreds = body.get("three_pid_creds", threePidCreds)
        if threePidCreds is None:
            raise SynapseError(400, "Missing param", Codes.MISSING_PARAM)

        requester = yield self.auth.get_user_by_req(request)
        user_id = requester.user.to_string()

        threepid = yield self.identity_handler.threepid_from_creds(threePidCreds)

        if not threepid:
            raise SynapseError(400, "Failed to auth 3pid", Codes.THREEPID_AUTH_FAILED)

        for reqd in ["medium", "address", "validated_at"]:
            if reqd not in threepid:
                logger.warn("Couldn't add 3pid: invalid response from ID sevrer")
                raise SynapseError(500, "Invalid response from ID Server")

        yield self.auth_handler.add_threepid(user_id, threepid["medium"], threepid["address"], threepid["validated_at"])

        if "bind" in body and body["bind"]:
            logger.debug("Binding emails %s to %s", threepid, user_id)
            yield self.identity_handler.bind_threepid(threePidCreds, user_id)

        defer.returnValue((200, {}))
Exemplo n.º 5
0
    def requestMsisdnToken(
            self, id_server, country, phone_number,
            client_secret, send_attempt, **kwargs
    ):
        yield run_on_reactor()

        if not self._should_trust_id_server(id_server):
            raise SynapseError(
                400, "Untrusted ID server '%s'" % id_server,
                Codes.SERVER_NOT_TRUSTED
            )

        params = {
            'country': country,
            'phone_number': phone_number,
            'client_secret': client_secret,
            'send_attempt': send_attempt,
        }
        params.update(kwargs)

        try:
            data = yield self.http_client.post_json_get_json(
                "https://%s%s" % (
                    id_server,
                    "/_matrix/identity/api/v1/validate/msisdn/requestToken"
                ),
                params
            )
            defer.returnValue(data)
        except MatrixCodeMessageException as e:
            logger.info("Proxied requestToken failed with Matrix error: %r", e)
            raise SynapseError(e.code, e.msg, e.errcode)
        except CodeMessageException as e:
            logger.info("Proxied requestToken failed: %r", e)
            raise e
Exemplo n.º 6
0
    def bind_threepid(self, creds, mxid):
        yield run_on_reactor()
        logger.debug("binding threepid %r to %s", creds, mxid)
        data = None

        if 'id_server' in creds:
            id_server = creds['id_server']
        elif 'idServer' in creds:
            id_server = creds['idServer']
        else:
            raise SynapseError(400, "No id_server in creds")

        if 'client_secret' in creds:
            client_secret = creds['client_secret']
        elif 'clientSecret' in creds:
            client_secret = creds['clientSecret']
        else:
            raise SynapseError(400, "No client_secret in creds")

        try:
            data = yield self.http_client.post_urlencoded_get_json(
                "https://%s%s" % (
                    id_server, "/_matrix/identity/api/v1/3pid/bind"
                ),
                {
                    'sid': creds['sid'],
                    'client_secret': client_secret,
                    'mxid': mxid,
                }
            )
            logger.debug("bound threepid %r to %s", creds, mxid)
        except CodeMessageException as e:
            data = json.loads(e.msg)
        defer.returnValue(data)
Exemplo n.º 7
0
    def _do_create(self, requester, user_json):
        yield run_on_reactor()

        if "localpart" not in user_json:
            raise SynapseError(400, "Expected 'localpart' key.")

        if "displayname" not in user_json:
            raise SynapseError(400, "Expected 'displayname' key.")

        localpart = user_json["localpart"].encode("utf-8")
        displayname = user_json["displayname"].encode("utf-8")
        password_hash = user_json["password_hash"].encode("utf-8") \
            if user_json.get("password_hash") else None

        handler = self.handlers.registration_handler
        user_id, token = yield handler.get_or_create_user(
            requester=requester,
            localpart=localpart,
            displayname=displayname,
            password_hash=password_hash
        )

        defer.returnValue({
            "user_id": user_id,
            "access_token": token,
            "home_server": self.hs.hostname,
        })
Exemplo n.º 8
0
    def add_auth_events(self, builder, context):
        yield run_on_reactor()

        if builder.type == EventTypes.Create:
            builder.auth_events = []
            return

        auth_ids = []

        key = (EventTypes.PowerLevels, "", )
        power_level_event = context.current_state.get(key)

        if power_level_event:
            auth_ids.append(power_level_event.event_id)

        key = (EventTypes.JoinRules, "", )
        join_rule_event = context.current_state.get(key)

        key = (EventTypes.Member, builder.user_id, )
        member_event = context.current_state.get(key)

        key = (EventTypes.Create, "", )
        create_event = context.current_state.get(key)
        if create_event:
            auth_ids.append(create_event.event_id)

        if join_rule_event:
            join_rule = join_rule_event.content.get("join_rule")
            is_public = join_rule == JoinRules.PUBLIC if join_rule else False
        else:
            is_public = False

        if builder.type == EventTypes.Member:
            e_type = builder.content["membership"]
            if e_type in [Membership.JOIN, Membership.INVITE]:
                if join_rule_event:
                    auth_ids.append(join_rule_event.event_id)

            if e_type == Membership.JOIN:
                if member_event and not is_public:
                    auth_ids.append(member_event.event_id)
            else:
                if member_event:
                    auth_ids.append(member_event.event_id)
        elif member_event:
            if member_event.content["membership"] == Membership.JOIN:
                auth_ids.append(member_event.event_id)

        auth_events_entries = yield self.store.add_event_hashes(
            auth_ids
        )

        builder.auth_events = auth_events_entries

        context.auth_events = {
            k: v
            for k, v in context.current_state.items()
            if v.event_id in auth_ids
        }
Exemplo n.º 9
0
    def _on_new_room_event(self, event, snapshot, extra_destinations=[],
                           extra_users=[], suppress_auth=False,
                           do_invite_host=None):
        yield run_on_reactor()

        snapshot.fill_out_prev_events(event)

        yield self.state_handler.annotate_event_with_state(event)

        yield self.auth.add_auth_events(event)

        logger.debug("Signing event...")

        add_hashes_and_signatures(
            event, self.server_name, self.signing_key
        )

        logger.debug("Signed event.")

        if not suppress_auth:
            logger.debug("Authing...")
            self.auth.check(event, auth_events=event.old_state_events)
            logger.debug("Authed")
        else:
            logger.debug("Suppressed auth.")

        if do_invite_host:
            federation_handler = self.hs.get_handlers().federation_handler
            invite_event = yield federation_handler.send_invite(
                do_invite_host,
                event
            )

            # FIXME: We need to check if the remote changed anything else
            event.signatures = invite_event.signatures

        yield self.store.persist_event(event)

        destinations = set(extra_destinations)
        # Send a PDU to all hosts who have joined the room.

        for k, s in event.state_events.items():
            try:
                if k[0] == RoomMemberEvent.TYPE:
                    if s.content["membership"] == Membership.JOIN:
                        destinations.add(
                            self.hs.parse_userid(s.state_key).domain
                        )
            except:
                logger.warn(
                    "Failed to get destination from event %s", s.event_id
                )

        event.destinations = list(destinations)

        yield self.notifier.on_new_room_event(event, extra_users=extra_users)

        federation_handler = self.hs.get_handlers().federation_handler
        yield federation_handler.handle_new_event(event, snapshot)
Exemplo n.º 10
0
    def on_GET(self, request):
        yield run_on_reactor()

        requester = yield self.auth.get_user_by_req(request)

        threepids = yield self.hs.get_datastore().user_get_threepids(requester.user.to_string())

        defer.returnValue((200, {"threepids": threepids}))
Exemplo n.º 11
0
    def register(self, localpart=None, password=None):
        """Registers a new client on the server.

        Args:
            localpart : The local part of the user ID to register. If None,
              one will be randomly generated.
            password (str) : The password to assign to this user so they can
            login again.
        Returns:
            A tuple of (user_id, access_token).
        Raises:
            RegistrationError if there was a problem registering.
        """
        yield run_on_reactor()
        password_hash = None
        if password:
            password_hash = bcrypt.hashpw(password, bcrypt.gensalt())

        if localpart:
            user = UserID(localpart, self.hs.hostname)
            user_id = user.to_string()

            token = self._generate_token(user_id)
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=password_hash
            )

            yield self.distributor.fire("registered_user", user)
        else:
            # autogen a random user ID
            attempts = 0
            user_id = None
            token = None
            while not user_id and not token:
                try:
                    localpart = self._generate_user_id()
                    user = UserID(localpart, self.hs.hostname)
                    user_id = user.to_string()

                    token = self._generate_token(user_id)
                    yield self.store.register(
                        user_id=user_id,
                        token=token,
                        password_hash=password_hash)

                    self.distributor.fire("registered_user", user)
                except SynapseError:
                    # if user id is taken, just generate another
                    user_id = None
                    token = None
                    attempts += 1
                    if attempts > 5:
                        raise RegistrationError(
                            500, "Cannot generate user ID.")

        defer.returnValue((user_id, token))
Exemplo n.º 12
0
    def handle_new_client_event(self, event, context, extra_destinations=[],
                                extra_users=[], suppress_auth=False):
        yield run_on_reactor()

        # We now need to go and hit out to wherever we need to hit out to.

        if not suppress_auth:
            self.auth.check(event, auth_events=context.current_state)

        yield self.store.persist_event(event, context=context)

        federation_handler = self.hs.get_handlers().federation_handler

        if event.type == EventTypes.Member:
            if event.content["membership"] == Membership.INVITE:
                invitee = UserID.from_string(event.state_key)
                if not self.hs.is_mine(invitee):
                    # TODO: Can we add signature from remote server in a nicer
                    # way? If we have been invited by a remote server, we need
                    # to get them to sign the event.
                    returned_invite = yield federation_handler.send_invite(
                        invitee.domain,
                        event,
                    )

                    # TODO: Make sure the signatures actually are correct.
                    event.signatures.update(
                        returned_invite.signatures
                    )

        destinations = set(extra_destinations)
        for k, s in context.current_state.items():
            try:
                if k[0] == EventTypes.Member:
                    if s.content["membership"] == Membership.JOIN:
                        destinations.add(
                            UserID.from_string(s.state_key).domain
                        )
            except SynapseError:
                logger.warn(
                    "Failed to get destination from event %s", s.event_id
                )

        # Don't block waiting on waking up all the listeners.
        d = self.notifier.on_new_room_event(event, extra_users=extra_users)

        def log_failure(f):
            logger.warn(
                "Failed to notify about %s: %s",
                event.event_id, f.value
            )

        d.addErrback(log_failure)

        yield federation_handler.handle_new_event(
            event, destinations=destinations,
        )
Exemplo n.º 13
0
 def _notify():
     yield run_on_reactor()
     try:
         self.notifier.on_new_room_event(
             event, event_stream_id, max_stream_id,
             extra_users=extra_users
         )
     except Exception:
         logger.exception("Error notifying about new room event")
Exemplo n.º 14
0
    def add_auth_events(self, builder, context):
        yield run_on_reactor()

        auth_ids = self.compute_auth_events(builder, context.current_state)

        auth_events_entries = yield self.store.add_event_hashes(
            auth_ids
        )

        builder.auth_events = auth_events_entries
Exemplo n.º 15
0
    def _do_local_membership_update(self, event, context):
        yield run_on_reactor()

        target_user = UserID.from_string(event.state_key)

        yield self.handle_new_client_event(
            event,
            context,
            extra_users=[target_user],
        )
Exemplo n.º 16
0
    def _do_shared_secret(self, request, register_json, session):
        yield run_on_reactor()

        if not isinstance(register_json.get("mac", None), string_types):
            raise SynapseError(400, "Expected mac.")
        if not isinstance(register_json.get("user", None), string_types):
            raise SynapseError(400, "Expected 'user' key.")
        if not isinstance(register_json.get("password", None), string_types):
            raise SynapseError(400, "Expected 'password' key.")

        if not self.hs.config.registration_shared_secret:
            raise SynapseError(400, "Shared secret registration is not enabled")

        user = register_json["user"].encode("utf-8")
        password = register_json["password"].encode("utf-8")
        admin = register_json.get("admin", None)

        # Its important to check as we use null bytes as HMAC field separators
        if b"\x00" in user:
            raise SynapseError(400, "Invalid user")
        if b"\x00" in password:
            raise SynapseError(400, "Invalid password")

        # str() because otherwise hmac complains that 'unicode' does not
        # have the buffer interface
        got_mac = str(register_json["mac"])

        want_mac = hmac.new(
            key=self.hs.config.registration_shared_secret.encode(),
            digestmod=sha1,
        )
        want_mac.update(user)
        want_mac.update(b"\x00")
        want_mac.update(password)
        want_mac.update(b"\x00")
        want_mac.update(b"admin" if admin else b"notadmin")
        want_mac = want_mac.hexdigest()

        if compare_digest(want_mac, got_mac):
            handler = self.handlers.registration_handler
            user_id, token = yield handler.register(
                localpart=user.lower(),
                password=password,
                admin=bool(admin),
            )
            self._remove_session(session)
            defer.returnValue({
                "user_id": user_id,
                "access_token": token,
                "home_server": self.hs.hostname,
            })
        else:
            raise SynapseError(
                403, "HMAC incorrect",
            )
Exemplo n.º 17
0
    def _do_local_membership_update(self, event, membership, context,
                                    do_auth):
        yield run_on_reactor()

        target_user = self.hs.parse_userid(event.state_key)

        yield self.handle_new_client_event(
            event,
            context,
            extra_users=[target_user],
            suppress_auth=(not do_auth),
        )
Exemplo n.º 18
0
    def get_or_create_user(self, localpart, displayname, duration_seconds):
        """Creates a new user if the user does not exist,
        else revokes all previous access tokens and generates a new one.

        Args:
            localpart : The local part of the user ID to register. If None,
              one will be randomly generated.
        Returns:
            A tuple of (user_id, access_token).
        Raises:
            RegistrationError if there was a problem registering.
        """
        yield run_on_reactor()

        if localpart is None:
            raise SynapseError(400, "Request must include user id")

        need_register = True

        try:
            yield self.check_username(localpart)
        except SynapseError as e:
            if e.errcode == Codes.USER_IN_USE:
                need_register = False
            else:
                raise

        user = UserID(localpart, self.hs.hostname)
        user_id = user.to_string()
        auth_handler = self.hs.get_handlers().auth_handler
        token = auth_handler.generate_short_term_login_token(user_id, duration_seconds)

        if need_register:
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=None
            )

            yield registered_user(self.distributor, user)
        else:
            yield self.store.user_delete_access_tokens(user_id=user_id)
            yield self.store.add_access_token_to_user(user_id=user_id, token=token)

        if displayname is not None:
            logger.info("setting user display name: %s -> %s", user_id, displayname)
            profile_handler = self.hs.get_handlers().profile_handler
            yield profile_handler.set_displayname(
                user, Requester(user, token, False), displayname
            )

        defer.returnValue((user_id, token))
Exemplo n.º 19
0
    def check_username(self, localpart):
        yield run_on_reactor()

        if urllib.quote(localpart) != localpart:
            raise SynapseError(400, "User ID must only contain characters which do not" " require URL encoding.")

        user = UserID(localpart, self.hs.hostname)
        user_id = user.to_string()

        yield self.check_user_id_is_valid(user_id)

        users = yield self.store.get_users_by_id_case_insensitive(user_id)
        if users:
            raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE)
Exemplo n.º 20
0
    def get_or_create_user(self, requester, localpart, displayname,
                           password_hash=None):
        """Creates a new user if the user does not exist,
        else revokes all previous access tokens and generates a new one.

        Args:
            localpart : The local part of the user ID to register. If None,
              one will be randomly generated.
        Returns:
            A tuple of (user_id, access_token).
        Raises:
            RegistrationError if there was a problem registering.
        """
        yield run_on_reactor()

        if localpart is None:
            raise SynapseError(400, "Request must include user id")

        need_register = True

        try:
            yield self.check_username(localpart)
        except SynapseError as e:
            if e.errcode == Codes.USER_IN_USE:
                need_register = False
            else:
                raise

        user = UserID(localpart, self.hs.hostname)
        user_id = user.to_string()
        token = self.macaroon_gen.generate_access_token(user_id)

        if need_register:
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=password_hash,
                create_profile_with_localpart=user.localpart,
            )
        else:
            yield self._auth_handler.delete_access_tokens_for_user(user_id)
            yield self.store.add_access_token_to_user(user_id=user_id, token=token)

        if displayname is not None:
            logger.info("setting user display name: %s -> %s", user_id, displayname)
            yield self.profile_handler.set_displayname(
                user, requester, displayname, by_admin=True,
            )

        defer.returnValue((user_id, token))
Exemplo n.º 21
0
    def test_shortpoll(self):
        self.room_members = [self.u_apple, self.u_banana]

        self.mock_datastore.set_presence_state.return_value = defer.succeed(
            {"state": ONLINE}
        )
        self.mock_datastore.get_presence_list.return_value = defer.succeed(
            []
        )

        (code, response) = yield self.mock_resource.trigger("GET",
                "/events?timeout=0", None)

        self.assertEquals(200, code)

        # We've forced there to be only one data stream so the tokens will
        # all be ours

        # I'll already get my own presence state change
        self.assertEquals({"start": "0_1_0", "end": "0_1_0", "chunk": []},
            response
        )

        self.mock_datastore.set_presence_state.return_value = defer.succeed(
            {"state": ONLINE}
        )
        self.mock_datastore.get_presence_list.return_value = defer.succeed([])

        yield self.presence.set_state(self.u_banana, self.u_banana,
            state={"presence": ONLINE}
        )

        yield run_on_reactor()

        (code, response) = yield self.mock_resource.trigger("GET",
                "/events?from=s0_1_0&timeout=0", None)

        self.assertEquals(200, code)
        self.assertEquals({"start": "s0_1_0", "end": "s0_2_0", "chunk": [
            {"type": "m.presence",
             "content": {
                 "user_id": "@banana:test",
                 "presence": ONLINE,
                 "displayname": "Frank",
                 "last_active_ago": 0,
            }},
        ]}, response)
Exemplo n.º 22
0
    def on_new_room_event(self, event, extra_users=[]):
        """ Used by handlers to inform the notifier something has happened
        in the room, room event wise.

        This triggers the notifier to wake up any listeners that are
        listening to the room, and any listeners for the users in the
        `extra_users` param.
        """
        yield run_on_reactor()
        room_id = event.room_id

        room_source = self.event_sources.sources["room"]

        listeners = self.rooms_to_listeners.get(room_id, set()).copy()

        for user in extra_users:
            listeners |= self.user_to_listeners.get(user, set()).copy()

        logger.debug("on_new_room_event listeners %s", listeners)

        # TODO (erikj): Can we make this more efficient by hitting the
        # db once?

        @defer.inlineCallbacks
        def notify(listener):
            events, end_key = yield room_source.get_new_events_for_user(
                listener.user,
                listener.from_token.room_key,
                listener.limit,
            )

            if events:
                end_token = listener.from_token.copy_and_replace(
                    "room_key", end_key
                )

                listener.notify(
                    self, events, listener.from_token, end_token
                )

        def eb(failure):
            logger.exception("Failed to notify listener", failure)

        with PreserveLoggingContext():
            yield defer.DeferredList(
                [notify(l).addErrback(eb) for l in listeners]
            )
Exemplo n.º 23
0
    def on_new_user_event(self, users=[], rooms=[]):
        """ Used to inform listeners that something has happend
        presence/user event wise.

        Will wake up all listeners for the given users and rooms.
        """
        yield run_on_reactor()
        presence_source = self.event_sources.sources["presence"]

        listeners = set()

        for user in users:
            listeners |= self.user_to_listeners.get(user, set()).copy()

        for room in rooms:
            listeners |= self.rooms_to_listeners.get(room, set()).copy()

        @defer.inlineCallbacks
        def notify(listener):
            events, end_key = yield presence_source.get_new_events_for_user(
                listener.user,
                listener.from_token.presence_key,
                listener.limit,
            )

            if events:
                end_token = listener.from_token.copy_and_replace(
                    "presence_key", end_key
                )

                listener.notify(
                    self, events, listener.from_token, end_token
                )

        def eb(failure):
            logger.error(
                "Failed to notify listener",
                exc_info=(
                    failure.type,
                    failure.value,
                    failure.getTracebackObject())
            )

        with PreserveLoggingContext():
            yield defer.DeferredList(
                [notify(l).addErrback(eb) for l in listeners]
            )
Exemplo n.º 24
0
    def threepid_from_creds(self, creds):
        yield run_on_reactor()

        if 'id_server' in creds:
            id_server = creds['id_server']
        elif 'idServer' in creds:
            id_server = creds['idServer']
        else:
            raise SynapseError(400, "No id_server in creds")

        if 'client_secret' in creds:
            client_secret = creds['client_secret']
        elif 'clientSecret' in creds:
            client_secret = creds['clientSecret']
        else:
            raise SynapseError(400, "No client_secret in creds")

        if id_server not in self.trusted_id_servers:
            if self.trust_any_id_server_just_for_testing_do_not_use:
                logger.warn(
                    "Trusting untrustworthy ID server %r even though it isn't"
                    " in the trusted id list for testing because"
                    " 'use_insecure_ssl_client_just_for_testing_do_not_use'"
                    " is set in the config",
                    id_server,
                )
            else:
                logger.warn('%s is not a trusted ID server: rejecting 3pid ' +
                            'credentials', id_server)
                defer.returnValue(None)

        data = {}
        try:
            data = yield self.http_client.get_json(
                "https://%s%s" % (
                    id_server,
                    "/_matrix/identity/api/v1/3pid/getValidated3pid"
                ),
                {'sid': creds['sid'], 'client_secret': client_secret}
            )
        except CodeMessageException as e:
            data = json.loads(e.msg)

        if 'medium' in data:
            defer.returnValue(data)
        defer.returnValue(None)
Exemplo n.º 25
0
    def on_POST(self, request):
        yield run_on_reactor()

        body = parse_json_object_from_request(request)

        authed, result, params, _ = yield self.auth_handler.check_auth([
            [LoginType.PASSWORD],
            [LoginType.EMAIL_IDENTITY]
        ], body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, result))

        user_id = None
        requester = None

        if LoginType.PASSWORD in result:
            # if using password, they should also be logged in
            requester = yield self.auth.get_user_by_req(request)
            user_id = requester.user.to_string()
            if user_id != result[LoginType.PASSWORD]:
                raise LoginError(400, "", Codes.UNKNOWN)
        elif 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 using email, we must know about the email they're authing with!
            threepid_user_id = yield self.hs.get_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!", result.keys())
            raise SynapseError(500, "", Codes.UNKNOWN)

        if 'new_password' not in params:
            raise SynapseError(400, "", Codes.MISSING_PARAM)
        new_password = params['new_password']

        yield self.auth_handler.set_password(
            user_id, new_password, requester
        )

        defer.returnValue((200, {}))
Exemplo n.º 26
0
    def handle_new_event(self, event, destinations):
        """ Takes in an event from the client to server side, that has already
        been authed and handled by the state module, and sends it to any
        remote home servers that may be interested.

        Args:
            event: The event to send
            destinations: A list of destinations to send it to

        Returns:
            Deferred: Resolved when it has successfully been queued for
            processing.
        """

        yield run_on_reactor()

        self.replication_layer.send_pdu(event, destinations)
Exemplo n.º 27
0
    def _check_email_identity(self, authdict, _):
        yield run_on_reactor()

        if 'threepid_creds' not in authdict:
            raise LoginError(400, "Missing threepid_creds", Codes.MISSING_PARAM)

        threepid_creds = authdict['threepid_creds']
        identity_handler = self.hs.get_handlers().identity_handler

        logger.info("Getting validated threepid. threepidcreds: %r" % (threepid_creds,))
        threepid = yield identity_handler.threepid_from_creds(threepid_creds)

        if not threepid:
            raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)

        threepid['threepid_creds'] = authdict['threepid_creds']

        defer.returnValue(threepid)
Exemplo n.º 28
0
    def threepid_from_creds(self, creds):
        yield run_on_reactor()

        # TODO: get this from the homeserver rather than creating a new one for
        # each request
        http_client = SimpleHttpClient(self.hs)
        # XXX: make this configurable!
        # trustedIdServers = ['matrix.org', 'localhost:8090']
        trustedIdServers = ['matrix.org']

        if 'id_server' in creds:
            id_server = creds['id_server']
        elif 'idServer' in creds:
            id_server = creds['idServer']
        else:
            raise SynapseError(400, "No id_server in creds")

        if 'client_secret' in creds:
            client_secret = creds['client_secret']
        elif 'clientSecret' in creds:
            client_secret = creds['clientSecret']
        else:
            raise SynapseError(400, "No client_secret in creds")

        if id_server not in trustedIdServers:
            logger.warn('%s is not a trusted ID server: rejecting 3pid ' +
                        'credentials', id_server)
            defer.returnValue(None)

        data = {}
        try:
            data = yield http_client.get_json(
                "https://%s%s" % (
                    id_server,
                    "/_matrix/identity/api/v1/3pid/getValidated3pid"
                ),
                {'sid': creds['sid'], 'client_secret': client_secret}
            )
        except CodeMessageException as e:
            data = json.loads(e.msg)

        if 'medium' in data:
            defer.returnValue(data)
        defer.returnValue(None)
Exemplo n.º 29
0
    def on_new_notifications(self, min_stream_id, max_stream_id):
        yield run_on_reactor()
        try:
            users_affected = yield self.store.get_push_action_users_in_range(
                min_stream_id, max_stream_id
            )

            deferreds = []

            for u in users_affected:
                if u in self.pushers:
                    for p in self.pushers[u].values():
                        deferreds.append(
                            p.on_new_notifications(min_stream_id, max_stream_id)
                        )

            yield defer.gatherResults(deferreds)
        except:
            logger.exception("Exception in pusher on_new_notifications")
Exemplo n.º 30
0
    def on_new_room_event(self, event, room_stream_id, max_room_stream_id,
                          extra_users=[]):
        """ Used by handlers to inform the notifier something has happened
        in the room, room event wise.

        This triggers the notifier to wake up any listeners that are
        listening to the room, and any listeners for the users in the
        `extra_users` param.

        The events can be peristed out of order. The notifier will wait
        until all previous events have been persisted before notifying
        the client streams.
        """
        yield run_on_reactor()

        self.pending_new_room_events.append((
            room_stream_id, event, extra_users
        ))
        self._notify_pending_new_room_events(max_room_stream_id)
Exemplo n.º 31
0
    def register(self, localpart=None, password=None, generate_token=True):
        """Registers a new client on the server.

        Args:
            localpart : The local part of the user ID to register. If None,
              one will be randomly generated.
            password (str) : The password to assign to this user so they can
            login again. This can be None which means they cannot login again
            via a password (e.g. the user is an application service user).
        Returns:
            A tuple of (user_id, access_token).
        Raises:
            RegistrationError if there was a problem registering.
        """
        yield run_on_reactor()
        password_hash = None
        if password:
            password_hash = self.auth_handler().hash(password)

        if localpart:
            yield self.check_username(localpart)

            user = UserID(localpart, self.hs.hostname)
            user_id = user.to_string()

            token = None
            if generate_token:
                token = self.auth_handler().generate_access_token(user_id)
            yield self.store.register(user_id=user_id,
                                      token=token,
                                      password_hash=password_hash)

            yield registered_user(self.distributor, user)
        else:
            # autogen a random user ID
            attempts = 0
            user_id = None
            token = None
            while not user_id:
                try:
                    localpart = self._generate_user_id()
                    user = UserID(localpart, self.hs.hostname)
                    user_id = user.to_string()
                    yield self.check_user_id_is_valid(user_id)
                    if generate_token:
                        token = self.auth_handler().generate_access_token(
                            user_id)
                    yield self.store.register(user_id=user_id,
                                              token=token,
                                              password_hash=password_hash)

                    yield registered_user(self.distributor, user)
                except SynapseError:
                    # if user id is taken, just generate another
                    user_id = None
                    token = None
                    attempts += 1
                    if attempts > 5:
                        raise RegistrationError(500,
                                                "Cannot generate user ID.")

        # We used to generate default identicons here, but nowadays
        # we want clients to generate their own as part of their branding
        # rather than there being consistent matrix-wide ones, so we don't.

        defer.returnValue((user_id, token))
Exemplo n.º 32
0
    def _attempt_new_transaction(self, destination):
        # list of (pending_pdu, deferred, order)
        if destination in self.pending_transactions:
            # XXX: pending_transactions can get stuck on by a never-ending
            # request at which point pending_pdus_by_dest just keeps growing.
            # we need application-layer timeouts of some flavour of these
            # requests
            logger.debug(
                "TX [%s] Transaction already in progress",
                destination
            )
            return

        try:
            self.pending_transactions[destination] = 1

            yield run_on_reactor()

            while True:
                    pending_pdus = self.pending_pdus_by_dest.pop(destination, [])
                    pending_edus = self.pending_edus_by_dest.pop(destination, [])
                    pending_presence = self.pending_presence_by_dest.pop(destination, {})
                    pending_failures = self.pending_failures_by_dest.pop(destination, [])

                    pending_edus.extend(
                        self.pending_edus_keyed_by_dest.pop(destination, {}).values()
                    )

                    limiter = yield get_retry_limiter(
                        destination,
                        self.clock,
                        self.store,
                    )

                    device_message_edus, device_stream_id = (
                        yield self._get_new_device_messages(destination)
                    )

                    pending_edus.extend(device_message_edus)
                    if pending_presence:
                        pending_edus.append(
                            Edu(
                                origin=self.server_name,
                                destination=destination,
                                edu_type="m.presence",
                                content={
                                    "push": [
                                        format_user_presence_state(
                                            presence, self.clock.time_msec()
                                        )
                                        for presence in pending_presence.values()
                                    ]
                                },
                            )
                        )

                    if pending_pdus:
                        logger.debug("TX [%s] len(pending_pdus_by_dest[dest]) = %d",
                                     destination, len(pending_pdus))

                    if not pending_pdus and not pending_edus and not pending_failures:
                        logger.debug("TX [%s] Nothing to send", destination)
                        self.last_device_stream_id_by_dest[destination] = (
                            device_stream_id
                        )
                        return

                    success = yield self._send_new_transaction(
                        destination, pending_pdus, pending_edus, pending_failures,
                        device_stream_id,
                        should_delete_from_device_stream=bool(device_message_edus),
                        limiter=limiter,
                    )
                    if not success:
                        break
        except NotRetryingDestination:
            logger.info(
                "TX [%s] not ready for retry yet - "
                "dropping transaction for now",
                destination,
            )
        finally:
            # We want to be *very* sure we delete this after we stop processing
            self.pending_transactions.pop(destination, None)
Exemplo n.º 33
0
    def on_POST(self, request):
        yield run_on_reactor()

        kind = "user"
        if "kind" in request.args:
            kind = request.args["kind"][0]

        if kind == "guest":
            ret = yield self._do_guest_registration()
            defer.returnValue(ret)
            return
        elif kind != "user":
            raise UnrecognizedRequestError(
                "Do not understand membership kind: %s" % (kind, ))

        if '/register/email/requestToken' in request.path:
            ret = yield self.onEmailTokenRequest(request)
            defer.returnValue(ret)

        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 username/password provided to us.
        desired_password = None
        if 'password' in body:
            if (not isinstance(body['password'], basestring)
                    or len(body['password']) > 512):
                raise SynapseError(400, "Invalid password")
            desired_password = body["password"]

        desired_username = None
        if 'username' in body:
            if (not isinstance(body['username'], basestring)
                    or len(body['username']) > 512):
                raise SynapseError(400, "Invalid username")
            desired_username = body['username']

        appservice = None
        if 'access_token' in request.args:
            appservice = yield self.auth.get_appservice_by_req(request)

        # fork off as soon as possible for ASes and shared secret auth which
        # have completely different registration flows to normal users

        # == Application Service Registration ==
        if appservice:
            # Set the desired user according to the AS API (which uses the
            # 'user' key not 'username'). Since this is a new addition, we'll
            # fallback to 'username' if they gave one.
            if isinstance(body.get("user"), basestring):
                desired_username = body["user"]
            result = yield self._do_appservice_registration(
                desired_username, request.args["access_token"][0])
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == Shared Secret Registration == (e.g. create new user scripts)
        if 'mac' in body:
            # FIXME: Should we really be determining if this is shared secret
            # auth based purely on the 'mac' key?
            result = yield self._do_shared_secret_registration(
                desired_username, desired_password, body["mac"])
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == Normal User Registration == (everyone else)
        if not self.hs.config.enable_registration:
            raise SynapseError(403, "Registration has been disabled")

        guest_access_token = body.get("guest_access_token", None)

        session_id = self.auth_handler.get_session_id(body)
        registered_user_id = None
        if session_id:
            # if we get a registered user id out of here, it means we previously
            # registered a user for this session, so we could just return the
            # user here. We carry on and go through the auth checks though,
            # for paranoia.
            registered_user_id = self.auth_handler.get_session_data(
                session_id, "registered_user_id", None)

        if desired_username is not None:
            yield self.registration_handler.check_username(
                desired_username,
                guest_access_token=guest_access_token,
                assigned_user_id=registered_user_id,
            )

        if self.hs.config.enable_registration_captcha:
            flows = [[LoginType.RECAPTCHA],
                     [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]]
        else:
            flows = [[LoginType.DUMMY], [LoginType.EMAIL_IDENTITY]]

        authed, result, params, session_id = yield self.auth_handler.check_auth(
            flows, body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, result))
            return

        if registered_user_id is not None:
            logger.info("Already registered user ID %r for this session",
                        registered_user_id)
            access_token = yield self.auth_handler.issue_access_token(
                registered_user_id)
            refresh_token = yield self.auth_handler.issue_refresh_token(
                registered_user_id)
            defer.returnValue((200, {
                "user_id": registered_user_id,
                "access_token": access_token,
                "home_server": self.hs.hostname,
                "refresh_token": refresh_token,
            }))

        # NB: This may be from the auth handler and NOT from the POST
        if 'password' not in params:
            raise SynapseError(400, "Missing password.", Codes.MISSING_PARAM)

        desired_username = params.get("username", None)
        new_password = params.get("password", None)
        guest_access_token = params.get("guest_access_token", None)

        (user_id, token) = yield self.registration_handler.register(
            localpart=desired_username,
            password=new_password,
            guest_access_token=guest_access_token,
        )

        # remember that we've now registered that user account, and with what
        # user ID (since the user may not have specified)
        self.auth_handler.set_session_data(session_id, "registered_user_id",
                                           user_id)

        if result and LoginType.EMAIL_IDENTITY in result:
            threepid = result[LoginType.EMAIL_IDENTITY]

            for reqd in ['medium', 'address', 'validated_at']:
                if reqd not in threepid:
                    logger.info("Can't add incomplete 3pid")
                else:
                    yield self.auth_handler.add_threepid(
                        user_id,
                        threepid['medium'],
                        threepid['address'],
                        threepid['validated_at'],
                    )

                    # And we add an email pusher for them by default, but only
                    # if email notifications are enabled (so people don't start
                    # getting mail spam where they weren't before if email
                    # notifs are set up on a home server)
                    if (self.hs.config.email_enable_notifs
                            and self.hs.config.email_notif_for_new_users):
                        # Pull the ID of the access token back out of the db
                        # It would really make more sense for this to be passed
                        # up when the access token is saved, but that's quite an
                        # invasive change I'd rather do separately.
                        user_tuple = yield self.store.get_user_by_access_token(
                            token)

                        yield self.hs.get_pusherpool().add_pusher(
                            user_id=user_id,
                            access_token=user_tuple["token_id"],
                            kind="email",
                            app_id="m.email",
                            app_display_name="Email Notifications",
                            device_display_name=threepid["address"],
                            pushkey=threepid["address"],
                            lang=None,  # We don't know a user's language here
                            data={},
                        )

            if 'bind_email' in params and params['bind_email']:
                logger.info("bind_email specified: binding")

                emailThreepid = result[LoginType.EMAIL_IDENTITY]
                threepid_creds = emailThreepid['threepid_creds']
                logger.debug("Binding emails %s to %s" %
                             (emailThreepid, user_id))
                yield self.identity_handler.bind_threepid(
                    threepid_creds, user_id)
            else:
                logger.info("bind_email not specified: not binding email")

        result = yield self._create_registration_details(user_id, token)
        defer.returnValue((200, result))
Exemplo n.º 34
0
 def _notify():
     yield run_on_reactor()
     self.notifier.on_new_room_event(event,
                                     event_stream_id,
                                     max_stream_id,
                                     extra_users=extra_users)
Exemplo n.º 35
0
    def _transaction_transmission_loop(self, destination):
        pending_pdus = []
        try:
            self.pending_transactions[destination] = 1

            # This will throw if we wouldn't retry. We do this here so we fail
            # quickly, but we will later check this again in the http client,
            # hence why we throw the result away.
            yield get_retry_limiter(destination, self.clock, self.store)

            # XXX: what's this for?
            yield run_on_reactor()

            pending_pdus = []
            while True:
                device_message_edus, device_stream_id, dev_list_id = (
                    yield self._get_new_device_messages(destination))

                # BEGIN CRITICAL SECTION
                #
                # In order to avoid a race condition, we need to make sure that
                # the following code (from popping the queues up to the point
                # where we decide if we actually have any pending messages) is
                # atomic - otherwise new PDUs or EDUs might arrive in the
                # meantime, but not get sent because we hold the
                # pending_transactions flag.

                pending_pdus = self.pending_pdus_by_dest.pop(destination, [])
                pending_edus = self.pending_edus_by_dest.pop(destination, [])
                pending_presence = self.pending_presence_by_dest.pop(
                    destination, {})
                pending_failures = self.pending_failures_by_dest.pop(
                    destination, [])

                pending_edus.extend(
                    self.pending_edus_keyed_by_dest.pop(destination,
                                                        {}).values())

                pending_edus.extend(device_message_edus)
                if pending_presence:
                    pending_edus.append(
                        Edu(
                            origin=self.server_name,
                            destination=destination,
                            edu_type="m.presence",
                            content={
                                "push": [
                                    format_user_presence_state(
                                        presence, self.clock.time_msec())
                                    for presence in pending_presence.values()
                                ]
                            },
                        ))

                if pending_pdus:
                    logger.debug(
                        "TX [%s] len(pending_pdus_by_dest[dest]) = %d",
                        destination, len(pending_pdus))

                if not pending_pdus and not pending_edus and not pending_failures:
                    logger.debug("TX [%s] Nothing to send", destination)
                    self.last_device_stream_id_by_dest[destination] = (
                        device_stream_id)
                    return

                # END CRITICAL SECTION

                success = yield self._send_new_transaction(
                    destination,
                    pending_pdus,
                    pending_edus,
                    pending_failures,
                )
                if success:
                    sent_transactions_counter.inc()
                    # Remove the acknowledged device messages from the database
                    # Only bother if we actually sent some device messages
                    if device_message_edus:
                        yield self.store.delete_device_msgs_for_remote(
                            destination, device_stream_id)
                        logger.info("Marking as sent %r %r", destination,
                                    dev_list_id)
                        yield self.store.mark_as_sent_devices_by_remote(
                            destination, dev_list_id)

                    self.last_device_stream_id_by_dest[
                        destination] = device_stream_id
                    self.last_device_list_stream_id_by_dest[
                        destination] = dev_list_id
                else:
                    break
        except NotRetryingDestination as e:
            logger.debug(
                "TX [%s] not ready for retry yet (next retry at %s) - "
                "dropping transaction for now",
                destination,
                datetime.datetime.fromtimestamp(
                    (e.retry_last_ts + e.retry_interval) / 1000.0),
            )
        except FederationDeniedError as e:
            logger.info(e)
        except Exception as e:
            logger.warn(
                "TX [%s] Failed to send transaction: %s",
                destination,
                e,
            )
            for p, _ in pending_pdus:
                logger.info("Failed to send event %s to %s", p.event_id,
                            destination)
        finally:
            # We want to be *very* sure we delete this after we stop processing
            self.pending_transactions.pop(destination, None)
Exemplo n.º 36
0
 def _check_dummy_auth(self, authdict, _):
     yield run_on_reactor()
     defer.returnValue(True)
Exemplo n.º 37
0
    def register(
        self,
        localpart=None,
        password=None,
        generate_token=True,
        guest_access_token=None,
        make_guest=False,
        admin=False,
    ):
        """Registers a new client on the server.

        Args:
            localpart : The local part of the user ID to register. If None,
              one will be generated.
            password (str) : The password to assign to this user so they can
              login again. This can be None which means they cannot login again
              via a password (e.g. the user is an application service user).
            generate_token (bool): Whether a new access token should be
              generated. Having this be True should be considered deprecated,
              since it offers no means of associating a device_id with the
              access_token. Instead you should call auth_handler.issue_access_token
              after registration.
        Returns:
            A tuple of (user_id, access_token).
        Raises:
            RegistrationError if there was a problem registering.
        """
        yield run_on_reactor()
        password_hash = None
        if password:
            password_hash = password

        if localpart:
            yield self.check_username(localpart,
                                      guest_access_token=guest_access_token)

            was_guest = guest_access_token is not None

            if not was_guest:
                try:
                    int(localpart)
                    raise RegistrationError(
                        400, "Numeric user IDs are reserved for guest users.")
                except ValueError:
                    pass

            user = UserID(localpart, self.hs.hostname)
            user_id = user.to_string()

            token = None
            if generate_token:
                token = self.macaroon_gen.generate_access_token(user_id)
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=password_hash,
                was_guest=was_guest,
                make_guest=make_guest,
                create_profile_with_localpart=(
                    # If the user was a guest then they already have a profile
                    None if was_guest else user.localpart),
                admin=admin,
            )

            if self.hs.config.user_directory_search_all_users:
                profile = yield self.store.get_profileinfo(localpart)
                yield self.user_directory_handler.handle_local_profile_change(
                    user_id, profile)

        else:
            # autogen a sequential user ID
            attempts = 0
            token = None
            user = None
            while not user:
                localpart = yield self._generate_user_id(attempts > 0)
                user = UserID(localpart, self.hs.hostname)
                user_id = user.to_string()
                yield self.check_user_id_not_appservice_exclusive(user_id)
                if generate_token:
                    token = self.macaroon_gen.generate_access_token(user_id)
                try:
                    yield self.store.register(
                        user_id=user_id,
                        token=token,
                        password_hash=password_hash,
                        make_guest=make_guest,
                        create_profile_with_localpart=user.localpart,
                    )
                except SynapseError:
                    # if user id is taken, just generate another
                    user = None
                    user_id = None
                    token = None
                    attempts += 1

        # We used to generate default identicons here, but nowadays
        # we want clients to generate their own as part of their branding
        # rather than there being consistent matrix-wide ones, so we don't.

        defer.returnValue((user_id, token))
Exemplo n.º 38
0
    def compute_event_context(self, event, old_state=None):
        """ Fills out the context with the `current state` of the graph. The
        `current state` here is defined to be the state of the event graph
        just before the event - i.e. it never includes `event`

        If `event` has `auth_events` then this will also fill out the
        `auth_events` field on `context` from the `current_state`.

        Args:
            event (EventBase)
        Returns:
            an EventContext
        """
        context = EventContext()

        yield run_on_reactor()

        if old_state:
            context.current_state = {(s.type, s.state_key): s
                                     for s in old_state}
            context.state_group = None

            if hasattr(event, "auth_events") and event.auth_events:
                auth_ids = zip(*event.auth_events)[0]
                context.auth_events = {
                    k: v
                    for k, v in context.current_state.items()
                    if v.event_id in auth_ids
                }
            else:
                context.auth_events = {}

            if event.is_state():
                key = (event.type, event.state_key)
                if key in context.current_state:
                    replaces = context.current_state[key]
                    if replaces.event_id != event.event_id:  # Paranoia check
                        event.unsigned["replaces_state"] = replaces.event_id

            context.prev_state_events = []
            defer.returnValue(context)

        if event.is_state():
            ret = yield self.resolve_state_groups(
                [e for e, _ in event.prev_events],
                event_type=event.type,
                state_key=event.state_key,
            )
        else:
            ret = yield self.resolve_state_groups(
                [e for e, _ in event.prev_events], )

        group, curr_state, prev_state = ret

        context.current_state = curr_state
        context.state_group = group if not event.is_state() else None

        prev_state = yield self.store.add_event_hashes(prev_state)

        if event.is_state():
            key = (event.type, event.state_key)
            if key in context.current_state:
                replaces = context.current_state[key]
                event.unsigned["replaces_state"] = replaces.event_id

        if hasattr(event, "auth_events") and event.auth_events:
            auth_ids = zip(*event.auth_events)[0]
            context.auth_events = {
                k: v
                for k, v in context.current_state.items()
                if v.event_id in auth_ids
            }
        else:
            context.auth_events = {}

        context.prev_state_events = prev_state
        defer.returnValue(context)
Exemplo n.º 39
0
    def on_POST(self, request):
        yield run_on_reactor()

        body = parse_request_allow_empty(request)
        if 'password' not in body:
            raise SynapseError(400, "", Codes.MISSING_PARAM)

        if 'username' in body:
            desired_username = body['username']
            yield self.registration_handler.check_username(desired_username)

        is_using_shared_secret = False
        is_application_server = False

        service = None
        if 'access_token' in request.args:
            service = yield self.auth.get_appservice_by_req(request)

        if self.hs.config.enable_registration_captcha:
            flows = [
                [LoginType.RECAPTCHA],
                [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]
            ]
        else:
            flows = [
                [LoginType.DUMMY],
                [LoginType.EMAIL_IDENTITY]
            ]

        result = None
        if service:
            is_application_server = True
            params = body
        elif 'mac' in body:
            # Check registration-specific shared secret auth
            if 'username' not in body:
                raise SynapseError(400, "", Codes.MISSING_PARAM)
            self._check_shared_secret_auth(
                body['username'], body['mac']
            )
            is_using_shared_secret = True
            params = body
        else:
            authed, result, params = yield self.auth_handler.check_auth(
                flows, body, self.hs.get_ip_from_request(request)
            )

            if not authed:
                defer.returnValue((401, result))

        can_register = (
            not self.hs.config.disable_registration
            or is_application_server
            or is_using_shared_secret
        )
        if not can_register:
            raise SynapseError(403, "Registration has been disabled")

        if 'password' not in params:
            raise SynapseError(400, "", Codes.MISSING_PARAM)
        desired_username = params['username'] if 'username' in params else None
        new_password = params['password']

        (user_id, token) = yield self.registration_handler.register(
            localpart=desired_username,
            password=new_password
        )

        if result and LoginType.EMAIL_IDENTITY in result:
            threepid = result[LoginType.EMAIL_IDENTITY]

            for reqd in ['medium', 'address', 'validated_at']:
                if reqd not in threepid:
                    logger.info("Can't add incomplete 3pid")
                else:
                    yield self.login_handler.add_threepid(
                        user_id,
                        threepid['medium'],
                        threepid['address'],
                        threepid['validated_at'],
                    )

            if 'bind_email' in params and params['bind_email']:
                logger.info("bind_email specified: binding")

                emailThreepid = result[LoginType.EMAIL_IDENTITY]
                threepid_creds = emailThreepid['threepid_creds']
                logger.debug("Binding emails %s to %s" % (
                    emailThreepid, user_id
                ))
                yield self.identity_handler.bind_threepid(threepid_creds, user_id)
            else:
                logger.info("bind_email not specified: not binding email")

        result = {
            "user_id": user_id,
            "access_token": token,
            "home_server": self.hs.hostname,
        }

        defer.returnValue((200, result))
Exemplo n.º 40
0
    def on_POST(self, request):
        yield run_on_reactor()

        if '/register/email/requestToken' in request.path:
            ret = yield self.onEmailTokenRequest(request)
            defer.returnValue(ret)

        body = parse_json_dict_from_request(request)

        # we do basic sanity checks here because the auth layer will store these
        # in sessions. Pull out the username/password provided to us.
        desired_password = None
        if 'password' in body:
            if (not isinstance(body['password'], basestring)
                    or len(body['password']) > 512):
                raise SynapseError(400, "Invalid password")
            desired_password = body["password"]

        desired_username = None
        if 'username' in body:
            if (not isinstance(body['username'], basestring)
                    or len(body['username']) > 512):
                raise SynapseError(400, "Invalid username")
            desired_username = body['username']

        appservice = None
        if 'access_token' in request.args:
            appservice = yield self.auth.get_appservice_by_req(request)

        # fork off as soon as possible for ASes and shared secret auth which
        # have completely different registration flows to normal users

        # == Application Service Registration ==
        if appservice:
            result = yield self._do_appservice_registration(
                desired_username, request.args["access_token"][0])
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == Shared Secret Registration == (e.g. create new user scripts)
        if 'mac' in body:
            # FIXME: Should we really be determining if this is shared secret
            # auth based purely on the 'mac' key?
            result = yield self._do_shared_secret_registration(
                desired_username, desired_password, body["mac"])
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == Normal User Registration == (everyone else)
        if self.hs.config.disable_registration:
            raise SynapseError(403, "Registration has been disabled")

        if desired_username is not None:
            yield self.registration_handler.check_username(desired_username)

        if self.hs.config.enable_registration_captcha:
            flows = [[LoginType.RECAPTCHA],
                     [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]]
        else:
            flows = [[LoginType.DUMMY], [LoginType.EMAIL_IDENTITY]]

        authed, result, params = yield self.auth_handler.check_auth(
            flows, body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, result))
            return

        # NB: This may be from the auth handler and NOT from the POST
        if 'password' not in params:
            raise SynapseError(400, "Missing password.", Codes.MISSING_PARAM)

        desired_username = params.get("username", None)
        new_password = params.get("password", None)

        (user_id, token) = yield self.registration_handler.register(
            localpart=desired_username, password=new_password)

        if result and LoginType.EMAIL_IDENTITY in result:
            threepid = result[LoginType.EMAIL_IDENTITY]

            for reqd in ['medium', 'address', 'validated_at']:
                if reqd not in threepid:
                    logger.info("Can't add incomplete 3pid")
                else:
                    yield self.auth_handler.add_threepid(
                        user_id,
                        threepid['medium'],
                        threepid['address'],
                        threepid['validated_at'],
                    )

            if 'bind_email' in params and params['bind_email']:
                logger.info("bind_email specified: binding")

                emailThreepid = result[LoginType.EMAIL_IDENTITY]
                threepid_creds = emailThreepid['threepid_creds']
                logger.debug("Binding emails %s to %s" %
                             (emailThreepid, user_id))
                yield self.identity_handler.bind_threepid(
                    threepid_creds, user_id)
            else:
                logger.info("bind_email not specified: not binding email")

        result = self._create_registration_details(user_id, token)
        defer.returnValue((200, result))
Exemplo n.º 41
0
 def observer():
     yield run_on_reactor()
     raise MyException("Oopsie")
Exemplo n.º 42
0
    def _attempt_new_transaction(self, destination):
        yield run_on_reactor()

        # list of (pending_pdu, deferred, order)
        if destination in self.pending_transactions:
            # XXX: pending_transactions can get stuck on by a never-ending
            # request at which point pending_pdus_by_dest just keeps growing.
            # we need application-layer timeouts of some flavour of these
            # requests
            logger.debug("TX [%s] Transaction already in progress",
                         destination)
            return

        pending_pdus = self.pending_pdus_by_dest.pop(destination, [])
        pending_edus = self.pending_edus_by_dest.pop(destination, [])
        pending_failures = self.pending_failures_by_dest.pop(destination, [])

        if pending_pdus:
            logger.debug("TX [%s] len(pending_pdus_by_dest[dest]) = %d",
                         destination, len(pending_pdus))

        if not pending_pdus and not pending_edus and not pending_failures:
            logger.debug("TX [%s] Nothing to send", destination)
            return

        try:
            self.pending_transactions[destination] = 1

            logger.debug("TX [%s] _attempt_new_transaction", destination)

            # Sort based on the order field
            pending_pdus.sort(key=lambda t: t[2])

            pdus = [x[0] for x in pending_pdus]
            edus = [x[0] for x in pending_edus]
            failures = [x[0].get_dict() for x in pending_failures]
            deferreds = [
                x[1] for x in pending_pdus + pending_edus + pending_failures
            ]

            txn_id = str(self._next_txn_id)

            limiter = yield get_retry_limiter(
                destination,
                self._clock,
                self.store,
            )

            logger.debug(
                "TX [%s] {%s} Attempting new transaction"
                " (pdus: %d, edus: %d, failures: %d)", destination, txn_id,
                len(pending_pdus), len(pending_edus), len(pending_failures))

            logger.debug("TX [%s] Persisting transaction...", destination)

            transaction = Transaction.create_new(
                origin_server_ts=int(self._clock.time_msec()),
                transaction_id=txn_id,
                origin=self.server_name,
                destination=destination,
                pdus=pdus,
                edus=edus,
                pdu_failures=failures,
            )

            self._next_txn_id += 1

            yield self.transaction_actions.prepare_to_send(transaction)

            logger.debug("TX [%s] Persisted transaction", destination)
            logger.info(
                "TX [%s] {%s} Sending transaction [%s],"
                " (PDUs: %d, EDUs: %d, failures: %d)",
                destination,
                txn_id,
                transaction.transaction_id,
                len(pending_pdus),
                len(pending_edus),
                len(pending_failures),
            )

            with limiter:
                # Actually send the transaction

                # FIXME (erikj): This is a bit of a hack to make the Pdu age
                # keys work
                def json_data_cb():
                    data = transaction.get_dict()
                    now = int(self._clock.time_msec())
                    if "pdus" in data:
                        for p in data["pdus"]:
                            if "age_ts" in p:
                                unsigned = p.setdefault("unsigned", {})
                                unsigned["age"] = now - int(p["age_ts"])
                                del p["age_ts"]
                    return data

                try:
                    response = yield self.transport_layer.send_transaction(
                        transaction, json_data_cb)
                    code = 200

                    if response:
                        for e_id, r in response.get("pdus", {}).items():
                            if "error" in r:
                                logger.warn(
                                    "Transaction returned error for %s: %s",
                                    e_id,
                                    r,
                                )
                except HttpResponseException as e:
                    code = e.code
                    response = e.response

                logger.info("TX [%s] {%s} got %d response", destination,
                            txn_id, code)

                logger.debug("TX [%s] Sent transaction", destination)
                logger.debug("TX [%s] Marking as delivered...", destination)

            yield self.transaction_actions.delivered(transaction, code,
                                                     response)

            logger.debug("TX [%s] Marked as delivered", destination)

            logger.debug("TX [%s] Yielding to callbacks...", destination)

            for deferred in deferreds:
                if code == 200:
                    deferred.callback(None)
                else:
                    deferred.errback(RuntimeError("Got status %d" % code))

                # Ensures we don't continue until all callbacks on that
                # deferred have fired
                try:
                    yield deferred
                except:
                    pass

            logger.debug("TX [%s] Yielded to callbacks", destination)
        except NotRetryingDestination:
            logger.info(
                "TX [%s] not ready for retry yet - "
                "dropping transaction for now",
                destination,
            )
        except RuntimeError as e:
            # We capture this here as there as nothing actually listens
            # for this finishing functions deferred.
            logger.warn(
                "TX [%s] Problem in _attempt_transaction: %s",
                destination,
                e,
            )
        except Exception as e:
            # We capture this here as there as nothing actually listens
            # for this finishing functions deferred.
            logger.warn(
                "TX [%s] Problem in _attempt_transaction: %s",
                destination,
                e,
            )

            for deferred in deferreds:
                if not deferred.called:
                    deferred.errback(e)

        finally:
            # We want to be *very* sure we delete this after we stop processing
            self.pending_transactions.pop(destination, None)

            # Check to see if there is anything else to send.
            self._attempt_new_transaction(destination)
Exemplo n.º 43
0
    def on_POST(self, request):
        yield run_on_reactor()

        body = parse_json_object_from_request(request)

        kind = "user"
        if "kind" in request.args:
            kind = request.args["kind"][0]

        if kind == "guest":
            ret = yield self._do_guest_registration(body)
            defer.returnValue(ret)
            return
        elif kind != "user":
            raise UnrecognizedRequestError(
                "Do not understand membership kind: %s" % (kind, ))

        # we do basic sanity checks here because the auth layer will store these
        # in sessions. Pull out the username/password provided to us.
        desired_password = None
        if 'password' in body:
            if (not isinstance(body['password'], basestring)
                    or len(body['password']) > 512):
                raise SynapseError(400, "Invalid password")
            desired_password = body["password"]

        desired_username = None
        if 'username' in body:
            if (not isinstance(body['username'], basestring)
                    or len(body['username']) > 512):
                raise SynapseError(400, "Invalid username")
            desired_username = body['username']

        appservice = None
        if has_access_token(request):
            appservice = yield self.auth.get_appservice_by_req(request)

        # fork off as soon as possible for ASes and shared secret auth which
        # have completely different registration flows to normal users

        # == Application Service Registration ==
        if appservice:
            # Set the desired user according to the AS API (which uses the
            # 'user' key not 'username'). Since this is a new addition, we'll
            # fallback to 'username' if they gave one.
            desired_username = body.get("user", desired_username)
            access_token = get_access_token_from_request(request)

            if isinstance(desired_username, basestring):
                result = yield self._do_appservice_registration(
                    desired_username, access_token, body)
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == Shared Secret Registration == (e.g. create new user scripts)
        if 'mac' in body:
            # FIXME: Should we really be determining if this is shared secret
            # auth based purely on the 'mac' key?
            result = yield self._do_shared_secret_registration(
                desired_username, desired_password, body)
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == Normal User Registration == (everyone else)
        if not self.hs.config.enable_registration:
            raise SynapseError(403, "Registration has been disabled")

        guest_access_token = body.get("guest_access_token", None)

        if ('initial_device_display_name' in body and 'password' not in body):
            # ignore 'initial_device_display_name' if sent without
            # a password to work around a client bug where it sent
            # the 'initial_device_display_name' param alone, wiping out
            # the original registration params
            logger.warn(
                "Ignoring initial_device_display_name without password")
            del body['initial_device_display_name']

        session_id = self.auth_handler.get_session_id(body)
        registered_user_id = None
        if session_id:
            # if we get a registered user id out of here, it means we previously
            # registered a user for this session, so we could just return the
            # user here. We carry on and go through the auth checks though,
            # for paranoia.
            registered_user_id = self.auth_handler.get_session_data(
                session_id, "registered_user_id", None)

        if desired_username is not None:
            yield self.registration_handler.check_username(
                desired_username,
                guest_access_token=guest_access_token,
                assigned_user_id=registered_user_id,
            )

        # Only give msisdn flows if the x_show_msisdn flag is given:
        # this is a hack to work around the fact that clients were shipped
        # that use fallback registration if they see any flows that they don't
        # recognise, which means we break registration for these clients if we
        # advertise msisdn flows. Once usage of Riot iOS <=0.3.9 and Riot
        # Android <=0.6.9 have fallen below an acceptable threshold, this
        # parameter should go away and we should always advertise msisdn flows.
        show_msisdn = False
        if 'x_show_msisdn' in body and body['x_show_msisdn']:
            show_msisdn = True

        if self.hs.config.enable_registration_captcha:
            flows = [
                [LoginType.RECAPTCHA],
                [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
            ]
            if show_msisdn:
                flows.extend([
                    [LoginType.MSISDN, LoginType.RECAPTCHA],
                    [
                        LoginType.MSISDN, LoginType.EMAIL_IDENTITY,
                        LoginType.RECAPTCHA
                    ],
                ])
        else:
            flows = [
                [LoginType.DUMMY],
                [LoginType.EMAIL_IDENTITY],
            ]
            if show_msisdn:
                flows.extend([
                    [LoginType.MSISDN],
                    [LoginType.MSISDN, LoginType.EMAIL_IDENTITY],
                ])

        authed, auth_result, params, session_id = yield self.auth_handler.check_auth(
            flows, body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, auth_result))
            return

        if registered_user_id is not None:
            logger.info("Already registered user ID %r for this session",
                        registered_user_id)
            # don't re-register the threepids
            add_email = False
            add_msisdn = False
        else:
            # NB: This may be from the auth handler and NOT from the POST
            if 'password' not in params:
                raise SynapseError(400, "Missing password.",
                                   Codes.MISSING_PARAM)

            desired_username = params.get("username", None)
            new_password = params.get("password", None)
            guest_access_token = params.get("guest_access_token", None)

            (registered_user_id, _) = yield self.registration_handler.register(
                localpart=desired_username,
                password=new_password,
                guest_access_token=guest_access_token,
                generate_token=False,
            )

            # auto-join the user to any rooms we're supposed to dump them into
            fake_requester = synapse.types.create_requester(registered_user_id)
            for r in self.hs.config.auto_join_rooms:
                try:
                    yield self._join_user_to_room(fake_requester, r)
                except Exception as e:
                    logger.error("Failed to join new user to %r: %r", r, e)

            # remember that we've now registered that user account, and with
            #  what user ID (since the user may not have specified)
            self.auth_handler.set_session_data(session_id,
                                               "registered_user_id",
                                               registered_user_id)

            add_email = True
            add_msisdn = True

        return_dict = yield self._create_registration_details(
            registered_user_id, params)

        if add_email and auth_result and LoginType.EMAIL_IDENTITY in auth_result:
            threepid = auth_result[LoginType.EMAIL_IDENTITY]
            yield self._register_email_threepid(registered_user_id, threepid,
                                                return_dict["access_token"],
                                                params.get("bind_email"))

        if add_msisdn and auth_result and LoginType.MSISDN in auth_result:
            threepid = auth_result[LoginType.MSISDN]
            yield self._register_msisdn_threepid(registered_user_id, threepid,
                                                 return_dict["access_token"],
                                                 params.get("bind_msisdn"))

        defer.returnValue((200, return_dict))