Skip to content

Commit

Permalink
module-graph
Browse files Browse the repository at this point in the history
  • Loading branch information
jiasli committed Sep 11, 2024
1 parent def9fdb commit beaa4df
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 103,556 deletions.
8 changes: 7 additions & 1 deletion src/azure-cli/azure/cli/command_modules/keyvault/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,13 @@
helps['keyvault create'] = """
type: command
short-summary: Create a Vault or HSM.
long-summary: If `--enable-rbac-authorization` is not specified, then default permissions are created for the current user or service principal unless the `--no-self-perms` flag is specified.
long-summary: >-
RBAC authorization is enabled by default. If `--enable-rbac-authorization` is manually specified to `false` and
`--no-self-perms` flag is not specified, default permissions are created for the current user or service principal.
If you want to assign the default permission, you have to change the default subscription with `az account set`
first, instead of using `--subscription`.
examples:
- name: Create a key vault with network ACLs specified (use --network-acls to specify IP and VNet rules by using a JSON string).
Expand Down
70 changes: 5 additions & 65 deletions src/azure-cli/azure/cli/command_modules/keyvault/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,60 +305,6 @@ def list_vault(client, resource_group_name=None):
return list(vault_list)


def _get_current_user_object_id(graph_client):
current_user = graph_client.signed_in_user_get()
return current_user['id']


def _get_object_id_by_spn(graph_client, spn):
accounts = list(graph_client.service_principal_list(
filter="servicePrincipalNames/any(c:c eq '{}')".format(spn)))
if not accounts:
logger.warning("Unable to find user with spn '%s'", spn)
return None
if len(accounts) > 1:
logger.warning("Multiple service principals found with spn '%s'. "
"You can avoid this by specifying object id.", spn)
return None
return accounts[0]['id']


def _get_object_id_by_upn(graph_client, upn):
accounts = list(graph_client.user_list(filter="userPrincipalName eq '{}'".format(upn)))
if not accounts:
logger.warning("Unable to find user with upn '%s'", upn)
return None
if len(accounts) > 1:
logger.warning("Multiple users principals found with upn '%s'. "
"You can avoid this by specifying object id.", upn)
return None
return accounts[0]['id']


def _get_object_id_from_subscription(graph_client, subscription):
if not subscription:
return None

if subscription['user']:
if subscription['user']['type'] == 'user':
return _get_object_id_by_upn(graph_client, subscription['user']['name'])
if subscription['user']['type'] == 'servicePrincipal':
return _get_object_id_by_spn(graph_client, subscription['user']['name'])
logger.warning("Unknown user type '%s'", subscription['user']['type'])
else:
logger.warning('Current credentials are not from a user or service principal. '
'Azure Key Vault does not work with certificate credentials.')
return None


def _get_object_id(graph_client, subscription=None, spn=None, upn=None):
if spn:
return _get_object_id_by_spn(graph_client, spn)
if upn:
return _get_object_id_by_upn(graph_client, upn)
return _get_object_id_from_subscription(graph_client, subscription)


def _create_network_rule_set(cmd, bypass=None, default_action=None):
NetworkRuleSet = cmd.get_models('NetworkRuleSet', resource_type=ResourceType.MGMT_KEYVAULT)
NetworkRuleBypassOptions = cmd.get_models('NetworkRuleBypassOptions', resource_type=ResourceType.MGMT_KEYVAULT)
Expand Down Expand Up @@ -671,8 +617,6 @@ def create_vault(cmd, client, # pylint: disable=too-many-locals, too-many-state
# if client.get raise exception, we can take it as no existing vault found
# just continue the normal creation process
pass
from azure.cli.core._profile import Profile, _TENANT_ID
from azure.cli.command_modules.role import graph_client_factory, GraphError

VaultCreateOrUpdateParameters = cmd.get_models('VaultCreateOrUpdateParameters',
resource_type=ResourceType.MGMT_KEYVAULT)
Expand All @@ -685,8 +629,8 @@ def create_vault(cmd, client, # pylint: disable=too-many-locals, too-many-state
Sku = cmd.get_models('Sku', resource_type=ResourceType.MGMT_KEYVAULT)
VaultProperties = cmd.get_models('VaultProperties', resource_type=ResourceType.MGMT_KEYVAULT)

from azure.cli.core._profile import Profile, _TENANT_ID
profile = Profile(cli_ctx=cmd.cli_ctx)
graph_client = graph_client_factory(cmd.cli_ctx)
subscription = profile.get_subscription(subscription=cmd.cli_ctx.data.get('subscription_id', None))
tenant_id = subscription[_TENANT_ID]

Expand Down Expand Up @@ -751,10 +695,8 @@ def create_vault(cmd, client, # pylint: disable=too-many-locals, too-many-state
StoragePermissions.getsas,
StoragePermissions.deletesas])

try:
object_id = _get_current_user_object_id(graph_client)
except GraphError:
object_id = _get_object_id(graph_client, subscription=subscription)
from azure.cli.command_modules.role.util import get_current_identity_object_id
object_id = get_current_identity_object_id(cmd.cli_ctx)
if not object_id:
raise CLIError('Cannot create vault.\nUnable to query active directory for information '
'about the current user.\nYou may try the --no-self-perms flag to '
Expand Down Expand Up @@ -901,10 +843,8 @@ def update_hsm(cmd, instance,

def _object_id_args_helper(cli_ctx, object_id, spn, upn):
if not object_id:
from azure.cli.command_modules.role import graph_client_factory

graph_client = graph_client_factory(cli_ctx)
object_id = _get_object_id(graph_client, spn=spn, upn=upn)
from azure.cli.command_modules.role.util import get_object_id
object_id = get_object_id(cli_ctx, spn=spn, upn=upn)
if not object_id:
raise CLIError('Unable to get object id from principal name.')
return object_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class GraphClient:
GraphClient should NEVER be instantiated directly, but always through the client factory
azure.cli.command_modules.role.graph_client_factory.
This class internally calls azure.cli.core.util.send_raw_request to make REST API calls.
The authentication is based on the implementation of send_raw_request, so the default account's
auth context is used (the one shown by `az account show`).
For full documentation, see doc/microsoft_graph_client.md in this repo.
"""

Expand Down
80 changes: 80 additions & 0 deletions src/azure-cli/azure/cli/command_modules/role/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from knack.log import get_logger

logger = get_logger(__name__)


def _get_current_user_object_id(graph_client):
# Calling this API with a service principal raises GraphError:
# /me request is only valid with delegated authentication flow.
current_user = graph_client.signed_in_user_get()
return current_user['id']


def _get_object_id_by_spn(graph_client, spn):
accounts = list(graph_client.service_principal_list(filter="servicePrincipalNames/any(c:c eq '{}')".format(spn)))
# 2+ matches should never happen, so we only check 'no match' here
if not accounts:
logger.warning("Unable to find service principal with spn '%s'", spn)
return None
return accounts[0]['id']


def _get_object_id_by_upn(graph_client, upn):
accounts = list(graph_client.user_list(filter="userPrincipalName eq '{}'".format(upn)))
# 2+ matches should never happen, so we only check 'no match' here
if not accounts:
logger.warning("Unable to find user with upn '%s'", upn)
return None
return accounts[0]['id']


def _get_object_id_from_subscription(graph_client, subscription):
if subscription['user']['type'] == 'user':
return _get_object_id_by_upn(graph_client, subscription['user']['name'])
if subscription['user']['type'] == 'servicePrincipal':
return _get_object_id_by_spn(graph_client, subscription['user']['name'])
return None


def _get_object_id(graph_client, spn=None, upn=None):
if spn:
return _get_object_id_by_spn(graph_client, spn)
if upn:
return _get_object_id_by_upn(graph_client, upn)
return None


def get_object_id(cli_ctx, spn=None, upn=None):
from azure.cli.command_modules.role import graph_client_factory
graph_client = graph_client_factory(cli_ctx)
return _get_object_id(graph_client, spn=spn, upn=upn)


def get_current_identity_object_id(cli_ctx):
"""This function tries to get the current identity's object ID following below steps:
1. First try to resolve with /me API: https://learn.microsoft.com/en-us/graph/api/user-get
2. If failed, try to resolve with either
- /users API: https://learn.microsoft.com/en-us/graph/api/user-list
- /servicePrincipals API: https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list
If all of these attempts fail, return None.
"""
from azure.cli.command_modules.role import graph_client_factory, GraphError
graph_client = graph_client_factory(cli_ctx)

try:
return _get_current_user_object_id(graph_client)
except GraphError:
from azure.cli.core._profile import Profile
profile = Profile(cli_ctx)
# To align with _get_current_user_object_id, only look up the current upn/spn, so
# cli_ctx.data['subscription_id'] should not be used in get_subscription.
# Otherwise, it may result in looking up a upn/spn different from the current login context.
subscription = profile.get_subscription()
return _get_object_id_from_subscription(graph_client, subscription)
23 changes: 8 additions & 15 deletions src/azure-cli/azure/cli/command_modules/servicefabric/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

from azure.cli.core.util import CLIError, get_file_json, b64_to_hex, sdk_no_wait
from azure.cli.core.commands import LongRunningOperation
from azure.graphrbac import GraphRbacManagementClient
from azure.cli.core.profiles import ResourceType, get_sdk
from azure.cli.command_modules.servicefabric._arm_deployment_utils import validate_and_deploy_arm_template
from azure.cli.command_modules.servicefabric._sf_utils import _get_resource_group_by_name, _create_resource_group_name
Expand Down Expand Up @@ -1565,16 +1564,6 @@ def _create_keyvault(cmd,
enabled_for_disk_encryption=None,
enabled_for_template_deployment=None,
no_self_perms=None, tags=None):

from azure.cli.core._profile import Profile
from azure.graphrbac.models import GraphErrorException
profile = Profile(cli_ctx=cli_ctx)
cred, _, tenant_id = profile.get_login_credentials(
resource=cli_ctx.cloud.endpoints.active_directory_graph_resource_id)
graph_client = GraphRbacManagementClient(cred,
tenant_id,
base_url=cli_ctx.cloud.endpoints.active_directory_graph_resource_id)
subscription = profile.get_subscription()
VaultCreateOrUpdateParameters = cmd.get_models('VaultCreateOrUpdateParameters', resource_type=ResourceType.MGMT_KEYVAULT, operation_group='vaults')
VaultProperties = cmd.get_models('VaultProperties', resource_type=ResourceType.MGMT_KEYVAULT, operation_group='vaults')
KeyVaultSku = cmd.get_models('Sku', resource_type=ResourceType.MGMT_KEYVAULT, operation_group='vaults')
Expand All @@ -1585,6 +1574,11 @@ def _create_keyvault(cmd,
SecretPermissions = get_sdk(cli_ctx, ResourceType.MGMT_KEYVAULT, 'models#SecretPermissions', operation_group='vaults')
KeyVaultSkuName = cmd.get_models('SkuName', resource_type=ResourceType.MGMT_KEYVAULT, operation_group='vaults')

from azure.cli.core._profile import Profile, _TENANT_ID
profile = Profile(cli_ctx=cmd.cli_ctx)
subscription = profile.get_subscription(subscription=cmd.cli_ctx.data.get('subscription_id', None))
tenant_id = subscription[_TENANT_ID]

if not sku:
sku = KeyVaultSkuName.standard.value

Expand Down Expand Up @@ -1619,10 +1613,9 @@ def _create_keyvault(cmd,
CertificatePermissions.deleteissuers,
CertificatePermissions.manageissuers,
CertificatePermissions.recover])
try:
object_id = _get_current_user_object_id(graph_client)
except GraphErrorException:
object_id = _get_object_id(graph_client, subscription=subscription)

from azure.cli.command_modules.role.util import get_current_identity_object_id
object_id = get_current_identity_object_id(cli_ctx)
if not object_id:
raise CLIError('Cannot create vault.\n'
'Unable to query active directory for information '
Expand Down
Loading

0 comments on commit beaa4df

Please sign in to comment.