def delete(self, email: str, verify: bool = True) -> bool:
        """
        Deletes the user with the given email address.
        :param email: email address of user
        :param verify: whether to wait to confirm that the user has been deleted
        :return: `True` if the user has been deleted, else `False` if they haven't because they didn't exist
        """
        user = self.get(email)
        if user is None:
            return False

        account = {"uid": user["uid"], "ke": user["ke"], "mail": email}

        # Odd interface, defined here:
        # https://gitlab.com/Shinobi-Systems/Shinobi/-/blob/dev/libs/webServerSuperPaths.js#L385
        response = requests.post(f"{self._base_url}/deleteAdmin",
                                 json=dict(account=account))
        raise_if_errors(response)

        if verify:
            if not wait_and_verify(lambda: self.get(email) is None):
                raise RuntimeError(
                    f"User with email \"{email}\" was not deleted")

        return True
    def modify(self, email: str, *, password: str) -> bool:
        """
        Modify a user.
        :param email: the email address of the user to modify
        :param password: new password
        :return: whether the user was modified
        """
        try:
            user = self.get(email, password)
            # The user can't be none as otherwise their credentials shouldn't have worked at getting the user's details
            assert user is not None
            return False
        except ShinobiWrongPasswordError:
            pass

        user = self.get(email)
        if user is None:
            raise ShinobiUserDoesNotExistError(email)

        data = {
            "mail": email,
            "pass": password,
            "password_again": password,
        }
        account = {"mail": email, "uid": user["uid"], "ke": user["ke"]}
        response = requests.post(f"{self._base_url}/editAdmin",
                                 json=dict(data=data, account=account))
        raise_if_errors(response)

        return True
    def create(self, email: str, password: str, verify: bool = True) -> Dict:
        """
        Creates a user with the given details.
        :param email: email address of the user
        :param password: password for the user
        :param verify: whether to wait to confirm that the user has been created
        :return: details about created user
        """
        # Not trusting Shinobi's API to give back anything useful if the user already exists
        if self.get(email):
            raise ShinobiUserAlreadyExistsError(email)

        # The required post does not align with the API documentation (https://shinobi.video/docs/api)
        # Exploiting the undocumented API successfully used by UI.
        # See source: https://gitlab.com/Shinobi-Systems/Shinobi/-/blob/dev/libs/webServerSuperPaths.js
        data = {
            "mail":
            email,
            "pass":
            password,
            "password_again":
            password,
            "details":
            json.dumps({
                "factorAuth": "0",
                "size": "",
                "days": "",
                "event_days": "",
                "log_days": "",
                "max_camera": "",
                "permissions": "all",
                "edit_size": "1",
                "edit_days": "1",
                "edit_event_days": "1",
                "edit_log_days": "1",
                "use_admin": "1",
                "use_aws_s3": "1",
                "use_whcs": "1",
                "use_sftp": "1",
                "use_webdav": "1",
                "use_discordbot": "1",
                "use_ldap": "1",
                "aws_use_global": "0",
                "b2_use_global": "0",
                "webdav_use_global": "0"
            })
        }
        response = requests.post(f"{self._base_url}/registerAdmin",
                                 json=dict(data=data))
        raise_if_errors(response)
        create_user = response.json()

        if verify:
            if not wait_and_verify(lambda: self.get(email) is not None):
                raise RuntimeError("Unable to verify created user")

        return ShinobiUserOrm._create_improved_user_entry(create_user["user"])
 def get_all(self) -> Tuple:
     """
     Gets details about all users.
     :return: tuple where each element contains details about a specific user
     """
     response = requests.get(f"{self._base_url}/list")
     raise_if_errors(response)
     return tuple(
         ShinobiUserOrm._create_improved_user_entry(user)
         for user in response.json()["users"])
    def _configure(self, monitor_id: str, configuration: Dict):
        """
        Configures the monitor with the given ID with the given configuration.

        Will create the monitor if it does not exist.
        :param monitor_id: ID of the monitor
        :param configuration: configuration of the monitor
        """
        # Note: Shinobi used to represent "details" as a JSON dumped string but now needs to be JSON
        configuration["details"] = ShinobiMonitorOrm._parse_details(
            configuration["details"])
        response = requests.post(
            f"{self.base_url}/configureMonitor/{self.group_key}/{monitor_id}",
            json=dict(data=configuration))
        raise_if_errors(response)
    def delete(self, monitor_id: str, verify: bool = True) -> bool:
        """
        Deletes the monitor with the given ID.

        NoOp if the monitor does not exist.
        :param monitor_id: ID of the monitor
        :param verify: wait and verify that the monitor has been deleted if `True`
        :return: `True` if the monitor was deleted
        """
        # Note: if we don"t do this check, Shinobi errors (and the connection hangs) if asked to remove a non-existent
        #       monitor
        if not self.get(monitor_id):
            return False

        response = requests.post(
            f"{self.base_url}/configureMonitor/{self.group_key}/{monitor_id}/delete"
        )
        raise_if_errors(response)
        if verify and not wait_and_verify(
                lambda: self.get(monitor_id) is None):
            raise RuntimeError(f"Could not delete monitor: {monitor_id}")

        return True
    def _get_as_user(self, email: str, password: str) -> Dict:
        """
        Gets details about the user with the given email address, using the user's own credentials.
        :param email: user's email address
        :param password: user's password
        :return: details about user
        :raises ShinobiWrongPasswordError: raised if an incorrect email/password pair is supplied
        """
        response = requests.post(
            f"http://{self.shinobi_client.host}:{self.shinobi_client.port}/?json=true",
            data={
                "mail": email,
                "pass": password
            })
        raise_if_errors(response, raise_if_json_not_ok=False)

        if not response.json()["ok"]:
            # We can only assume this means that the password was incorrect/the user doesn't exist...
            raise ShinobiWrongPasswordError(email, password)

        user = response.json()["$user"]
        user["pass"] = password
        return ShinobiUserOrm._create_improved_user_entry(user)