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 )
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"])
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"])
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
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)
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)
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"])
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
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")
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"])
def test_utility_get_password(monkeypatch): original = "getpass.getpass.__code__" replacement = (lambda _: "password123").__code__ monkeypatch.setattr(original, replacement) assert Utility.get_password() == "password123"
def test_utility_get_current_time(): assert Utility.get_current_time() // 1000 == int(time.time())
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)