Skip to content

Commit 395b415

Browse files
author
Christine WANJAU
committed
breakdown tests into different files
1 parent cf83ff0 commit 395b415

13 files changed

+3766
-3629
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import json
2+
3+
from azure.cli.testsdk.scenario_tests import RecordingProcessor
4+
from azure.cli.testsdk.scenario_tests.utilities import is_json_payload
5+
from azure.cli.core.util import shell_safe_json_parse
6+
7+
def _create_config_store(test, kwargs):
8+
if 'retention_days' not in kwargs:
9+
kwargs.update({
10+
'retention_days': 1
11+
})
12+
test.cmd('appconfig create -n {config_store_name} -g {rg} -l {rg_loc} --sku {sku} --retention-days {retention_days}')
13+
14+
15+
class CredentialResponseSanitizer(RecordingProcessor):
16+
def process_response(self, response):
17+
if is_json_payload(response):
18+
try:
19+
json_data = shell_safe_json_parse(response["body"]["string"])
20+
21+
if isinstance(json_data.get("value"), list):
22+
for idx, credential in enumerate(json_data["value"]):
23+
self._try_replace_secret(credential, idx)
24+
25+
response["body"]["string"] = json.dumps(json_data)
26+
27+
elif isinstance(json_data, dict):
28+
self._try_replace_secret(json_data)
29+
30+
response["body"]["string"] = json.dumps(json_data)
31+
32+
except Exception:
33+
pass
34+
35+
return response
36+
37+
def _try_replace_secret(self, credential, idx = 0):
38+
if "connectionString" in credential:
39+
credential["id"] = "sanitized_id{}".format(idx + 1)
40+
credential["value"] = "sanitized_secret{}".format(idx + 1)
41+
42+
endpoint = next(
43+
filter(lambda x: x.startswith("Endpoint="), credential["connectionString"].split(";")))[len("Endpoint="):]
44+
45+
credential["connectionString"] = "Endpoint={};Id={};Secret={}".format(
46+
endpoint, credential["id"], credential["value"])
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
# pylint: disable=line-too-long
7+
8+
import json
9+
import os
10+
import time
11+
12+
from knack.util import CLIError
13+
from azure.cli.testsdk import (ResourceGroupPreparer, live_only, ScenarioTest)
14+
from azure.cli.testsdk.scenario_tests import AllowLargeResponse
15+
from azure.cli.command_modules.appconfig.tests.latest._test_utils import _create_config_store, CredentialResponseSanitizer
16+
from azure.cli.command_modules.appconfig._constants import FeatureFlagConstants, KeyVaultConstants
17+
18+
TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..'))
19+
20+
class AppConfigAadAuthLiveScenarioTest(ScenarioTest):
21+
22+
def __init__(self, *args, **kwargs):
23+
kwargs["recording_processors"] = kwargs.get("recording_processors", []) + [CredentialResponseSanitizer()]
24+
super().__init__(*args, **kwargs)
25+
26+
@live_only()
27+
@AllowLargeResponse()
28+
@ResourceGroupPreparer(parameter_name_for_location='location')
29+
def test_azconfig_aad_auth(self, resource_group, location):
30+
config_store_name = self.create_random_name(prefix='AadTest', length=15)
31+
32+
location = 'eastus'
33+
sku = 'standard'
34+
self.kwargs.update({
35+
'config_store_name': config_store_name,
36+
'rg_loc': location,
37+
'rg': resource_group,
38+
'sku': sku
39+
})
40+
_create_config_store(self, self.kwargs)
41+
42+
# Get connection string and add a key-value and feature flag using the default "key" auth mode
43+
credential_list = self.cmd(
44+
'appconfig credential list -n {config_store_name} -g {rg}').get_output_in_json()
45+
self.kwargs.update({
46+
'connection_string': credential_list[0]['connectionString']
47+
})
48+
49+
# Add a key-value
50+
entry_key = "Color"
51+
entry_value = "Red"
52+
self.kwargs.update({
53+
'key': entry_key,
54+
'value': entry_value
55+
})
56+
self.cmd('appconfig kv set --connection-string {connection_string} --key {key} --value {value} -y',
57+
checks=[self.check('key', entry_key),
58+
self.check('value', entry_value)])
59+
60+
# add a feature flag
61+
entry_feature = 'Beta'
62+
internal_feature_key = FeatureFlagConstants.FEATURE_FLAG_PREFIX + entry_feature
63+
default_description = ""
64+
default_conditions = "{{\'client_filters\': []}}"
65+
default_locked = False
66+
default_state = "off"
67+
self.kwargs.update({
68+
'feature': entry_feature,
69+
'description': default_description
70+
})
71+
self.cmd('appconfig feature set --connection-string {connection_string} --feature {feature} -y',
72+
checks=[self.check('locked', default_locked),
73+
self.check('name', entry_feature),
74+
self.check('key', internal_feature_key),
75+
self.check('description', default_description),
76+
self.check('state', default_state),
77+
self.check('conditions', default_conditions)])
78+
79+
# Get information about account logged in with 'az login'
80+
appconfig_id = self.cmd('appconfig show -n {config_store_name} -g {rg}').get_output_in_json()['id']
81+
account_info = self.cmd('account show').get_output_in_json()
82+
endpoint = "https://" + config_store_name + ".azconfig.io"
83+
self.kwargs.update({
84+
'appconfig_id': appconfig_id,
85+
'user_id': account_info['user']['name'],
86+
'endpoint': endpoint
87+
})
88+
89+
# Before assigning data reader role, read operation should fail with AAD auth.
90+
# The exception really depends on the which identity is used to run this testcase.
91+
with self.assertRaisesRegex(CLIError, "Operation returned an invalid status 'Forbidden'"):
92+
self.cmd('appconfig kv show --endpoint {endpoint} --auth-mode login --key {key}')
93+
94+
# Assign data reader role to current user
95+
self.cmd('role assignment create --assignee {user_id} --role "App Configuration Data Reader" --scope {appconfig_id}')
96+
time.sleep(900) # It takes around 15 mins for RBAC permissions to propagate
97+
98+
# After asssigning data reader role, read operation should succeed
99+
self.cmd('appconfig kv show --endpoint {endpoint} --auth-mode login --key {key}',
100+
checks=[self.check('key', entry_key),
101+
self.check('value', entry_value)])
102+
103+
# Since the logged in account also has "Contributor" role, providing --name instead of --endpoint should succeed
104+
self.cmd('appconfig feature show --name {config_store_name} --auth-mode login --feature {feature}',
105+
checks=[self.check('locked', default_locked),
106+
self.check('name', entry_feature),
107+
self.check('key', internal_feature_key),
108+
self.check('description', default_description),
109+
self.check('state', default_state),
110+
self.check('conditions', default_conditions)])
111+
112+
# Write operations should fail with "Forbidden" error
113+
updated_value = "Blue"
114+
self.kwargs.update({
115+
'value': updated_value
116+
})
117+
with self.assertRaisesRegex(CLIError, "Operation returned an invalid status 'Forbidden'"):
118+
self.cmd('appconfig kv set --endpoint {endpoint} --auth-mode login --key {key} --value {value} -y')
119+
120+
# Export from appconfig to file should succeed
121+
os.environ['AZURE_APPCONFIG_FM_COMPATIBLE'] = 'True'
122+
exported_file_path = os.path.join(TEST_DIR, 'export_aad_1.json')
123+
expected_exported_file_path = os.path.join(TEST_DIR, 'expected_export_aad_1.json')
124+
self.kwargs.update({
125+
'import_source': 'file',
126+
'imported_format': 'json',
127+
'separator': '/',
128+
'exported_file_path': exported_file_path
129+
})
130+
self.cmd(
131+
'appconfig kv export --endpoint {endpoint} --auth-mode login -d {import_source} --path "{exported_file_path}" --format {imported_format} --separator {separator} -y')
132+
with open(expected_exported_file_path) as json_file:
133+
expected_exported_kvs = json.load(json_file)
134+
with open(exported_file_path) as json_file:
135+
exported_kvs = json.load(json_file)
136+
assert expected_exported_kvs == exported_kvs
137+
os.remove(exported_file_path)
138+
139+
# Assign data owner role to current user
140+
self.cmd('role assignment create --assignee {user_id} --role "App Configuration Data Owner" --scope {appconfig_id}')
141+
time.sleep(900) # It takes around 15 mins for RBAC permissions to propagate
142+
143+
# After assigning data owner role, write operation should succeed
144+
self.cmd('appconfig kv set --endpoint {endpoint} --auth-mode login --key {key} --value {value} -y',
145+
checks=[self.check('key', entry_key),
146+
self.check('value', updated_value)])
147+
148+
# Add a KeyVault reference
149+
keyvault_key = "HostSecrets"
150+
keyvault_id = "https://fake.vault.azure.net/secrets/fakesecret"
151+
appconfig_keyvault_value = f"{{{json.dumps({'uri': keyvault_id})}}}"
152+
self.kwargs.update({
153+
'key': keyvault_key,
154+
'secret_identifier': keyvault_id
155+
})
156+
self.cmd('appconfig kv set-keyvault --endpoint {endpoint} --auth-mode login --key {key} --secret-identifier {secret_identifier} -y',
157+
checks=[self.check('contentType', KeyVaultConstants.KEYVAULT_CONTENT_TYPE),
158+
self.check('key', keyvault_key),
159+
self.check('value', appconfig_keyvault_value)])
160+
161+
# Import to appconfig should succeed
162+
imported_file_path = os.path.join(TEST_DIR, 'import_aad.json')
163+
exported_file_path = os.path.join(TEST_DIR, 'export_aad_2.json')
164+
expected_exported_file_path = os.path.join(TEST_DIR, 'expected_export_aad_2.json')
165+
self.kwargs.update({
166+
'imported_file_path': imported_file_path,
167+
'exported_file_path': exported_file_path
168+
})
169+
self.cmd(
170+
'appconfig kv import --endpoint {endpoint} --auth-mode login -s {import_source} --path "{imported_file_path}" --format {imported_format} --separator {separator} -y')
171+
172+
# Export from appconfig to file should succeed
173+
self.cmd(
174+
'appconfig kv export --endpoint {endpoint} --auth-mode login -d {import_source} --path "{exported_file_path}" --format {imported_format} --separator {separator} -y')
175+
with open(expected_exported_file_path) as json_file:
176+
expected_exported_kvs = json.load(json_file)
177+
with open(exported_file_path) as json_file:
178+
exported_kvs = json.load(json_file)
179+
assert expected_exported_kvs == exported_kvs
180+
os.remove(exported_file_path)

0 commit comments

Comments
 (0)