From 5b2a95b9ab13ad3498c32ea768bbb714c8f619fa Mon Sep 17 00:00:00 2001 From: barrybingo Date: Sun, 13 Dec 2020 10:30:00 +0000 Subject: [PATCH 1/6] Enable edit of 'Retention Days' for B2 backup target --- management/templates/system-backup.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/templates/system-backup.html b/management/templates/system-backup.html index 7cdc3803..a432d6b2 100644 --- a/management/templates/system-backup.html +++ b/management/templates/system-backup.html @@ -138,7 +138,7 @@ -
+
From 03e258e738504ebb4a50be6b7d59876a49b8ac46 Mon Sep 17 00:00:00 2001 From: barrybingo Date: Sun, 13 Dec 2020 11:13:54 +0000 Subject: [PATCH 2/6] Throw appropriate error if any B2 option contains a forward slash character --- management/backup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/management/backup.py b/management/backup.py index 0a8a021e..e8c04626 100755 --- a/management/backup.py +++ b/management/backup.py @@ -457,6 +457,13 @@ def list_target_files(config): return [(key.name[len(path):], key.size) for key in bucket.list(prefix=path)] elif target.scheme == 'b2': + # InvalidBackendURL error for B2 backend if application key contains a '/' character + # See: https://bugs.launchpad.net/duplicity/+bug/1819390 + # With a slash anywhere after b2::// the above urlparse will put something into target.path, thus + if not "".__eq__(target.path): + raise ValueError("""No B2 configuration option can contain '/' the foward slash character. + Please create a new API key or Bucket that does not contain any forward slashes""") + from b2sdk.v1 import InMemoryAccountInfo, B2Api from b2sdk.v1.exception import NonExistentBucket info = InMemoryAccountInfo() From b5a9d30201f972bf768fbacd591a3aa8a904e063 Mon Sep 17 00:00:00 2001 From: barrybingo Date: Sun, 13 Dec 2020 11:31:01 +0000 Subject: [PATCH 3/6] B2 buckets can't contain a slash so simplify error message --- management/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/backup.py b/management/backup.py index e8c04626..1a7cdedb 100755 --- a/management/backup.py +++ b/management/backup.py @@ -462,7 +462,7 @@ def list_target_files(config): # With a slash anywhere after b2::// the above urlparse will put something into target.path, thus if not "".__eq__(target.path): raise ValueError("""No B2 configuration option can contain '/' the foward slash character. - Please create a new API key or Bucket that does not contain any forward slashes""") + Please create a new API key that does not contain any forward slashes""") from b2sdk.v1 import InMemoryAccountInfo, B2Api from b2sdk.v1.exception import NonExistentBucket From 2adbae9a2dd296be71c6475e37940365fc0dd467 Mon Sep 17 00:00:00 2001 From: barrybingo Date: Sun, 13 Dec 2020 14:51:13 +0000 Subject: [PATCH 4/6] Simplify expression --- management/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/backup.py b/management/backup.py index 1a7cdedb..119a7c63 100755 --- a/management/backup.py +++ b/management/backup.py @@ -460,7 +460,7 @@ def list_target_files(config): # InvalidBackendURL error for B2 backend if application key contains a '/' character # See: https://bugs.launchpad.net/duplicity/+bug/1819390 # With a slash anywhere after b2::// the above urlparse will put something into target.path, thus - if not "".__eq__(target.path): + if target.path != "": raise ValueError("""No B2 configuration option can contain '/' the foward slash character. Please create a new API key that does not contain any forward slashes""") From 803d73cb4e05c30cc9e94eaf998d20e6ee51d7f9 Mon Sep 17 00:00:00 2001 From: barrybingo Date: Sun, 13 Dec 2020 15:38:02 +0000 Subject: [PATCH 5/6] Quote B2 url to convert / to %2F for duplicity --- management/backup.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/management/backup.py b/management/backup.py index 119a7c63..518f824c 100755 --- a/management/backup.py +++ b/management/backup.py @@ -457,22 +457,17 @@ def list_target_files(config): return [(key.name[len(path):], key.size) for key in bucket.list(prefix=path)] elif target.scheme == 'b2': - # InvalidBackendURL error for B2 backend if application key contains a '/' character - # See: https://bugs.launchpad.net/duplicity/+bug/1819390 - # With a slash anywhere after b2::// the above urlparse will put something into target.path, thus - if target.path != "": - raise ValueError("""No B2 configuration option can contain '/' the foward slash character. - Please create a new API key that does not contain any forward slashes""") - from b2sdk.v1 import InMemoryAccountInfo, B2Api from b2sdk.v1.exception import NonExistentBucket info = InMemoryAccountInfo() b2_api = B2Api(info) - # Extract information from target - b2_application_keyid = target.netloc[:target.netloc.index(':')] - b2_application_key = target.netloc[target.netloc.index(':')+1:target.netloc.index('@')] - b2_bucket = target.netloc[target.netloc.index('@')+1:] + escaped_url = urllib.parse.unquote(target.netloc) + + # Extract information from escaped_url + b2_application_keyid = escaped_url[:escaped_url.index(':')] + b2_application_key = escaped_url[escaped_url.index(':')+1:escaped_url.index('@')] + b2_bucket = escaped_url[escaped_url.index('@')+1:] try: b2_api.authorize_account("production", b2_application_keyid, b2_application_key) @@ -492,7 +487,13 @@ def backup_set_custom(env, target, target_user, target_pass, min_age): if isinstance(min_age, str): min_age = int(min_age) - config["target"] = target + # b2 url must be escaped + if target.startswith('b2://'): + import urllib.parse + config["target"] = 'b2://' + urllib.parse.quote(target[5:], safe=':@') + else: + config["target"] = target + config["target_user"] = target_user config["target_pass"] = target_pass config["min_age_in_days"] = min_age @@ -538,6 +539,11 @@ def get_backup_config(env, for_save=False, for_ui=False): if field in config: del config[field] + if config["target"].startswith('b2://'): + import urllib.parse + # unquote the URL. + config["target"] = urllib.parse.unquote(config["target"]) + # helper fields for the admin config["file_target_directory"] = os.path.join(backup_root, 'encrypted') config["enc_pw_file"] = os.path.join(backup_root, 'secret_key.txt') From ef4cb6c4658424904e06b3bfbca903ce5afa43ba Mon Sep 17 00:00:00 2001 From: barrybingo Date: Sun, 13 Dec 2020 15:56:43 +0000 Subject: [PATCH 6/6] Tidy up --- management/backup.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/management/backup.py b/management/backup.py index 518f824c..5ace4c62 100755 --- a/management/backup.py +++ b/management/backup.py @@ -462,12 +462,12 @@ def list_target_files(config): info = InMemoryAccountInfo() b2_api = B2Api(info) - escaped_url = urllib.parse.unquote(target.netloc) + unquoted_url = urllib.parse.unquote(target.netloc) - # Extract information from escaped_url - b2_application_keyid = escaped_url[:escaped_url.index(':')] - b2_application_key = escaped_url[escaped_url.index(':')+1:escaped_url.index('@')] - b2_bucket = escaped_url[escaped_url.index('@')+1:] + # Extract information from unquoted_url + b2_application_keyid = unquoted_url[:unquoted_url.index(':')] + b2_application_key = unquoted_url[unquoted_url.index(':')+1:unquoted_url.index('@')] + b2_bucket = unquoted_url[unquoted_url.index('@')+1:] try: b2_api.authorize_account("production", b2_application_keyid, b2_application_key) @@ -541,7 +541,7 @@ def get_backup_config(env, for_save=False, for_ui=False): if config["target"].startswith('b2://'): import urllib.parse - # unquote the URL. + # unquote the URL for the admin config["target"] = urllib.parse.unquote(config["target"]) # helper fields for the admin