Esempio n. 1
0
class Core:
    def __init__(self):
        # check if older versions exist
        self.__initToolVersions()
        # create API client
        self.cl = AC()
        # Send statistics
        self.cl.setUserAgent("ISPAPICLI", __version__)
        # init db
        self.dbObj = DB()

    def initParser(self, args=None):
        """
        Initialize the parser using the python standard library 'argparse'
        This step adds all commands to be executed by the end-user

        Returns:
        --------
        ArgumentParser: parser
        """
        parser = argparse.ArgumentParser(add_help=False)
        parser.prog = "ispapi"
        parser.formatter_class = argparse.RawDescriptionHelpFormatter
        parser.epilog = textwrap.dedent("""
                ------------------------------------------------------------
                - You must login first to start requesting commands
                - You can use the command '--help <command>' to know everything about a specific command,
                - Or visit our documentation on: https://github.com/hexonet/hexonet-api-documentation
                ------------------------------------------------------------
            """)
        # args: will be the positional args
        parser.add_argument("args",
                            nargs=argparse.REMAINDER,
                            help="All additional args, e.g. limit=5")
        # command: is the user command to be executed e.g. QueryDomainList
        parser.add_argument(
            "--command",
            "-c",
            metavar="<command>",
            help="Enter a command e.g. -c=CheckDomain or -c CheckDomain",
        )
        # userid: used in login
        parser.add_argument("--userid",
                            "-u",
                            metavar="<user id>",
                            help="Your login user ID")
        # password: used in login
        parser.add_argument("--password",
                            "-p",
                            metavar="<your password>",
                            help="Your login password")
        # entitiy: either live or ote, used in login
        parser.add_argument(
            "--entity",
            "-e",
            choices={"live", "ote"},
            help="Set entity to either live or ote system e.g. -e=ote",
        )
        # gui: used to start the gui from the terminal
        parser.add_argument(
            "--gui",
            "-g",
            const="gui",
            nargs="?",
            metavar="<>",
            help="Start graphical application",
        )
        # help: used to show help about how to use the tool, generated by argparse dynamically
        parser.add_argument(
            "--help",
            "-h",
            const="all",
            nargs="?",
            metavar="<command>,<>",
            help="Show detailed use of a 'command' OR use --help to show help",
        )
        # list: used to list all the commands avaiable
        parser.add_argument(
            "--list",
            "-li",
            const="list",
            nargs="?",
            metavar="<>",
            help="List all commands' names",
        )
        # logout: used to kill the user session (locally and remotely)
        parser.add_argument(
            "--logout",
            "-l",
            const="logout",
            nargs="?",
            metavar="<>",
            help="Destroy your current session",
        )
        # update the local user commands. This will call scraper
        parser.add_argument(
            "--update",
            "-up",
            const="update",
            nargs="?",
            metavar="<>",
            help="Update local command list",
        )
        # version: show the tool version

        parser.add_argument("--version",
                            "-v",
                            action="version",
                            version=__version__)

        return parser

    def parseArgs(self, args):
        """
        This function will check which command is triggered by the user and call a function accordingly

        Returns
        -------
        Tuple: (string, string)
        """
        # case logout
        if args["logout"] is not None:
            result = self.logout()
            return "logout", result
        # case gui
        if args["gui"] is not None:
            return "gui", ""
        # case help
        if args["help"] is not None:
            if args["help"] == "all":
                return "help", ""
            else:
                command_help = args["help"]
                help_info = self.getCommandHelp(command_help)
                return "help_command", help_info
        # case list of all commands
        if args["list"] is not None:
            commands_list = self.getCommandList()
            return "list", commands_list
        # case update commands triggered
        if args["update"]:
            return "update", ""

        # case login
        # check if login credentials provided
        if None not in (args["userid"], args["password"], args["entity"]):
            result, msg = self.login(args)
            return "msg", msg

        # otherwise it is a command requested
        # if logged in, and there is a session, then execute a command
        session_status = self.checkSession(args)
        if session_status == "valid":
            if args["command"] is not None:
                cmd_struct = {}
                cmd_struct["command"] = args["command"]
                return "cmd", cmd_struct
            if args["COMMAND"] is not None:
                cmd_struct = {}
                cmd_struct["command"] = args["COMMAND"]
                return "cmd", cmd_struct
            # case user trying to log in while his session is valid
            elif None not in (args["userid"], args["password"],
                              args["entity"]):
                msg = "You are already logged in, your session is valid."
                return "msg", msg
            else:
                msg = "Command is not recognized!"
                return "cmd_unknown", msg
        # initial running
        elif session_status == "init":
            msg = "Login first, you can run the command: -u = <your user id> -p = <your password> -e = {ote,live}"
            return "msg", msg
        # case sessin expired
        elif session_status == "expired":
            msg = """
                Session expired. Please login again.
                Use the command: -u = <your user id> -p = <your password> -e = {ote,live}
                """
            return "msg", msg
        # case unknown command requested
        else:
            msg = "No command found!"
            return "cmd_unknown", msg

    def login(self, args, session_status=""):
        """
        This function perform remote login on the server

        Returns
        -------
        Tuple: ((True | False), msg:string)

        """
        user = args["userid"]
        password = args["password"]
        entity = args["entity"]
        # check which system to use, live of test
        if entity == "ote":
            # case ote is set, otherwise by default the system is live
            self.cl.useOTESystem()
        self.cl.setCredentials(user, password)
        r = self.cl.login()
        if r.isSuccess():
            # save login session
            loginSession = self.cl.getSession()
            # save session
            self.__saveLocalSession(loginSession, entity)
            msg = "Login success. Your session valid for one hour max of idle time"
            return True, msg
        else:
            desc = r.getDescription()
            code = r.getCode()
            msg = "Server response: " + str(code) + " " + desc
            return False, msg

    def checkSession(self, args=""):
        """
        Check local session.

        Returns
        -------
        String: 'valid' | 'init' | 'expired'
        """
        # check if there is a session already exist
        try:
            # query for login data
            data = self.dbObj.getLoginInfo()
            if not data:
                raise Exception
            # get login data
            entity = data[0]["entity"]
            t_old = data[0]["ts"]
            session = data[0]["session"]
            time_format = "%Y-%m-%d %H:%M:%S"
            t_now = datetime.now().strftime(time_format)
            t_now_object = datetime.strptime(t_now, time_format)
            t_old_object = datetime.strptime(t_old, time_format)
            t_new = t_now_object - timedelta(hours=1)
            if t_new < t_old_object:
                result = self.cl.setSession(session)
                if entity == "ote":
                    self.cl.useOTESystem()
                return "valid"
            else:
                return "expired"
            # Do something with the session file
        except Exception:
            return "init"

    def logout(self):
        """
        Delete user session.

        Returns:
        --------
        String: msg
        """
        try:
            msg = ""
            # query for login data
            data = self.dbObj.getLoginInfo()
            if not data:
                raise Exception
            # get login data
            entity = data[0]["entity"]
            session = data[0]["session"]
            if entity == "ote":
                self.cl.useOTESystem()
            # delete remote session
            self.cl.setSession(str(session))
            r = self.cl.logout()
            if r.isSuccess():
                flag = True
            else:
                flag = False

            # delete local session
            self.dbObj.deleteLoginInfo()

            # return message
            if flag:
                msg = "Successfully logged out!"
                return msg
            else:
                msg = "Local session deleted but couldn't delete remote session!"
                return msg

        except Exception as e:
            return "Couldn't delete remote session due to: " + str(e)

    def request(self, commands):
        """
        Request command from remote server.

        Returns:
        -------
        Response: response
        """
        response = self.cl.request(commands)
        return response

    def getResponse(self, response, mode=""):
        """
        Parse response based on user preference.

        Returns:
        --------
        List | String
        """
        if mode == "properties":
            return response.getListHash()
        elif mode == "list":
            return response.getListHash()
        elif mode == "plain":
            return response.getPlain()
        else:
            code = response.getCode()
            description = response.getDescription()
            message = "Server response: " + str(code) + " " + description
            return message

    def getCommandHelp(self, command_name):
        """
        Get help for a specific command: command_name.

        Returns:
        -------
        String: <>
        """
        data = self.dbObj.getCommand(command_name)
        for item in data:
            try:
                command_name_lower_case = (item["command"]).lower()
                if command_name_lower_case == command_name.lower():
                    command = item["command"]
                    description = item["description"]
                    availability = item["availability"]
                    paramaters = item["paramaters"]
                    basic_info = f"""
                                Command: {command}
                                Description: {description}
                                Availability: {availability}
                                Parameters:"""
                    # dedent, remove spaces
                    basic_info = textwrap.dedent(basic_info).strip()
                    headers = ["Parameter", "Min", "Definition", "Type"]
                    table = []
                    t = TextWrapper(width=30)
                    for row in paramaters:
                        row_data = []
                        for key in row:
                            if key == "Definition":
                                row_data.append(t.fill(row[key]))
                            else:
                                row_data.append(row[key])
                        table.append(row_data)

                    paramaters_table = tabulate(table,
                                                headers,
                                                tablefmt="fancy_grid")
                    return basic_info + "\n" + paramaters_table
            except Exception:
                continue
        else:
            return f"Command '{command_name}' not found!"

    def __saveLocalSession(self, loginSession, entity):
        """
        Saves users session in local file.

        Returns:
        --------
        Bool: True | False
        """
        try:
            time_format = "%Y-%m-%d %H:%M:%S"
            ts = datetime.now().strftime(time_format)
            data = {}
            data["session"] = loginSession
            data["ts"] = ts
            data["entity"] = entity
            # insert login
            if self.dbObj.setLoginInfo(data):
                return True
            else:
                raise Exception
        except Exception:
            return False

    def parseParameters(self, parameters):
        """
        Parse positional arguments, e.g. limit = 5.

        Returns:
        --------
        Set: params
        """
        params_len = len(parameters)
        params = {}
        i = 0
        while i < (params_len):
            if "=" in parameters[i]:
                key, value = parameters[i].split("=")
                params[key] = value
            else:
                key = parameters[i]
                i += 1
                value = parameters[i]
                params[key] = value
            i += 1
        # return result
        return params

    def getCommandList(self):
        """
        Get all commands from local commands' files

        Returns:
        --------
        String: return_list
        """
        return_list = ""
        data = self.dbObj.getAllCommands()
        for item in data:
            try:
                return_list += item["command"] + "\n"
            except Exception:
                continue
        return return_list

    def getSubUserList(self):
        """
        Get all subusers from local commands' files

        Returns:
        --------
        String: return_list
        """
        return_list = ""
        data = self.dbObj.getAllSubusers()
        for item in data:
            try:
                return_list += item["subuser"] + "\n"
            except Exception:
                continue
        return return_list

    def getMinParameters(self, command_name):
        """
        Get minimum required parameters of the command: command_name

        Returns:
        --------
        List: returnData
        """
        returnData = []
        data = self.dbObj.getCommand(command_name)
        if data:
            for item in data:
                try:
                    command_name_lower_case = (item["command"]).lower()
                    if command_name_lower_case == command_name.lower():
                        paramaters = item["paramaters"]
                        for row in paramaters:
                            paramater = row["Parameter"]
                            minValue = row["Min"]
                            if minValue == "1" and paramater != "COMMAND":
                                returnData.append(paramater.lower())
                except Exception:
                    continue
        return list(dict.fromkeys(returnData))

    def getSubUsers(self):
        cmd = {
            "command": "finduser",
            "pattern": "%",
            "userdepth": "all",
            "orderby": "userid",
        }
        response = self.request(cmd)
        listResult = response.getListHash()["LIST"]
        for item in listResult:
            subusers = {}
            subusers["subuser"] = item["USER"]
            self.dbObj.insertSubuser(item["USER"], subusers)
        pass

    def getCurrentVersion(self):
        return __version__

    def __initToolVersions(self):
        currentVersion = __version__.split(".")
        currentVersion[2] = str(int(currentVersion[2]) + 1)
        currentVersion = ".".join(currentVersion)
        currentToolVersion = "ispapicli-" + currentVersion
        print(os.listdir())
        files = os.listdir()
        ispapicliVersions = []
        for file in files:
            if file.startswith("ispapicli"):
                ispapicliVersions.append(file)
        print(ispapicliVersions, len(ispapicliVersions))
        if len(ispapicliVersions) <= 1:
            return
        else:
            for ver in ispapicliVersions:
                if currentToolVersion != ver:
                    os.remove(ver)
                    print(currentToolVersion, ver, "removed")
Esempio n. 2
0
def test_apiclientmethods():
    cl = AC()
    rtm.addTemplate(
        "login200",
        "[RESPONSE]\r\nPROPERTY[SESSION][0]=h8JLZZHdF2WgWWXlwbKWzEG3XrzoW4y" +
        "shhvtqyg0LCYiX55QnhgYX9cB0W4mlpbx\r\nDESCRIPTION=Command completed" +
        " successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.169\r\nEOF\r" +
        "\n",
    )
    rtm.addTemplate("login500",
                    rtm.generateTemplate("530", "Authentication failed"))
    rtm.addTemplate(
        "OK", rtm.generateTemplate("200", "Command completed successfully"))
    rtm.addTemplate(
        "listP0",
        "[RESPONSE]\r\nPROPERTY[TOTAL][0]=2701\r\nPROPERTY[FIRST][0]=0\r\nP" +
        "ROPERTY[DOMAIN][0]=0-60motorcycletimes.com\r\nPROPERTY[DOMAIN][1]=" +
        "0-be-s01-0.com\r\nPROPERTY[COUNT][0]=2\r\nPROPERTY[LAST][0]=1\r\nP" +
        "ROPERTY[LIMIT][0]=2\r\nDESCRIPTION=Command completed successfully" +
        "\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.023\r\nEOF\r\n",
    )
    rtm.addTemplate(
        "listP1",
        "[RESPONSE]\r\nPROPERTY[TOTAL][0]=2701\r\nPROPERTY[FIRST][0]=2\r\nP" +
        "ROPERTY[DOMAIN][0]=0-qas-ao17-0.org\r\nPROPERTY[DOMAIN][1]=0-sunny" +
        "da222y.com\r\nPROPERTY[COUNT][0]=2\r\nPROPERTY[LAST][0]=3\r\nPROPE" +
        "RTY[LIMIT][0]=2\r\nDESCRIPTION=Command completed successfully\r\nC" +
        "ODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.032\r\nEOF\r\n",
    )
    rtm.addTemplate(
        "listFP0",
        "[RESPONSE]\r\nPROPERTY[TOTAL][0]=3\r\nPROPERTY[FIRST][0]=0\r\nPROP" +
        "ERTY[DOMAIN][0]=0-60motorcycletimes.com\r\nPROPERTY[COUNT][0]=1\r" +
        "\nPROPERTY[LAST][0]=1\r\nPROPERTY[LIMIT][0]=1\r\nDESCRIPTION=Comma" +
        "nd completed successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0." +
        "023\r\nEOF\r\n",
    )
    rtm.addTemplate(
        "listFP1",
        "[RESPONSE]\r\nPROPERTY[TOTAL][0]=3\r\nPROPERTY[FIRST][0]=1\r\nPROP" +
        "ERTY[DOMAIN][0]=0-be-s01-0.com\r\nPROPERTY[COUNT][0]=1\r\nPROPERTY" +
        "[LAST][0]=2\r\nPROPERTY[LIMIT][0]=1\r\nDESCRIPTION=Command complet" +
        "ed successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.032\r\nEOF" +
        "\r\n",
    )
    rtm.addTemplate(
        "listFP2",
        "[RESPONSE]\r\nPROPERTY[TOTAL][0]=3\r\nPROPERTY[FIRST][0]=2\r\nPROP" +
        "ERTY[DOMAIN][0]=0-qas-ao17-0.org\r\nPROPERTY[COUNT][0]=2\r\nPROPER" +
        "TY[LAST][0]=3\r\nPROPERTY[LIMIT][0]=1\r\nDESCRIPTION=Command compl" +
        "eted successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.032\r\nE" +
        "OF\r\n",
    )

    # #.getPOSTData()
    # test object input with special chars
    validate = ("s_entity=54cd&s_command=AUTH%3Dgwrgwqg%25" +
                "%26%5C44t3%2A%0ACOMMAND%3DModifyDomain")
    enc = cl.getPOSTData({
        "COMMAND": "ModifyDomain",
        "AUTH": "gwrgwqg%&\\44t3*"
    })
    assert enc == validate

    # test string input
    enc = cl.getPOSTData("gregergege")
    assert enc == "s_entity=54cd&s_command=gregergege"

    # test object input with null value in parameter
    validate = "s_entity=54cd&s_command=COMMAND%3DModifyDomain"
    enc = cl.getPOSTData({"COMMAND": "ModifyDomain", "AUTH": None})
    assert enc == validate

    # test secured passwords
    cl.setCredentials("test.user", "test.passw0rd")
    enc = cl.getPOSTData(
        {
            "COMMAND": "CheckAuthentication",
            "SUBUSER": "******",
            "PASSWORD": "******",
        },
        True,
    )
    cl.setCredentials("", "")
    expected = (
        "s_entity=54cd&s_login=test.user&s_pw=***&" +
        "s_command=COMMAND%3DCheckAuthentication%0APASSWORD%3D%2A%2A%2A%0ASUBUSER%3Dtest.user"
    )
    assert expected == enc

    # #.enableDebugMode()
    cl.enableDebugMode()
    cl.disableDebugMode()

    # #.getSession()
    # initial value
    session = cl.getSession()
    assert session is None
    # custom value
    sessid = "testSessionID12345678"
    cl.setSession(sessid)
    session = cl.getSession()
    assert session is sessid
    cl.setSession("")

    # #.getURL()
    assert cl.getURL() == ISPAPI_CONNECTION_URL_LIVE

    # #.getUserAgent()
    pid = "PYTHON-SDK"
    pyv = platform.python_version()
    pf = platform.system()
    arch = platform.architecture()[0]
    ua = "%s (%s; %s; rv:%s) python/%s" % (pid, pf, arch, cl.getVersion(), pyv)
    assert cl.getUserAgent() == ua

    # #.setUserAgent()
    pid = "WHMCS"
    rv = "7.7.0"
    pid2 = "python-sdk"
    pyv = platform.python_version()
    pf = platform.system()
    arch = platform.architecture()[0]
    ua = "%s (%s; %s; rv:%s) %s/%s python/%s" % (
        pid,
        pf,
        arch,
        rv,
        pid2,
        cl.getVersion(),
        pyv,
    )
    cl2 = cl.setUserAgent(pid, rv)
    assert isinstance(cl2, AC) is True
    assert cl.getUserAgent() == ua

    mods = ["reg/2.6.2", "ssl/7.2.2", "dc/8.2.2"]
    ua = "%s (%s; %s; rv:%s) %s %s/%s python/%s" % (
        pid,
        pf,
        arch,
        rv,
        " ".join(mods),
        pid2,
        cl.getVersion(),
        pyv,
    )
    cl2 = cl.setUserAgent(pid, rv, mods)
    assert isinstance(cl2, AC) is True
    assert cl.getUserAgent() == ua

    # #.setURL()
    tmp = ISPAPI_CONNECTION_URL_PROXY
    url = cl.setURL(tmp).getURL()
    assert url is tmp
    cl.setURL(ISPAPI_CONNECTION_URL_LIVE)

    # #.setOTP()
    # [otp set]
    cl.setOTP("12345678")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_otp=12345678&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # [otp reset]
    cl.setOTP("")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # #.setSession()
    # [session set]
    cl.setSession("12345678")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # [credentials and session set]
    cl.setRoleCredentials("myaccountid", "myrole", "mypassword")
    cl.setOTP("12345678")
    cl.setSession("12345678")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # [session reset]
    cl.setSession("")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    assert tmp == "s_entity=54cd&s_command=COMMAND%3DStatusAccount"

    # #.saveSession/reuseSession
    sessionobj = {}
    cl.setSession("12345678").saveSession(sessionobj)
    cl2 = AC()
    cl2.reuseSession(sessionobj)
    tmp = cl2.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp
    cl.setSession("")

    # #.setRemoteIPAddress()
    # [ip set]
    cl.setRemoteIPAddress("10.10.10.10")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_remoteaddr=10.10.10.10&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # [ip reset]
    cl.setRemoteIPAddress("")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    assert tmp == "s_entity=54cd&s_command=COMMAND%3DStatusAccount"

    # #.setCredentials()
    # [credentials set]
    cl.setCredentials("myaccountid", "mypassword")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_login=myaccountid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # [session reset]
    cl.setCredentials("", "")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    assert tmp == "s_entity=54cd&s_command=COMMAND%3DStatusAccount"

    # #.setRoleCredentials()
    # [role credentials set]
    cl.setRoleCredentials("myaccountid", "myroleid", "mypassword")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    exp = "s_entity=54cd&s_login=myaccountid%21myroleid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount"
    assert tmp == exp

    # [role credentials reset]
    cl.setRoleCredentials("", "", "")
    tmp = cl.getPOSTData({"COMMAND": "StatusAccount"})
    assert tmp == "s_entity=54cd&s_command=COMMAND%3DStatusAccount"

    # #.login()
    # [login succeeded; no role used]
    cl.useOTESystem()
    cl.setCredentials("test.user", "test.passw0rd")
    cl.setRemoteIPAddress("1.2.3.4")
    r = cl.login()
    assert isinstance(r, R) is True
    assert r.isSuccess() is True, ("{0} {1}").format(r.getCode(),
                                                     r.getDescription())
    rec = r.getRecord(0)
    assert rec is not None
    assert rec.getDataByKey("SESSION") is not None

    # support bulk parameters also as nested array (flattenCommand)
    r = cl.request({
        "COMMAND": "CheckDomains",
        "DOMAIN": ["example.com", "example.net"]
    })
    assert isinstance(r, R) is True
    assert r.isSuccess() is True
    assert r.getCode() is 200
    assert r.getDescription() == "Command completed successfully"
    cmd = r.getCommand()
    keys = cmd.keys()
    assert ("DOMAIN0" in keys) is True
    assert ("DOMAIN1" in keys) is True
    assert ("DOMAIN" in keys) is False
    assert cmd["DOMAIN0"] == "example.com"
    assert cmd["DOMAIN1"] == "example.net"

    # support autoIDNConvert
    r = cl.request({
        "COMMAND": "CheckDomains",
        "DOMAIN": ["example.com", "dömäin.example", "example.net"],
    })
    assert isinstance(r, R) is True
    assert r.isSuccess() is True
    assert r.getCode() is 200
    assert r.getDescription() == "Command completed successfully"
    cmd = r.getCommand()
    keys = cmd.keys()
    assert ("DOMAIN0" in keys) is True
    assert ("DOMAIN1" in keys) is True
    assert ("DOMAIN2" in keys) is True
    assert ("DOMAIN" in keys) is False
    assert cmd["DOMAIN0"] == "example.com"
    assert cmd["DOMAIN1"] == "xn--dmin-moa0i.example"
    assert cmd["DOMAIN2"] == "example.net"

    # [login succeeded; role used]
    # cl.useOTESystem()
    # cl.setRoleCredentials('test.user', 'testrole', 'test.passw0rd')
    # r = cl.login()
    # assert isinstance(r, R)
    # assert r.isSuccess() is True, ("{0} {1}").format(r.getCode(), r.getDescription())
    # rec = r.getRecord(0)
    # assert rec is not None
    # assert rec.getDataByKey('SESSION') is not None

    # [login failed; wrong credentials]
    cl.setCredentials("test.user", "WRONGPASSWORD")
    r = cl.login()
    assert isinstance(r, R)
    assert r.isError() is True

    # [login failed; http error]
    tpl = rtm.getTemplate("httperror")
    old = cl.getURL()
    cl.setURL("https://iwontsucceedgregegeg343teagr43.com/api/call.cgi")
    cl.setCredentials("test.user", "WRONGPASSWORD")
    r = cl.login()
    assert isinstance(r, R)
    assert r.isTmpError() is True
    assert r.getDescription() == tpl.getDescription()
    cl.setURL(old)

    # [login succeeded; no session returned]
    # TODO: need network mock
    # tpl = R(rtm.getTemplate('OK').getPlain())
    # cl.useOTESystem()
    # cl.setCredentials('test.user', 'test.passw0rd')
    # r = cl.login()
    # assert isinstance(r, R)
    # assert r.isSuccess() is True
    # rec = r.getRecord(0)
    # assert rec is Node

    # #.loginExtended()
    # [login succeeded; no role used]
    cl.useOTESystem()
    cl.setCredentials("test.user", "test.passw0rd")
    r = cl.loginExtended({"TIMEOUT": 60})
    assert isinstance(r, R) is True
    assert r.isSuccess() is True
    rec = r.getRecord(0)
    assert rec is not None
    assert rec.getDataByKey("SESSION") is not None

    # #.logout()
    # [logout succeeded]
    r = cl.logout()
    assert isinstance(r, R)
    assert r.isSuccess() is True

    # [logout failed; session no longer exists]
    tpl = R(rtm.getTemplate("login200").getPlain())
    cl.enableDebugMode()
    cl.setSession(tpl.getRecord(0).getDataByKey("SESSION"))
    r = cl.logout()
    assert isinstance(r, R) is True
    assert r.isError() is True

    # #.request()
    # [200 < r.statusCode > 299]
    # TODO need network mock
    # tpl2 = R(rtm.getTemplate('httperror').getPlain())
    # cl.setCredentials('test.user', 'test.passw0rd')
    # cl.useOTESystem()
    # r = cl.request({ 'COMMAND': 'GetUserIndex' })
    # assert isinstance(r, R) is True
    # assert r.isTmpError() is True
    # assert r.getCode() == tpl2.getCode()
    # assert r.getDescription() == tpl2.getDescription()

    # [200 < r.statusCode > 299, no debug]
    # TODO need network mock
    # tpl2 = R(rtm.getTemplate('httperror').getPlain())
    # cl.disableDebugMode()
    # r = cl.request({ 'COMMAND': 'GetUserIndex' })
    # assert isinstance(r, R)
    # assert r.isTmpError() is True
    # assert r.getCode() == tpl2.getCode()
    # assert r.getDescription() == tpl2.getDescription()

    # .requestNextResponsePage
    # [no LAST set]
    cl.setCredentials("test.user", "test.passw0rd")
    cl.useOTESystem()
    r = R(
        rtm.getTemplate("listP0").getPlain(),
        {
            "COMMAND": "QueryDomainList",
            "LIMIT": 2,
            "FIRST": 0
        },
    )
    nr = cl.requestNextResponsePage(r)
    assert r.isSuccess() is True
    assert nr.isSuccess() is True
    assert r.getRecordsLimitation() == 2
    assert nr.getRecordsLimitation() == 2
    assert r.getRecordsCount() == 2
    assert nr.getRecordsCount() == 2
    assert r.getFirstRecordIndex() == 0
    assert r.getLastRecordIndex() == 1
    assert nr.getFirstRecordIndex() == 2
    assert nr.getLastRecordIndex() == 3

    # [LAST set]
    r = R(
        rtm.getTemplate("listP0").getPlain(),
        {
            "COMMAND": "QueryDomainList",
            "LIMIT": 2,
            "FIRST": 0,
            "LAST": 1
        },
    )
    with pytest.raises(Exception, match=r"Parameter LAST in use."):
        cl.requestNextResponsePage(r)

    # [no FIRST set]
    cl.disableDebugMode()
    r = R(
        rtm.getTemplate("listP0").getPlain(), {
            "COMMAND": "QueryDomainList",
            "LIMIT": 2
        })
    nr = cl.requestNextResponsePage(r)
    assert r.isSuccess() is True
    assert nr.isSuccess() is True
    assert r.getRecordsLimitation() == 2
    assert nr.getRecordsLimitation() == 2
    assert r.getRecordsCount() == 2
    assert nr.getRecordsCount() == 2
    assert r.getFirstRecordIndex() == 0
    assert r.getLastRecordIndex() == 1
    assert nr.getFirstRecordIndex() == 2
    assert nr.getLastRecordIndex() == 3

    # #.requestAllResponsePages()
    # [success case]
    nr = cl.requestAllResponsePages({
        "COMMAND": "QuerySSLCertList",
        "FIRST": 0,
        "LIMIT": 100
    })
    assert len(nr) > 0

    # #.setUserView()
    cl.setUserView("hexotestman.com")
    r = cl.request({"COMMAND": "GetUserIndex"})
    assert isinstance(r, R) is True
    assert r.isSuccess() is True

    # #.resetUserView()
    cl.setUserView("")
    r = cl.request({"COMMAND": "GetUserIndex"})
    assert isinstance(r, R) is True
    assert r.isSuccess() is True

    # #.setProxy
    cl.setProxy("https://127.0.0.1:8080")
    assert cl.getProxy() == "https://127.0.0.1:8080"
    cl.setProxy("")

    # #.setReferer
    cl.setReferer("https://www.hexonet.net/")
    assert cl.getReferer() == "https://www.hexonet.net/"
    cl.setReferer("")

    # #.useHighPerformanceConnectionSetup
    cl.useHighPerformanceConnectionSetup()
    assert cl.getURL() == ISPAPI_CONNECTION_URL_PROXY

    # #.useDefaultConnectionSetup
    cl.useDefaultConnectionSetup()
    assert cl.getURL() == ISPAPI_CONNECTION_URL_LIVE
Esempio n. 3
0
class Core:
    def __init__(self):
        # create API client
        self.cl = AC()
        # Send statistics
        self.cl.setUserAgent("ISPAPICLI", __version__)
        # init cross OS path reader
        self.initAppDirectories()

    def initAppDirectories(self):
        '''
        This function checks whether the apps is running in your editor or
        in an executable file
        This is important to correctly associate the path

        Returns:
        None
        '''
        if getattr(sys, 'frozen', False):
            self.absolute_dirpath = os.path.dirname(sys.executable)
        elif __file__:
            self.absolute_dirpath = os.path.dirname(__file__)
        # check if commands exist
        if not os.path.exists(
                os.path.join(self.absolute_dirpath, '../commands/')):
            os.makedirs(os.path.join(self.absolute_dirpath, '../commands/'))
        # check config directory
        if not os.path.exists(os.path.join(self.absolute_dirpath,
                                           '../config/')):
            os.makedirs(os.path.join(self.absolute_dirpath, '../config/'))
        # set path variables
        self.command_path = os.path.join(self.absolute_dirpath, '../commands/')
        self.session_path = os.path.join(self.absolute_dirpath,
                                         '../config/session.json')
        return None

    def initParser(self, args=None):
        '''
        Initialize the parser using the python standard library 'argparse'
        This step adds all commands to be executed by the end-user

        Returns:
        --------
        ArgumentParser: parser
        '''
        parser = argparse.ArgumentParser(add_help=False)
        parser.prog = 'ispapi'
        parser.formatter_class = argparse.RawDescriptionHelpFormatter
        parser.epilog = textwrap.dedent('''
                ------------------------------------------------------------
                - You must login first to start requesting commands
                - You can use the command '--help <command>' to know everything about a specific command,
                - Or visit our documentation on: https://github.com/hexonet/hexonet-api-documentation
                ------------------------------------------------------------
            ''')
        # args: will be the positional args
        parser.add_argument('args',
                            nargs=argparse.REMAINDER,
                            help='All additional args, e.g. limit=5')
        # command: is the user command to be executed e.g. QueryDomainList
        parser.add_argument(
            '--command',
            '-c',
            metavar='<command>',
            help='Enter a command e.g. -c=CheckDomain or -c CheckDomain')
        # userid: used in login
        parser.add_argument('--userid',
                            '-u',
                            metavar='<user id>',
                            help='Your login user ID')
        # password: used in login
        parser.add_argument('--password',
                            '-p',
                            metavar='<your password>',
                            help="Your login password")
        # entitiy: either live or ote, used in login
        parser.add_argument(
            '--entity',
            '-e',
            choices={'live', 'ote'},
            help="Set entity to either live or ote system e.g. -e=ote")
        # gui: used to start the gui from the terminal
        parser.add_argument('--gui',
                            '-g',
                            const='gui',
                            nargs='?',
                            metavar='<>',
                            help="Start graphical application")
        # help: used to show help about how to use the tool, generated by argparse dynamically
        parser.add_argument(
            '--help',
            '-h',
            const='all',
            nargs='?',
            metavar='<command>,<>',
            help="Show detailed use of a 'command' OR use --help to show help")
        # list: used to list all the commands avaiable
        parser.add_argument('--list',
                            '-li',
                            const='list',
                            nargs='?',
                            metavar='<>',
                            help="List all commands' names")
        # logout: used to kill the user session (locally and remotely)
        parser.add_argument('--logout',
                            '-l',
                            const='logout',
                            nargs='?',
                            metavar='<>',
                            help="Destroy your current session")
        # update the local user commands. This will call scraper
        parser.add_argument('--update',
                            '-up',
                            const='update',
                            nargs='?',
                            metavar='<>',
                            help="Update local command list")
        # version: show the tool version

        parser.add_argument('--version',
                            '-v',
                            action='version',
                            version='%(prog)s %(__version__)s')

        return parser

    def parseArgs(self, args):
        '''
        This function will check which command is triggered by the user and call a function accordingly

        Returns
        -------
        Tuple: (string, string)
        '''
        # case logout
        if args['logout'] is not None:
            result = self.logout()
            return 'logout', result
        # case gui
        if args['gui'] is not None:
            return 'gui', ''
        # case help
        if args['help'] is not None:
            if args['help'] == 'all':
                return 'help', ''
            else:
                command_help = args['help']
                help_info = self.getCommandHelp(command_help)
                return 'help_command', help_info
        # case list of all commands
        if args['list'] is not None:
            commands_list = self.getCommandList()
            return 'list', commands_list
        # case update commands triggered
        if args['update']:
            return 'update', ''

        # case login
        # check if login credentials provided
        if None not in (args['userid'], args['password'], args['entity']):
            result, msg = self.login(args)
            return 'msg', msg

        # otherwise it is a command requested
        # if logged in, and there is a session, then execute a command
        session_status = self.checkSession(args)
        if session_status == 'valid':
            if args['command'] is not None:
                cmd_struct = {}
                cmd_struct['command'] = args['command']
                return 'cmd', cmd_struct
            # case user trying to log in while his session is valid
            elif None not in (args['userid'], args['password'],
                              args['entity']):
                msg = 'You are already logged in, your session is valid.'
                return 'msg', msg
            else:
                msg = 'Command is not recognized!'
                return 'cmd_unknown', msg
        # initial running
        elif session_status == 'init':
            msg = "Login first, you can run the command: -u = <your user id> -p = <your password> -e = {ote,live}"
            return 'msg', msg
        # case sessin expired
        elif session_status == 'expired':
            msg = """
                Session expired. Please login again.
                Use the command: -u = <your user id> -p = <your password> -e = {ote,live}
                """
            return 'msg', msg
        # case unknown command requested
        else:
            msg = 'No command found!'
            return 'cmd_unknown', msg

    def login(self, args, session_status=''):
        '''
        This function perform remote login on the server

        Returns
        -------
        Tuple: ((True | False), msg:string)

        '''
        user = args['userid']
        password = args['password']
        entity = args['entity']
        # check which system to use, live of test
        if entity == 'ote':
            # case ote is set, otherwise by default the system is live
            self.cl.useOTESystem()
        self.cl.setCredentials(user, password)
        r = self.cl.login()
        if r.isSuccess():
            # save login session
            loginSession = self.cl.getSession()
            # save session
            self.__saveLocalSession(loginSession, entity)
            msg = "Login success. Your session valid for one hour max of idle time"
            return True, msg
        else:
            desc = r.getDescription()
            code = r.getCode()
            msg = "Server response: " + str(code) + " " + desc
            return False, msg

    def checkSession(self, args=''):
        '''
        Check local session.

        Returns
        -------
        String: 'valid' | 'init' | 'expired'
        '''
        data = {}
        # check if there is a session already exist
        p = self.session_path
        try:
            f = open(p, 'r')
            data = json.load(f)
            f.close()
            entity = data['entity']
            time_format = "%Y-%m-%d %H:%M:%S"
            t_now = datetime.now().strftime(time_format)
            t_now_object = datetime.strptime(t_now, time_format)
            t_old = data['ts']
            t_old_object = datetime.strptime(t_old, time_format)
            t_new = t_now_object - timedelta(hours=1)
            if t_new < t_old_object:
                result = self.cl.setSession(data['session'])
                if entity == 'ote':
                    self.cl.useOTESystem()
                return 'valid'
            else:
                return 'expired'
            # Do something with the session file
        except IOError:
            return 'init'

    def logout(self):
        '''
        Delete user session.

        Returns:
        --------
        String: msg
        '''
        p = self.session_path
        try:
            msg = ''
            f = open(p, 'r')
            data = json.load(f)
            f.close()
            entity = data['entity']
            if entity == 'ote':
                self.cl.useOTESystem()
            # delete remote session
            self.cl.setSession(str(data['session']))
            r = self.cl.logout()
            if r.isSuccess():
                flag = True
            else:
                flag = False

            # delete local session
            path = self.session_path
            if os.path.exists(path):
                os.remove(path)  # delete local session
                if flag:
                    msg = 'Successfully logged out!'
                    return msg
                else:
                    msg = "Local session deleted but couldn't delete remote session!"
                    return msg
            else:
                msg = 'Session already deleted'
                return msg

        except Exception as e:
            return "Couldn't delete remote session due to: " + str(e)

    def request(self, commands):
        '''
        Request command from remote server.

        Returns:
        -------
        Response: response
        '''
        response = self.cl.request(commands)
        return response

    def getResponse(self, response, mode=''):
        '''
        Parse response based on user preference.

        Returns:
        --------
        List | String
        '''
        if mode == 'properties':
            return response.getListHash()
        elif mode == 'list':
            return response.getListHash()
        elif mode == 'plain':
            return response.getPlain()
        else:
            code = response.getCode()
            description = response.getDescription()
            message = "Server response: " + str(code) + " " + description
            return message

    def getCommandHelp(self, command_name):
        '''
        Get help for a specific command: command_name.

        Returns:
        -------
        String: <>
        '''
        command_name = command_name.lower()
        path = self.command_path
        data = {}
        files = os.listdir(path)
        for file in files:
            try:
                file_name, ext = file.split('.')
                file_name_lower_case = file_name.lower()
                if file_name_lower_case == command_name:
                    file_path = os.path.join(path, file)
                    f = open(file_path, 'r')
                    data = json.load(f)
                    f.close()
                    command = data['command']
                    description = data['description']
                    availability = data['availability']
                    paramaters = data['paramaters']
                    basic_info = f'''
                                Command: {command}
                                Description: {description}
                                Availability: {availability}
                                Parameters:'''
                    # dedent, remove spaces
                    basic_info = textwrap.dedent(basic_info).strip()
                    headers = ['Parameter', 'Min', 'Definition', 'Type']
                    table = []
                    t = TextWrapper(width=30)
                    for row in paramaters:
                        row_data = []
                        for key in row:
                            if key == 'Definition':
                                row_data.append(t.fill(row[key]))
                            else:
                                row_data.append(row[key])
                        table.append(row_data)

                    paramaters_table = tabulate(table,
                                                headers,
                                                tablefmt="fancy_grid")
                    return basic_info + '\n' + paramaters_table
            except Exception:
                continue
        else:
            return f"Command '{command_name}' not found!"

    def __saveLocalSession(self, loginSession, entity):
        '''
        Saves users session in local file.

        Returns:
        --------
        Bool: True | False
        '''
        try:
            time_format = "%Y-%m-%d %H:%M:%S"
            ts = datetime.now().strftime(time_format)
            data = {}
            data['session'] = loginSession
            data['ts'] = ts
            data['entity'] = entity
            # write session and current time to local file
            path = self.session_path
            f = open(path, 'w')
            json.dump(data, f)
            f.close()
            return True
        except Exception as e:
            return False

    def parseParameters(self, parameters):
        '''
        Parse positional arguments, e.g. limit = 5.

        Returns:
        --------
        Set: params
        '''
        params_len = len(parameters)
        params = {}
        i = 0
        while i < (params_len):
            if '=' in parameters[i]:
                key, value = parameters[i].split('=')
                params[key] = value
            else:
                key = parameters[i]
                i += 1
                value = parameters[i]
                params[key] = value
            i += 1
        # return result
        return params

    def getCommandList(self):
        '''
        Get all commands from local commands' files

        Returns:
        --------
        String: return_list
        '''
        path = self.command_path
        data = {}
        c_names = os.listdir(path)
        return_list = ''
        for name in sorted(c_names):
            try:
                file_name, ext = name.split('.')
                return_list += file_name + '\n'
            except Exception:
                continue
        return return_list

    def getMinParameters(self, command_name):
        '''
        Get minimum required parameters of the command: command_name

        Returns:
        --------
        List: returnData
        '''
        command_name = command_name.lower()
        path = self.command_path
        returnData = []
        files = os.listdir(path)
        for file in files:
            try:
                file_name, ext = file.split('.')
                file_name_lower_case = file_name.lower()
                if file_name_lower_case == command_name:
                    file_path = os.path.join(path, file)
                    f = open(file_path, 'r')
                    data = json.load(f)
                    f.close()
                    paramaters = data['paramaters']
                    for row in paramaters:
                        paramater = row['Parameter']
                        minValue = row['Min']
                        if minValue == '1' and paramater != 'COMMAND':
                            returnData.append(paramater.lower())
            except Exception:
                continue
        return returnData
Esempio n. 4
0
rhash = r.getHash()
rplain = r.getPlain()

# Get the response code and the response description
code = r.getCode()
description = r.getDescription()

# -------- SESSION-BASED COMMUNICATION -----------

cl = AC()
cl.useOTESystem()
cl.setCredentials('test.user', 'test.passw0rd')
cl.setRemoteIPAddress('1.2.3.4')

# Call a command
r = cl.login()
# cl.login('12345678'); -> 2FA: one time password

if (r.isSuccess()):
    print("LOGIN SUCCEEDED!")
    r = cl.request({
        'Command': "QueryDomainList", 'limit': 5
    })
    rplain = r.getPlain()
    # ... further commands ...
    r = cl.logout()
    if (r.isSuccess()):
        print("LOGOUT SUCCEEDED!")

    
def test_apiclientmethods():
    cl = AC()
    rtm = RTM.getInstance()
    rtm.addTemplate(
        'login200',
        '[RESPONSE]\r\nPROPERTY[SESSION][0]=h8JLZZHdF2WgWWXlwbKWzEG3XrzoW4y' +
        'shhvtqyg0LCYiX55QnhgYX9cB0W4mlpbx\r\nDESCRIPTION=Command completed' +
        ' successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.169\r\nEOF\r' +
        '\n')
    rtm.addTemplate('login500',
                    rtm.generateTemplate('530', 'Authentication failed'))
    rtm.addTemplate(
        'OK', rtm.generateTemplate('200', 'Command completed successfully'))
    rtm.addTemplate(
        'listP0',
        '[RESPONSE]\r\nPROPERTY[TOTAL][0]=2701\r\nPROPERTY[FIRST][0]=0\r\nP' +
        'ROPERTY[DOMAIN][0]=0-60motorcycletimes.com\r\nPROPERTY[DOMAIN][1]=' +
        '0-be-s01-0.com\r\nPROPERTY[COUNT][0]=2\r\nPROPERTY[LAST][0]=1\r\nP' +
        'ROPERTY[LIMIT][0]=2\r\nDESCRIPTION=Command completed successfully' +
        '\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.023\r\nEOF\r\n')
    rtm.addTemplate(
        'listP1',
        '[RESPONSE]\r\nPROPERTY[TOTAL][0]=2701\r\nPROPERTY[FIRST][0]=2\r\nP' +
        'ROPERTY[DOMAIN][0]=0-qas-ao17-0.org\r\nPROPERTY[DOMAIN][1]=0-sunny' +
        'da222y.com\r\nPROPERTY[COUNT][0]=2\r\nPROPERTY[LAST][0]=3\r\nPROPE' +
        'RTY[LIMIT][0]=2\r\nDESCRIPTION=Command completed successfully\r\nC' +
        'ODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.032\r\nEOF\r\n')
    rtm.addTemplate(
        'listFP0',
        '[RESPONSE]\r\nPROPERTY[TOTAL][0]=3\r\nPROPERTY[FIRST][0]=0\r\nPROP' +
        'ERTY[DOMAIN][0]=0-60motorcycletimes.com\r\nPROPERTY[COUNT][0]=1\r' +
        '\nPROPERTY[LAST][0]=1\r\nPROPERTY[LIMIT][0]=1\r\nDESCRIPTION=Comma' +
        'nd completed successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.' +
        '023\r\nEOF\r\n')
    rtm.addTemplate(
        'listFP1',
        '[RESPONSE]\r\nPROPERTY[TOTAL][0]=3\r\nPROPERTY[FIRST][0]=1\r\nPROP' +
        'ERTY[DOMAIN][0]=0-be-s01-0.com\r\nPROPERTY[COUNT][0]=1\r\nPROPERTY' +
        '[LAST][0]=2\r\nPROPERTY[LIMIT][0]=1\r\nDESCRIPTION=Command complet' +
        'ed successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.032\r\nEOF' +
        '\r\n')
    rtm.addTemplate(
        'listFP2',
        '[RESPONSE]\r\nPROPERTY[TOTAL][0]=3\r\nPROPERTY[FIRST][0]=2\r\nPROP' +
        'ERTY[DOMAIN][0]=0-qas-ao17-0.org\r\nPROPERTY[COUNT][0]=2\r\nPROPER' +
        'TY[LAST][0]=3\r\nPROPERTY[LIMIT][0]=1\r\nDESCRIPTION=Command compl' +
        'eted successfully\r\nCODE=200\r\nQUEUETIME=0\r\nRUNTIME=0.032\r\nE' +
        'OF\r\n')

    # #.getPOSTData()
    # test object input with special chars
    validate = (
        's_entity=54cd&s_command=COMMAND%3DModifyDomain%0AAUTH%3Dgwrgwqg%25' +
        '%26%5C44t3%2A')
    enc = cl.getPOSTData({
        'COMMAND': 'ModifyDomain',
        'AUTH': 'gwrgwqg%&\\44t3*'
    })
    assert enc == validate

    # test string input
    enc = cl.getPOSTData('gregergege')
    assert enc == 's_entity=54cd&s_command='

    # test object input with null value in parameter
    validate = 's_entity=54cd&s_command=COMMAND%3DModifyDomain'
    enc = cl.getPOSTData({"COMMAND": 'ModifyDomain', "AUTH": None})
    assert enc == validate

    # #.enableDebugMode()
    cl.enableDebugMode()
    cl.disableDebugMode()

    # #.getSession()
    # initial value
    session = cl.getSession()
    assert session is None
    # custom value
    sessid = 'testSessionID12345678'
    cl.setSession(sessid)
    session = cl.getSession()
    assert session is sessid
    cl.setSession('')

    # #.getURL()
    assert cl.getURL() == 'https://coreapi.1api.net/api/call.cgi'

    # #.setURL()
    tmp = 'http://coreapi.1api.net/api/call.cgi'
    url = cl.setURL(tmp).getURL()
    assert url is tmp
    cl.setURL('https://coreapi.1api.net/api/call.cgi')

    # #.setOTP()
    # [otp set]
    cl.setOTP('12345678')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = 's_entity=54cd&s_otp=12345678&s_command=COMMAND%3DStatusAccount'
    assert tmp == exp

    # [otp reset]
    cl.setOTP('')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = 's_entity=54cd&s_command=COMMAND%3DStatusAccount'
    assert tmp == exp

    # #.setSession()
    # [session set]
    cl.setSession('12345678')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = 's_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount'
    assert tmp == exp

    # [credentials and session set]
    cl.setRoleCredentials('myaccountid', 'myrole', 'mypassword')
    cl.setOTP('12345678')
    cl.setSession('12345678')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = 's_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount'
    assert tmp == exp

    # [session reset]
    cl.setSession('')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    assert tmp == 's_entity=54cd&s_command=COMMAND%3DStatusAccount'

    # #.saveSession/reuseSession
    sessionobj = {}
    cl.setSession('12345678').saveSession(sessionobj)
    cl2 = AC()
    cl2.reuseSession(sessionobj)
    tmp = cl2.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = 's_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount'
    assert tmp == exp
    cl.setSession('')

    # #.setRemoteIPAddress()
    # [ip set]
    cl.setRemoteIPAddress('10.10.10.10')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = (
        's_entity=54cd&s_remoteaddr=10.10.10.10&s_command=COMMAND%3DStatusA' +
        'ccount')
    assert tmp == exp

    # [ip reset]
    cl.setRemoteIPAddress('')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    assert tmp == 's_entity=54cd&s_command=COMMAND%3DStatusAccount'

    # #.setCredentials()
    # [credentials set]
    cl.setCredentials('myaccountid', 'mypassword')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = (
        's_entity=54cd&s_login=myaccountid&s_pw=mypassword&s_command=COMMAN' +
        'D%3DStatusAccount')
    assert tmp == exp

    # [session reset]
    cl.setCredentials('', '')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    assert tmp == 's_entity=54cd&s_command=COMMAND%3DStatusAccount'

    # #.setRoleCredentials()
    # [role credentials set]
    cl.setRoleCredentials('myaccountid', 'myroleid', 'mypassword')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    exp = (
        's_entity=54cd&s_login=myaccountid%21myroleid&s_pw=mypassword&s_com' +
        'mand=COMMAND%3DStatusAccount')
    assert tmp == exp

    # [role credentials reset]
    cl.setRoleCredentials('', '', '')
    tmp = cl.getPOSTData({'COMMAND': 'StatusAccount'})
    assert tmp == 's_entity=54cd&s_command=COMMAND%3DStatusAccount'

    # #.login()
    # [login succeeded; no role used]
    cl.useOTESystem()
    cl.setCredentials('test.user', 'test.passw0rd')
    cl.setRemoteIPAddress('1.2.3.4')
    r = cl.login()
    assert isinstance(r, R) is True
    assert r.isSuccess() is True, ("{0} {1}").format(r.getCode(),
                                                     r.getDescription())
    rec = r.getRecord(0)
    assert rec is not None
    assert rec.getDataByKey('SESSION') is not None

    # [login succeeded; role used]
    cl.useOTESystem()
    cl.setRoleCredentials('test.user', 'testrole', 'test.passw0rd')
    r = cl.login()
    assert isinstance(r, R)
    assert r.isSuccess() is True, ("{0} {1}").format(r.getCode(),
                                                     r.getDescription())
    rec = r.getRecord(0)
    assert rec is not None
    assert rec.getDataByKey('SESSION') is not None

    # [login failed; wrong credentials]
    cl.setCredentials('test.user', 'WRONGPASSWORD')
    r = cl.login()
    assert isinstance(r, R)
    assert r.isError() is True

    # [login failed; http error]
    tpl = rtm.getTemplate('httperror')
    old = cl.getURL()
    cl.setURL('https://iwontsucceedgregegeg343teagr43.com/api/call.cgi')
    cl.setCredentials('test.user', 'WRONGPASSWORD')
    r = cl.login()
    assert isinstance(r, R)
    assert r.isTmpError() is True
    assert r.getDescription() == tpl.getDescription()
    cl.setURL(old)

    # [login succeeded; no session returned]
    # TODO: need network mock
    # tpl = R(rtm.getTemplate('OK').getPlain())
    # cl.useOTESystem()
    # cl.setCredentials('test.user', 'test.passw0rd')
    # r = cl.login()
    # assert isinstance(r, R)
    # assert r.isSuccess() is True
    # rec = r.getRecord(0)
    # assert rec is Node

    # #.loginExtended()
    # [login succeeded; no role used]
    cl.useOTESystem()
    cl.setCredentials('test.user', 'test.passw0rd')
    r = cl.loginExtended({'TIMEOUT': 60})
    assert isinstance(r, R) is True
    assert r.isSuccess() is True
    rec = r.getRecord(0)
    assert rec is not None
    assert rec.getDataByKey('SESSION') is not None

    # #.logout()
    # [logout succeeded]
    r = cl.logout()
    assert isinstance(r, R)
    assert r.isSuccess() is True

    # [logout failed; session no longer exists]
    tpl = R(rtm.getTemplate('login200').getPlain())
    cl.enableDebugMode()
    cl.setSession(tpl.getRecord(0).getDataByKey('SESSION'))
    r = cl.logout()
    assert isinstance(r, R) is True
    assert r.isError() is True

    # #.request()
    # [200 < r.statusCode > 299]
    # TODO need network mock
    # tpl2 = R(rtm.getTemplate('httperror').getPlain())
    # cl.setCredentials('test.user', 'test.passw0rd')
    # cl.useOTESystem()
    # r = cl.request({ 'COMMAND': 'GetUserIndex' })
    # assert isinstance(r, R) is True
    # assert r.isTmpError() is True
    # assert r.getCode() == tpl2.getCode()
    # assert r.getDescription() == tpl2.getDescription()

    # [200 < r.statusCode > 299, no debug]
    # TODO need network mock
    # tpl2 = R(rtm.getTemplate('httperror').getPlain())
    # cl.disableDebugMode()
    # r = cl.request({ 'COMMAND': 'GetUserIndex' })
    # assert isinstance(r, R)
    # assert r.isTmpError() is True
    # assert r.getCode() == tpl2.getCode()
    # assert r.getDescription() == tpl2.getDescription()

    # .requestNextResponsePage
    # [no LAST set]
    cl.setCredentials('test.user', 'test.passw0rd')
    cl.useOTESystem()
    r = R(
        rtm.getTemplate('listP0').getPlain(), {
            'COMMAND': 'QueryDomainList',
            'LIMIT': 2,
            'FIRST': 0
        })
    nr = cl.requestNextResponsePage(r)
    assert r.isSuccess() is True
    assert nr.isSuccess() is True
    assert r.getRecordsLimitation() == 2
    assert nr.getRecordsLimitation() == 2
    assert r.getRecordsCount() == 2
    assert nr.getRecordsCount() == 2
    assert r.getFirstRecordIndex() == 0
    assert r.getLastRecordIndex() == 1
    assert nr.getFirstRecordIndex() == 2
    assert nr.getLastRecordIndex() == 3

    # [LAST set]
    r = R(
        rtm.getTemplate('listP0').getPlain(), {
            'COMMAND': 'QueryDomainList',
            'LIMIT': 2,
            'FIRST': 0,
            'LAST': 1
        })
    with pytest.raises(Exception, match=r'Parameter LAST in use.'):
        cl.requestNextResponsePage(r)

    # [no FIRST set]
    cl.disableDebugMode()
    r = R(
        rtm.getTemplate('listP0').getPlain(), {
            'COMMAND': 'QueryDomainList',
            'LIMIT': 2
        })
    nr = cl.requestNextResponsePage(r)
    assert r.isSuccess() is True
    assert nr.isSuccess() is True
    assert r.getRecordsLimitation() == 2
    assert nr.getRecordsLimitation() == 2
    assert r.getRecordsCount() == 2
    assert nr.getRecordsCount() == 2
    assert r.getFirstRecordIndex() == 0
    assert r.getLastRecordIndex() == 1
    assert nr.getFirstRecordIndex() == 2
    assert nr.getLastRecordIndex() == 3

    # #.requestAllResponsePages()
    # [success case]
    nr = cl.requestAllResponsePages({
        'COMMAND': 'QuerySSLCertList',
        'FIRST': 0,
        'LIMIT': 100
    })
    assert len(nr) > 0

    # #.setUserView()
    cl.setUserView('hexotestman.com')
    r = cl.request({'COMMAND': 'GetUserIndex'})
    assert isinstance(r, R) is True
    assert r.isSuccess() is True

    # #.resetUserView()
    cl.setUserView('')
    r = cl.request({'COMMAND': 'GetUserIndex'})
    assert isinstance(r, R) is True
    assert r.isSuccess() is True