azure-devops/azext_devops/dev/team/credentials.py (86 lines of code) (raw):
# coding=utf-8
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from __future__ import print_function
import sys
from knack.log import get_logger
from knack.prompting import NoTTYException, prompt_pass
from knack.util import CLIError
from msrest.authentication import BasicAuthentication
from azext_devops.dev.common._credentials import clear_credential, set_credential
from azext_devops.dev.common.services import _get_connection, get_base_url
logger = get_logger(__name__)
def credential_set(organization=None):
"""Set the credential (PAT) to use for a particular organization.
Refer https://aka.ms/azure-devops-cli-auth for more information on providing PAT as input.
"""
token = _get_pat_token()
if organization is not None:
organization = get_base_url(organization)
logger.info("Creating connection with personal access token.")
_verify_token(organization=organization, token=token)
try:
set_credential(organization=organization, token=token)
except Exception as ex: # pylint: disable=bare-except
logger.warning("Unable to use secure credential store in this environment.")
logger.warning("Please refer to alternate methods at https://aka.ms/azure-devops-cli-auth")
logger.warning("using Environment variable")
logger.warning("or use 'az login'")
raise CLIError(ex)
_check_and_set_default_organization(organization)
def credential_clear(organization=None):
"""Clear the credential for all or a particular organization
:param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/.
If no organization is specified, all organizations will be logged out.
:type organization: str
"""
if organization is not None:
organization = get_base_url(organization)
clear_credential(organization)
if organization is not None:
print('The credential was successfully cleared.')
else:
print('Logged out of all Azure DevOps organizations.')
_check_and_clear_default_organization(organization)
def _verify_token(organization, token):
credentials = BasicAuthentication('', token)
connection = _get_connection(organization, credentials)
vstsDir = 'azext_devops.devops_sdk.'
location_client = connection.get_client(vstsDir + 'v5_0.location.location_client.LocationClient')
try:
connection_data = location_client.get_connection_data()
except Exception as ex2:
logger.debug(ex2, exc_info=True)
raise CLIError("Failed to authenticate using the supplied token.")
# An organization with public project enabled will not throw any exception for invalid token.
# Hence, handle anonymous user case here.
if connection_data.authenticated_user.id == _ANONYMOUS_USER_ID:
raise CLIError("Failed to authenticate using the supplied token.")
def _get_pat_token():
try:
token = prompt_pass('Token: ', confirm=False, help_string="The token (PAT) to authenticate with.")
while len(token) <= 1:
logger.warning('Please provide a PAT token.')
logger.warning('If you are using CTRL + V to paste the token, it won\'t work '
'on windows command prompt or powershell - '
'https://github.com/microsoft/knack/issues/160.')
logger.warning('Use right click or console menu to paste the token.')
token = prompt_pass('Token: ', confirm=False, help_string="The token (PAT) to authenticate with.")
except NoTTYException:
logger.info("Getting PAT token in non-interactive mode.")
token = sys.stdin.readline().rstrip()
return token
# Sets organization if the default is not set
def _check_and_set_default_organization(organization):
if organization is not None:
from azext_devops.dev.common.config import azdevops_config
from azext_devops.dev.common.const import DEFAULTS_SECTION, DEVOPS_ORGANIZATION_DEFAULT
from .configure import configure
current_org_default = None
if azdevops_config.has_option(DEFAULTS_SECTION, DEVOPS_ORGANIZATION_DEFAULT):
current_org_default = azdevops_config.get(DEFAULTS_SECTION, DEVOPS_ORGANIZATION_DEFAULT)
if current_org_default is None or current_org_default == '':
configure(defaults=['organization={}'.format(organization)])
logger.debug("Setting this organization as default. No default was set earlier.")
else:
logger.debug("Another organization is already set as default.")
# Clears organization if the default is set to same
def _check_and_clear_default_organization(organization):
if organization is not None:
from azext_devops.dev.common.config import azdevops_config
from azext_devops.dev.common.const import DEFAULTS_SECTION, DEVOPS_ORGANIZATION_DEFAULT
from .configure import configure
current_org_default = None
if azdevops_config.has_option(DEFAULTS_SECTION, DEVOPS_ORGANIZATION_DEFAULT):
current_org_default = azdevops_config.get(DEFAULTS_SECTION, DEVOPS_ORGANIZATION_DEFAULT)
if current_org_default == organization:
configure(defaults=["organization="])
logger.debug("Resetting default organization.")
else:
logger.debug("Default org not reset. Different organization is set as default.")
_ANONYMOUS_USER_ID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'