Skip to content

Commit

Permalink
{Service Connector}: AAD rebranding and fix sql connection (#7041)
Browse files Browse the repository at this point in the history
* prompt for updating sql user

* update version

* lint

* update

* update help

* update
  • Loading branch information
xfz11 authored Dec 1, 2023
1 parent 8c5be48 commit 3fdc841
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 36 deletions.
4 changes: 4 additions & 0 deletions src/serviceconnector-passwordless/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
Release History
===============
0.3.13
++++++
* AAD rebranding and make some improvements

0.3.12
++++++
* make some improvements and support slot.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
get_source_resource_name,
get_target_resource_name,
)
from ._utils import run_cli_cmd, get_local_ip, confirm_all_ip_allow
from ._utils import run_cli_cmd, get_local_ip, confirm_all_ip_allow, confirm_admin_set
logger = get_logger(__name__)

AUTHTYPES = {
Expand All @@ -42,7 +42,7 @@


# pylint: disable=line-too-long, consider-using-f-string, too-many-statements
# For db(mysqlFlex/psql/psqlFlex/sql) linker with auth type=systemAssignedIdentity, enable AAD auth and create db user on data plane
# For db(mysqlFlex/psql/psqlFlex/sql) linker with auth type=systemAssignedIdentity, enable Microsoft Entra auth and create db user on data plane
# For other linker, ignore the steps
def get_enable_mi_for_db_linker_func(yes=False):
def enable_mi_for_db_linker(cmd, source_id, target_id, auth_info, client_type, connection_name):
Expand Down Expand Up @@ -99,7 +99,7 @@ def enable_mi_for_db_linker(cmd, source_id, target_id, auth_info, client_type, c
except CLIInternalError as e:
if 'AADSTS530003' in e.error_msg:
logger.warning(
'Please ask your IT department for help to join this device to Azure Active Directory.')
'Please ask your IT department for help to join this device to Microsoft Entra ID.')
raise e
elif auth_info['auth_type'] == AUTHTYPES[AUTH_TYPE.UserIdentity]:
mi_client_id = auth_info.get('client_id')
Expand Down Expand Up @@ -132,15 +132,15 @@ def enable_mi_for_db_linker(cmd, source_id, target_id, auth_info, client_type, c
except CLIInternalError as e:
if 'AADSTS530003' in e.error_msg:
logger.warning(
'Please ask your IT department for help to join this device to Azure Active Directory.')
'Please ask your IT department for help to join this device to Microsoft Entra ID.')
raise e

# enable target aad authentication and set login user as db aad admin
# enable target Microsoft Entra authentication and set login user as db Microsoft Entra admin
target_handler.enable_target_aad_auth()
target_handler.set_user_admin(
user_object_id, mysql_identity_id=auth_info.get('mysql-identity-id'))

# create an aad user in db
# create an Microsoft Entra user in db
target_handler.create_aad_user()
return target_handler.get_auth_config(user_object_id)

Expand Down Expand Up @@ -274,23 +274,23 @@ def set_user_admin(self, user_object_id, **kwargs):
if not user_object_id:
if not admins:
e = ValidationError(
'No AAD admin found. Please set current user as AAD admin and try again.')
'No Microsoft Entra admin found. Please set current user as Microsoft Entra admin and try again.')
telemetry.set_exception(e, "Missing-Aad-Admin")
raise e
else:
logger.warning(
'Unable to check if current user is AAD admin. Please confirm current user as AAD admin manually.')
'Unable to check if current user is Microsoft Entra admin. Please confirm current user as Microsoft Entra admin manually.')
return
admin_info = next((ad for ad in admins if ad.get('sid') == user_object_id), None)
if admin_info:
self.admin_username = admin_info.get('login')
return

logger.warning('Set current user as DB Server AAD Administrators.')
# set user as AAD admin
logger.warning('Set current user as DB Server Microsoft Entra Administrators.')
# set user as Microsoft Entra admin
if mysql_identity_id is None:
e = ValidationError(
"Provide '{} mysql-identity-id=<user-assigned managed identity ID>' to update AAD authentication.".format(
"Provide '{} mysql-identity-id=<user-assigned managed identity ID>' to update Microsoft Entra authentication.".format(
self.get_auth_flag()))
telemetry.set_exception(e, "Missing-Mysql-Umi")
raise e
Expand Down Expand Up @@ -388,7 +388,7 @@ def create_aad_user_in_mysql(self, connection_kwargs, query_list):
try:
connection = pymysql.connect(**connection_kwargs)
logger.warning(
"Adding new AAD user %s to database...", self.aad_username)
"Adding new Microsoft Entra user %s to database...", self.aad_username)
cursor = connection.cursor()
for q in query_list:
if q:
Expand Down Expand Up @@ -452,7 +452,7 @@ def __init__(self, cmd, target_id, target_type, auth_info, connection_name, skip
def check_db_existence(self):
try:
db_info = run_cli_cmd(
'az sql db show --ids {}'.format(self.target_id))
'az sql db show --ids "{}"'.format(self.target_id))
if db_info is None:
e = ResourceNotFoundError(
"No database found with name {}".format(self.dbname))
Expand All @@ -465,24 +465,28 @@ def check_db_existence(self):
def set_user_admin(self, user_object_id, **kwargs):
# pylint: disable=not-an-iterable
admins = run_cli_cmd(
'az sql server ad-admin list --ids {}'.format(self.target_id))
'az sql server ad-admin list --ids "{}"'.format(self.target_id))
if not user_object_id:
if not admins:
e = ValidationError(
'No AAD admin found. Please set current user as AAD admin and try again.')
'No Microsoft Entra admin found. Please set current user as Microsoft Entra admin and try again.')
telemetry.set_exception(e, "Missing-Aad-Admin")
raise e
else:
logger.warning(
'Unable to check if current user is AAD admin. Please confirm current user as AAD admin manually.')
'Unable to check if current user is Microsoft Entra admin. Please confirm current user as Microsoft Entra admin manually.')
return
admin_info = next((ad for ad in admins if ad.get('sid') == user_object_id), None)
if not admin_info:
logger.warning('Setting current user as database server AAD admin:'
' user=%s object id=%s', self.login_username, user_object_id)
admin_info = run_cli_cmd('az sql server ad-admin create -g {} --server-name {} --display-name {} --object-id {} --subscription {}'.format(
self.resource_group, self.server, self.login_username, user_object_id, self.subscription))
self.admin_username = admin_info.get('login', self.login_username)
set_admin = True
if not self.skip_prompt:
set_admin = confirm_admin_set()
if set_admin:
logger.warning('Setting current user as database server Microsoft Entra admin:'
' user=%s object id=%s', self.login_username, user_object_id)
admin_info = run_cli_cmd('az sql server ad-admin create -g {} --server-name {} --display-name "{}" --object-id {} --subscription {}'.format(
self.resource_group, self.server, self.login_username, user_object_id, self.subscription))
self.admin_username = admin_info.get('login', self.login_username) if admin_info else self.login_username

def create_aad_user(self):

Expand Down Expand Up @@ -530,7 +534,7 @@ def create_aad_user(self):
def set_target_firewall(self, is_add, ip_name, start_ip=None, end_ip=None):
if is_add:
target = run_cli_cmd(
'az sql server show --ids {}'.format(self.target_id))
'az sql server show --ids "{}"'.format(self.target_id))
# logger.warning("Update database server firewall rule to connect...")
if target.get('publicNetworkAccess') == "Disabled":
ex = AzureConnectionError(
Expand Down Expand Up @@ -581,7 +585,7 @@ def create_aad_user_in_sql(self, connection_args, query_list):
with pyodbc.connect(connection_args.get("connection_string").format(driver=drivers[0]), attrs_before=connection_args.get("attrs_before")) as conn:
with conn.cursor() as cursor:
logger.warning(
"Adding new AAD user %s to database...", self.aad_username)
"Adding new Microsoft Entra user %s to database...", self.aad_username)
for execution_query in query_list:
try:
logger.debug(execution_query)
Expand Down Expand Up @@ -664,16 +668,16 @@ def set_user_admin(self, user_object_id, **kwargs):
if not user_object_id:
if not admins:
e = ValidationError(
'No AAD admin found. Please set current user as AAD admin and try again.')
'No Microsoft Entra admin found. Please set current user as Microsoft Entra admin and try again.')
telemetry.set_exception(e, "Missing-Aad-Admin")
raise e
else:
logger.warning(
'Unable to check if current user is AAD admin. Please confirm current user as AAD admin manually.')
'Unable to check if current user is Microsoft Entra admin. Please confirm current user as Microsoft Entra admin manually.')
return
admin_info = next((ad for ad in admins if ad.get('objectId', "") == user_object_id), None)
if not admin_info:
logger.warning('Set current user as DB Server AAD Administrators.')
logger.warning('Set current user as DB Server Microsoft Entra Administrators.')
admin_info = run_cli_cmd('az postgres flexible-server ad-admin create -u {} -i {} -g {} -s {} --subscription {} -t {}'.format(
self.login_username, user_object_id, self.resource_group, self.db_server, self.subscription, self.login_usertype))
self.admin_username = admin_info.get('principalName', self.login_username)
Expand All @@ -688,6 +692,8 @@ def create_aad_user(self):
self.create_aad_user_in_pg(connection_string, query_list)
except AzureConnectionError as e:
logger.warning(e)
if 'password authentication failed' in str(e):
raise ValidationError('Please confirm current user as Microsoft Entra admin and try again.')
# allow local access
ip_address = self.ip or get_local_ip()
if not ip_address:
Expand Down Expand Up @@ -774,7 +780,7 @@ def create_aad_user_in_pg(self, conn_string, query_list):

conn.autocommit = True
cursor = conn.cursor()
logger.warning("Adding new AAD user %s to database...",
logger.warning("Adding new Microsoft Entra user %s to database...",
self.aad_username)
for execution_query in query_list:
if execution_query:
Expand Down Expand Up @@ -846,16 +852,16 @@ def set_user_admin(self, user_object_id, **kwargs):
if not user_object_id:
if not admins:
e = ValidationError(
'No AAD admin found. Please set current user as AAD admin and try again.')
'No Microsoft Entra admin found. Please set current user as Microsoft Entra admin and try again.')
telemetry.set_exception(e, "Missing-Aad-Admin")
raise e
else:
logger.warning(
'Unable to check if current user is AAD admin. Please confirm current user as AAD admin manually.')
'Unable to check if current user is Microsoft Entra admin. Please confirm current user as Microsoft Entra admin manually.')
return
admin_info = next((ad for ad in admins if ad.get('sid') == user_object_id), None)
if not admin_info:
logger.warning('Setting current user as database server AAD admin:'
logger.warning('Setting current user as database server Microsoft Entra admin:'
' user=%s object id=%s', self.login_username, user_object_id)
admin_info = run_cli_cmd('az postgres server ad-admin create -g {} --server-name {} --display-name {} --object-id {}'
' --subscription {}'.format(rg, server, self.login_username, user_object_id, sub))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def get_source_display_name(sourcename):
long-summary: |
Usage: --system-identity mysql-identity-id=xx
mysql-identity-id : Optional. ID of identity used for MySQL flexible server AAD Authentication. Ignore it if you are the server AAD administrator.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server Microsoft Entra Authentication. Ignore it if you are the server Microsoft Entra administrator.
'''
else:
system_identity_param = '''
Expand All @@ -274,7 +274,7 @@ def get_source_display_name(sourcename):
client-id : Required. Client id of the user assigned identity.
subs-id : Required. Subscription id of the user assigned identity.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server AAD Authentication. Ignore it if you are the server AAD administrator.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server Microsoft Entra Authentication. Ignore it if you are the server Microsoft Entra administrator.
'''
else:
user_identity_param = '''
Expand All @@ -298,7 +298,7 @@ def get_source_display_name(sourcename):
client-id : Required. Client id of the service principal.
object-id : Optional. Object id of the service principal (Enterprise Application).
secret : Required. Secret of the service principal.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server AAD Authentication. Ignore it if you are the server AAD administrator.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server Microsoft Entra Authentication. Ignore it if you are the server Microsoft Entra administrator.
'''
else:
service_principal_param = '''
Expand Down Expand Up @@ -558,7 +558,7 @@ def get_source_display_name(sourcename):
Usage: --user-account mysql-identity-id=xx object-id=XX
object-id : Optional. Object id of current login user. It will be set automatically if not provided.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server AAD Authentication. Ignore it if you are the server AAD administrator.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server Microsoft Entra Authentication. Ignore it if you are the server Microsoft Entra administrator.
'''
else:
user_account_param = '''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

IP_ADDRESS_CHECKER = 'https://api.ipify.org'
OPEN_ALL_IP_MESSAGE = 'Do you want to enable access for all IPs to allow local environment connecting to database?'
SET_ADMIN_MESSAGE = 'Do you want to set current user as Entra admin?'


def should_load_source(source):
Expand Down Expand Up @@ -80,3 +81,13 @@ def confirm_all_ip_allow():
telemetry.set_exception(e, "No-TTY")
raise CLIInternalError(
'Unable to prompt for confirmation as no tty available. Use --yes.') from e


def confirm_admin_set():
try:
return prompt_y_n(SET_ADMIN_MESSAGE)
except NoTTYException as e:
telemetry.set_exception(e, "No-TTY")
logger.warning(
'Unable to prompt for confirmation as no tty available. Use --yes to enable the operation.')
return False
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# --------------------------------------------------------------------------------------------


VERSION = '0.3.12'
VERSION = '0.3.13'
NAME = 'serviceconnector-passwordless'
2 changes: 1 addition & 1 deletion src/serviceconnector-passwordless/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
logger.warn("Wheel is not available, disabling bdist_wheel hook")


VERSION = '0.3.12'
VERSION = '0.3.13'
try:
from azext_serviceconnector_passwordless.config import VERSION
except ImportError:
Expand Down

0 comments on commit 3fdc841

Please sign in to comment.