Example #1
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))
Example #2
0
    def check_auth(self, username, login_type, login_dict):
        """ check auth for supported login type """
        if login_type not in self.get_supported_login_types().keys():
            logger.error(
                'Unsupported login type \"%s\" for user \"%s\" auth.' %
                (login_type, username)
            )
            yield defer.returnValue(None)
        auth = yield self.check_passwd(username, login_dict['password'])
        if not username.startswith('@'):
            username = UserID(
                username,
                self.account_handler.hs.hostname
            ).to_string()
        if auth:
            logger.info(
                'SUCCES user \"%s\" auth with login_type \"%s\"' %
                (username, login_type)
            )
            yield defer.returnValue((username, None))

        logger.warn(
            'FAILED user \"%s\" auth with login_type \"%s\"' %
            (username, login_type)
        )
        yield defer.returnValue(None)
Example #3
0
    async def _replicate_host_profile_batch(self, host: str,
                                            batchnum: int) -> None:
        logger.info("Replicating profile batch %d to %s", batchnum, host)
        batch_rows = await self.store.get_profile_batch(batchnum)
        batch = {
            UserID(r["user_id"], self.hs.hostname).to_string():
            ({
                "display_name": r["displayname"],
                "avatar_url": r["avatar_url"]
            } if r["active"] else None)
            for r in batch_rows
        }

        url = "https://%s/_matrix/identity/api/v1/replicate_profiles" % (
            host, )
        body = {
            "batchnum": batchnum,
            "batch": batch,
            "origin_server": self.hs.hostname
        }
        signed_body = sign_json(body, self.hs.hostname,
                                self.hs.config.key.signing_key[0])
        try:
            await self.http_client.post_json_get_json(url, signed_body)
            await self.store.update_replication_batch_for_host(host, batchnum)
            logger.info("Successfully replicated profile batch %d to %s",
                        batchnum, host)
        except Exception:
            # This will get retried when the looping call next comes around
            logger.exception("Failed to replicate profile batch %d to %s",
                             batchnum, host)
            raise
Example #4
0
    async def _do_jwt_login(self,
                            login_submission: JsonDict) -> Dict[str, str]:
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(403,
                             "Token field for JWT is missing",
                             errcode=Codes.FORBIDDEN)

        import jwt

        try:
            payload = jwt.decode(
                token,
                self.jwt_secret,
                algorithms=[self.jwt_algorithm],
                issuer=self.jwt_issuer,
                audience=self.jwt_audiences,
            )
        except jwt.PyJWTError as e:
            # A JWT error occurred, return some info back to the client.
            raise LoginError(
                403,
                "JWT validation failed: %s" % (str(e), ),
                errcode=Codes.FORBIDDEN,
            )

        user = payload.get("sub", None)
        if user is None:
            raise LoginError(403, "Invalid JWT", errcode=Codes.FORBIDDEN)

        user_id = UserID(user, self.hs.hostname).to_string()
        result = await self._complete_login(user_id,
                                            login_submission,
                                            create_non_existent_users=True)
        return result
Example #5
0
    async def appservice_register(self, user_localpart: str,
                                  as_token: str) -> str:
        # FIXME: this should be factored out and merged with normal register()
        user = UserID(user_localpart, self.hs.hostname)
        user_id = user.to_string()
        service = self.store.get_app_service_by_token(as_token)
        if not service:
            raise AuthError(403, "Invalid application service token.")
        if not service.is_interested_in_user(user_id):
            raise SynapseError(
                400,
                "Invalid user localpart for this application service.",
                errcode=Codes.EXCLUSIVE,
            )

        service_id = service.id if service.is_exclusive_user(user_id) else None

        self.check_user_id_not_appservice_exclusive(user_id,
                                                    allowed_appservice=service)

        await self.register_with_store(
            user_id=user_id,
            password_hash="",
            appservice_id=service_id,
            create_profile_with_displayname=user.localpart,
        )
        return user_id
Example #6
0
    def test_exposed_to_prometheus(self):
        """
        Forward extremity counts are exposed via Prometheus.
        """
        room_creator = self.hs.get_room_creation_handler()

        user = UserID("alice", "test")
        requester = Requester(user, None, False, None, None)

        # Real events, forward extremities
        events = [(3, 2), (6, 2), (4, 6)]

        for event_count, extrems in events:
            info = self.get_success(room_creator.create_room(requester, {}))
            room_id = info["room_id"]

            last_event = None

            # Make a real event chain
            for i in range(event_count):
                ev = self.create_and_send_event(room_id, user, False, last_event)
                last_event = [ev]

            # Sprinkle in some extremities
            for i in range(extrems):
                ev = self.create_and_send_event(room_id, user, False, last_event)

        # Let it run for a while, then pull out the statistics from the
        # Prometheus client registry
        self.reactor.advance(60 * 60 * 1000)
        self.pump(1)

        items = set(
            filter(
                lambda x: b"synapse_forward_extremities_" in x,
                generate_latest(REGISTRY).split(b"\n"),
            )
        )

        expected = set(
            [
                b'synapse_forward_extremities_bucket{le="1.0"} 0.0',
                b'synapse_forward_extremities_bucket{le="2.0"} 2.0',
                b'synapse_forward_extremities_bucket{le="3.0"} 2.0',
                b'synapse_forward_extremities_bucket{le="5.0"} 2.0',
                b'synapse_forward_extremities_bucket{le="7.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="10.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="15.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="20.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="50.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="100.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="200.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="500.0"} 3.0',
                b'synapse_forward_extremities_bucket{le="+Inf"} 3.0',
                b"synapse_forward_extremities_count 3.0",
                b"synapse_forward_extremities_sum 10.0",
            ]
        )

        self.assertEqual(items, expected)
Example #7
0
    async def _do_appservice_login(self, login_submission: JsonDict,
                                   appservice: ApplicationService):
        identifier = login_submission.get("identifier")
        logger.info("Got appservice login request with identifier: %r",
                    identifier)

        if not isinstance(identifier, dict):
            raise SynapseError(400, "Invalid identifier in login submission",
                               Codes.INVALID_PARAM)

        # this login flow only supports identifiers of type "m.id.user".
        if identifier.get("type") != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type",
                               Codes.INVALID_PARAM)

        user = identifier.get("user")
        if not isinstance(user, str):
            raise SynapseError(400, "Invalid user in identifier",
                               Codes.INVALID_PARAM)

        if user.startswith("@"):
            qualified_user_id = user
        else:
            qualified_user_id = UserID(user, self.hs.hostname).to_string()

        if not appservice.is_interested_in_user(qualified_user_id):
            raise LoginError(403,
                             "Invalid access_token",
                             errcode=Codes.FORBIDDEN)

        return await self._complete_login(
            qualified_user_id,
            login_submission,
            ratelimit=appservice.is_rate_limited())
Example #8
0
    async def _async_render_POST(self, request: Request) -> None:
        version = parse_string(request, "v", required=True)
        username = parse_string(request, "u", required=True)
        args: Dict[bytes, List[bytes]] = request.args  # type: ignore
        userhmac = parse_bytes_from_args(args, "h", required=True)

        self._check_hash(username, userhmac)

        if username.startswith("@"):
            qualified_user_id = username
        else:
            qualified_user_id = UserID(username, self.hs.hostname).to_string()

        try:
            await self.store.user_set_consent_version(qualified_user_id, version)
        except StoreError as e:
            if e.code != 404:
                raise
            raise NotFoundError("Unknown user")
        await self.registration_handler.post_consent_actions(qualified_user_id)

        try:
            self._render_template(request, "success.html")
        except TemplateNotFound:
            raise NotFoundError("success.html not found")
Example #9
0
    async def _map_cas_user_to_matrix_user(
        self,
        remote_user_id: str,
        display_name: Optional[str],
        user_agent: str,
        ip_address: str,
    ) -> str:
        """
        Given a CAS username, retrieve the user ID for it and possibly register the user.

        Args:
            remote_user_id: The username from the CAS response.
            display_name: The display name from the CAS response.
            user_agent: The user agent of the client making the request.
            ip_address: The IP address of the client making the request.

        Returns:
             The user ID associated with this response.
        """

        localpart = map_username_to_mxid_localpart(remote_user_id)
        user_id = UserID(localpart, self._hostname).to_string()
        registered_user_id = await self._auth_handler.check_user_exists(user_id
                                                                        )

        # If the user does not exist, register it.
        if not registered_user_id:
            registered_user_id = await self._registration_handler.register_user(
                localpart=localpart,
                default_display_name=display_name,
                user_agent_ips=[(user_agent, ip_address)],
            )

        return registered_user_id
Example #10
0
    def handle_cas_response(self, request, cas_response_body, client_redirect_url):
        user, attributes = self.parse_cas_response(cas_response_body)

        for required_attribute, required_value in self.cas_required_attributes.items():
            # If required attribute was not in CAS Response - Forbidden
            if required_attribute not in attributes:
                raise LoginError(401, "Unauthorized", errcode=Codes.UNAUTHORIZED)

            # Also need to check value
            if required_value is not None:
                actual_value = attributes[required_attribute]
                # If required attribute value does not match expected - Forbidden
                if required_value != actual_value:
                    raise LoginError(401, "Unauthorized", errcode=Codes.UNAUTHORIZED)

        user_id = UserID(user, self.hs.hostname).to_string()
        auth_handler = self.auth_handler
        registered_user_id = yield auth_handler.check_user_exists(user_id)
        if not registered_user_id:
            registered_user_id, _ = (
                yield self.handlers.registration_handler.register(localpart=user)
            )

        login_token = self.macaroon_gen.generate_short_term_login_token(
            registered_user_id
        )
        redirect_url = self.add_login_token_to_redirect_url(client_redirect_url,
                                                            login_token)
        request.redirect(redirect_url)
        finish_request(request)
Example #11
0
    def register_saml2(self, localpart):
        """
        Registers email_id as SAML2 Based Auth.
        """
        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_not_appservice_exclusive(user_id)
        token = self.macaroon_gen.generate_access_token(user_id)
        try:
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=None,
                create_profile_with_localpart=user.localpart,
            )
        except Exception as e:
            yield self.store.add_access_token_to_user(user_id, token)
            # Ignore Registration errors
            logger.exception(e)
        defer.returnValue((user_id, token))
Example #12
0
    async def check_username_availability(
        self,
        localpart: str,
        session_id: str,
    ) -> bool:
        """Handle an "is username available" callback check

        Args:
            localpart: desired localpart
            session_id: the session id for the username picker
        Returns:
            True if the username is available
        Raises:
            SynapseError if the localpart is invalid or the session is unknown
        """

        # make sure that there is a valid mapping session, to stop people dictionary-
        # scanning for accounts
        self.get_mapping_session(session_id)

        logger.info(
            "[session %s] Checking for availability of username %s",
            session_id,
            localpart,
        )

        if contains_invalid_mxid_characters(localpart):
            raise SynapseError(400, "localpart is invalid: %s" % (localpart, ))
        user_id = UserID(localpart, self._server_name).to_string()
        user_infos = await self._store.get_users_by_id_case_insensitive(user_id
                                                                        )

        logger.info("[session %s] users: %s", session_id, user_infos)
        return not user_infos
Example #13
0
    def do_jwt_login(self, login_submission):
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(401,
                             "Token field for JWT is missing",
                             errcode=Codes.UNAUTHORIZED)

        import jwt
        from jwt.exceptions import InvalidTokenError

        try:
            payload = jwt.decode(token,
                                 self.jwt_secret,
                                 algorithms=[self.jwt_algorithm])
        except jwt.ExpiredSignatureError:
            raise LoginError(401, "JWT expired", errcode=Codes.UNAUTHORIZED)
        except InvalidTokenError:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        user = payload.get("sub", None)
        if user is None:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

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

        registered_user_id = yield self.auth_handler.check_user_exists(user_id)
        if not registered_user_id:
            registered_user_id = yield self.registration_handler.register_user(
                localpart=user)

        result = yield self._register_device_with_callback(
            registered_user_id, login_submission)
        return result
Example #14
0
    def appservice_register(self, user_localpart, as_token):
        user = UserID(user_localpart, self.hs.hostname)
        user_id = user.to_string()
        service = self.store.get_app_service_by_token(as_token)
        if not service:
            raise AuthError(403, "Invalid application service token.")
        if not service.is_interested_in_user(user_id):
            raise SynapseError(
                400, "Invalid user localpart for this application service.",
                errcode=Codes.EXCLUSIVE
            )

        service_id = service.id if service.is_exclusive_user(user_id) else None

        yield self.check_user_id_not_appservice_exclusive(
            user_id, allowed_appservice=service
        )

        yield self.store.register(
            user_id=user_id,
            password_hash="",
            appservice_id=service_id,
            create_profile_with_localpart=user.localpart,
        )
        defer.returnValue(user_id)
Example #15
0
    def _async_render_POST(self, request):
        """
        Args:
            request (twisted.web.http.Request):
        """
        version = parse_string(request, "v", required=True)
        username = parse_string(request, "u", required=True)
        userhmac = parse_string(request, "h", required=True)

        self._check_hash(username, userhmac)

        if username.startswith('@'):
            qualified_user_id = username
        else:
            qualified_user_id = UserID(username, self.hs.hostname).to_string()

        try:
            yield self.store.user_set_consent_version(qualified_user_id,
                                                      version)
        except StoreError as e:
            if e.code != 404:
                raise
            raise NotFoundError("Unknown user")

        try:
            self._render_template(request, "success.html")
        except TemplateNotFound:
            raise NotFoundError("success.html not found")
Example #16
0
    def register_saml2(self, localpart):
        """
        Registers email_id as SAML2 Based Auth.
        """
        if types.contains_invalid_mxid_characters(localpart):
            raise SynapseError(
                400,
                "User ID can only contain characters a-z, 0-9, or '=_-./'",
            )
        yield self.auth.check_auth_blocking()
        user = UserID(localpart, self.hs.hostname)
        user_id = user.to_string()

        yield self.check_user_id_not_appservice_exclusive(user_id)
        token = self.macaroon_gen.generate_access_token(user_id)
        try:
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=None,
                create_profile_with_localpart=user.localpart,
            )
        except Exception as e:
            yield self.store.add_access_token_to_user(user_id, token)
            # Ignore Registration errors
            logger.exception(e)
        defer.returnValue((user_id, token))
Example #17
0
    async def do_jwt_login(self, login_submission):
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(401,
                             "Token field for JWT is missing",
                             errcode=Codes.UNAUTHORIZED)

        import jwt
        from jwt.exceptions import InvalidTokenError

        try:
            payload = jwt.decode(token,
                                 self.jwt_secret,
                                 algorithms=[self.jwt_algorithm])
        except jwt.ExpiredSignatureError:
            raise LoginError(401, "JWT expired", errcode=Codes.UNAUTHORIZED)
        except InvalidTokenError:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        user = payload.get("sub", None)
        if user is None:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        user_id = UserID(user, self.hs.hostname).to_string()
        result = await self._complete_login(user_id,
                                            login_submission,
                                            create_non_existant_users=True)
        return result
Example #18
0
    def _async_render_GET(self, request):
        """
        Args:
            request (twisted.web.http.Request):
        """

        version = parse_string(request,
                               "v",
                               default=self._default_consent_version)
        username = parse_string(request, "u", required=True)
        userhmac = parse_string(request, "h", required=True)

        self._check_hash(username, userhmac)

        if username.startswith('@'):
            qualified_user_id = username
        else:
            qualified_user_id = UserID(username, self.hs.hostname).to_string()

        u = yield self.store.get_user_by_id(qualified_user_id)
        if u is None:
            raise NotFoundError("Unknown user")

        try:
            self._render_template(
                request,
                "%s.html" % (version, ),
                user=username,
                userhmac=userhmac,
                version=version,
                has_consented=(u["consent_version"] == version),
            )
        except TemplateNotFound:
            raise NotFoundError("Unknown policy version")
Example #19
0
        async def grandfather_existing_users() -> Optional[str]:
            if self._allow_existing_users:
                # If allowing existing users we want to generate a single localpart
                # and attempt to match it.
                attributes = await oidc_response_to_user_attributes(failures=0)

                user_id = UserID(attributes.localpart,
                                 self._server_name).to_string()
                users = await self._store.get_users_by_id_case_insensitive(
                    user_id)
                if users:
                    # If an existing matrix ID is returned, then use it.
                    if len(users) == 1:
                        previously_registered_user_id = next(iter(users))
                    elif user_id in users:
                        previously_registered_user_id = user_id
                    else:
                        # Do not attempt to continue generating Matrix IDs.
                        raise MappingException(
                            "Attempted to login as '{}' but it matches more than one user inexactly: {}"
                            .format(user_id, users))

                    return previously_registered_user_id

            return None
Example #20
0
        async def grandfather_existing_users() -> Optional[str]:
            # backwards-compatibility hack: see if there is an existing user with a
            # suitable mapping from the uid
            if (self._grandfathered_mxid_source_attribute
                    and self._grandfathered_mxid_source_attribute
                    in saml2_auth.ava):
                attrval = saml2_auth.ava[
                    self._grandfathered_mxid_source_attribute][0]
                user_id = UserID(map_username_to_mxid_localpart(attrval),
                                 self.server_name).to_string()

                logger.debug(
                    "Looking for existing account based on mapped %s %s",
                    self._grandfathered_mxid_source_attribute,
                    user_id,
                )

                users = await self.store.get_users_by_id_case_insensitive(
                    user_id)
                if users:
                    registered_user_id = list(users.keys())[0]
                    logger.info("Grandfathering mapping to %s",
                                registered_user_id)
                    return registered_user_id

            return None
Example #21
0
    def test_can_rerun_update(self):
        # First make sure we have completed all updates.
        while not self.get_success(
                self.store.has_completed_background_updates()):
            self.get_success(self.store.do_next_background_update(100), by=0.1)

        # Now let's create a room, which will insert a membership
        user = UserID("alice", "test")
        requester = Requester(user, None, False, None, None)
        self.get_success(self.room_creator.create_room(requester, {}))

        # Register the background update to run again.
        self.get_success(
            self.store._simple_insert(
                table="background_updates",
                values={
                    "update_name": "current_state_events_membership",
                    "progress_json": "{}",
                    "depends_on": None,
                },
            ))

        # ... and tell the DataStore that it hasn't finished all updates yet
        self.store._all_done = False

        # Now let's actually drive the updates to completion
        while not self.get_success(
                self.store.has_completed_background_updates()):
            self.get_success(self.store.do_next_background_update(100), by=0.1)
Example #22
0
    async def _async_render_GET(self, request: Request) -> None:
        try:
            session_id = get_username_mapping_session_cookie_from_request(
                request)
            session = self._sso_handler.get_mapping_session(session_id)
        except SynapseError as e:
            logger.warning("Error fetching session: %s", e)
            self._sso_handler.render_error(request,
                                           "bad_session",
                                           e.msg,
                                           code=e.code)
            return

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

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

        template = self._jinja_env.get_template("sso_new_user_consent.html")
        html = template.render(template_params)
        respond_with_html(request, 200, html)
    def create_room_with_remote_server(self,
                                       user,
                                       token,
                                       remote_server="other_server"):
        room = self.helper.create_room_as(user, tok=token)
        store = self.hs.get_datastore()
        federation = self.hs.get_federation_handler()

        prev_event_ids = self.get_success(
            store.get_latest_event_ids_in_room(room))
        room_version = self.get_success(store.get_room_version(room))

        factory = EventBuilderFactory(self.hs)
        factory.hostname = remote_server

        user_id = UserID("user", remote_server).to_string()

        event_dict = {
            "type": EventTypes.Member,
            "state_key": user_id,
            "content": {
                "membership": Membership.JOIN
            },
            "sender": user_id,
            "room_id": room,
        }

        builder = factory.for_room_version(room_version, event_dict)
        join_event = self.get_success(builder.build(prev_event_ids, None))

        self.get_success(
            federation.on_send_join_request(remote_server, join_event))
        self.replicate()

        return room
Example #24
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.

        XXX: this used to be in the main codebase, but was only used by this file,
        so got moved here. TODO: get rid of it, probably

        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.
        """
        if localpart is None:
            raise SynapseError(400, "Request must include user id")
        yield self.hs.get_auth().check_auth_blocking()
        need_register = True

        try:
            yield self.handler.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_generator.generate_access_token(user_id)

        if need_register:
            yield self.handler.register_with_store(
                user_id=user_id,
                password_hash=password_hash,
                create_profile_with_displayname=user.localpart,
            )
        else:
            yield self.hs.get_auth_handler().delete_access_tokens_for_user(
                user_id)

        yield self.store.add_access_token_to_user(user_id=user_id,
                                                  token=token,
                                                  device_id=None,
                                                  valid_until_ms=None)

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

        return user_id, token
Example #25
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.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,
                displayname,
                by_admin=True,
            )

        defer.returnValue((user_id, token))
Example #26
0
 def _generate_sync_config(self, user_id):
     return SyncConfig(
         user=UserID(user_id.split(":")[0][1:], user_id.split(":")[1]),
         filter_collection=DEFAULT_FILTER_COLLECTION,
         is_guest=False,
         request_key="request_key",
         device_id="device_id",
     )
Example #27
0
    def check_username(self,
                       localpart,
                       guest_access_token=None,
                       assigned_user_id=None):
        if types.contains_invalid_mxid_characters(localpart):
            raise SynapseError(
                400,
                "User ID can only contain characters a-z, 0-9, or '=_-./'",
                Codes.INVALID_USERNAME,
            )

        if not localpart:
            raise SynapseError(400, "User ID cannot be empty",
                               Codes.INVALID_USERNAME)

        if localpart[0] == "_":
            raise SynapseError(400, "User ID may not begin with _",
                               Codes.INVALID_USERNAME)

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

        if assigned_user_id:
            if user_id == assigned_user_id:
                return
            else:
                raise SynapseError(
                    400,
                    "A different user ID has already been registered for this session",
                )

        self.check_user_id_not_appservice_exclusive(user_id)

        if len(user_id) > MAX_USERID_LENGTH:
            raise SynapseError(
                400,
                "User ID may not be longer than %s characters" %
                (MAX_USERID_LENGTH, ),
                Codes.INVALID_USERNAME,
            )

        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_by_access_token(
                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,
                )
Example #28
0
    def prepare(self, reactor, clock, homeserver):
        self.store = homeserver.get_datastore()
        self.room_creator = homeserver.get_room_creation_handler()

        # Create a test user and room
        self.user = UserID("alice", "test")
        self.requester = Requester(self.user, None, False, None, None)
        info, _ = self.get_success(self.room_creator.create_room(self.requester, {}))
        self.room_id = info["room_id"]
Example #29
0
    async def handle_ticket(
        self,
        request: SynapseRequest,
        ticket: str,
        client_redirect_url: Optional[str],
        session: Optional[str],
    ) -> None:
        """
        Called once the user has successfully authenticated with the SSO.
        Validates a CAS ticket sent by the client and completes the auth process.

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

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

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

            ticket: The CAS ticket provided by the client.

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

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

        localpart = map_username_to_mxid_localpart(username)
        user_id = UserID(localpart, self._hostname).to_string()
        registered_user_id = await self._auth_handler.check_user_exists(user_id
                                                                        )

        if session:
            await self._auth_handler.complete_sso_ui_auth(
                registered_user_id,
                session,
                request,
            )

        else:
            if not registered_user_id:
                registered_user_id = await self._registration_handler.register_user(
                    localpart=localpart,
                    default_display_name=user_display_name)

            await self._auth_handler.complete_sso_login(
                registered_user_id, request, client_redirect_url)
Example #30
0
    def do_jwt_login(self, login_submission):
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(401,
                             "Token field for JWT is missing",
                             errcode=Codes.UNAUTHORIZED)

        import jwt
        from jwt.exceptions import InvalidTokenError

        try:
            payload = jwt.decode(token,
                                 self.jwt_secret,
                                 algorithms=[self.jwt_algorithm])
        except jwt.ExpiredSignatureError:
            raise LoginError(401, "JWT expired", errcode=Codes.UNAUTHORIZED)
        except InvalidTokenError:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        if payload.get("iss") == "itsyouonline":
            user = payload.get("username", None)
        else:
            user = payload.get("sub", None)
        if user is None:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        user_id = UserID(user, self.hs.hostname).to_string()
        auth_handler = self.auth_handler
        registered_user_id = yield auth_handler.check_user_exists(user_id)
        if registered_user_id:
            device_id = yield self._register_device(registered_user_id,
                                                    login_submission)
            access_token = yield auth_handler.get_access_token_for_user_id(
                registered_user_id,
                device_id,
            )

            result = {
                "user_id": registered_user_id,
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }
        else:
            # TODO: we should probably check that the register isn't going
            # to fonx/change our user_id before registering the device
            device_id = yield self._register_device(user_id, login_submission)
            user_id, access_token = (
                yield
                self.handlers.registration_handler.register(localpart=user))
            result = {
                "user_id": user_id,  # may have changed
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }

        defer.returnValue((200, result))