diff --git a/src/containerapp/HISTORY.rst b/src/containerapp/HISTORY.rst index 12807a2bb9a..8e61aac719d 100644 --- a/src/containerapp/HISTORY.rst +++ b/src/containerapp/HISTORY.rst @@ -9,7 +9,6 @@ Release History * Added 'az containerapp env certificate' to manage certificates in a container app environment * Added 'az containerapp hostname' to manage hostnames in a container app * Added 'az containerapp ssl upload' to upload a certificate, add a hostname and the binding to a container app -* Added 'az containerapp auth' to manage AuthConfigs for a containerapp 0.3.4 ++++++ diff --git a/src/containerapp/azext_containerapp/_constants.py b/src/containerapp/azext_containerapp/_constants.py index 1ba84998feb..ca0a0afd70e 100644 --- a/src/containerapp/azext_containerapp/_constants.py +++ b/src/containerapp/azext_containerapp/_constants.py @@ -14,13 +14,4 @@ MAX_ENV_PER_LOCATION = 2 -MICROSOFT_SECRET_SETTING_NAME = "microsoft-provider-authentication-secret" -FACEBOOK_SECRET_SETTING_NAME = "facebook-provider-authentication-secret" -GITHUB_SECRET_SETTING_NAME = "github-provider-authentication-secret" -GOOGLE_SECRET_SETTING_NAME = "google-provider-authentication-secret" -MSA_SECRET_SETTING_NAME = "msa-provider-authentication-secret" -TWITTER_SECRET_SETTING_NAME = "twitter-provider-authentication-secret" -APPLE_SECRET_SETTING_NAME = "apple-provider-authentication-secret" -UNAUTHENTICATED_CLIENT_ACTION = ['RedirectToLoginPage', 'AllowAnonymous', 'RejectWith401', 'RejectWith404'] -FORWARD_PROXY_CONVENTION = ['NoProxy', 'Standard', 'Custom'] CHECK_CERTIFICATE_NAME_AVAILABILITY_TYPE = "Microsoft.App/managedEnvironments/certificates" diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index bbe07960a82..4f19429f41f 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -807,224 +807,3 @@ text: | az containerapp hostname list -n MyContainerapp -g MyResourceGroup """ - -# Auth commands -helps['containerapp auth'] = """ -type: group -short-summary: Manage containerapp authentication and authorization. -""" - -helps['containerapp auth show'] = """ -type: command -short-summary: Show the authentication settings for the containerapp. -examples: - - name: Show the authentication settings for the containerapp. - text: az containerapp auth show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth update'] = """ -type: command -short-summary: Update the authentication settings for the containerapp. -examples: - - name: Update the client ID of the AAD provider already configured. - text: | - az containerapp auth update -g myResourceGroup --name MyContainerapp --set identityProviders.azureActiveDirectory.registration.clientId=my-client-id - - name: Configure the app with file based authentication by setting the config file path. - text: | - az containerapp auth update -g myResourceGroup --name MyContainerapp --config-file-path D:\\home\\site\\wwwroot\\auth.json - - name: Configure the app to allow unauthenticated requests to hit the app. - text: | - az containerapp auth update -g myResourceGroup --name MyContainerapp --unauthenticated-client-action AllowAnonymous - - name: Configure the app to redirect unauthenticated requests to the Facebook provider. - text: | - az containerapp auth update -g myResourceGroup --name MyContainerapp --redirect-provider Facebook - - name: Configure the app to listen to the forward headers X-FORWARDED-HOST and X-FORWARDED-PROTO. - text: | - az containerapp auth update -g myResourceGroup --name MyContainerapp --proxy-convention Standard -""" - -helps['containerapp auth apple'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the Apple identity provider. -""" - -helps['containerapp auth apple show'] = """ -type: command -short-summary: Show the authentication settings for the Apple identity provider. -examples: - - name: Show the authentication settings for the Apple identity provider. - text: az containerapp auth apple show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth apple update'] = """ -type: command -short-summary: Update the client id and client secret for the Apple identity provider. -examples: - - name: Update the client id and client secret for the Apple identity provider. - text: | - az containerapp auth apple update -g myResourceGroup --name MyContainerapp \\ - --client-id my-client-id --client-secret very_secret_password -""" - -helps['containerapp auth facebook'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the Facebook identity provider. -""" - -helps['containerapp auth facebook show'] = """ -type: command -short-summary: Show the authentication settings for the Facebook identity provider. -examples: - - name: Show the authentication settings for the Facebook identity provider. - text: az containerapp auth facebook show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth facebook update'] = """ -type: command -short-summary: Update the app id and app secret for the Facebook identity provider. -examples: - - name: Update the app id and app secret for the Facebook identity provider. - text: | - az containerapp auth facebook update -g myResourceGroup --name MyContainerapp \\ - --app-id my-client-id --app-secret very_secret_password -""" - -helps['containerapp auth github'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the GitHub identity provider. -""" - -helps['containerapp auth github show'] = """ -type: command -short-summary: Show the authentication settings for the GitHub identity provider. -examples: - - name: Show the authentication settings for the GitHub identity provider. - text: az containerapp auth github show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth github update'] = """ -type: command -short-summary: Update the client id and client secret for the GitHub identity provider. -examples: - - name: Update the client id and client secret for the GitHub identity provider. - text: | - az containerapp auth github update -g myResourceGroup --name MyContainerapp \\ - --client-id my-client-id --client-secret very_secret_password -""" - -helps['containerapp auth google'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the Google identity provider. -""" - -helps['containerapp auth google show'] = """ -type: command -short-summary: Show the authentication settings for the Google identity provider. -examples: - - name: Show the authentication settings for the Google identity provider. - text: az containerapp auth google show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth google update'] = """ -type: command -short-summary: Update the client id and client secret for the Google identity provider. -examples: - - name: Update the client id and client secret for the Google identity provider. - text: | - az containerapp auth google update -g myResourceGroup --name MyContainerapp \\ - --client-id my-client-id --client-secret very_secret_password -""" - -helps['containerapp auth microsoft'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the Microsoft identity provider. -""" - -helps['containerapp auth microsoft show'] = """ -type: command -short-summary: Show the authentication settings for the Azure Active Directory identity provider. -examples: - - name: Show the authentication settings for the Azure Active Directory identity provider. - text: az containerapp auth microsoft show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth microsoft update'] = """ -type: command -short-summary: Update the client id and client secret for the Azure Active Directory identity provider. -examples: - - name: Update the open id issuer, client id and client secret for the Azure Active Directory identity provider. - text: | - az containerapp auth microsoft update -g myResourceGroup --name MyContainerapp \\ - --client-id my-client-id --client-secret very_secret_password \\ - --issuer https://sts.windows.net/54826b22-38d6-4fb2-bad9-b7983a3e9c5a/ -""" - -helps['containerapp auth openid-connect'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the custom OpenID Connect identity providers. -""" - -helps['containerapp auth openid-connect show'] = """ -type: command -short-summary: Show the authentication settings for the custom OpenID Connect identity provider. -examples: - - name: Show the authentication settings for the custom OpenID Connect identity provider. - text: az containerapp auth openid-connect show --name MyContainerapp --resource-group MyResourceGroup \\ - --provider-name myOpenIdConnectProvider -""" - -helps['containerapp auth openid-connect add'] = """ -type: command -short-summary: Configure a new custom OpenID Connect identity provider. -examples: - - name: Configure a new custom OpenID Connect identity provider. - text: | - az containerapp auth openid-connect add -g myResourceGroup --name MyContainerapp \\ - --provider-name myOpenIdConnectProvider --client-id my-client-id \\ - --client-secret-name MY_SECRET_APP_SETTING \\ - --openid-configuration https://myopenidprovider.net/.well-known/openid-configuration -""" - -helps['containerapp auth openid-connect update'] = """ -type: command -short-summary: Update the client id and client secret setting name for an existing custom OpenID Connect identity provider. -examples: - - name: Update the client id and client secret setting name for an existing custom OpenID Connect identity provider. - text: | - az containerapp auth openid-connect update -g myResourceGroup --name MyContainerapp \\ - --provider-name myOpenIdConnectProvider --client-id my-client-id \\ - --client-secret-name MY_SECRET_APP_SETTING -""" - -helps['containerapp auth openid-connect remove'] = """ -type: command -short-summary: Removes an existing custom OpenID Connect identity provider. -examples: - - name: Removes an existing custom OpenID Connect identity provider. - text: | - az containerapp auth openid-connect remove --name MyContainerapp --resource-group MyResourceGroup \\ - --provider-name myOpenIdConnectProvider -""" - -helps['containerapp auth twitter'] = """ -type: group -short-summary: Manage containerapp authentication and authorization of the Twitter identity provider. -""" - -helps['containerapp auth twitter show'] = """ -type: command -short-summary: Show the authentication settings for the Twitter identity provider. -examples: - - name: Show the authentication settings for the Twitter identity provider. - text: az containerapp auth twitter show --name MyContainerapp --resource-group MyResourceGroup -""" - -helps['containerapp auth twitter update'] = """ -type: command -short-summary: Update the consumer key and consumer secret for the Twitter identity provider. -examples: - - name: Update the consumer key and consumer secret for the Twitter identity provider. - text: | - az containerapp auth twitter update -g myResourceGroup --name MyContainerapp \\ - --consumer-key my-client-id --consumer-secret very_secret_password -""" diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index adcfaddf145..ff4d5112f2b 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -13,7 +13,6 @@ from ._validators import (validate_memory, validate_cpu, validate_managed_env_name_or_id, validate_registry_server, validate_registry_user, validate_registry_pass, validate_target_port, validate_ingress) -from ._constants import UNAUTHENTICATED_CLIENT_ACTION, FORWARD_PROXY_CONVENTION def load_arguments(self, _): @@ -224,7 +223,6 @@ def load_arguments(self, _): c.argument('secret_name', help="The name of the secret to show.") c.argument('secret_names', nargs='+', help="A list of secret(s) for the container app. Space-separated secret values names.") c.argument('show_values', help='Show the secret values.') - c.ignore('disable_max_length') with self.argument_context('containerapp env dapr-component') as c: c.argument('dapr_app_id', help="The Dapr app ID.") @@ -274,42 +272,6 @@ def load_arguments(self, _): c.argument('service_principal_client_secret', help='The service principal client secret. Used by Github Actions to authenticate with Azure.', options_list=["--service-principal-client-secret", "--sp-sec"]) c.argument('service_principal_tenant_id', help='The service principal tenant ID. Used by Github Actions to authenticate with Azure.', options_list=["--service-principal-tenant-id", "--sp-tid"]) - with self.argument_context('containerapp auth') as c: - # subgroup update - c.argument('client_id', options_list=['--client-id'], help='The Client ID of the app used for login.') - c.argument('client_secret', options_list=['--client-secret'], help='The client secret.') - c.argument('client_secret_setting_name', options_list=['--client-secret-name'], help='The app secret name that contains the client secret of the relying party application.') - c.argument('issuer', options_list=['--issuer'], help='The OpenID Connect Issuer URI that represents the entity which issues access tokens for this application.') - c.argument('allowed_token_audiences', options_list=['--allowed-token-audiences', '--allowed-audiences'], help='The configuration settings of the allowed list of audiences from which to validate the JWT token.') - c.argument('client_secret_certificate_thumbprint', options_list=['--thumbprint', '--client-secret-certificate-thumbprint'], help='Alternative to AAD Client Secret, thumbprint of a certificate used for signing purposes') - c.argument('client_secret_certificate_san', options_list=['--san', '--client-secret-certificate-san'], help='Alternative to AAD Client Secret and thumbprint, subject alternative name of a certificate used for signing purposes') - c.argument('client_secret_certificate_issuer', options_list=['--certificate-issuer', '--client-secret-certificate-issuer'], help='Alternative to AAD Client Secret and thumbprint, issuer of a certificate used for signing purposes') - c.argument('yes', options_list=['--yes', '-y'], help='Do not prompt for confirmation.', action='store_true') - c.argument('tenant_id', options_list=['--tenant-id'], help='The tenant id of the application.') - c.argument('app_id', options_list=['--app-id'], help='The App ID of the app used for login.') - c.argument('app_secret', options_list=['--app-secret'], help='The app secret.') - c.argument('app_secret_setting_name', options_list=['--app-secret-name', '--secret-name'], help='The app secret name that contains the app secret.') - c.argument('graph_api_version', options_list=['--graph-api-version'], help='The version of the Facebook api to be used while logging in.') - c.argument('scopes', options_list=['--scopes'], help='A list of the scopes that should be requested while authenticating.') - c.argument('consumer_key', options_list=['--consumer-key'], help='The OAuth 1.0a consumer key of the Twitter application used for sign-in.') - c.argument('consumer_secret', options_list=['--consumer-secret'], help='The consumer secret.') - c.argument('consumer_secret_setting_name', options_list=['--consumer-secret-name', '--secret-name'], help='The consumer secret name that contains the app secret.') - c.argument('provider_name', options_list=['--provider-name'], required=True, help='The name of the custom OpenID Connect provider.') - c.argument('openid_configuration', options_list=['--openid-configuration'], help='The endpoint that contains all the configuration endpoints for the provider.') - # auth update - c.argument('set_string', options_list=['--set'], help='Value of a specific field within the configuration settings for the Azure App Service Authentication / Authorization feature.') - c.argument('config_file_path', options_list=['--config-file-path'], help='The path of the config file containing auth settings if they come from a file.') - c.argument('unauthenticated_client_action', options_list=['--unauthenticated-client-action', '--action'], arg_type=get_enum_type(UNAUTHENTICATED_CLIENT_ACTION), help='The action to take when an unauthenticated client attempts to access the app.') - c.argument('redirect_provider', options_list=['--redirect-provider'], help='The default authentication provider to use when multiple providers are configured.') - c.argument('enable_token_store', options_list=['--enable-token-store'], arg_type=get_three_state_flag(return_label=True), help='true to durably store platform-specific security tokens that are obtained during login flows; otherwise, false.') - c.argument('require_https', options_list=['--require-https'], arg_type=get_three_state_flag(return_label=True), help='false if the authentication/authorization responses not having the HTTPS scheme are permissible; otherwise, true.') - c.argument('proxy_convention', options_list=['--proxy-convention'], arg_type=get_enum_type(FORWARD_PROXY_CONVENTION), help='The convention used to determine the url of the request made.') - c.argument('proxy_custom_host_header', options_list=['--proxy-custom-host-header', '--custom-host-header'], help='The name of the header containing the host of the request.') - c.argument('proxy_custom_proto_header', options_list=['--proxy-custom-proto-header', '--custom-proto-header'], help='The name of the header containing the scheme of the request.') - c.argument('excluded_paths', options_list=['--excluded-paths'], help='The list of paths that should be excluded from authentication rules.') - c.argument('enabled', options_list=['--enabled'], arg_type=get_three_state_flag(return_label=True), help='true if the Authentication / Authorization feature is enabled for the current app; otherwise, false.') - c.argument('runtime_version', options_list=['--runtime-version'], help='The RuntimeVersion of the Authentication / Authorization feature in use for the current app.') - with self.argument_context('containerapp ssl upload') as c: c.argument('hostname', help='The custom domain name.') c.argument('environment', options_list=['--environment', '-e'], help='Name or resource id of the Container App environment.') diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 5b27d3581c4..401ad0caade 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -1193,70 +1193,6 @@ def create_new_acr(cmd, registry_name, resource_group_name, location=None, sku=" return acr -def set_field_in_auth_settings(auth_settings, set_string): - if set_string is not None: - split1 = set_string.split("=") - fieldName = split1[0] - fieldValue = split1[1] - split2 = fieldName.split(".") - auth_settings = set_field_in_auth_settings_recursive(split2, fieldValue, auth_settings) - return auth_settings - - -def set_field_in_auth_settings_recursive(field_name_split, field_value, auth_settings): - if len(field_name_split) == 1: - if not field_value.startswith('[') or not field_value.endswith(']'): - auth_settings[field_name_split[0]] = field_value - else: - field_value_list_string = field_value[1:-1] - auth_settings[field_name_split[0]] = field_value_list_string.split(",") - return auth_settings - - remaining_field_names = field_name_split[1:] - if field_name_split[0] not in auth_settings: - auth_settings[field_name_split[0]] = {} - auth_settings[field_name_split[0]] = set_field_in_auth_settings_recursive(remaining_field_names, - field_value, - auth_settings[field_name_split[0]]) - return auth_settings - - -def update_http_settings_in_auth_settings(auth_settings, require_https, proxy_convention, - proxy_custom_host_header, proxy_custom_proto_header): - if require_https is not None: - if "httpSettings" not in auth_settings: - auth_settings["httpSettings"] = {} - auth_settings["httpSettings"]["requireHttps"] = require_https - - if proxy_convention is not None: - if "httpSettings" not in auth_settings: - auth_settings["httpSettings"] = {} - if "forwardProxy" not in auth_settings["httpSettings"]: - auth_settings["httpSettings"]["forwardProxy"] = {} - auth_settings["httpSettings"]["forwardProxy"]["convention"] = proxy_convention - - if proxy_custom_host_header is not None: - if "httpSettings" not in auth_settings: - auth_settings["httpSettings"] = {} - if "forwardProxy" not in auth_settings["httpSettings"]: - auth_settings["httpSettings"]["forwardProxy"] = {} - auth_settings["httpSettings"]["forwardProxy"]["customHostHeaderName"] = proxy_custom_host_header - - if proxy_custom_proto_header is not None: - if "httpSettings" not in auth_settings: - auth_settings["httpSettings"] = {} - if "forwardProxy" not in auth_settings["httpSettings"]: - auth_settings["httpSettings"]["forwardProxy"] = {} - auth_settings["httpSettings"]["forwardProxy"]["customProtoHeaderName"] = proxy_custom_proto_header - - return auth_settings - - -def get_oidc_client_setting_app_setting_name(provider_name): - provider_name_prefix = provider_name.lower() - return provider_name_prefix + "-provider-authentication-secret" - - # only accept .pfx or .pem file def load_cert_file(file_path, cert_password=None): from base64 import b64encode @@ -1288,7 +1224,7 @@ def load_cert_file(file_path, cert_password=None): else: raise FileOperationError('Not a valid file type. Only .PFX and .PEM files are supported.') except Exception as e: - raise CLIInternalError(e) from e + raise CLIInternalError(e) return blob, thumbprint diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index c1c355cdd5f..43b57720e47 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -8,7 +8,6 @@ # from msrestazure.tools import is_valid_resource_id, parse_resource_id from azext_containerapp._client_factory import ex_handler_factory from ._validators import validate_ssh -from ._clients import STABLE_API_VERSION def transform_containerapp_output(app): @@ -44,16 +43,8 @@ def transform_revision_list_output(revs): return [transform_revision_output(r) for r in revs] -def auth_config_client_factory(cli_ctx, *_): - from azure.cli.core.commands.client_factory import get_mgmt_service_client - from azure.cli.core.profiles import CustomResourceType - MGMT_APPCONTAINERS = CustomResourceType(import_prefix='azure.mgmt.appcontainers', client_name='ContainerAppsAPIClient') - return get_mgmt_service_client(cli_ctx, MGMT_APPCONTAINERS, api_version=STABLE_API_VERSION).container_apps_auth_configs - - def load_command_table(self, _): - - with self.command_group('containerapp') as g: + with self.command_group('containerapp', is_preview=True) as g: g.custom_show_command('show', 'show_containerapp', table_transformer=transform_containerapp_output) g.custom_command('list', 'list_containerapp', table_transformer=transform_containerapp_list_output) g.custom_command('create', 'create_containerapp', supports_no_wait=True, exception_handler=ex_handler_factory(), table_transformer=transform_containerapp_output) @@ -63,11 +54,11 @@ def load_command_table(self, _): g.custom_command('up', 'containerapp_up', supports_no_wait=False, exception_handler=ex_handler_factory()) g.custom_command('browse', 'open_containerapp_in_browser') - with self.command_group('containerapp replica') as g: + with self.command_group('containerapp replica', is_preview=True) as g: g.custom_show_command('show', 'get_replica') # TODO implement the table transformer g.custom_command('list', 'list_replicas') - with self.command_group('containerapp logs') as g: + with self.command_group('containerapp logs', is_preview=True) as g: g.custom_show_command('show', 'stream_containerapp_logs', validator=validate_ssh) with self.command_group('containerapp env') as g: @@ -87,7 +78,7 @@ def load_command_table(self, _): g.custom_command('upload', 'upload_certificate') g.custom_command('delete', 'delete_certificate', confirmation=True, exception_handler=ex_handler_factory()) - with self.command_group('containerapp env storage', is_preview=True) as g: + with self.command_group('containerapp env storage') as g: g.custom_show_command('show', 'show_storage') g.custom_command('list', 'list_storage') g.custom_command('set', 'create_or_update_storage', supports_no_wait=True, exception_handler=ex_handler_factory()) @@ -141,40 +132,6 @@ def load_command_table(self, _): g.custom_command('enable', 'enable_dapr', exception_handler=ex_handler_factory()) g.custom_command('disable', 'disable_dapr', exception_handler=ex_handler_factory()) - with self.command_group('containerapp auth', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'show_auth_config', exception_handler=ex_handler_factory()) - g.custom_command('update', 'update_auth_config', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp auth microsoft', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_aad_settings') - g.custom_command('update', 'update_aad_settings', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp auth facebook', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_facebook_settings') - g.custom_command('update', 'update_facebook_settings', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp auth github', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_github_settings') - g.custom_command('update', 'update_github_settings') - - with self.command_group('containerapp auth google', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_google_settings') - g.custom_command('update', 'update_google_settings', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp auth twitter', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_twitter_settings') - g.custom_command('update', 'update_twitter_settings', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp auth apple', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_apple_settings') - g.custom_command('update', 'update_apple_settings', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp auth openid-connect', client_factory=auth_config_client_factory) as g: - g.custom_show_command('show', 'get_openid_connect_provider_settings') - g.custom_command('add', 'add_openid_connect_provider_settings', exception_handler=ex_handler_factory()) - g.custom_command('update', 'update_openid_connect_provider_settings', exception_handler=ex_handler_factory()) - g.custom_command('remove', 'remove_openid_connect_provider_settings') - with self.command_group('containerapp ssl') as g: g.custom_command('upload', 'upload_ssl', exception_handler=ex_handler_factory()) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index c71570e4230..ef558eb21ce 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -16,13 +16,11 @@ ResourceNotFoundError, CLIError, CLIInternalError, - InvalidArgumentValueError, - ArgumentUsageError) + InvalidArgumentValueError) from azure.cli.core.commands.client_factory import get_subscription_id from azure.cli.core.util import open_page_in_browser from azure.cli.command_modules.appservice.utils import _normalize_location from knack.log import get_logger -from knack.prompting import prompt_y_n from msrestazure.tools import parse_resource_id, is_valid_resource_id from msrest.exceptions import DeserializationError @@ -66,8 +64,7 @@ from ._ssh_utils import (SSH_DEFAULT_ENCODING, WebSocketConnection, read_ssh, get_stdin_writer, SSH_CTRL_C_MSG, SSH_BACKUP_ENCODING) -from ._constants import (MAXIMUM_SECRET_LENGTH, MICROSOFT_SECRET_SETTING_NAME, FACEBOOK_SECRET_SETTING_NAME, GITHUB_SECRET_SETTING_NAME, - GOOGLE_SECRET_SETTING_NAME, TWITTER_SECRET_SETTING_NAME, APPLE_SECRET_SETTING_NAME, CONTAINER_APPS_RP) +from ._constants import MAXIMUM_SECRET_LENGTH, CONTAINER_APPS_RP logger = get_logger(__name__) @@ -1855,7 +1852,6 @@ def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait=False): def set_secrets(cmd, name, resource_group_name, secrets, # yaml=None, - disable_max_length=False, no_wait=False): _validate_subscription_registered(cmd, CONTAINER_APPS_RP) @@ -1863,7 +1859,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, if s: parsed = s.split("=") if parsed: - if len(parsed[0]) > MAXIMUM_SECRET_LENGTH and not disable_max_length: + if len(parsed[0]) > MAXIMUM_SECRET_LENGTH: raise ValidationError(f"Secret names cannot be longer than {MAXIMUM_SECRET_LENGTH}. " f"Please shorten {parsed[0]}") @@ -2378,6 +2374,7 @@ def upload_certificate(cmd, name, resource_group_name, certificate_file, certifi cert_name = None if certificate_name: if not check_cert_name_availability(cmd, resource_group_name, name, certificate_name): + from knack.prompting import prompt_y_n msg = 'A certificate with the name {} already exists in {}. If continue with this name, it will be overwritten by the new certificate file.\nOverwrite?' overwrite = prompt_y_n(msg.format(certificate_name, name)) if overwrite: @@ -2539,7 +2536,7 @@ def create_or_update_storage(cmd, storage_name, resource_group_name, name, azure pass if r: - logger.warning("Only AzureFile account can be updated. In order to change the AzureFile share name or account name, please delete this storage and create a new one.") + logger.warning("Only AzureFile account keys can be updated. In order to change the AzureFile share name or account name, please delete this storage and create a new one.") storage_def = AzureFilePropertiesModel storage_def["accountKey"] = azure_file_account_key @@ -2563,760 +2560,3 @@ def remove_storage(cmd, storage_name, name, resource_group_name, no_wait=False): return StorageClient.delete(cmd, resource_group_name, name, storage_name, no_wait) except CLIError as e: handle_raw_exception(e) - - -# TODO: Refactor provider code to make it cleaner -def update_aad_settings(cmd, client, resource_group_name, name, - client_id=None, client_secret_setting_name=None, - issuer=None, allowed_token_audiences=None, client_secret=None, - client_secret_certificate_thumbprint=None, - client_secret_certificate_san=None, - client_secret_certificate_issuer=None, - yes=False, tenant_id=None): - - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if client_secret is not None and client_secret_setting_name is not None: - raise ArgumentUsageError('Usage Error: --client-secret and --client-secret-setting-name cannot both be ' - 'configured to non empty strings') - - if client_secret_setting_name is not None and client_secret_certificate_thumbprint is not None: - raise ArgumentUsageError('Usage Error: --client-secret-setting-name and --thumbprint cannot both be ' - 'configured to non empty strings') - - if client_secret is not None and client_secret_certificate_thumbprint is not None: - raise ArgumentUsageError('Usage Error: --client-secret and --thumbprint cannot both be ' - 'configured to non empty strings') - - if client_secret is not None and client_secret_certificate_san is not None: - raise ArgumentUsageError('Usage Error: --client-secret and --san cannot both be ' - 'configured to non empty strings') - - if client_secret_setting_name is not None and client_secret_certificate_san is not None: - raise ArgumentUsageError('Usage Error: --client-secret-setting-name and --san cannot both be ' - 'configured to non empty strings') - - if client_secret_certificate_thumbprint is not None and client_secret_certificate_san is not None: - raise ArgumentUsageError('Usage Error: --thumbprint and --san cannot both be ' - 'configured to non empty strings') - - if ((client_secret_certificate_san is not None and client_secret_certificate_issuer is None) or - (client_secret_certificate_san is None and client_secret_certificate_issuer is not None)): - raise ArgumentUsageError('Usage Error: --san and --certificate-issuer must both be ' - 'configured to non empty strings') - - if issuer is not None and (tenant_id is not None): - raise ArgumentUsageError('Usage Error: --issuer and --tenant-id cannot be configured ' - 'to non empty strings at the same time.') - - is_new_aad_app = False - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth = {} - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - registration = {} - validation = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "azureActiveDirectory" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["azureActiveDirectory"] = {} - is_new_aad_app = True - - if is_new_aad_app and issuer is None and tenant_id is None: - raise ArgumentUsageError('Usage Error: Either --issuer or --tenant-id must be specified when configuring the ' - 'Microsoft auth registration.') - - if client_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - openid_issuer = issuer - if openid_issuer is None: - # cmd.cli_ctx.cloud resolves to whichever cloud the customer is currently logged into - authority = cmd.cli_ctx.cloud.endpoints.active_directory - - if tenant_id is not None: - openid_issuer = authority + "/" + tenant_id + "/v2.0" - - registration = {} - validation = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "azureActiveDirectory" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["azureActiveDirectory"] = {} - if (client_id is not None or client_secret is not None or - client_secret_setting_name is not None or openid_issuer is not None or - client_secret_certificate_thumbprint is not None or - client_secret_certificate_san is not None or - client_secret_certificate_issuer is not None): - if "registration" not in existing_auth["identityProviders"]["azureActiveDirectory"]: - existing_auth["identityProviders"]["azureActiveDirectory"]["registration"] = {} - registration = existing_auth["identityProviders"]["azureActiveDirectory"]["registration"] - if allowed_token_audiences is not None: - if "validation" not in existing_auth["identityProviders"]["azureActiveDirectory"]: - existing_auth["identityProviders"]["azureActiveDirectory"]["validation"] = {} - validation = existing_auth["identityProviders"]["azureActiveDirectory"]["validation"] - - if client_id is not None: - registration["clientId"] = client_id - if client_secret_setting_name is not None: - registration["clientSecretSettingName"] = client_secret_setting_name - if client_secret is not None: - registration["clientSecretSettingName"] = MICROSOFT_SECRET_SETTING_NAME - set_secrets(cmd, name, resource_group_name, secrets=[f"{MICROSOFT_SECRET_SETTING_NAME}={client_secret}"], no_wait=True, disable_max_length=True) - if client_secret_setting_name is not None or client_secret is not None: - fields = ["clientSecretCertificateThumbprint", "clientSecretCertificateSubjectAlternativeName", "clientSecretCertificateIssuer"] - for field in [f for f in fields if registration.get(f)]: - registration[field] = None - if client_secret_certificate_thumbprint is not None: - registration["clientSecretCertificateThumbprint"] = client_secret_certificate_thumbprint - fields = ["clientSecretSettingName", "clientSecretCertificateSubjectAlternativeName", "clientSecretCertificateIssuer"] - for field in [f for f in fields if registration.get(f)]: - registration[field] = None - if client_secret_certificate_san is not None: - registration["clientSecretCertificateSubjectAlternativeName"] = client_secret_certificate_san - if client_secret_certificate_issuer is not None: - registration["clientSecretCertificateIssuer"] = client_secret_certificate_issuer - if client_secret_certificate_san is not None and client_secret_certificate_issuer is not None: - if "clientSecretSettingName" in registration: - registration["clientSecretSettingName"] = None - if "clientSecretCertificateThumbprint" in registration: - registration["clientSecretCertificateThumbprint"] = None - if openid_issuer is not None: - registration["openIdIssuer"] = openid_issuer - if allowed_token_audiences is not None: - validation["allowedAudiences"] = allowed_token_audiences.split(",") - existing_auth["identityProviders"]["azureActiveDirectory"]["validation"] = validation - if (client_id is not None or client_secret is not None or - client_secret_setting_name is not None or issuer is not None or - client_secret_certificate_thumbprint is not None or - client_secret_certificate_san is not None or - client_secret_certificate_issuer is not None): - existing_auth["identityProviders"]["azureActiveDirectory"]["registration"] = registration - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize()["properties"] - return updated_auth_settings["identityProviders"]["azureActiveDirectory"] - - -def get_aad_settings(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - return {} - if "azureActiveDirectory" not in auth_settings["identityProviders"]: - return {} - return auth_settings["identityProviders"]["azureActiveDirectory"] - - -def get_facebook_settings(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings(): - return {} - if "facebook" not in auth_settings["identityProviders"]: - return {} - return auth_settings["identityProviders"]["facebook"] - - -def update_facebook_settings(cmd, client, resource_group_name, name, - app_id=None, app_secret_setting_name=None, - graph_api_version=None, scopes=None, app_secret=None, yes=False): - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if app_secret is not None and app_secret_setting_name is not None: - raise ArgumentUsageError('Usage Error: --app-secret and --app-secret-setting-name cannot both be configured ' - 'to non empty strings') - - if app_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth = {} - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - registration = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "facebook" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["facebook"] = {} - if app_id is not None or app_secret is not None or app_secret_setting_name is not None: - if "registration" not in existing_auth["identityProviders"]["facebook"]: - existing_auth["identityProviders"]["facebook"]["registration"] = {} - registration = existing_auth["identityProviders"]["facebook"]["registration"] - if scopes is not None: - if "login" not in existing_auth["identityProviders"]["facebook"]: - existing_auth["identityProviders"]["facebook"]["login"] = {} - - if app_id is not None: - registration["appId"] = app_id - if app_secret_setting_name is not None: - registration["appSecretSettingName"] = app_secret_setting_name - if app_secret is not None: - registration["appSecretSettingName"] = FACEBOOK_SECRET_SETTING_NAME - set_secrets(cmd, name, resource_group_name, secrets=[f"{FACEBOOK_SECRET_SETTING_NAME}={app_secret}"], no_wait=True, disable_max_length=True) - if graph_api_version is not None: - existing_auth["identityProviders"]["facebook"]["graphApiVersion"] = graph_api_version - if scopes is not None: - existing_auth["identityProviders"]["facebook"]["login"]["scopes"] = scopes.split(",") - if app_id is not None or app_secret is not None or app_secret_setting_name is not None: - existing_auth["identityProviders"]["facebook"]["registration"] = registration - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize()["properties"] - return updated_auth_settings["identityProviders"]["facebook"] - - -def get_github_settings(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - return {} - if "gitHub" not in auth_settings["identityProviders"]: - return {} - return auth_settings["identityProviders"]["gitHub"] - - -def update_github_settings(cmd, client, resource_group_name, name, - client_id=None, client_secret_setting_name=None, - scopes=None, client_secret=None, yes=False): - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if client_secret is not None and client_secret_setting_name is not None: - raise ArgumentUsageError('Usage Error: --client-secret and --client-secret-setting-name cannot ' - 'both be configured to non empty strings') - - if client_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth = {} - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - registration = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "gitHub" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["gitHub"] = {} - if client_id is not None or client_secret is not None or client_secret_setting_name is not None: - if "registration" not in existing_auth["identityProviders"]["gitHub"]: - existing_auth["identityProviders"]["gitHub"]["registration"] = {} - registration = existing_auth["identityProviders"]["gitHub"]["registration"] - if scopes is not None: - if "login" not in existing_auth["identityProviders"]["gitHub"]: - existing_auth["identityProviders"]["gitHub"]["login"] = {} - - if client_id is not None: - registration["clientId"] = client_id - if client_secret_setting_name is not None: - registration["clientSecretSettingName"] = client_secret_setting_name - if client_secret is not None: - registration["clientSecretSettingName"] = GITHUB_SECRET_SETTING_NAME - set_secrets(cmd, name, resource_group_name, secrets=[f"{GITHUB_SECRET_SETTING_NAME}={client_secret}"], no_wait=True, disable_max_length=True) - if scopes is not None: - existing_auth["identityProviders"]["gitHub"]["login"]["scopes"] = scopes.split(",") - if client_id is not None or client_secret is not None or client_secret_setting_name is not None: - existing_auth["identityProviders"]["gitHub"]["registration"] = registration - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize()["properties"] - return updated_auth_settings["identityProviders"]["gitHub"] - - -def get_google_settings(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - return {} - if "google" not in auth_settings["identityProviders"]: - return {} - return auth_settings["identityProviders"]["google"] - - -def update_google_settings(cmd, client, resource_group_name, name, - client_id=None, client_secret_setting_name=None, - scopes=None, allowed_token_audiences=None, client_secret=None, yes=False): - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if client_secret is not None and client_secret_setting_name is not None: - raise ArgumentUsageError('Usage Error: --client-secret and --client-secret-setting-name cannot ' - 'both be configured to non empty strings') - - if client_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth = {} - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - registration = {} - validation = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "google" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["google"] = {} - if client_id is not None or client_secret is not None or client_secret_setting_name is not None: - if "registration" not in existing_auth["identityProviders"]["google"]: - existing_auth["identityProviders"]["google"]["registration"] = {} - registration = existing_auth["identityProviders"]["google"]["registration"] - if scopes is not None: - if "login" not in existing_auth["identityProviders"]["google"]: - existing_auth["identityProviders"]["google"]["login"] = {} - if allowed_token_audiences is not None: - if "validation" not in existing_auth["identityProviders"]["google"]: - existing_auth["identityProviders"]["google"]["validation"] = {} - - if client_id is not None: - registration["clientId"] = client_id - if client_secret_setting_name is not None: - registration["clientSecretSettingName"] = client_secret_setting_name - if client_secret is not None: - registration["clientSecretSettingName"] = GOOGLE_SECRET_SETTING_NAME - set_secrets(cmd, name, resource_group_name, secrets=[f"{GOOGLE_SECRET_SETTING_NAME}={client_secret}"], no_wait=True, disable_max_length=True) - if scopes is not None: - existing_auth["identityProviders"]["google"]["login"]["scopes"] = scopes.split(",") - if allowed_token_audiences is not None: - validation["allowedAudiences"] = allowed_token_audiences.split(",") - existing_auth["identityProviders"]["google"]["validation"] = validation - if client_id is not None or client_secret is not None or client_secret_setting_name is not None: - existing_auth["identityProviders"]["google"]["registration"] = registration - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize()["properties"] - return updated_auth_settings["identityProviders"]["google"] - - -def get_twitter_settings(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - return {} - if "twitter" not in auth_settings["identityProviders"]: - return {} - return auth_settings["identityProviders"]["twitter"] - - -def update_twitter_settings(cmd, client, resource_group_name, name, - consumer_key=None, consumer_secret_setting_name=None, - consumer_secret=None, yes=False): - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if consumer_secret is not None and consumer_secret_setting_name is not None: - raise ArgumentUsageError('Usage Error: --consumer-secret and --consumer-secret-setting-name cannot ' - 'both be configured to non empty strings') - - if consumer_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth = {} - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - registration = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "twitter" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["twitter"] = {} - if consumer_key is not None or consumer_secret is not None or consumer_secret_setting_name is not None: - if "registration" not in existing_auth["identityProviders"]["twitter"]: - existing_auth["identityProviders"]["twitter"]["registration"] = {} - registration = existing_auth["identityProviders"]["twitter"]["registration"] - - if consumer_key is not None: - registration["consumerKey"] = consumer_key - if consumer_secret_setting_name is not None: - registration["consumerSecretSettingName"] = consumer_secret_setting_name - if consumer_secret is not None: - registration["consumerSecretSettingName"] = TWITTER_SECRET_SETTING_NAME - set_secrets(cmd, name, resource_group_name, secrets=[f"{TWITTER_SECRET_SETTING_NAME}={consumer_secret}"], no_wait=True, disable_max_length=True) - if consumer_key is not None or consumer_secret is not None or consumer_secret_setting_name is not None: - existing_auth["identityProviders"]["twitter"]["registration"] = registration - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize()["properties"] - return updated_auth_settings["identityProviders"]["twitter"] - - -def get_apple_settings(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - return {} - if "apple" not in auth_settings["identityProviders"]: - return {} - return auth_settings["identityProviders"]["apple"] - - -def update_apple_settings(cmd, client, resource_group_name, name, - client_id=None, client_secret_setting_name=None, - scopes=None, client_secret=None, yes=False): - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if client_secret is not None and client_secret_setting_name is not None: - raise ArgumentUsageError('Usage Error: --client-secret and --client-secret-setting-name ' - 'cannot both be configured to non empty strings') - - if client_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth = {} - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - registration = {} - if "identityProviders" not in existing_auth: - existing_auth["identityProviders"] = {} - if "apple" not in existing_auth["identityProviders"]: - existing_auth["identityProviders"]["apple"] = {} - if client_id is not None or client_secret is not None or client_secret_setting_name is not None: - if "registration" not in existing_auth["identityProviders"]["apple"]: - existing_auth["identityProviders"]["apple"]["registration"] = {} - registration = existing_auth["identityProviders"]["apple"]["registration"] - if scopes is not None: - if "login" not in existing_auth["identityProviders"]["apple"]: - existing_auth["identityProviders"]["apple"]["login"] = {} - - if client_id is not None: - registration["clientId"] = client_id - if client_secret_setting_name is not None: - registration["clientSecretSettingName"] = client_secret_setting_name - if client_secret is not None: - registration["clientSecretSettingName"] = APPLE_SECRET_SETTING_NAME - set_secrets(cmd, name, resource_group_name, secrets=[f"{APPLE_SECRET_SETTING_NAME}={client_secret}"], no_wait=True, disable_max_length=True) - if scopes is not None: - existing_auth["identityProviders"]["apple"]["login"]["scopes"] = scopes.split(",") - if client_id is not None or client_secret is not None or client_secret_setting_name is not None: - existing_auth["identityProviders"]["apple"]["registration"] = registration - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize()["properties"] - return updated_auth_settings["identityProviders"]["apple"] - - -def get_openid_connect_provider_settings(client, resource_group_name, name, provider_name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - if "customOpenIdConnectProviders" not in auth_settings["identityProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - if provider_name not in auth_settings["identityProviders"]["customOpenIdConnectProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - return auth_settings["identityProviders"]["customOpenIdConnectProviders"][provider_name] - - -def add_openid_connect_provider_settings(cmd, client, resource_group_name, name, provider_name, - client_id=None, client_secret_setting_name=None, - openid_configuration=None, scopes=None, - client_secret=None, yes=False): - from ._utils import get_oidc_client_setting_app_setting_name - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if client_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - auth_settings = {} - auth_settings["platform"] = {} - auth_settings["platform"]["enabled"] = True - auth_settings["globalValidation"] = {} - auth_settings["login"] = {} - - if "identityProviders" not in auth_settings: - auth_settings["identityProviders"] = {} - if "customOpenIdConnectProviders" not in auth_settings["identityProviders"]: - auth_settings["identityProviders"]["customOpenIdConnectProviders"] = {} - if provider_name in auth_settings["identityProviders"]["customOpenIdConnectProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider has already been ' - 'configured: ' + provider_name + '. Please use `az containerapp auth oidc update` to ' - 'update the provider.') - - final_client_secret_setting_name = client_secret_setting_name - if client_secret is not None: - final_client_secret_setting_name = get_oidc_client_setting_app_setting_name(provider_name) - set_secrets(cmd, name, resource_group_name, secrets=[f"{final_client_secret_setting_name}={client_secret}"], no_wait=True, disable_max_length=True) - - auth_settings["identityProviders"]["customOpenIdConnectProviders"][provider_name] = { - "registration": { - "clientId": client_id, - "clientCredential": { - "clientSecretSettingName": final_client_secret_setting_name - }, - "openIdConnectConfiguration": { - "wellKnownOpenIdConfiguration": openid_configuration - } - } - } - login = {} - if scopes is not None: - login["scopes"] = scopes.split(',') - else: - login["scopes"] = ["openid"] - - auth_settings["identityProviders"]["customOpenIdConnectProviders"][provider_name]["login"] = login - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=auth_settings).serialize()["properties"] - return updated_auth_settings["identityProviders"]["customOpenIdConnectProviders"][provider_name] - - -def update_openid_connect_provider_settings(cmd, client, resource_group_name, name, provider_name, - client_id=None, client_secret_setting_name=None, - openid_configuration=None, scopes=None, - client_secret=None, yes=False): - from ._utils import get_oidc_client_setting_app_setting_name - try: - show_ingress(cmd, name, resource_group_name) - except Exception as e: - raise ValidationError("Authentication requires ingress to be enabled for your containerapp.") from e - - if client_secret is not None and not yes: - msg = 'Configuring --client-secret will add a secret to the containerapp. Are you sure you want to continue?' - if not prompt_y_n(msg, default="n"): - raise ArgumentUsageError('Usage Error: --client-secret cannot be used without agreeing to add secret ' - 'to the containerapp.') - - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - auth_settings = {} - auth_settings["platform"] = {} - auth_settings["platform"]["enabled"] = True - auth_settings["globalValidation"] = {} - auth_settings["login"] = {} - - if "identityProviders" not in auth_settings: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - if "customOpenIdConnectProviders" not in auth_settings["identityProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - if provider_name not in auth_settings["identityProviders"]["customOpenIdConnectProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - - custom_open_id_connect_providers = auth_settings["identityProviders"]["customOpenIdConnectProviders"] - registration = {} - if client_id is not None or client_secret_setting_name is not None or openid_configuration is not None: - if "registration" not in custom_open_id_connect_providers[provider_name]: - custom_open_id_connect_providers[provider_name]["registration"] = {} - registration = custom_open_id_connect_providers[provider_name]["registration"] - - if client_secret_setting_name is not None or client_secret is not None: - if "clientCredential" not in custom_open_id_connect_providers[provider_name]["registration"]: - custom_open_id_connect_providers[provider_name]["registration"]["clientCredential"] = {} - - if openid_configuration is not None: - if "openIdConnectConfiguration" not in custom_open_id_connect_providers[provider_name]["registration"]: - custom_open_id_connect_providers[provider_name]["registration"]["openIdConnectConfiguration"] = {} - - if scopes is not None: - if "login" not in auth_settings["identityProviders"]["customOpenIdConnectProviders"][provider_name]: - custom_open_id_connect_providers[provider_name]["login"] = {} - - if client_id is not None: - registration["clientId"] = client_id - if client_secret_setting_name is not None: - registration["clientCredential"]["clientSecretSettingName"] = client_secret_setting_name - if client_secret is not None: - final_client_secret_setting_name = get_oidc_client_setting_app_setting_name(provider_name) - registration["clientSecretSettingName"] = final_client_secret_setting_name - set_secrets(cmd, name, resource_group_name, secrets=[f"{final_client_secret_setting_name}={client_secret}"], no_wait=True, disable_max_length=True) - if openid_configuration is not None: - registration["openIdConnectConfiguration"]["wellKnownOpenIdConfiguration"] = openid_configuration - if scopes is not None: - custom_open_id_connect_providers[provider_name]["login"]["scopes"] = scopes.split(",") - if client_id is not None or client_secret_setting_name is not None or openid_configuration is not None: - custom_open_id_connect_providers[provider_name]["registration"] = registration - auth_settings["identityProviders"]["customOpenIdConnectProviders"] = custom_open_id_connect_providers - - updated_auth_settings = client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=auth_settings).serialize()["properties"] - return updated_auth_settings["identityProviders"]["customOpenIdConnectProviders"][provider_name] - - -def remove_openid_connect_provider_settings(client, resource_group_name, name, provider_name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - if "identityProviders" not in auth_settings: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - if "customOpenIdConnectProviders" not in auth_settings["identityProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - if provider_name not in auth_settings["identityProviders"]["customOpenIdConnectProviders"]: - raise ArgumentUsageError('Usage Error: The following custom OpenID Connect provider ' - 'has not been configured: ' + provider_name) - auth_settings["identityProviders"]["customOpenIdConnectProviders"].pop(provider_name, None) - client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=auth_settings).serialize() - return {} - - -def update_auth_config(client, resource_group_name, name, set_string=None, enabled=None, - runtime_version=None, config_file_path=None, unauthenticated_client_action=None, - redirect_provider=None, enable_token_store=None, require_https=None, - proxy_convention=None, proxy_custom_host_header=None, - proxy_custom_proto_header=None, excluded_paths=None): - from ._utils import set_field_in_auth_settings, update_http_settings_in_auth_settings - existing_auth = {} - try: - existing_auth = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = True - existing_auth["globalValidation"] = {} - existing_auth["login"] = {} - - existing_auth = set_field_in_auth_settings(existing_auth, set_string) - - if enabled is not None: - if "platform" not in existing_auth: - existing_auth["platform"] = {} - existing_auth["platform"]["enabled"] = enabled - - if runtime_version is not None: - if "platform" not in existing_auth: - existing_auth["platform"] = {} - existing_auth["platform"]["runtimeVersion"] = runtime_version - - if config_file_path is not None: - if "platform" not in existing_auth: - existing_auth["platform"] = {} - existing_auth["platform"]["configFilePath"] = config_file_path - - if unauthenticated_client_action is not None: - if "globalValidation" not in existing_auth: - existing_auth["globalValidation"] = {} - existing_auth["globalValidation"]["unauthenticatedClientAction"] = unauthenticated_client_action - - if redirect_provider is not None: - if "globalValidation" not in existing_auth: - existing_auth["globalValidation"] = {} - existing_auth["globalValidation"]["redirectToProvider"] = redirect_provider - - if enable_token_store is not None: - if "login" not in existing_auth: - existing_auth["login"] = {} - if "tokenStore" not in existing_auth["login"]: - existing_auth["login"]["tokenStore"] = {} - existing_auth["login"]["tokenStore"]["enabled"] = enable_token_store - - if excluded_paths is not None: - if "globalValidation" not in existing_auth: - existing_auth["globalValidation"] = {} - excluded_paths_list_string = excluded_paths[1:-1] - existing_auth["globalValidation"]["excludedPaths"] = excluded_paths_list_string.split(",") - - existing_auth = update_http_settings_in_auth_settings(existing_auth, require_https, - proxy_convention, proxy_custom_host_header, - proxy_custom_proto_header) - - return client.create_or_update(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current", auth_config_envelope=existing_auth).serialize() - - -def show_auth_config(client, resource_group_name, name): - auth_settings = {} - try: - auth_settings = client.get(resource_group_name=resource_group_name, container_app_name=name, auth_config_name="current").serialize()["properties"] - except: - pass - return auth_settings diff --git a/src/containerapp/setup.py b/src/containerapp/setup.py index c5926c9c0ec..1d484ae339c 100644 --- a/src/containerapp/setup.py +++ b/src/containerapp/setup.py @@ -38,8 +38,7 @@ # TODO: Add any additional SDK dependencies here DEPENDENCIES = [ - 'azure-cli-core', - 'azure-mgmt-appcontainers==1.0.0' + 'azure-cli-core' ] with open('README.rst', 'r', encoding='utf-8') as f: