def delete_media(
        self,
        mediaid: str = None,
        *,
        timestamp: int = None,
        size_gt: int = None,
        keep_profiles: bool = None,
        server_name: str = None,
        remote: bool = False
    ) -> Union[bool, int]:
        """Helper method for deleting both local and remote media

        Args:
            mediaid (str, optional): the media id that is intended for deletion. Defaults to None. # noqa: E501
            timestamp (int, optional): timestamp in millisecond. Defaults to None. # noqa: E501
            size_gt (int, optional): file size in byte. Defaults to None.
            keep_profiles (bool, optional): whether to keep media related to profiles. Defaults to None. # noqa: E501
            server_name (str, optional): designated homeserver address. Defaults to None. # noqa: E501
            remote (bool, optional): whether to delete remote media cache. Defaults to False. # noqa: E501

        Returns:
            If mediaid is not None return bool: the deletion is success or not
            If remote is False returns Contents: a list of deleted media
            If remote is True returns int: number of deleted media
        """
        if mediaid and (timestamp or size_gt or keep_profiles):
            raise ValueError(
                "Argument mediaid cannot be mixed with "
                "timestamp, size_gt and keep_profiles"
            )

        if remote:
            if mediaid:
                print(
                    "WARNING! argument mediaid is"
                    "ignored when remote is True"
                )
            return self.purge_remote_media(Utility.get_current_time())

        if mediaid:
            mediaid = self.extract_media_id(mediaid)
            return self.delete_local_media(mediaid, server_name)

        if timestamp or size_gt or keep_profiles:
            if timestamp is None:
                timestamp = Utility.get_current_time()
            if keep_profiles is None:
                keep_profiles = True
            if size_gt is None:
                size_gt = 0

            return self.delete_local_media_by_condition(
                timestamp,
                size_gt,
                keep_profiles,
                server_name
            )
Example #2
0
    def purge_remote_media(
        self, timestamp: int = Utility.get_current_time()) -> int:
        """Purge remote homeserver media

        https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/media_admin_api.md#purge-remote-media-api

        Args:
            timestamp (int, optional): timestamp in millisecond. Defaults to Utility.get_current_time(). # noqa: E501

        Returns:
            list: number of deleted media
        """
        if not isinstance(timestamp, int):
            raise TypeError("Argument 'timestamp' should be an "
                            f"int but not {type(timestamp)}")
        resp = self.connection.request(
            "POST",
            self.admin_patterns("/purge_media_cache?"
                                f"before_ts={timestamp}", 1),
            json={},
        )
        data = resp.json()
        if resp.status_code == 200:
            return data["deleted"]
        else:
            if self.suppress_exception:
                return False, data
            else:
                raise SynapseException(data["errcode"], data["error"])
Example #3
0
    def reset_password(self,
                       userid: str,
                       password: str = None,
                       logout: bool = True) -> bool:
        """Reset a user password

        https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/user_admin_api.md#reset-password

        Args:
            userid (str): the account you want to reset their password
            password (str, optional): the new password. Defaults to None.
            logout (bool, optional): whether or not to logout all current devices. Defaults to True. # noqa: E501

        Returns:
            bool: success or not
        """
        userid = self.validate_username(userid)
        if password is None:
            password = Utility.get_password()
        resp = self.connection.request("POST",
                                       self.admin_patterns(
                                           f"/reset_password/{userid}", 1),
                                       json={
                                           "new_password": password,
                                           "logout_devices": logout
                                       })
        if resp.status_code == 200:
            return True
        else:
            data = resp.json()
            if self.suppress_exception:
                return False, data
            else:
                raise SynapseException(data["errcode"], data["error"])
    def admin_login(protocol: str,
                    host: str,
                    port: str,
                    username: str = None,
                    password: str = None,
                    suppress_exception: bool = False,
                    no_admin: bool = False) -> str:
        """Login and get an access token

        Args:
            protocol (str): "http://" or "https://". Defaults to None. # noqa: E501
            host (str): homeserver address. Defaults to None.
            port (int): homeserver listening port. Defaults to None.
            username (str, optional): just username. Defaults to None.
            password (str, optional): just password. Defaults to None.
            suppress_exception (bool, optional): suppress exception or not, if not return False and the error in dict. Defaults to False. # noqa: E501

        Returns:
            str: access token
        """
        if username is None:
            username = input("Enter a username: "******"identifier": {
                "type": "m.id.user",
                "user": username
            },
            "type": "m.login.password",
            "password": password,
            "initial_device_display_name": "matrix-synapse-admin"
        }
        http = Client()
        base_url = f"{protocol}{host}:{port}"
        while True:
            resp = http.post(f"{base_url}{ClientAPI.BASE_PATH}/login",
                             json=login_data)
            data = resp.json()
            if "errcode" in data and data["errcode"] == "M_LIMIT_EXCEEDED":
                time.sleep(data["retry_after_ms"] / 1000)
                continue
            if resp.status_code == 200:
                access_token = data["access_token"]
                if not no_admin:
                    resp = User(host, port, access_token,
                                protocol).query(username)
                    if "errcode" not in resp:
                        return data["access_token"]
                    else:
                        data = resp
                else:
                    return access_token
            if suppress_exception:
                return False, data
            else:
                raise SynapseException(data["errcode"], data["error"])
Example #5
0
def test_user_registration_tokens_lists():
    assert user_handler.registration_tokens.lists() == []
    for _ in range(2):
        assert user_handler.registration_tokens.create()
    assert len(user_handler.registration_tokens.lists()) == 2
    assert user_handler.registration_tokens.create(
        expiry_time=Utility.get_current_time(10))
    time.sleep(10)
    assert len(user_handler.registration_tokens.lists(True)) == 2
    assert len(user_handler.registration_tokens.lists(False)) == 1
    assert len(user_handler.registration_tokens.lists()) == 3
Example #6
0
    def reactivate(self, userid: str, password: str = None) -> bool:
        """Reactivate a deactivated account

        Args:
            userid (str): the account you want to reactivate
            password (str, optional): a new password for the account. Defaults to None. # noqa: E501

        Returns:
            bool: success or not
        """
        if password is None:
            password = Utility.get_password()
        if not isinstance(password, str):
            raise TypeError("Argument 'password' should be a "
                            f"string but not {type(password)}")
        return self.modify(userid, password=password, deactivated=False)
Example #7
0
def test_management_purge_history():
    global shared_variable
    roomid, eventid = shared_variable
    shared_variable = []
    purgeid = mgt_handler.purge_history(roomid, eventid, True)
    assert isinstance(purgeid, str)
    time.sleep(1)
    shared_variable.append(purgeid)
    purgeid = mgt_handler.purge_history(roomid, Utility.get_current_time(),
                                        True)
    assert isinstance(purgeid, str)
    shared_variable.append(purgeid)
    with pytest.raises(SynapseException):
        purgeid = mgt_handler.purge_history("invalid", eventid, True)
        purgeid = mgt_handler.purge_history("invalid", "invalid", True)
        purgeid = mgt_handler.purge_history(roomid, "invalid", True)
Example #8
0
    def register(self,
                 username: str,
                 shared_secret: Union[str, bytes],
                 *,
                 displayname: str,
                 password: str = None,
                 admin: bool = False) -> dict:
        """Register a new user

        https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/register_api.md#shared-secret-registration

        Args:
            username (str): the username
            shared_secret (Union[str, bytes]): the shared secret defined in homeserver.yaml # noqa: E501
            displayname (str): the display name for the user
            password (str, optional): the password for the user. Defaults to None.
            admin (bool, optional): whether or not to set the user as server admin. Defaults to False. # noqa: E501

        Returns:
            dict: a dict including access token and other information of the new account
        """
        nonce = self._get_register_nonce()
        if password is None:
            password = Utility.get_password()
        data = {
            "nonce": nonce,
            "username": username,
            "display_name": displayname,
            "password": password,
            "admin": admin,
            "mac": self._generate_mac(nonce, username, password, shared_secret)
        }
        resp = self.connection.request("POST",
                                       self.admin_patterns("/register", 1),
                                       json=data)

        data = resp.json()
        if resp.status_code == 200:
            return data
        else:
            if self.suppress_exception:
                return False, data
            else:
                raise SynapseException(data["errcode"], data["error"])
    def delete_local_media_by_condition(
        self,
        timestamp: int = Utility.get_current_time(),
        size_gt: int = 0,
        keep_profiles: bool = True,
        server_name: str = None
    ) -> Contents:
        """Delete local media with condition

        https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/media_admin_api.md#delete-local-media-by-date-or-size

        Args:
            timestamp (int, optional): delete media sent before this timestamp. Defaults to Utility.get_current_time() (current time). # noqa: E501
            size_gt (int, optional): delete media in which their size are greater than this size in bytes. Defaults to None.
            keep_profiles (bool, optional): whether to keep profiles media or not. Defaults to True.
            server_name (str, optional): the source of the media. Defaults to your local server name (None).

        Returns:
            Contents: a list of deleted media
        """
        if server_name is None:
            server_name = self.server_addr

        optional_str = ""
        if keep_profiles:
            optional_str += "&keep_profiles=true"

        if size_gt < 0:
            raise ValueError("Argument 'size_gt' must be a positive integer")
        if not isinstance(timestamp, int):
            raise TypeError("Argument 'timestamp' must be an integer")

        resp = self.connection.request(
            "POST",
            self.admin_patterns(
                f"/media/{server_name}/delete?before_ts="
                f"{timestamp}&size_gt={size_gt}{optional_str}", 1
            ),
            json={},
        )
        data = resp.json()
        return Contents(data["deleted_media"], data["total"])
Example #10
0
def test_media_delete_local_media_by_condition():
    """TODO: timestamp, keep_profiles"""
    assert media_handler.delete_local_media_by_condition(size_gt=9665784) == []
    deleted_media = media_id[-1]
    assert query_media(deleted_media).status_code == 200

    delete_time = Utility.get_current_time() + 1000

    assert media_handler.delete_local_media_by_condition(
        delete_time,
        9000000) == [media_handler.extract_media_id(deleted_media)]
    assert query_media(deleted_media).status_code == 404

    assert media_handler.delete_local_media_by_condition(
        delete_time, 10000000) == []

    deletion = media_handler.delete_local_media_by_condition(delete_time)
    assert deletion.total == 2
    for media in media_id:
        assert query_media(media).status_code == 404
Example #11
0
def test_user_registration_tokens_create():
    result = user_handler.registration_tokens.create("testing", length=10)
    result2 = user_handler.registration_tokens.create("testing1")
    result2["token"] = "testing"
    assert result == result2
    assert result == {
        "token": "testing",
        "uses_allowed": None,
        "pending": 0,
        "completed": 0,
        "expiry_time": None
    }
    result = user_handler.registration_tokens.create(uses_allowed=10,
                                                     length=10)
    assert len(result["token"]) == 10
    assert result["uses_allowed"] == 10
    expiry = Utility.get_current_time(10)
    result = user_handler.registration_tokens.create(expiry_time=expiry)
    assert result["expiry_time"] == expiry
    with pytest.raises(SynapseException):
        user_handler.registration_tokens.create("testing")
Example #12
0
def test_user_registration_tokens_update():
    result = user_handler.registration_tokens.update("testing",
                                                     uses_allowed=12)
    assert result == {
        "token": "testing",
        "uses_allowed": 12,
        "pending": 0,
        "completed": 0,
        "expiry_time": None
    }
    expiry = Utility.get_current_time(120)
    result = user_handler.registration_tokens.update("testing1",
                                                     expiry_time=expiry)
    assert result == {
        "token": "testing1",
        "uses_allowed": None,
        "pending": 0,
        "completed": 0,
        "expiry_time": expiry
    }
    with pytest.raises(SynapseException):
        user_handler.registration_tokens.update("invalid", 20)
    def client_upload_attachment(
            self, attachment: Union[str, bytes]) -> Tuple[str, str]:
        """Upload media as a client

        Args:
            attachment (Union[str, bytes]): path to the attachment or the attachment in bytes  # noqa: E501

        Returns:
            Tuple[str, str]: media mxc url, mime type
        """
        content_type = "application/octet-stream"
        if isinstance(attachment, str):
            guess, _ = mimetypes.guess_type(attachment)
            if guess:
                content_type = guess
            with open(
                    attachment,
                    "rb",
            ) as f:
                attachment = f.read()
        elif isinstance(attachment, bytes):
            guess = Utility.guess_type(attachment)
            if guess:
                content_type = guess
        else:
            raise TypeError("Argument attachment must be str or bytes")

        resp = self.connection.request("POST",
                                       "/_matrix/media/r0/upload",
                                       content=attachment,
                                       headers={"Content-Type": content_type})
        data = resp.json()
        if resp.status_code == 200:
            return data["content_uri"], content_type
        else:
            if self.suppress_exception:
                return False, data
            else:
                raise SynapseException(data["errcode"], data["error"])
Example #14
0
def test_utility_get_password(monkeypatch):
    original = "getpass.getpass.__code__"
    replacement = (lambda _: "password123").__code__
    monkeypatch.setattr(original, replacement)
    assert Utility.get_password() == "password123"
Example #15
0
def test_utility_get_current_time():
    assert Utility.get_current_time() // 1000 == int(time.time())
Example #16
0
def test_utility_get_bool():
    assert Utility.get_bool(True) == "true"
    assert Utility.get_bool(False) == "false"
    with pytest.raises(TypeError):
        Utility.get_bool("True")
        Utility.get_bool(0)