示例#1
0
def delete_database(data, backup_id, confirm=False, recreate=False):
    common.verify_token(data)

    # Check if the backup exists
    result = fetch_backups(data, [backup_id], "get")
    if result is None or len(result) == 0:
        return

    if not confirm:
        # Confirm deletion with user
        name = next(iter(result[0]))
        message = 'Delete database ' + str(backup_id)
        message += ' belonging to "' + name + '"?'
        options = '[y/N]:'
        agree = input(message + ' ' + options)
        if agree.lower() not in ["y", "yes"]:
            common.log_output("Database not deleted", True)
            return

    baseurl = common.create_baseurl(
        data, "/api/v1/backup/" + str(backup_id) + "/deletedb")
    cookies = common.create_cookies(data)
    headers = common.create_headers(data)
    verify = data.get("server", {}).get("verify", True)

    r = requests.post(baseurl, headers=headers, cookies=cookies, verify=verify)
    common.check_response(data, r.status_code)
    if r.status_code != 200:
        common.log_output("Error deleting database", True, r.status_code)
        return
    common.log_output("Database deleted", True, 200)
    if recreate:
        repair_database(data, backup_id)
示例#2
0
def call_backup_subcommand(data, url, fail_message, success_message):
    common.verify_token(data)

    baseurl = common.create_baseurl(data, url)
    cookies = common.create_cookies(data)
    headers = common.create_headers(data)
    verify = data.get("server", {}).get("verify", True)
    r = requests.post(baseurl, headers=headers, cookies=cookies, verify=verify)
    common.check_response(data, r.status_code)
    if r.status_code != 200:
        common.log_output(fail_message, True, r.status_code)
        return
    common.log_output(success_message, True, 200)
示例#3
0
def abort_task(data, task_id):
    common.verify_token(data)

    path = "/api/v1/task/" + str(task_id) + "/abort"
    baseurl = common.create_baseurl(data, path)
    cookies = common.create_cookies(data)
    headers = common.create_headers(data)
    verify = data.get("server", {}).get("verify", True)
    r = requests.post(baseurl, headers=headers, cookies=cookies, verify=verify)
    common.check_response(data, r.status_code)
    if r.status_code != 200:
        common.log_output("Error aborting task ", True, r.status_code)
        return
    common.log_output("Task aborted", True, 200)
示例#4
0
def run_backup(data, backup_id):
    common.verify_token(data)

    path = "/api/v1/backup/" + str(backup_id) + "/run"
    baseurl = common.create_baseurl(data, path)
    cookies = common.create_cookies(data)
    headers = common.create_headers(data)
    verify = data.get("server", {}).get("verify", True)
    r = requests.post(baseurl, headers=headers, cookies=cookies, verify=verify)
    common.check_response(data, r.status_code)
    if r.status_code != 200:
        common.log_output("Error scheduling backup ", True, r.status_code)
        return
    common.log_output("Backup scheduled", True, 200)
def validate_database_exists(data, db_path):
    common.verify_token(data)

    # api/v1/filesystem/validate
    baseurl = common.create_baseurl(data, "/api/v1/filesystem/validate")
    cookies = common.create_cookies(data)
    headers = common.create_headers(data)
    payload = {'path': db_path}
    verify = data.get("server", {}).get("verify", True)
    r = requests.post(baseurl, headers=headers, params=payload,
                      cookies=cookies, verify=verify)
    common.check_response(data, r.status_code)
    if r.status_code != 200:
        return False
    return True
示例#6
0
def import_backup(data, import_file, backup_id=None, import_meta=None):
    # Don't load nonexisting files
    if os.path.isfile(import_file) is False:
        common.log_output(import_file + " not found", True)
        return

    # Load the import file
    with io.open(import_file, 'r', encoding="UTF-8") as file_handle:
        extension = splitext(import_file)[1]
        if extension.lower() in ['.yml', '.yaml']:
            try:
                backup_config = yaml.safe_load(file_handle)
            except yaml.YAMLError:
                common.log_output("Failed to load file as YAML", True)
                return

        elif extension.lower() == ".json":
            try:
                backup_config = json.load(file_handle)
            except Exception:
                common.log_output("Failed to load file as JSON", True)
                return

    # Determine if we're importing a new backup or updating an existing backup
    if backup_id is not None:
        return update_backup(data, backup_id, backup_config, import_meta)

    common.verify_token(data)

    # Strip metadata if requsted
    if import_meta is None or import_meta is not True:
        backup_config["Backup"]["Metadata"] = {}

    # Prepare the imported JSON object as a string
    backup_config = json.dumps(backup_config, default=str)

    # Upload our JSON string as a file with requests
    files = {
        'config': ('backup_config.json', backup_config, 'application/json')
    }

    # Will eventually support passphrase encrypted configs, but we will
    # need to decrypt them in the client in order to convert them
    payload = {
        'passphrase': '',
        'import_metadata': import_meta,
        'direct': True
    }
    cookies = common.create_cookies(data)
    baseurl = common.create_baseurl(data, "/api/v1/backups/import", True)
    verify = data.get("server", {}).get("verify", True)
    r = requests.post(baseurl,
                      files=files,
                      cookies=cookies,
                      data=payload,
                      verify=verify)
    common.check_response(data, r.status_code)
    # Code for extracting error messages posted with inline javascript
    # and with 200 OK http status code, preventing us from detecting
    # the error otherwise.
    try:
        text = r.text
        start = text.index("if (rp) { rp('") + 14
        end = text.index(", line ")
        error = text[start:end].replace("\\'", "'") + "."
        common.log_output(error, True)
        sys.exit(2)
    except ValueError:
        pass
    if r.status_code != 200:
        message = "Error importing backup configuration"
        common.log_output(message, True, r.status_code)
        sys.exit(2)
    common.log_output("Backup job created", True, 200)
示例#7
0
def login(data, input_url=None, password=None, verify=True,
          interactive=True, basic_user=None, basic_pass=None):
    if input_url is None:
        input_url = ""

    # Split protocol, url, and port
    input_url = input_url.replace("/", "").replace("_", "")
    count = input_url.count(":")
    protocol = ""
    url = ""
    port = ""
    if count == 2:
        protocol, url, port = input_url.split(":")
    elif count == 1 and input_url.index(":") < 6:
        protocol, url = input_url.split(":")
    elif count == 1:
        url, port = input_url.split(":")
    elif count == 0:
        url = input_url
    else:
        common.log_output("Invalid URL", True)
        sys.exit(2)

    # Strip nondigits
    port = ''.join(re.findall(r'\d+', port))

    # Default to config file values for any missing parameters
    if protocol is None or protocol.lower() not in ["http", "https"]:
        protocol = data["server"]["protocol"]
    if url is None or url == "":
        url = data["server"]["url"]
    if port is None or port == "":
        port = data["server"]["port"]

    # Update config
    data["server"]["protocol"] = protocol
    data["server"]["url"] = url
    data["server"]["port"] = port

    # Make the login attempt
    baseurl = common.create_baseurl(data, "")
    common.log_output("Connecting to " + baseurl + "...", False)
    r = requests.get(baseurl, allow_redirects=True, verify=verify)
    common.check_response(data, r.status_code)

    # Detect if we were redirected to https
    if "https://" in r.url and protocol != "https":
        data["server"]["protocol"] = "https"
        common.log_output("Redirected from http to https", True)

    # Detect if we're prompted for basic authentication
    auth_method = r.headers.get('WWW-Authenticate', False)
    if (auth_method):
        common.log_output('Basic authentication required...', False)
        if basic_user is None and interactive:
            basic_user = input('Basic username: '******'You must provide a basic auth username, --basic-user'
            common.log_output(message, True)
            sys.exit(2)

        if basic_pass is None and interactive:
            basic_pass = getpass.getpass('Basic password:'******'ascii'))
        # Create the authorization string
        basic_auth = "Basic " + secret.decode('utf-8')
        headers = {"Authorization": basic_auth}
        r = requests.get(baseurl, verify=verify, headers=headers,
                         allow_redirects=True)
        common.check_response(data, r.status_code)
        if r.status_code == 200:
            common.log_output('Passed basic auth', False)
            # Update basic auth secret in config file
            data['authorization'] = basic_auth

    # Detect if we were prompted to login
    login_redirect = "/login.html" in r.url

    if r.status_code == 200 and not login_redirect:
        common.log_output("OK", False, r.status_code)
        token = urllib.parse.unquote(r.cookies["xsrf-token"])
    elif r.status_code == 200 and login_redirect:
        password = prompt_password(password, interactive)

        common.log_output("Getting nonce and salt...", False)
        baseurl = common.create_baseurl(data, "/login.cgi")
        headers = common.create_headers(data)
        payload = {'get-nonce': 1}
        r = requests.post(baseurl, headers=headers, data=payload,
                          verify=verify)
        if r.status_code != 200:
            common.log_output("Error getting salt from server", True,
                              r.status_code)
            sys.exit(2)

        salt = r.json()["Salt"]
        data["nonce"] = urllib.parse.unquote(r.json()["Nonce"])
        token = urllib.parse.unquote(r.cookies["xsrf-token"])
        common.log_output("Hashing password...", False)
        salt_password = password.encode() + base64.b64decode(salt)
        saltedpwd = hashlib.sha256(salt_password).digest()
        nonce_password = base64.b64decode(data["nonce"]) + saltedpwd
        noncedpwd = hashlib.sha256(nonce_password).digest()

        common.log_output("Authenticating... ", False)
        payload = {
            "password": base64.b64encode(noncedpwd).decode('utf-8')
        }
        cookies = {
            "xsrf-token": token,
            "session-nonce": data.get("nonce", "")
        }
        r = requests.post(baseurl, headers=headers, data=payload,
                          cookies=cookies, verify=verify)
        common.check_response(data, r.status_code)
        if r.status_code == 200:
            common.log_output("Connected", False, r.status_code)
            data["session-auth"] = urllib.parse.unquote(
                                        r.cookies["session-auth"])
        else:
            message = "Error authenticating against the server"
            common.log_output(message, True, r.status_code)
            sys.exit(2)
    else:
        message = "Error connecting to server"
        common.log_output(message, True, r.status_code)
        sys.exit(2)

    # Update the config file with provided values
    data["token"] = token
    expiration = datetime.datetime.now() + datetime.timedelta(0, 600)
    data["token_expires"] = expiration
    data["last_login"] = datetime.datetime.now()
    common.write_config(data)
    common.log_output("Login successful", True)
    return data