Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACR] Add commands for Conditional Access policy (2022-02-01-preview API) #23323

Merged
merged 18 commits into from
Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,12 @@ def default_api_version(self):
'role_definitions': '2018-01-01-preview',
'provider_operations_metadata': '2018-01-01-preview'
}),
ResourceType.MGMT_CONTAINERREGISTRY: SDKProfile('2021-08-01-preview', {
ResourceType.MGMT_CONTAINERREGISTRY: SDKProfile('2022-02-01-preview', {
'agent_pools': '2019-06-01-preview',
'tasks': '2019-06-01-preview',
'task_runs': '2019-06-01-preview',
'runs': '2019-06-01-preview',
'network_rule': '2021-08-01-preview'
}),
# The order does make things different.
# Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
VERSION_2019_06_01_PREVIEW = "2019-06-01-preview"
VERSION_2020_11_01_PREVIEW = "2020-11-01-preview"
VERSION_2021_08_01_PREVIEW = "2021-08-01-preview"
VERSION_2022_02_01_PREVIEW = "2022-02-01-preview"


def get_acr_service_client(cli_ctx, api_version=None):
Expand All @@ -18,7 +19,11 @@ def get_acr_service_client(cli_ctx, api_version=None):


def cf_acr_registries(cli_ctx, *_):
return get_acr_service_client(cli_ctx).registries
return get_acr_service_client(cli_ctx, VERSION_2022_02_01_PREVIEW).registries


def cf_acr_network_rules(cli_ctx, *_):
return get_acr_service_client(cli_ctx, api_version=VERSION_2021_08_01_PREVIEW).registries


def cf_acr_registries_tasks(cli_ctx, *_):
Expand Down
2 changes: 2 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acr/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

ACR_RUN_DEFAULT_TIMEOUT_IN_SEC = 60 * 60 # 60 minutes

ACR_AUDIENCE_RESOURCE_NAME = "containerregistry"

ALLOWED_TASK_FILE_TYPES = ('.yaml', '.yml', '.toml', '.json', '.sh', '.bash', '.zsh', '.ps1',
'.ps', '.cmd', '.bat', '.ts', '.js', '.php', '.py', '.rb', '.lua')

Expand Down
34 changes: 28 additions & 6 deletions src/azure-cli/azure/cli/command_modules/acr/_docker_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@

from ._client_factory import cf_acr_registries
from ._constants import get_managed_sku
from ._constants import ACR_AUDIENCE_RESOURCE_NAME
from ._utils import get_registry_by_name, ResourceNotFound
from .policy import acr_config_authentication_as_arm_show
from ._format import add_timestamp


Expand Down Expand Up @@ -123,16 +125,23 @@ def _get_aad_token_after_challenge(cli_ctx,
repository,
artifact_repository,
permission,
is_diagnostics_context):
is_diagnostics_context,
use_acr_audience):
authurl = urlparse(token_params['realm'])
authhost = urlunparse((authurl[0], authurl[1], '/oauth2/exchange', '', '', ''))

from azure.cli.core._profile import Profile
profile = Profile(cli_ctx=cli_ctx)

scope = None
if use_acr_audience:
logger.debug("Using ACR audience token for authentication")
scope = "https://{}.azure.net".format(ACR_AUDIENCE_RESOURCE_NAME)

# this might be a cross tenant scenario, so pass subscription to get_raw_token
subscription = get_subscription_id(cli_ctx)
creds, _, tenant = profile.get_raw_token(subscription=subscription)
creds, _, tenant = profile.get_raw_token(subscription=subscription,
resource=scope)

headers = {'Content-Type': 'application/x-www-form-urlencoded'}
content = {
Expand Down Expand Up @@ -194,7 +203,8 @@ def _get_aad_token(cli_ctx,
repository=None,
artifact_repository=None,
permission=None,
is_diagnostics_context=False):
is_diagnostics_context=False,
use_acr_audience=False):
"""Obtains refresh and access tokens for an AAD-enabled registry.
:param str login_server: The registry login server URL to log in to
:param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens
Expand All @@ -219,7 +229,8 @@ def _get_aad_token(cli_ctx,
repository,
artifact_repository,
permission,
is_diagnostics_context)
is_diagnostics_context,
use_acr_audience)


def _get_token_with_username_and_password(login_server,
Expand Down Expand Up @@ -374,8 +385,19 @@ def _get_credentials(cmd, # pylint: disable=too-many-statements
if not registry or registry.sku.name in get_managed_sku(cmd):
logger.info("Attempting to retrieve AAD refresh token...")
try:
return login_server, EMPTY_GUID, _get_aad_token(
cli_ctx, login_server, only_refresh_token, repository, artifact_repository, permission)
use_acr_audience = False

if registry:
aad_auth_policy = acr_config_authentication_as_arm_show(cmd, registry_name, resource_group_name)
use_acr_audience = (aad_auth_policy and aad_auth_policy.status == 'disabled')

return login_server, EMPTY_GUID, _get_aad_token(cli_ctx,
login_server,
only_refresh_token,
repository,
artifact_repository,
permission,
use_acr_audience=use_acr_audience)
except CLIError as e:
logger.warning("%s: %s", AAD_TOKEN_BASE_ERROR_MESSAGE, str(e))

Expand Down
26 changes: 26 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acr/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@
short-summary: Manage content-trust policy for Azure Container Registries.
"""

helps['acr config authentication-as-arm'] = """
type: group
short-summary: Manage 'Azure AD authenticate as ARM' policy for Azure Container Registries.
"""

helps['acr config authentication-as-arm show'] = """
type: command
short-summary: Show the configured 'Azure AD authenticate as ARM' policy for an Azure Container Registry.
examples:
- name: Show the configured 'Azure AD authenticate as ARM' policy for an Azure Container Registry
text: >
az acr config authentication-as-arm show -r MyRegistry
"""

helps['acr config authentication-as-arm update'] = """
type: command
short-summary: Update 'Azure AD authenticate as ARM' policy for an Azure Container Registry.
examples:
- name: Disable 'Azure AD authenticate as ARM' policy for an Azure Container Registry, so only ACR audienced tokens can be used for authentication
text: >
az acr config authentication-as-arm update -r MyRegistry --status Disabled
- name: Enable 'Azure AD authenticate as ARM' policy for an Azure Container Registry, it will allow both ACR and ARM audienced tokens to be used for authentication
text: >
az acr config authentication-as-arm update -r MyRegistry --status Enabled
"""

helps['acr config content-trust show'] = """
type: command
short-summary: Show the configured content-trust policy for an Azure Container Registry.
Expand Down
4 changes: 4 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acr/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
c.argument('force', help='Overwrite the existing tag of the image to be imported.', action='store_true')
c.argument('no_wait', help="Do not wait for the import to complete and return immediately after queuing the import.", action='store_true')

with self.argument_context('acr config authentication-as-arm') as c:
c.argument('registry_name', options_list=['--registry', '-r', c.deprecate(target='-n', redirect='-r', hide=True), c.deprecate(target='--name', redirect='--registry', hide=True)])
c.argument('status', help="Indicate whether authentication-as-arm is enabled.", arg_type=get_enum_type(PolicyStatus))

with self.argument_context('acr config content-trust') as c:
c.argument('registry_name', options_list=['--registry', '-r', c.deprecate(target='-n', redirect='-r', hide=True), c.deprecate(target='--name', redirect='--registry', hide=True)])
c.argument('status', help="Indicates whether content-trust is enabled.", arg_type=get_enum_type(PolicyStatus))
Expand Down
4 changes: 4 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acr/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ def _metadata_deprecate_message(self):
g.show_command('show', 'acr_config_retention_show')
g.command('update', 'acr_config_retention_update')

with self.command_group('acr config authentication-as-arm', acr_policy_util, is_preview=True) as g:
g.show_command('show', 'acr_config_authentication_as_arm_show')
g.command('update', 'acr_config_authentication_as_arm_update')

def _helm_deprecate_message(self):
msg = "This {} has been deprecated and will be removed in future release.".format(self.object_type)
msg += " Use '{}' instead.".format(self.redirect)
Expand Down
61 changes: 56 additions & 5 deletions src/azure-cli/azure/cli/command_modules/acr/network_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,61 @@
# --------------------------------------------------------------------------------------------

from knack.util import CLIError

from ._utils import validate_premium_registry
from ._client_factory import cf_acr_network_rules
from typing import Optional, Union
import msrest.serialization


class VirtualNetworkRule(msrest.serialization.Model):
"""Virtual network rule.
All required parameters must be populated in order to send to Azure.
:ivar action: The action of virtual network rule. Possible values include: "Allow".
:vartype action: str or ~azure.mgmt.containerregistry.v2021_08_01_preview.models.Action
:ivar virtual_network_resource_id: Required. Resource ID of a subnet, for example:
'/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/
providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}.'
:vartype virtual_network_resource_id: str
"""

_validation = {
'virtual_network_resource_id': {'required': True},
}

_attribute_map = {
'action': {'key': 'action', 'type': 'str'},
'virtual_network_resource_id': {'key': 'id', 'type': 'str'},
}

def __init__(
self,
*,
virtual_network_resource_id: str,
action: Optional[Union[str, "Action"]] = None, # noqa: F821
**kwargs
):
"""
:keyword action: The action of virtual network rule. Possible values include: "Allow".
:paramtype action: str or ~azure.mgmt.containerregistry.v2021_08_01_preview.models.Action
:keyword virtual_network_resource_id: Required. Resource ID of a subnet, for example:
'/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/
providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}.'
:paramtype virtual_network_resource_id: str
"""
super(VirtualNetworkRule, self).__init__(**kwargs)
self.action = action
self.virtual_network_resource_id = virtual_network_resource_id


NETWORK_RULE_NOT_SUPPORTED = 'Network rules are only supported for managed registries in Premium SKU.'


def acr_network_rule_list(cmd, registry_name, resource_group_name=None):
registry, _ = validate_premium_registry(
_, resource_group_name = validate_premium_registry(
cmd, registry_name, resource_group_name, NETWORK_RULE_NOT_SUPPORTED)

client = cf_acr_network_rules(cmd.cli_ctx)
registry = client.get(resource_group_name, registry_name)
rules = registry.network_rule_set
delattr(rules, 'default_action')
return rules
Expand All @@ -26,14 +71,17 @@ def acr_network_rule_add(cmd,
vnet_name=None,
ip_address=None,
resource_group_name=None):
registry, resource_group_name = validate_premium_registry(
_, resource_group_name = validate_premium_registry(
cmd, registry_name, resource_group_name, NETWORK_RULE_NOT_SUPPORTED)

client = cf_acr_network_rules(cmd.cli_ctx)
registry = client.get(resource_group_name, registry_name)

rules = registry.network_rule_set

if subnet or vnet_name:
rules.virtual_network_rules = rules.virtual_network_rules if rules.virtual_network_rules else []
subnet_id = _validate_subnet(cmd.cli_ctx, subnet, vnet_name, resource_group_name)
VirtualNetworkRule = cmd.get_models('VirtualNetworkRule')
rules.virtual_network_rules.append(VirtualNetworkRule(virtual_network_resource_id=subnet_id))
if ip_address:
rules.ip_rules = rules.ip_rules if rules.ip_rules else []
Expand All @@ -52,8 +100,11 @@ def acr_network_rule_remove(cmd,
vnet_name=None,
ip_address=None,
resource_group_name=None):
registry, resource_group_name = validate_premium_registry(
_, resource_group_name = validate_premium_registry(
cmd, registry_name, resource_group_name, NETWORK_RULE_NOT_SUPPORTED)

client = cf_acr_network_rules(cmd.cli_ctx)
registry = client.get(resource_group_name, registry_name)
rules = registry.network_rule_set

if subnet or vnet_name:
Expand Down
45 changes: 44 additions & 1 deletion src/azure-cli/azure/cli/command_modules/acr/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from enum import Enum
from azure.cli.core.commands import LongRunningOperation
from ._utils import validate_premium_registry
from ._utils import validate_premium_registry, get_registry_by_name
from knack.util import CLIError

POLICIES_NOT_SUPPORTED = 'Policies are only supported for managed registries in Premium SKU.'

Expand Down Expand Up @@ -90,3 +91,45 @@ def acr_config_retention_update(cmd,
client.begin_update(resource_group_name, registry_name, parameters)
)
return updated_policies.policies.retention_policy


def acr_config_authentication_as_arm_show(cmd,
registry_name,
resource_group_name=None):

AzureADAuthenticationAsArmPolicy = cmd.get_models('AzureADAuthenticationAsArmPolicy')

try:
registry, _ = get_registry_by_name(cmd.cli_ctx, registry_name, resource_group_name)
except CLIError:
return AzureADAuthenticationAsArmPolicy()

AzureADAuthenticationAsArmPolicy = cmd.get_models('AzureADAuthenticationAsArmPolicy')
policies = registry.policies
aadAuth_policy = policies.azure_ad_authentication_as_arm_policy if policies else AzureADAuthenticationAsArmPolicy()

return aadAuth_policy


def acr_config_authentication_as_arm_update(cmd,
client,
registry_name,
status=None,
resource_group_name=None):
registry, resource_group_name = get_registry_by_name(cmd.cli_ctx, registry_name, resource_group_name)
policies = registry.policies
if status:
Policy = cmd.get_models('Policy')
policies = policies if policies else Policy()
AzureADAuthenticationAsArmPolicy = cmd.get_models('AzureADAuthenticationAsArmPolicy')
policies.azure_ad_authentication_as_arm_policy = policies.azure_ad_authentication_as_arm_policy \
if policies.azure_ad_authentication_as_arm_policy else AzureADAuthenticationAsArmPolicy()
policies.azure_ad_authentication_as_arm_policy.status = status

RegistryUpdateParameters = cmd.get_models('RegistryUpdateParameters')
parameters = RegistryUpdateParameters(policies=policies)
updated_policies = LongRunningOperation(cmd.cli_ctx)(
client.begin_update(resource_group_name, registry_name, parameters)
)

return updated_policies.policies.azure_ad_authentication_as_arm_policy
Loading