Skip to content

Commit 0bd61fb

Browse files
hallvictoriaVictoria Hall
andauthored
fix: validating undefined connection strings (#44)
* Error if invalid connection * Error for all types * Validate during type init * Missed refs * Removed testing line * moved to utils class * license for utils * refactor to use pydantic validator * added field * added check for whitespace * feedback * lint * revert pydantic changes * fix tests for base change * no validation on connection string obj * fixing tests --------- Co-authored-by: Victoria Hall <victoria.hall@microsoft.com>
1 parent 6174962 commit 0bd61fb

File tree

8 files changed

+180
-16
lines changed

8 files changed

+180
-16
lines changed

azurefunctions-extensions-bindings-blob/azurefunctions/extensions/bindings/blob/blobClient.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
# Licensed under the MIT License.
33

44
import json
5-
import os
65
from typing import Union
76

87
from azure.storage.blob import BlobClient as BlobClientSdk
98
from azurefunctions.extensions.base import Datum, SdkType
9+
from .utils import validate_connection_string
1010

1111

1212
class BlobClient(SdkType):
1313
def __init__(self, *, data: Union[bytes, Datum]) -> None:
14-
1514
# model_binding_data properties
1615
self._data = data
1716
self._version = None
@@ -25,7 +24,7 @@ def __init__(self, *, data: Union[bytes, Datum]) -> None:
2524
self._source = data.source
2625
self._content_type = data.content_type
2726
content_json = json.loads(data.content)
28-
self._connection = os.getenv(content_json["Connection"])
27+
self._connection = validate_connection_string(content_json["Connection"])
2928
self._containerName = content_json["ContainerName"]
3029
self._blobName = content_json["BlobName"]
3130

azurefunctions-extensions-bindings-blob/azurefunctions/extensions/bindings/blob/blobClientConverter.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class BlobClientConverter(
1616
binding="blob",
1717
trigger="blobTrigger",
1818
):
19-
2019
@classmethod
2120
def check_input_type_annotation(cls, pytype: type) -> bool:
2221
return issubclass(

azurefunctions-extensions-bindings-blob/azurefunctions/extensions/bindings/blob/containerClient.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
# Licensed under the MIT License.
33

44
import json
5-
import os
65
from typing import Union
76

87
from azure.storage.blob import ContainerClient as ContainerClientSdk
98
from azurefunctions.extensions.base import Datum, SdkType
9+
from .utils import validate_connection_string
1010

1111

1212
class ContainerClient(SdkType):
1313
def __init__(self, *, data: Union[bytes, Datum]) -> None:
14-
1514
# model_binding_data properties
1615
self._data = data
1716
self._version = ""
@@ -25,7 +24,7 @@ def __init__(self, *, data: Union[bytes, Datum]) -> None:
2524
self._source = data.source
2625
self._content_type = data.content_type
2726
content_json = json.loads(data.content)
28-
self._connection = os.getenv(content_json["Connection"])
27+
self._connection = validate_connection_string(content_json["Connection"])
2928
self._containerName = content_json["ContainerName"]
3029
self._blobName = content_json["BlobName"]
3130

azurefunctions-extensions-bindings-blob/azurefunctions/extensions/bindings/blob/storageStreamDownloader.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
# Licensed under the MIT License.
33

44
import json
5-
import os
65
from typing import Union
76

87
from azure.storage.blob import BlobClient as BlobClientSdk
98
from azurefunctions.extensions.base import Datum, SdkType
9+
from .utils import validate_connection_string
1010

1111

1212
class StorageStreamDownloader(SdkType):
1313
def __init__(self, *, data: Union[bytes, Datum]) -> None:
14-
1514
# model_binding_data properties
1615
self._data = data or {}
1716
self._version = ""
@@ -25,7 +24,7 @@ def __init__(self, *, data: Union[bytes, Datum]) -> None:
2524
self._source = data.source
2625
self._content_type = data.content_type
2726
content_json = json.loads(data.content)
28-
self._connection = os.getenv(content_json["Connection"])
27+
self._connection = validate_connection_string(content_json["Connection"])
2928
self._containerName = content_json["ContainerName"]
3029
self._blobName = content_json["BlobName"]
3130

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import os
4+
5+
6+
def validate_connection_string(connection_string: str) -> str:
7+
"""
8+
Validates the connection string. If the connection string is
9+
not an App Setting, an error will be thrown.
10+
"""
11+
if connection_string == None:
12+
raise ValueError(
13+
"Storage account connection string cannot be none. "
14+
"Please provide a connection string."
15+
)
16+
elif not os.getenv(connection_string):
17+
raise ValueError(
18+
f"Storage account connection string {connection_string} does not exist. "
19+
f"Please make sure that it is a defined App Setting."
20+
)
21+
return os.getenv(connection_string)

azurefunctions-extensions-bindings-blob/tests/test_blobclient.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,55 @@ def test_input_populated(self):
126126
self.assertIsNotNone(sdk_result)
127127
self.assertIsInstance(sdk_result, BlobClientSdk)
128128

129+
def test_invalid_input_populated(self):
130+
content = {
131+
"Connection": "NotARealConnectionString",
132+
"ContainerName": "test-blob",
133+
"BlobName": "text.txt",
134+
}
135+
136+
sample_mbd = MockMBD(
137+
version="1.0",
138+
source="AzureStorageBlobs",
139+
content_type="application/json",
140+
content=json.dumps(content),
141+
)
142+
143+
with self.assertRaises(ValueError) as e:
144+
datum: Datum = Datum(value=sample_mbd, type="model_binding_data")
145+
result: BlobClient = BlobClientConverter.decode(
146+
data=datum, trigger_metadata=None, pytype=BlobClient
147+
)
148+
self.assertEqual(
149+
e.exception.args[0],
150+
"Storage account connection string NotARealConnectionString does not exist. "
151+
"Please make sure that it is a defined App Setting.",
152+
)
153+
154+
def test_none_input_populated(self):
155+
content = {
156+
"Connection": None,
157+
"ContainerName": "test-blob",
158+
"BlobName": "text.txt",
159+
}
160+
161+
sample_mbd = MockMBD(
162+
version="1.0",
163+
source="AzureStorageBlobs",
164+
content_type="application/json",
165+
content=json.dumps(content),
166+
)
167+
168+
with self.assertRaises(ValueError) as e:
169+
datum: Datum = Datum(value=sample_mbd, type="model_binding_data")
170+
result: BlobClient = BlobClientConverter.decode(
171+
data=datum, trigger_metadata=None, pytype=BlobClient
172+
)
173+
self.assertEqual(
174+
e.exception.args[0],
175+
"Storage account connection string cannot be none. Please provide a connection string.",
176+
)
177+
129178
def test_input_invalid_pytype(self):
130179
content = {
131180
"Connection": "AzureWebJobsStorage",
@@ -169,7 +218,7 @@ def test_blob_client_invalid_creation(self):
169218
dict_repr,
170219
[
171220
'{"direction": "MockBindingDirection.IN", '
172-
'"dataType": null, "type": "blob", '
221+
'"type": "blob", '
173222
'"properties": '
174223
'{"SupportsDeferredBinding": false}}'
175224
],
@@ -202,7 +251,7 @@ def test_blob_client_valid_creation(self):
202251
dict_repr,
203252
[
204253
'{"direction": "MockBindingDirection.IN", '
205-
'"dataType": null, "type": "blob", '
254+
'"type": "blob", '
206255
'"properties": '
207256
'{"SupportsDeferredBinding": true}}'
208257
],

azurefunctions-extensions-bindings-blob/tests/test_containerclient.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,55 @@ def test_input_populated(self):
124124
self.assertIsNotNone(sdk_result)
125125
self.assertIsInstance(sdk_result, ContainerClientSdk)
126126

127+
def test_invalid_input_populated(self):
128+
content = {
129+
"Connection": "NotARealConnectionString",
130+
"ContainerName": "test-blob",
131+
"BlobName": "text.txt",
132+
}
133+
134+
sample_mbd = MockMBD(
135+
version="1.0",
136+
source="AzureStorageBlobs",
137+
content_type="application/json",
138+
content=json.dumps(content),
139+
)
140+
141+
with self.assertRaises(ValueError) as e:
142+
datum: Datum = Datum(value=sample_mbd, type="model_binding_data")
143+
result: ContainerClient = BlobClientConverter.decode(
144+
data=datum, trigger_metadata=None, pytype=ContainerClient
145+
)
146+
self.assertEqual(
147+
e.exception.args[0],
148+
"Storage account connection string NotARealConnectionString does not exist. "
149+
"Please make sure that it is a defined App Setting.",
150+
)
151+
152+
def test_none_input_populated(self):
153+
content = {
154+
"Connection": None,
155+
"ContainerName": "test-blob",
156+
"BlobName": "text.txt",
157+
}
158+
159+
sample_mbd = MockMBD(
160+
version="1.0",
161+
source="AzureStorageBlobs",
162+
content_type="application/json",
163+
content=json.dumps(content),
164+
)
165+
166+
with self.assertRaises(ValueError) as e:
167+
datum: Datum = Datum(value=sample_mbd, type="model_binding_data")
168+
result: ContainerClient = BlobClientConverter.decode(
169+
data=datum, trigger_metadata=None, pytype=ContainerClient
170+
)
171+
self.assertEqual(
172+
e.exception.args[0],
173+
"Storage account connection string cannot be none. Please provide a connection string.",
174+
)
175+
127176
def test_input_invalid_pytype(self):
128177
content = {
129178
"Connection": "AzureWebJobsStorage",
@@ -167,7 +216,7 @@ def test_container_client_invalid_creation(self):
167216
dict_repr,
168217
[
169218
'{"direction": "MockBindingDirection.IN", '
170-
'"dataType": null, "type": "blob", '
219+
'"type": "blob", '
171220
'"properties": '
172221
'{"SupportsDeferredBinding": false}}'
173222
],
@@ -202,7 +251,7 @@ def test_container_client_valid_creation(self):
202251
dict_repr,
203252
[
204253
'{"direction": "MockBindingDirection.IN", '
205-
'"dataType": null, "type": "blob", '
254+
'"type": "blob", '
206255
'"properties": '
207256
'{"SupportsDeferredBinding": true}}'
208257
],

azurefunctions-extensions-bindings-blob/tests/test_ssd.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,55 @@ def test_input_populated(self):
129129
self.assertIsNotNone(sdk_result)
130130
self.assertIsInstance(sdk_result, SSDSdk)
131131

132+
def test_invalid_input_populated(self):
133+
content = {
134+
"Connection": "NotARealConnectionString",
135+
"ContainerName": "test-blob",
136+
"BlobName": "text.txt",
137+
}
138+
139+
sample_mbd = MockMBD(
140+
version="1.0",
141+
source="AzureStorageBlobs",
142+
content_type="application/json",
143+
content=json.dumps(content),
144+
)
145+
146+
with self.assertRaises(ValueError) as e:
147+
datum: Datum = Datum(value=sample_mbd, type="model_binding_data")
148+
result: StorageStreamDownloader = BlobClientConverter.decode(
149+
data=datum, trigger_metadata=None, pytype=StorageStreamDownloader
150+
)
151+
self.assertEqual(
152+
e.exception.args[0],
153+
"Storage account connection string NotARealConnectionString does not exist. "
154+
"Please make sure that it is a defined App Setting.",
155+
)
156+
157+
def test_none_input_populated(self):
158+
content = {
159+
"Connection": None,
160+
"ContainerName": "test-blob",
161+
"BlobName": "text.txt",
162+
}
163+
164+
sample_mbd = MockMBD(
165+
version="1.0",
166+
source="AzureStorageBlobs",
167+
content_type="application/json",
168+
content=json.dumps(content),
169+
)
170+
171+
with self.assertRaises(ValueError) as e:
172+
datum: Datum = Datum(value=sample_mbd, type="model_binding_data")
173+
result: StorageStreamDownloader = BlobClientConverter.decode(
174+
data=datum, trigger_metadata=None, pytype=StorageStreamDownloader
175+
)
176+
self.assertEqual(
177+
e.exception.args[0],
178+
"Storage account connection string cannot be none. Please provide a connection string.",
179+
)
180+
132181
def test_input_invalid_pytype(self):
133182
content = {
134183
"Connection": "AzureWebJobsStorage",
@@ -172,7 +221,7 @@ def test_ssd_invalid_creation(self):
172221
dict_repr,
173222
[
174223
'{"direction": "MockBindingDirection.IN", '
175-
'"dataType": null, "type": "blob", '
224+
'"type": "blob", '
176225
'"properties": '
177226
'{"SupportsDeferredBinding": false}}'
178227
],
@@ -207,7 +256,7 @@ def test_ssd_valid_creation(self):
207256
dict_repr,
208257
[
209258
'{"direction": "MockBindingDirection.IN", '
210-
'"dataType": null, "type": "blob", '
259+
'"type": "blob", '
211260
'"properties": '
212261
'{"SupportsDeferredBinding": true}}'
213262
],

0 commit comments

Comments
 (0)