Config Refactor Round 2. (#2771)

* Config Refactor Round 2.

* Adding docs.

* Fix file formatting.

* Removing.

* fixing imports.

* Removing.

* Fixing cli access token retrieval.

* Fixing authority check.

* Small edits.

* Removing duplicate.

* Adding uuid check.

* Possible to override with existing params.

* Allowing flags to override storage.

* Trying to fix config params.?

* Fixing.

* Set endpoint params via app function.

* Checking changes to params.

* Make tenant_domain default.

* Remove endoint params from models.

* UPdating docs.

* Setting

* Removing hardcoded values.

* Typo.

* Removing endpoint upload.

* Typo.

* Fixing typos.

* Fix error message about aad tenant.

* Responding to comments.

* Update src/ApiService/ApiService/UserCredentials.cs

Co-authored-by: Marc Greisen <mgreisen@microsoft.com>

---------

Co-authored-by: Marc Greisen <mgreisen@microsoft.com>
This commit is contained in:
Noah McGregor Harper
2023-01-31 23:03:38 +00:00
committed by GitHub
parent 3d2fb65c14
commit f402304084
16 changed files with 269 additions and 96 deletions

View File

@ -0,0 +1,34 @@
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
namespace Microsoft.OneFuzz.Service.Functions;
public class Config {
private readonly ILogTracer _log;
private readonly IOnefuzzContext _context;
public Config(ILogTracer log, IOnefuzzContext context) {
_log = log;
_context = context;
}
[Function("Config")]
public Async.Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "GET")] HttpRequestData req) {
return Get(req);
}
public async Async.Task<HttpResponseData> Get(HttpRequestData req) {
_log.Info($"getting endpoint config parameters");
var endpointParams = new ConfigResponse(
Authority: _context.ServiceConfiguration.Authority,
ClientId: _context.ServiceConfiguration.CliAppId,
TenantDomain: _context.ServiceConfiguration.TenantDomain);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(endpointParams);
return response;
}
}

View File

@ -159,6 +159,12 @@ public record ScalesetResponse(
Nodes: null);
}
public record ConfigResponse(
string? Authority,
string? ClientId,
string? TenantDomain
) : BaseResponse();
public class BaseResponseConverter : JsonConverter<BaseResponse> {
public override BaseResponse? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
return null;

View File

@ -26,7 +26,9 @@ public interface IServiceConfig {
public string? DiagnosticsAzureBlobContainerSasUrl { get; }
public string? DiagnosticsAzureBlobRetentionDays { get; }
public string? CliAppId { get; }
public string? Authority { get; }
public string? TenantDomain { get; }
public string? MultiTenantDomain { get; }
public ResourceIdentifier? OneFuzzDataStorage { get; }
public ResourceIdentifier? OneFuzzFuncStorage { get; }
@ -97,7 +99,9 @@ public class ServiceConfiguration : IServiceConfig {
public string? DiagnosticsAzureBlobContainerSasUrl { get => GetEnv("DIAGNOSTICS_AZUREBLOBCONTAINERSASURL"); }
public string? DiagnosticsAzureBlobRetentionDays { get => GetEnv("DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS"); }
public string? CliAppId { get => GetEnv("CLI_APP_ID"); }
public string? Authority { get => GetEnv("AUTHORITY"); }
public string? TenantDomain { get => GetEnv("TENANT_DOMAIN"); }
public string? MultiTenantDomain { get => GetEnv("MULTI_TENANT_DOMAIN"); }
public ResourceIdentifier? OneFuzzDataStorage {

View File

@ -92,7 +92,7 @@ public class UserCredentials : IUserCredentials {
} else {
var tenantsStr = allowedTenants.OkV is null ? "null" : String.Join(';', allowedTenants.OkV!);
_log.Error($"issuer not from allowed tenant. issuer: {token.Issuer:Tag:Issuer} - tenants: {tenantsStr:Tag:Tenants}");
return OneFuzzResult<UserAuthInfo>.Error(ErrorCode.INVALID_REQUEST, new[] { "unauthorized AAD issuer" });
return OneFuzzResult<UserAuthInfo>.Error(ErrorCode.INVALID_REQUEST, new[] { "unauthorized AAD issuer. If multi-tenant auth is failing, make sure to include all tenant_ids in the `allowed_aad_tenants` list in the instance_config. To see the current instance_config, run `onefuzz instance_config get`. " });
}
} else {
_log.Error($"Failed to get allowed tenants due to {allowedTenants.ErrorV:Tag:Error}");

View File

@ -26,7 +26,7 @@ public class ConfigOperations : Orm<InstanceConfig>, IConfigOperations {
private static readonly InstanceConfigCacheKey _key = new(); // singleton key
public Task<InstanceConfig> Fetch()
=> _cache.GetOrCreateAsync(_key, async entry => {
entry = entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)); // cached for 10 minutes
entry = entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(1)); // cached for 1 minute
var key = _context.ServiceConfiguration.OneFuzzInstanceName ?? throw new Exception("Environment variable ONEFUZZ_INSTANCE_NAME is not set");
return await GetEntityAsync(key, key);
});

View File

@ -25,6 +25,11 @@ public sealed class TestServiceConfiguration : IServiceConfig {
public string? OneFuzzTelemetry => "TestOneFuzzTelemetry";
public string? CliAppId => "TestGuid";
public string? Authority => "TestAuthority";
public string? TenantDomain => "TestDomain";
public string? MultiTenantDomain => null;
public string? OneFuzzInstanceName => "UnitTestInstance";

View File

@ -41,8 +41,9 @@ from .ssh import build_ssh_command, ssh_connect, temp_file
UUID_EXPANSION = TypeVar("UUID_EXPANSION", UUID, str)
DEFAULT = BackendConfig(
authority="https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47",
client_id="72f1562a-8c0c-41ea-beb9-fa2b71c80134",
authority="",
client_id="",
tenant_domain="",
)
# This was generated randomly and should be preserved moving forwards
@ -122,6 +123,10 @@ class Endpoint:
as_params: bool = False,
alternate_endpoint: Optional[str] = None,
) -> A:
# Retrieve Auth Parameters
self._req_config_params()
response = self._req_base(
method,
data=data,
@ -153,6 +158,33 @@ class Endpoint:
return [model.parse_obj(x) for x in response]
def _req_config_params(
self,
) -> None:
if self.onefuzz._backend.config.endpoint is None:
raise Exception("Endpoint Not Configured")
endpoint = self.onefuzz._backend.config.endpoint
response = self.onefuzz._backend.session.request(
"GET", endpoint + "/api/config"
)
logging.debug(response.json())
endpoint_params = responses.Config.parse_obj(response.json())
logging.debug(self.onefuzz._backend.config.authority)
# Will override values in storage w/ provided values for SP use
if self.onefuzz._backend.config.client_id == "":
self.onefuzz._backend.config.client_id = endpoint_params.client_id
if self.onefuzz._backend.config.authority == "":
self.onefuzz._backend.config.authority = endpoint_params.authority
if self.onefuzz._backend.config.tenant_domain == "":
self.onefuzz._backend.config.tenant_domain = endpoint_params.tenant_domain
self.onefuzz._backend.save_config()
def _disambiguate(
self,
name: str,
@ -1862,7 +1894,9 @@ class Onefuzz:
self.logger.debug("set config")
if reset:
self._backend.config = BackendConfig(authority="", client_id="")
self._backend.config = BackendConfig(
authority="", client_id="", tenant_domain=""
)
if endpoint is not None:
# The normal path for calling the API always uses the oauth2 workflow,

View File

@ -95,7 +95,7 @@ class BackendConfig(BaseModel):
client_id: str
endpoint: Optional[str]
features: Set[str] = Field(default_factory=set)
tenant_domain: Optional[str]
tenant_domain: str
class Backend:
@ -181,7 +181,7 @@ class Backend:
if not self.config.endpoint:
raise Exception("endpoint not configured")
if self.config.tenant_domain:
if "https://login.microsoftonline.com/common" in self.config.authority:
endpoint = urlparse(self.config.endpoint).netloc.split(".")[0]
scopes = [
f"api://{self.config.tenant_domain}/{endpoint}/.default",

View File

@ -8,6 +8,9 @@ param clientSecret string
param signedExpiry string
param app_func_issuer string
param app_func_audiences array
param cli_app_id string
param authority string
param tenant_domain string
param multi_tenant_domain string
param enable_remote_debugging bool = false
param enable_profiler bool = false
@ -239,6 +242,9 @@ module functionSettings 'bicep-templates/function-settings.bicep' = {
fuzz_storage_resource_id: storage.outputs.FuzzId
keyvault_name: keyVaultName
monitor_account_name: operationalInsights.outputs.monitorAccountName
cli_app_id: cli_app_id
authority: authority
tenant_domain: tenant_domain
multi_tenant_domain: multi_tenant_domain
enable_profiler: enable_profiler
app_config_endpoint: featureFlags.outputs.AppConfigEndpoint

View File

@ -8,6 +8,9 @@ param app_insights_key string
@secure()
param func_sas_url string
param cli_app_id string
param authority string
param tenant_domain string
param multi_tenant_domain string
@secure()
@ -52,6 +55,9 @@ resource functionSettings 'Microsoft.Web/sites/config@2021-03-01' = {
APPINSIGHTS_APPID: app_insights_app_id
ONEFUZZ_TELEMETRY: telemetry
AzureWebJobsStorage: func_sas_url
CLI_APP_ID: cli_app_id
AUTHORITY: authority
TENANT_DOMAIN: tenant_domain
MULTI_TENANT_DOMAIN: multi_tenant_domain
AzureWebJobsDisableHomepage: 'true'
AzureSignalRConnectionString: signal_r_connection_string

View File

@ -23,7 +23,6 @@ param diagnostics_log_level string
param log_retention int
param linux_fx_version string
var siteconfig = (use_windows) ? {
} : {
linuxFxVersion: linux_fx_version
@ -75,6 +74,7 @@ resource funcAuthSettings 'Microsoft.Web/sites/config@2021-03-01' = {
globalValidation: {
unauthenticatedClientAction: 'RedirectToLoginPage'
requireAuthentication: true
excludedPaths: [ '/api/config' ]
}
httpSettings: {
requireHttps: true

View File

@ -1,6 +1,12 @@
{
"tenant_id": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"tenant_domain": "azurewebsites.net",
"multi_tenant_domain": "",
"cli_client_id": "72f1562a-8c0c-41ea-beb9-fa2b71c80134",
"proxy_nsg_config": {
"allowed_ips": ["*"],
"allowed_ips": [
"*"
],
"allowed_service_tags": []
}
}

View File

@ -43,8 +43,9 @@ from azure.storage.blob import (
from msrest.serialization import TZ_UTC
from deploylib.configuration import (
Config,
InstanceConfigClient,
NetworkSecurityConfig,
NsgRule,
parse_rules,
update_admins,
update_allowed_aad_tenants,
@ -61,7 +62,6 @@ from deploylib.registration import (
get_application,
get_service_principal,
get_signed_in_user,
get_tenant_id,
query_microsoft_graph,
register_application,
set_app_audience,
@ -74,10 +74,6 @@ from deploylib.registration import (
USER_READ_PERMISSION = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
MICROSOFT_GRAPH_APP_ID = "00000003-0000-0000-c000-000000000000"
ONEFUZZ_CLI_AUTHORITY = (
"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47"
)
COMMON_AUTHORITY = "https://login.microsoftonline.com/common"
TELEMETRY_NOTICE = (
"Telemetry collection on stats and OneFuzz failures are sent to Microsoft. "
"To disable, delete the ONEFUZZ_TELEMETRY application setting in the "
@ -139,7 +135,7 @@ class Client:
location: str,
application_name: str,
owner: str,
nsg_config: str,
config: str,
client_id: Optional[str],
client_secret: Optional[str],
app_zip: str,
@ -167,7 +163,7 @@ class Client:
self.location = location
self.application_name = application_name
self.owner = owner
self.nsg_config = nsg_config
self.config = config
self.app_zip = app_zip
self.tools = tools
self.instance_specific = instance_specific
@ -180,10 +176,6 @@ class Client:
"client_id": client_id,
"client_secret": client_secret,
}
if self.multi_tenant_domain:
authority = COMMON_AUTHORITY
else:
authority = ONEFUZZ_CLI_AUTHORITY
self.migrations = migrations
self.export_appinsights = export_appinsights
self.admins = admins
@ -196,9 +188,15 @@ class Client:
self.host_dotnet_on_windows = host_dotnet_on_windows
self.enable_profiler = enable_profiler
self.rules: List[NsgRule] = []
self.tenant_id = ""
self.tenant_domain = ""
self.authority = ""
self.cli_config: Dict[str, Union[str, UUID]] = {
"client_id": self.cli_app_id,
"authority": authority,
"client_id": "",
"authority": "",
}
machine = platform.machine()
@ -305,7 +303,7 @@ class Client:
# The url to access the instance
# This also represents the legacy identifier_uris of the application
# registration
if self.multi_tenant_domain:
if self.multi_tenant_domain != "":
return "https://%s/%s" % (self.multi_tenant_domain, self.application_name)
else:
return "https://%s.azurewebsites.net" % self.application_name
@ -316,14 +314,14 @@ class Client:
# to be from an approved domain The format of this value is derived
# from the default value proposed by azure when creating an application
# registration api://{guid}/...
if self.multi_tenant_domain:
if self.multi_tenant_domain != "":
return "api://%s/%s" % (self.multi_tenant_domain, self.application_name)
else:
return "api://%s.azurewebsites.net" % self.application_name
def get_signin_audience(self) -> str:
# https://docs.microsoft.com/en-us/azure/active-directory/develop/supported-accounts-validation
if self.multi_tenant_domain:
if self.multi_tenant_domain != "":
return "AzureADMultipleOrgs"
else:
return "AzureADMyOrg"
@ -382,7 +380,7 @@ class Client:
else:
self.update_existing_app_registration(app, app_roles)
if self.multi_tenant_domain and app["signInAudience"] == "AzureADMyOrg":
if self.multi_tenant_domain != "" and app["signInAudience"] == "AzureADMyOrg":
set_app_audience(
app["id"],
"AzureADMultipleOrgs",
@ -419,13 +417,10 @@ class Client:
OnefuzzAppRole.CliClient,
self.get_subscription_id(),
)
if self.multi_tenant_domain:
authority = COMMON_AUTHORITY
else:
authority = app_info.authority
self.cli_config = {
"client_id": app_info.client_id,
"authority": authority,
"authority": self.authority,
}
else:
logger.error(
@ -437,14 +432,10 @@ class Client:
else:
onefuzz_cli_app = cli_app
authorize_application(uuid.UUID(onefuzz_cli_app["appId"]), app["appId"])
if self.multi_tenant_domain:
authority = COMMON_AUTHORITY
else:
tenant_id = get_tenant_id(self.get_subscription_id())
authority = "https://login.microsoftonline.com/%s" % tenant_id
self.cli_config = {
"client_id": onefuzz_cli_app["appId"],
"authority": authority,
"authority": self.authority,
}
# ensure replyURLs is set properly
@ -641,7 +632,7 @@ class Client:
# Add --custom_domain value to Allowed token audiences setting
if self.custom_domain:
if self.multi_tenant_domain:
if self.multi_tenant_domain != "":
root_domain = self.multi_tenant_domain
else:
root_domain = "%s.azurewebsites.net" % self.application_name
@ -653,7 +644,7 @@ class Client:
app_func_audiences.extend(custom_domains)
if self.multi_tenant_domain:
if self.multi_tenant_domain != "":
# clear the value in the Issuer Url field:
# https://docs.microsoft.com/en-us/sharepoint/dev/spfx/use-aadhttpclient-enterpriseapi-multitenant
app_func_issuer = ""
@ -680,6 +671,9 @@ class Client:
"clientSecret": {"value": self.results["client_secret"]},
"app_func_issuer": {"value": app_func_issuer},
"signedExpiry": {"value": expiry},
"cli_app_id": {"value": self.cli_app_id},
"authority": {"value": self.authority},
"tenant_domain": {"value": self.tenant_domain},
"multi_tenant_domain": multi_tenant_domain,
"workbookData": {"value": self.workbook_data},
"enable_remote_debugging": {"value": self.host_dotnet_on_windows},
@ -778,6 +772,38 @@ class Client:
table_service = TableService(account_name=name, account_key=key)
migrate(table_service, self.migrations)
def parse_config(self) -> None:
logger.info("parsing config: %s", self.config)
if self.config:
with open(self.config, "r") as template_handle:
config_template = json.load(template_handle)
try:
config = Config(config_template)
self.rules = parse_rules(config)
## Values provided via the CLI will override what's in the config.json
if self.authority == "":
self.authority = (
"https://login.microsoftonline.com/" + config.tenant_id
)
if self.tenant_domain == "":
self.tenant_domain = config.tenant_domain
if self.multi_tenant_domain == "":
self.multi_tenant_domain = config.multi_tenant_domain
if self.cli_app_id == "":
self.cli_app_id = config.cli_client_id
except Exception as ex:
logging.info(
"An Exception was encountered while parsing config file: %s", ex
)
raise Exception(
"config and sub-values were not properly included in config."
)
def set_instance_config(self) -> None:
logger.info("setting instance config")
name = self.results["deploy"]["func_name"]["value"]
@ -787,26 +813,7 @@ class Client:
config_client = InstanceConfigClient(table_service, self.application_name)
if self.nsg_config:
logger.info("deploying arm template: %s", self.nsg_config)
with open(self.nsg_config, "r") as template_handle:
config_template = json.load(template_handle)
try:
config = NetworkSecurityConfig(config_template)
rules = parse_rules(config)
except Exception as ex:
logging.info(
"An Exception was encountered while parsing nsg_config file: %s", ex
)
raise Exception(
"proxy_nsg_config and sub-values were not properly included in config."
+ "Please submit a configuration resembling"
+ " { 'proxy_nsg_config': { 'allowed_ips': [], 'allowed_service_tags': [] } }"
)
update_nsg(config_client, rules)
update_nsg(config_client, self.rules)
if self.admins:
update_admins(config_client, self.admins)
@ -1136,18 +1143,11 @@ class Client:
"config",
"--endpoint",
f"https://{self.application_name}.azurewebsites.net",
"--authority",
str(self.cli_config["authority"]),
"--client_id",
str(self.cli_config["client_id"]),
]
if "client_secret" in self.cli_config:
cmd += ["--client_secret", "YOUR_CLIENT_SECRET_HERE"]
if self.multi_tenant_domain:
cmd += ["--tenant_domain", str(self.multi_tenant_domain)]
as_str = " ".join(cmd)
logger.info(f"Update your CLI config via: {as_str}")
@ -1174,6 +1174,7 @@ def lower_case(arg: str) -> str:
def main() -> None:
rbac_only_states = [
("parse_config", Client.parse_config),
("check_region", Client.check_region),
("rbac", Client.setup_rbac),
("eventgrid", Client.remove_eventgrid),
@ -1200,7 +1201,7 @@ def main() -> None:
parser.add_argument("resource_group")
parser.add_argument("application_name", type=lower_case)
parser.add_argument("owner")
parser.add_argument("nsg_config")
parser.add_argument("config")
parser.add_argument(
"--bicep-template",
type=arg_file,
@ -1272,7 +1273,7 @@ def main() -> None:
parser.add_argument(
"--multi_tenant_domain",
type=str,
default=None,
default="",
help="enable multi-tenant authentication with this tenant domain",
)
parser.add_argument(
@ -1299,7 +1300,7 @@ def main() -> None:
parser.add_argument(
"--cli_app_id",
type=str,
default="72f1562a-8c0c-41ea-beb9-fa2b71c80134",
default="",
help="CLI App Registration to be used during deployment.",
)
parser.add_argument(
@ -1337,7 +1338,7 @@ def main() -> None:
location=args.location,
application_name=args.application_name,
owner=args.owner,
nsg_config=args.nsg_config,
config=args.config,
client_id=args.client_id,
client_secret=args.client_secret,
app_zip=args.app_zip,

View File

@ -48,12 +48,17 @@ class InstanceConfigClient:
self.enable_storage_client_logging()
class NetworkSecurityConfig:
class Config:
cli_client_id: str
tenant_id: str
tenant_domain: str
multi_tenant_domain: str
allowed_ips: List[str]
allowed_service_tags: List[str]
def __init__(self, config: Any):
self.parse_nsg_json(config)
self.parse_endpoint_json(config)
def parse_nsg_json(self, config: Any) -> None:
if not isinstance(config, Dict):
@ -107,6 +112,66 @@ class NetworkSecurityConfig:
self.allowed_ips = proxy_config["allowed_ips"]
self.allowed_service_tags = proxy_config["allowed_service_tags"]
def parse_endpoint_json(self, config: Any) -> None:
if "cli_client_id" not in config:
raise Exception(
"CLI client_id not provided as valid key. Please Provide Valid Config."
)
if (
not isinstance(config["cli_client_id"], str)
or config["cli_client_id"] == ""
):
raise Exception(
"client_id is not a string. Please provide valid client_id."
)
try:
UUID(config["cli_client_id"])
except ValueError:
raise Exception(
"client_id is not a valid UUID. Please provide valid client_id."
)
if "tenant_id" not in config:
raise Exception(
"tenant_id not provided as valid key. Please provide valid config."
)
if not isinstance(config["tenant_id"], str) or config["tenant_id"] == "":
raise Exception(
"tenant_id is not a string. Please provide valid tenant_id."
)
if "tenant_domain" not in config:
raise Exception(
"tenant_domain not provided as valid key. Please provide valid config."
)
if (
not isinstance(config["tenant_domain"], str)
or config["tenant_domain"] == ""
):
raise Exception(
"tenant_domain is not a string. Please provide valid tenant_domain."
)
if "multi_tenant_domain" not in config:
raise Exception(
"multi_tenant_domain not provided as valid key. Please provide valid config. If the instance is not multi-tenant, please provide an empty string."
)
if not isinstance(config["multi_tenant_domain"], str):
raise Exception(
"multi_tenant_domain is not a string. Please provide valid multi_tenant_domain. If the instance is not multi-tenant, please provide an empty string."
)
self.cli_client_id = config["cli_client_id"]
self.tenant_id = config["tenant_id"]
self.tenant_domain = config["tenant_domain"]
self.multi_tenant_domain = config["multi_tenant_domain"]
class NsgRule:
rule: str
@ -175,7 +240,7 @@ def update_admins(config_client: InstanceConfigClient, admins: List[UUID]) -> No
)
def parse_rules(proxy_config: NetworkSecurityConfig) -> List[NsgRule]:
def parse_rules(proxy_config: Config) -> List[NsgRule]:
allowed_ips = proxy_config.allowed_ips
allowed_service_tags = proxy_config.allowed_service_tags

View File

@ -6,7 +6,7 @@
import unittest
from typing import Any
from deploylib.configuration import NetworkSecurityConfig
from deploylib.configuration import Config
class DeployTests(unittest.TestCase):
@ -15,33 +15,33 @@ class DeployTests(unittest.TestCase):
# Test Dictionary
invalid_config: Any = ""
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test Empty Dic
invalid_config = {}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test Invalid Outer Keys
invalid_config = {"": ""}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test Inner Dictionary
invalid_config = {"proxy_nsg_config": ""}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test Inner Keys
invalid_config = {"proxy_nsg_config": {}}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test Inner Keys
invalid_config = {"proxy_nsg_config": {"allowed_ips": ""}}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test Inner Dict Values (lists)
invalid_config = {
"proxy_nsg_config": {"allowed_ips": [], "allowed_service_tags": ""}
}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
# Test List Values
invalid_config = {
"proxy_nsg_config": {
@ -50,19 +50,19 @@ class DeployTests(unittest.TestCase):
}
}
with self.assertRaises(Exception):
NetworkSecurityConfig(invalid_config)
Config(invalid_config)
## Test Valid Configs
# Test Empty Lists
valid_config: Any = {
"proxy_nsg_config": {"allowed_ips": [], "allowed_service_tags": []}
}
NetworkSecurityConfig(valid_config)
Config(valid_config)
# Test Wild Card Lists
valid_config = {
"proxy_nsg_config": {"allowed_ips": ["*"], "allowed_service_tags": []}
}
NetworkSecurityConfig(valid_config)
Config(valid_config)
# Test IPs Lists
valid_config = {
"proxy_nsg_config": {
@ -70,7 +70,7 @@ class DeployTests(unittest.TestCase):
"allowed_service_tags": [],
}
}
NetworkSecurityConfig(valid_config)
Config(valid_config)
# Test Tags Lists
valid_config = {
"proxy_nsg_config": {
@ -78,7 +78,7 @@ class DeployTests(unittest.TestCase):
"allowed_service_tags": ["Internet"],
}
}
NetworkSecurityConfig(valid_config)
Config(valid_config)
if __name__ == "__main__":

View File

@ -52,6 +52,12 @@ class Info(BaseResponse):
insights_instrumentation_key: Optional[str]
class Config(BaseResponse):
authority: str
client_id: str
tenant_domain: str
class ContainerInfoBase(BaseResponse):
name: str
metadata: Optional[Dict[str, str]]