From ec8086ee5af527165060423a30d220b6b9cb764a Mon Sep 17 00:00:00 2001 From: Fred Park Date: Thu, 5 Apr 2018 12:55:29 -0700 Subject: [PATCH] Detect non-base64 encoded storage account keys - Resolves #62 --- blobxfer/operations/azure/__init__.py | 18 +++++++++++++++--- tests/test_blobxfer_operations_azure.py | 10 +++++----- ...st_blobxfer_operations_azure_blob_append.py | 2 +- ...est_blobxfer_operations_azure_blob_block.py | 2 +- ...test_blobxfer_operations_azure_blob_page.py | 2 +- tests/test_blobxfer_operations_azure_file.py | 2 +- 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/blobxfer/operations/azure/__init__.py b/blobxfer/operations/azure/__init__.py index 480a6d4..057320e 100644 --- a/blobxfer/operations/azure/__init__.py +++ b/blobxfer/operations/azure/__init__.py @@ -30,6 +30,7 @@ from builtins import ( # noqa bytes, dict, int, list, object, range, ascii, chr, hex, input, next, oct, open, pow, round, super, filter, map, zip) # stdlib imports +import re # non-stdlib imports import requests # local imports @@ -86,6 +87,10 @@ class StorageCredentials(object): class StorageAccount(object): """Azure Storage Account""" + _VALID_BASE64_RE = re.compile( + '^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|' + '[A-Za-z0-9+/]{2}==)$') + def __init__(self, name, key, endpoint, transfer_threads, timeout, proxy): # type: (StorageAccount, str, str, str, int, # blobxfer.models.options.Timeout, @@ -112,9 +117,16 @@ class StorageAccount(object): self.endpoint = endpoint self.is_sas = StorageAccount._key_is_sas(self.key) self.create_containers = self._container_creation_allowed() - # normalize sas keys - if self.is_sas and self.key.startswith('?'): - self.key = self.key[1:] + if self.is_sas: + # normalize sas keys + if self.key.startswith('?'): + self.key = self.key[1:] + else: + # check if sa shared key is base64 + if StorageAccount._VALID_BASE64_RE.match(self.key) is None: + raise ValueError( + ('specified storage account key is invalid for storage ' + 'account: {}').format(self.name)) # create requests session for connection pooling self.session = requests.Session() self.session.mount( diff --git a/tests/test_blobxfer_operations_azure.py b/tests/test_blobxfer_operations_azure.py index beac2e3..4d835a1 100644 --- a/tests/test_blobxfer_operations_azure.py +++ b/tests/test_blobxfer_operations_azure.py @@ -71,12 +71,12 @@ def test_key_is_sas(): to.max_retries = None a = azops.StorageAccount( - 'name', 'abcdef', 'core.windows.net', 10, to, mock.MagicMock()) + 'name', 'AAAAAA==', 'core.windows.net', 10, to, mock.MagicMock()) assert not a.is_sas - a = azops.StorageAccount( - 'name', 'abcdef&blah', 'core.windows.net', 10, to, None) - assert not a.is_sas + with pytest.raises(ValueError): + a = azops.StorageAccount( + 'name', 'abcdef&blah', 'core.windows.net', 10, to, None) a = azops.StorageAccount( 'name', '?abcdef', 'core.windows.net', 10, to, None) @@ -100,7 +100,7 @@ def test_container_creation_allowed(): to.max_retries = None a = azops.StorageAccount( - 'name', 'abcdef', 'core.windows.net', 10, to, None) + 'name', 'AAAAAA==', 'core.windows.net', 10, to, None) assert a._container_creation_allowed() a = azops.StorageAccount( diff --git a/tests/test_blobxfer_operations_azure_blob_append.py b/tests/test_blobxfer_operations_azure_blob_append.py index a27f462..6b1553b 100644 --- a/tests/test_blobxfer_operations_azure_blob_append.py +++ b/tests/test_blobxfer_operations_azure_blob_append.py @@ -20,7 +20,7 @@ def test_create_client(): to.max_retries = None sa = azops.StorageAccount( - 'name', 'key', 'core.windows.net', 10, to, mock.MagicMock()) + 'name', 'AAAAAA==', 'core.windows.net', 10, to, mock.MagicMock()) client = ops.create_client(sa, to, mock.MagicMock()) assert client is not None assert isinstance(client, azure.storage.blob.AppendBlobService) diff --git a/tests/test_blobxfer_operations_azure_blob_block.py b/tests/test_blobxfer_operations_azure_blob_block.py index f866e7c..2737aad 100644 --- a/tests/test_blobxfer_operations_azure_blob_block.py +++ b/tests/test_blobxfer_operations_azure_blob_block.py @@ -20,7 +20,7 @@ def test_create_client(): to.max_retries = None sa = azops.StorageAccount( - 'name', 'key', 'core.windows.net', 10, to, mock.MagicMock()) + 'name', 'AAAAAA==', 'core.windows.net', 10, to, mock.MagicMock()) client = ops.create_client(sa, to, mock.MagicMock()) assert client is not None assert isinstance(client, azure.storage.blob.BlockBlobService) diff --git a/tests/test_blobxfer_operations_azure_blob_page.py b/tests/test_blobxfer_operations_azure_blob_page.py index fdfbf4c..fd0f590 100644 --- a/tests/test_blobxfer_operations_azure_blob_page.py +++ b/tests/test_blobxfer_operations_azure_blob_page.py @@ -20,7 +20,7 @@ def test_create_client(): to.max_retries = None sa = azops.StorageAccount( - 'name', 'key', 'core.windows.net', 10, to, mock.MagicMock()) + 'name', 'AAAAAA==', 'core.windows.net', 10, to, mock.MagicMock()) client = ops.create_client(sa, to, mock.MagicMock()) assert client is not None assert isinstance(client, azure.storage.blob.PageBlobService) diff --git a/tests/test_blobxfer_operations_azure_file.py b/tests/test_blobxfer_operations_azure_file.py index cd7dfb4..b89a03d 100644 --- a/tests/test_blobxfer_operations_azure_file.py +++ b/tests/test_blobxfer_operations_azure_file.py @@ -27,7 +27,7 @@ def test_create_client(): to.max_retries = None sa = azops.StorageAccount( - 'name', 'key', 'core.windows.net', 10, to, mock.MagicMock()) + 'name', 'AAAAAA==', 'core.windows.net', 10, to, mock.MagicMock()) client = ops.create_client(sa, to, mock.MagicMock()) assert client is not None assert isinstance(client, azure.storage.file.FileService)