diff --git a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py index b4fb6262376..3a5236f70dd 100644 --- a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py +++ b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py @@ -8,8 +8,13 @@ from json import loads import requests + +from ._utils import get_registry_by_name +from .credential import acr_credential_show + from azure.cli.core._profile import Profile from azure.cli.core._util import CLIError +from azure.cli.core.prompting import prompt, prompt_pass, NoTTYException def _get_login_token(login_server, only_refresh_token=True, repository=None): '''Obtains refresh and access tokens for an AAD-enabled registry. @@ -101,12 +106,51 @@ def get_login_access_token(login_server, repository=None): _, access_token = _get_login_token(login_server, only_refresh_token, repository) return access_token -def docker_login_to_registry(login_server): +def docker_login_to_registry(registry_name, username=None, password=None): '''Logs in the Docker client to a registry. - :param str login_server: The registry login server URL to log in to + :param str registry_name: The name of container registry + :param str username: The username used to log into the container registry + :param str password: The password used to log into the container registry ''' - refresh_token = _get_login_refresh_token(login_server) + registry, _ = get_registry_by_name(registry_name) + login_server = registry.login_server #pylint: disable=no-member + + # 1. if username was specified, verify that password was also specified + if username: + if not password: + try: + password = prompt_pass(msg='Password: ') + except NoTTYException: + raise CLIError('Please specify both username and password in non-interactive mode.') + + # 2. if we don't yet have credentials, attempt to get a refresh token + if not password: + try: + username = "00000000-0000-0000-0000-000000000000" + password = _get_login_refresh_token(login_server) + except: #pylint: disable=bare-except + pass + + # 3. if we still don't have credentials, attempt to get the admin credentials (if enabled) + if not password: + try: + cred = acr_credential_show(registry_name) + username = cred.username + password = cred.password + except: #pylint: disable=bare-except + pass + + # 4. if we still don't have credentials, prompt the user + if not password: + try: + username = prompt('Username: ') + password = prompt_pass(msg='Password: ') + except NoTTYException: + raise CLIError( + 'Unable to authenticate using admin login credentials or admin is not enabled. ' + + 'Please specify both username and password in non-interactive mode.') + call(["docker", "login", - "--username", "00000000-0000-0000-0000-000000000000", - "--password", refresh_token, + "--username", username, + "--password", password, login_server]) diff --git a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_utils.py b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_utils.py index 0bc115fe743..908794fa2a1 100644 --- a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_utils.py +++ b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_utils.py @@ -97,63 +97,6 @@ def get_access_key_by_storage_account_name(storage_account_name, resource_group_ return client.list_keys(resource_group_name, storage_account_name).keys[0].value #pylint: disable=no-member -def docker_login_to_registry(login_server): - '''Logs in the Docker client to a registry. - :param str login_server: The registry login server URL to log in to - ''' - profile = Profile() - _, _, tenant = profile.get_login_credentials() - refresh = profile.get_refresh_credentials() - base_endpoint = 'https://' + login_server.rstrip('/') - - challenge = requests.get(base_endpoint + '/v2/') - if challenge.status_code not in [401] or 'WWW-Authenticate' not in challenge.headers: - raise CLIError('Registry did not issue a challenge.') - - authenticate = challenge.headers['WWW-Authenticate'] - - tokens = authenticate.split(' ', 2) - if len(tokens) < 2 or tokens[0].lower() != 'bearer': - raise CLIError('Registry does not support AAD login.') - - params = {y[0]: y[1].strip('"') for y in - (x.strip().split('=', 2) for x in tokens[1].split(','))} - if 'realm' not in params or 'service' not in params: - raise CLIError('Registry does not support AAD login.') - - authurl = urlparse(params['realm']) - authhost = urlunparse((authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) - - headers = {'Content-Type': 'application/x-www-form-urlencoded'} - if isinstance(refresh, str): - content = { - 'service': params['service'], - 'user_type': 'user', - 'tenant': tenant, - 'refresh_token': refresh - } - else: - content = { - 'service': params['service'], - 'user_type': 'spn', - 'tenant': tenant, - 'username': refresh[1], - 'password': refresh[2] - } - - response = requests.post(authhost, urlencode(content), headers=headers) - - if response.status_code not in [200]: - raise CLIError( - "Access to registry was denied. Response code: {}".format(response.status_code)) - - refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] - - call(["docker", "login", - "--username", "00000000-0000-0000-0000-000000000000", - "--password", refresh_token, - login_server]) - def arm_deploy_template(resource_group_name, registry_name, location, diff --git a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py index 8884a609184..d16d43c6807 100644 --- a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py +++ b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py @@ -16,8 +16,7 @@ get_access_key_by_storage_account_name, get_resource_group_name_by_registry_name, arm_deploy_template, - random_storage_account_name, - get_registry_by_name + random_storage_account_name ) from ._docker_utils import ( docker_login_to_registry @@ -163,10 +162,10 @@ def acr_update_set(client, return client.update(resource_group_name, registry_name, parameters) -def acr_login(registry_name): +def acr_login(registry_name, username=None, password=None): '''Login to a container registry through Docker. :param str registry_name: The name of container registry + :param str username: The username used to log into the container registry + :param str password: The password used to log into the container registry ''' - registry, _ = get_registry_by_name(registry_name) - login_server = registry.login_server #pylint: disable=no-member - docker_login_to_registry(login_server) + docker_login_to_registry(registry_name, username, password)