Skip to content

Commit

Permalink
Commands related to Security permissions (#582)
Browse files Browse the repository at this point in the history
* Fix python versions in Mac machines

* Adding list and show namespace commands

* First draft of permissions commands

* Add and reset permission commands

* Resolve permissions from json response

* Adding some docs

* MInor

* Some changes in resolve permissions flow and table output

* Update security_tokens.md

* Update security_tokens.md

* Remove json resolution, instead adding a switch

* Refactoring the resolved json response

* Table format changes

* Selective output for add and reset commands

* PR comments

* Bug bash fixes and PR comments

* Pylint and Flake fixes

* Markdown styling fix
  • Loading branch information
ishitam8 committed May 17, 2019
1 parent f504e7c commit 155abe9
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 1 deletion.
21 changes: 21 additions & 0 deletions azure-devops/azext_devops/dev/common/identities.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ def resolve_identity_as_id(identity_filter, organization):
return None


def resolve_identity_as_identity_descriptor(identity_filter, organization):
"""Takes an identity name, email, alias, or id, and returns the id.
"""
if identity_filter is None:
return identity_filter
if identity_filter.lower() == ME:
return get_current_identity(organization).descriptor
identity = resolve_identity(identity_filter, organization)
if identity is not None:
return identity.descriptor
return None


def resolve_identity_as_display_name(identity_filter, organization):
"""Takes an identity name, email, alias, or id, and returns the display name.
"""
Expand Down Expand Up @@ -116,5 +129,13 @@ def get_account_from_identity(identity):
return identity.provider_display_name


def get_identity_descriptor_from_subject_descriptor(subject_descriptor, organization):
identity_client = get_identity_client(organization)
identities = identity_client.read_identities(subject_descriptors=subject_descriptor)
if identities:
return identities[0].descriptor
return subject_descriptor


ME = 'me'
_display_name_cache = get_cli_cache('identity_display_names', 3600 * 6)
5 changes: 5 additions & 0 deletions azure-devops/azext_devops/dev/common/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ def get_policy_client(organization=None):
return connection.get_client(VSTS_MODULE + 'v5_0.policy.policy_client.PolicyClient')


def get_security_client(organization=None):
connection = get_connection(organization)
return connection.get_client(VSTS_MODULE + 'v5_0.security.security_client.SecurityClient')


def get_settings_client(organization=None):
connection = get_connection(organization)
return connection.get_client(VSTS_MODULE + 'v5_0.settings.settings_client.SettingsClient')
Expand Down
77 changes: 77 additions & 0 deletions azure-devops/azext_devops/dev/team/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import print_function
from collections import OrderedDict
from knack.util import CLIError
from azext_devops.dev.common.format import trim_for_display, date_time_to_only_date


Expand Down Expand Up @@ -166,6 +167,78 @@ def _transform_membership_row(row):
return table_row


def transform_namespaces_table_output(result):
table_output = []
for item in result:
table_output.append(_transform_namespace_row(item))
return table_output


def _transform_namespace_row(row):
table_row = OrderedDict()
table_row['Id'] = row['namespaceId']
table_row['Name'] = row['name']
return table_row


def transform_namespace_table_output(result):
table_output = []
for item in result[0]['actions']:
table_output.append(_transform_namespace_details_row(item))
return table_output


def _transform_namespace_details_row(row):
table_row = OrderedDict()
table_row['Name'] = row['name']
table_row['Permission Description'] = row['displayName']
table_row['Permission Bit'] = row['bit']
return table_row


def transform_acl_output(result):
table_output = []
for item in result:
table_output.append(_transform_acl_details_row(item))
return table_output


def _transform_acl_details_row(row):
if len(row['acesDictionary']) > 1:
raise CLIError('More than one entry found in Aces dictionary for this user/group.')
table_row = OrderedDict()
table_row['token'] = row['token']
ace = list(row['acesDictionary'].values())[0]
if row['includeExtendedInfo']:
if ace['extendedInfo']['effectiveAllow'] is not None:
table_row['Effective Allow'] = ace['extendedInfo']['effectiveAllow']
else:
table_row['Effective Allow'] = 0
if ace['extendedInfo']['effectiveDeny'] is not None:
table_row['Effective Deny'] = ace['extendedInfo']['effectiveDeny']
else:
table_row['Effective Deny'] = 0
return table_row


def transform_resolve_permission_bits(result):
table_output = []
ace_entry = list(result[0]['acesDictionary'].values())[0]
permissions = ace_entry['resolvedPermissions']
for permission in permissions:
table_output.append(_transform_resolve_bits_row(permission))
return table_output


def _transform_resolve_bits_row(row):
table_row = OrderedDict()
table_row['Name'] = row['name']
table_row['Bit'] = row['bit']
table_row['Permission Description'] = row['displayName']
table_row['Permission Value'] = row['effectivePermission']
return table_row


def transform_teams_table_output(result):
table_output = []
for item in sorted(result, key=_get_team_key):
Expand Down Expand Up @@ -265,6 +338,10 @@ def _get_extension_key(extension):
return extension['extensionName'].lower()


def _get_permission_key(permission_row):
return permission_row['displayName'].lower()


def _get_service_endpoint_key(service_endpoint_row):
return service_endpoint_row['name'].lower()

Expand Down
10 changes: 10 additions & 0 deletions azure-devops/azext_devops/dev/team/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ def load_team_help():
short-summary: Manage memberships for security groups
"""

helps['devops security permission'] = """
type: group
short-summary: Manage security permissions
"""

helps['devops security permission namespace'] = """
type: group
short-summary: Manage security namespaces
"""

helps['devops team'] = """
type: group
short-summary: Manage teams
Expand Down
24 changes: 23 additions & 1 deletion azure-devops/azext_devops/dev/team/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


from knack.arguments import enum_choice_list
from azure.cli.core.commands.parameters import get_enum_type
from azure.cli.core.commands.parameters import get_enum_type, get_three_state_flag
from azext_devops.dev.common.const import _TRUE_FALSE_SWITCH
from .const import (SERVICE_ENDPOINT_AUTHORIZATION_PERSONAL_ACCESS_TOKEN,
SERVICE_ENDPOINT_TYPE_GITHUB,
Expand Down Expand Up @@ -106,6 +106,28 @@ def load_team_arguments(self, _):
context.argument('relationship', arg_type=get_enum_type(_RELATIONSHIP_TYPES),
help='Get member of/members for this group.')

with self.argument_context('devops security permission') as context:
context.argument('namespace_id', options_list=('--namespace-id', '--id'),
help='ID of security namespace')
context.argument('token',
help='Security token.')
context.argument('subject',
help='User Email ID or Group descriptor')

with self.argument_context('devops security permission update') as context:
context.argument('merge', arg_type=get_three_state_flag(),
help='If set, the existing ACE has its allow and deny merged with \
the incoming ACE\'s allow and deny. If unset, the existing ACE is displaced.')
context.argument('allow_bit', type=int,
help='Allow bit or addition of bits. Required if --deny-bit is missing.')
context.argument('deny_bit', type=int,
help='Deny bit or addition of bits. Required if --allow-bit is missing.')

with self.argument_context('devops security permission reset') as context:
context.argument('permission_bit', type=int,
help='Permission bit or addition of permission bits which needs to be reset\
for given user/group and token.')

with self.argument_context('devops extension') as context:
context.argument('include_built_in', arg_type=get_enum_type(_TRUE_FALSE_SWITCH),
help='Include built in extensions.')
Expand Down
20 changes: 20 additions & 0 deletions azure-devops/azext_devops/dev/team/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
transform_group_table_output,
transform_memberships_table_output,
transform_membership_table_output,
transform_namespaces_table_output,
transform_namespace_table_output,
transform_acl_output,
transform_resolve_permission_bits,
transform_team_table_output,
transform_teams_table_output,
transform_team_members_table_output,
Expand Down Expand Up @@ -74,6 +78,10 @@
exception_handler=azure_devops_exception_handler
)

security_permissionOps = CliCommandType(
operations_tmpl='azext_devops.dev.team.security_permission#{}',
exception_handler=azure_devops_exception_handler
)

wikiOps = CliCommandType(
operations_tmpl='azext_devops.dev.team.wiki#{}',
Expand Down Expand Up @@ -143,6 +151,18 @@ def load_team_commands(self, _):
g.command('add', 'add_membership', table_transformer=transform_membership_table_output)
g.command('remove', 'remove_membership', confirmation='Are you sure you want to delete this relationship?')

with self.command_group('devops security permission', command_type=security_permissionOps) as g:
g.command('list', 'list_tokens', table_transformer=transform_acl_output)
g.command('update', 'update_permissions', table_transformer=transform_resolve_permission_bits)
g.command('reset-all', 'reset_all_permissions',
confirmation='Are you sure you want to reset all explicit permissions for this user/group and token?')
g.command('reset', 'reset_permissions', table_transformer=transform_resolve_permission_bits)
g.command('show', 'show_permissions', table_transformer=transform_resolve_permission_bits)

with self.command_group('devops security permission namespace', command_type=security_permissionOps) as g:
g.command('list', 'list_namespaces', table_transformer=transform_namespaces_table_output)
g.command('show', 'show_namespace', table_transformer=transform_namespace_table_output)

with self.command_group('devops wiki', command_type=wikiOps) as g:
g.command('create', 'create_wiki', table_transformer=transform_wiki_table_output)
g.command('list', 'list_wiki', table_transformer=transform_wikis_table_output)
Expand Down
Loading

0 comments on commit 155abe9

Please sign in to comment.