Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/cosmosdb-preview/HISTORY.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
.. :changelog:
Release History
===============
1.5.0
* Add support for Gremlin/Cassandra/Mongo RBAC role definition and assignment CRUD actions.

+++++++
1.4.0
* Add support for private endpoint in VPN based datacenter deployments in managed cassandra.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ def cf_gremlin_resources(cli_ctx, _):
return cf_cosmosdb_preview(cli_ctx).gremlin_resources


def cf_cassandra_resources(cli_ctx, _):
return cf_cosmosdb_preview(cli_ctx).cassandra_resources


def cf_mongo_mi_resources(cli_ctx, _):
return cf_cosmosdb_preview(cli_ctx).mongo_mi_resources


def cf_table_resources(cli_ctx, _):
return cf_cosmosdb_preview(cli_ctx).table_resources

Expand Down
464 changes: 463 additions & 1 deletion src/cosmosdb-preview/azext_cosmosdb_preview/_help.py

Large diffs are not rendered by default.

107 changes: 106 additions & 1 deletion src/cosmosdb-preview/azext_cosmosdb_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@
validate_mongo_user_definition_id,
validate_table_role_definition_body,
validate_table_role_definition_id,
validate_table_role_assignment_id)
validate_table_role_assignment_id,
validate_gremlin_role_definition_body,
validate_gremlin_role_definition_id,
validate_gremlin_role_assignment_id,
validate_cassandra_role_definition_body,
validate_cassandra_role_definition_id,
validate_cassandra_role_assignment_id,
validate_mongoMI_role_definition_body,
validate_mongoMI_role_definition_id,
validate_mongoMI_role_assignment_id)

from azext_cosmosdb_preview.actions import (
CreateGremlinDatabaseRestoreResource,
Expand Down Expand Up @@ -76,6 +85,60 @@
}"
"""

GREMLIN_ROLE_DEFINITION_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleName\\": \\"MyTestRole\\",
\\"type\\": \\"CustomRole\\",
\\"description\\": \\"Custom role to read Cosmos DB metadata\\",
\\"AssignableScopes\\":[\\"/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/MyDBAccountName\\"],
\\"Permissions\\": [{\\"dataActions\\": [\\"Microsoft.DocumentDB/databaseAccounts/readMetadata\\"]}]
}"
"""

GREMLIN_ROLE_ASSIGNMENT_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleDefinitionId\\": \\"MyTestRoleAssignment\\",
\\"PrincipalId\\": \\"efc9875a-2cc4-40d5-8958-566017875b39\\",
\\"Scope\\":\\"/subscriptions/cfe9875a-2cc4-40d5-8958-566017875b39/resourceGroups/MyResourceGroup/providers/Microsoft.DocumentDB/databaseAccounts/MyDBAccountName\\",
}"
"""

CASSANDRA_ROLE_DEFINITION_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleName\\": \\"MyTestRole\\",
\\"type\\": \\"CustomRole\\",
\\"description\\": \\"Custom role to read Cosmos DB metadata\\",
\\"AssignableScopes\\":[\\"/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/MyDBAccountName\\"],
\\"Permissions\\": [{\\"dataActions\\": [\\"Microsoft.DocumentDB/databaseAccounts/readMetadata\\"]}]
}"
"""

CASSANDRA_ROLE_ASSIGNMENT_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleDefinitionId\\": \\"MyTestRoleAssignment\\",
\\"PrincipalId\\": \\"efc9875a-2cc4-40d5-8958-566017875b39\\",
\\"Scope\\":\\"/subscriptions/cfe9875a-2cc4-40d5-8958-566017875b39/resourceGroups/MyResourceGroup/providers/Microsoft.DocumentDB/databaseAccounts/MyDBAccountName\\",
}"
"""

MONGOMI_ROLE_DEFINITION_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleName\\": \\"MyTestRole\\",
\\"type\\": \\"CustomRole\\",
\\"description\\": \\"Custom role to read Cosmos DB metadata\\",
\\"AssignableScopes\\":[\\"/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/MyDBAccountName\\"],
\\"Permissions\\": [{\\"dataActions\\": [\\"Microsoft.DocumentDB/databaseAccounts/readMetadata\\"]}]
}"
"""

MONGOMI_ROLE_ASSIGNMENT_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleDefinitionId\\": \\"MyTestRoleAssignment\\",
\\"PrincipalId\\": \\"efc9875a-2cc4-40d5-8958-566017875b39\\",
\\"Scope\\":\\"/subscriptions/cfe9875a-2cc4-40d5-8958-566017875b39/resourceGroups/MyResourceGroup/providers/Microsoft.DocumentDB/databaseAccounts/MyDBAccountName\\",
}"
"""

MONGO_ROLE_DEFINITION_EXAMPLE = """--body "{
\\"Id\\": \\"be79875a-2cc4-40d5-8958-566017875b39\\",
\\"RoleName\\": \\"MyRWRole\\",
Expand Down Expand Up @@ -689,3 +752,45 @@ def load_arguments(self, _):
c.argument('role_definition_name', options_list=['--role-definition-name', '-n'], help="Unique Name of the Role Definition that this Role Assignment refers to. Eg. 'Contoso Reader Role'.")
c.argument('scope', options_list=['--scope', '-s'], help="Data plane resource path at which this Role Assignment is being granted.")
c.argument('principal_id', options_list=['--principal-id', '-p'], help="AAD Object ID of the principal to which this Role Assignment is being granted.")

# gremlin role definition
with self.argument_context('cosmosdb gremlin role definition') as c:
c.argument('account_name', account_name_type, id_part=None)
c.argument('role_definition_id', options_list=['--role-definition-id', '-i'], validator=validate_gremlin_role_definition_id, help="Unique ID for the Gremlin Role Definition.")
c.argument('gremlin_role_definition_body', options_list=['--body', '-b'], validator=validate_gremlin_role_definition_body, completer=FilesCompleter(), help="Role Definition body with Id (Optional for create), Type (Default is CustomRole), RoleName, Description, AssignableScopes, Permissions. You can enter it as a string or as a file, e.g., --body @gremlin-role_definition-body-file.json or " + GREMLIN_ROLE_DEFINITION_EXAMPLE)

with self.argument_context('cosmosdb gremlin role assignment') as c:
c.argument('account_name', account_name_type, id_part=None)
c.argument('role_assignment_id', options_list=['--role-assignment-id', '-i'], validator=validate_gremlin_role_assignment_id, help="Optional for Create. Unique ID for the Role Assignment. If not provided, a new GUID will be used.")
c.argument('role_definition_id', options_list=['--role-definition-id', '-d'], help="Unique ID of the Role Definition that this Role Assignment refers to.")
c.argument('role_definition_name', options_list=['--role-definition-name', '-n'], help="Unique Name of the Role Definition that this Role Assignment refers to. Eg. 'Contoso Reader Role'.")
c.argument('scope', options_list=['--scope', '-s'], help="Data plane resource path at which this Role Assignment is being granted.")
c.argument('principal_id', options_list=['--principal-id', '-p'], help="AAD Object ID of the principal to which this Role Assignment is being granted.")

# cassandra role definition
with self.argument_context('cosmosdb cassandra role definition') as c:
c.argument('account_name', account_name_type, id_part=None)
c.argument('role_definition_id', options_list=['--role-definition-id', '-i'], validator=validate_cassandra_role_definition_id, help="Unique ID for the Cassandra Role Definition.")
c.argument('cassandra_role_definition_body', options_list=['--body', '-b'], validator=validate_cassandra_role_definition_body, completer=FilesCompleter(), help="Role Definition body with Id (Optional for create), Type (Default is CustomRole), RoleName, Description, AssignableScopes, Permissions. You can enter it as a string or as a file, e.g., --body @cassandra-role_definition-body-file.json or " + GREMLIN_ROLE_DEFINITION_EXAMPLE)

with self.argument_context('cosmosdb cassandra role assignment') as c:
c.argument('account_name', account_name_type, id_part=None)
c.argument('role_assignment_id', options_list=['--role-assignment-id', '-i'], validator=validate_cassandra_role_assignment_id, help="Optional for Create. Unique ID for the Role Assignment. If not provided, a new GUID will be used.")
c.argument('role_definition_id', options_list=['--role-definition-id', '-d'], help="Unique ID of the Role Definition that this Role Assignment refers to.")
c.argument('role_definition_name', options_list=['--role-definition-name', '-n'], help="Unique Name of the Role Definition that this Role Assignment refers to. Eg. 'Contoso Reader Role'.")
c.argument('scope', options_list=['--scope', '-s'], help="Data plane resource path at which this Role Assignment is being granted.")
c.argument('principal_id', options_list=['--principal-id', '-p'], help="AAD Object ID of the principal to which this Role Assignment is being granted.")

# mongoMI role definition
with self.argument_context('cosmosdb mongomi role definition') as c:
c.argument('account_name', account_name_type, id_part=None)
c.argument('role_definition_id', options_list=['--role-definition-id', '-i'], validator=validate_mongoMI_role_definition_id, help="Unique ID for the MongoMI Role Definition.")
c.argument('mongoMI_role_definition_body', options_list=['--body', '-b'], validator=validate_mongoMI_role_definition_body, completer=FilesCompleter(), help="Role Definition body with Id (Optional for create), Type (Default is CustomRole), RoleName, Description, AssignableScopes, Permissions. You can enter it as a string or as a file, e.g., --body @mongoMI-role_definition-body-file.json or " + GREMLIN_ROLE_DEFINITION_EXAMPLE)

with self.argument_context('cosmosdb mongomi role assignment') as c:
c.argument('account_name', account_name_type, id_part=None)
c.argument('role_assignment_id', options_list=['--role-assignment-id', '-i'], validator=validate_mongoMI_role_assignment_id, help="Optional for Create. Unique ID for the Role Assignment. If not provided, a new GUID will be used.")
c.argument('role_definition_id', options_list=['--role-definition-id', '-d'], help="Unique ID of the Role Definition that this Role Assignment refers to.")
c.argument('role_definition_name', options_list=['--role-definition-name', '-n'], help="Unique Name of the Role Definition that this Role Assignment refers to. Eg. 'Contoso Reader Role'.")
c.argument('scope', options_list=['--scope', '-s'], help="Data plane resource path at which this Role Assignment is being granted.")
c.argument('principal_id', options_list=['--principal-id', '-p'], help="AAD Object ID of the principal to which this Role Assignment is being granted.")
150 changes: 145 additions & 5 deletions src/cosmosdb-preview/azext_cosmosdb_preview/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,11 @@ def validate_table_role_definition_body(cmd, ns):
if 'RoleName' not in table_role_definition or not isinstance(table_role_definition['RoleName'], str) or len(table_role_definition['RoleName']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid table role name. A valid string role name is expected.')

if 'AssignableScopes' not in table_role_definition or not isinstance(table_role_definition['AssignableScopes'], list) or len(table_role_definition['AssignableScopes']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid Table role definition for AssignableScopes. A valid list of strings is expected.')
'Role creation failed. Invalid Table role definition for AssignableScopes. A valid list of strings is expected.')

if 'Permissions' not in table_role_definition or not isinstance(table_role_definition['Permissions'], list) or len(table_role_definition['Permissions']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid Table role Permissions. A valid List JSON representation is expected.')
Expand All @@ -280,12 +280,152 @@ def validate_table_role_definition_body(cmd, ns):

ns.table_role_definition_body = table_role_definition


def validate_table_role_definition_id(ns):
""" Extracts Guid role definition Id """
if ns.role_definition_id is not None:
ns.role_definition_id = _parse_resource_path(ns.role_definition_id, False, "tableRoleDefinitions")



def validate_table_role_assignment_id(ns):
""" Extracts Guid role assignment Id """
if ns.role_assignment_id is not None:
ns.role_assignment_id = _parse_resource_path(ns.role_assignment_id, False, "tableRoleAssignments")
ns.role_assignment_id = _parse_resource_path(ns.role_assignment_id, False, "tableRoleAssignments")


def validate_gremlin_role_definition_body(cmd, ns):
""" Extracts role definition body """
from azext_cosmosdb_preview.vendored_sdks.azure_mgmt_cosmosdb.models import RoleDefinitionType
from azure.cli.core.util import get_file_json, shell_safe_json_parse
import os

if ns.gremlin_role_definition_body is not None:
if os.path.exists(ns.gremlin_role_definition_body):
gremlin_role_definition = get_file_json(ns.gremlin_role_definition_body)
else:
gremlin_role_definition = shell_safe_json_parse(ns.gremlin_role_definition_body)

if not isinstance(gremlin_role_definition, dict):
raise InvalidArgumentValueError(
'Role creation failed. Invalid gremlin role definition. A valid dictionary JSON representation is expected.')

if 'RoleName' not in gremlin_role_definition or not isinstance(gremlin_role_definition['RoleName'], str) or len(gremlin_role_definition['RoleName']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid gremlin role name. A valid string role name is expected.')

if 'AssignableScopes' not in gremlin_role_definition or not isinstance(gremlin_role_definition['AssignableScopes'], list) or len(gremlin_role_definition['AssignableScopes']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid Gremlin role definition for AssignableScopes. A valid list of strings is expected.')

if 'Permissions' not in gremlin_role_definition or not isinstance(gremlin_role_definition['Permissions'], list) or len(gremlin_role_definition['Permissions']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid Gremlin role Permissions. A valid List JSON representation is expected.')

if 'Type' not in gremlin_role_definition:
gremlin_role_definition['Type'] = RoleDefinitionType.custom_role

ns.gremlin_role_definition_body = gremlin_role_definition


def validate_gremlin_role_definition_id(ns):
""" Extracts Guid role definition Id """
if ns.role_definition_id is not None:
ns.role_definition_id = _parse_resource_path(ns.role_definition_id, False, "gremlinRoleDefinitions")


def validate_gremlin_role_assignment_id(ns):
""" Extracts Guid role assignment Id """
if ns.role_assignment_id is not None:
ns.role_assignment_id = _parse_resource_path(ns.role_assignment_id, False, "gremlinRoleAssignments")


def validate_cassandra_role_definition_body(cmd, ns):
""" Extracts role definition body """
from azext_cosmosdb_preview.vendored_sdks.azure_mgmt_cosmosdb.models import RoleDefinitionType
from azure.cli.core.util import get_file_json, shell_safe_json_parse
import os

if ns.cassandra_role_definition_body is not None:
if os.path.exists(ns.cassandra_role_definition_body):
cassandra_role_definition = get_file_json(ns.cassandra_role_definition_body)
else:
cassandra_role_definition = shell_safe_json_parse(ns.cassandra_role_definition_body)

if not isinstance(cassandra_role_definition, dict):
raise InvalidArgumentValueError(
'Role creation failed. Invalid cassandra role definition. A valid dictionary JSON representation is expected.')

if 'RoleName' not in cassandra_role_definition or not isinstance(cassandra_role_definition['RoleName'], str) or len(cassandra_role_definition['RoleName']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid cassandra role name. A valid string role name is expected.')

if 'AssignableScopes' not in cassandra_role_definition or not isinstance(cassandra_role_definition['AssignableScopes'], list) or len(cassandra_role_definition['AssignableScopes']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid Cassandra role definition for AssignableScopes. A valid list of strings is expected.')

if 'Permissions' not in cassandra_role_definition or not isinstance(cassandra_role_definition['Permissions'], list) or len(cassandra_role_definition['Permissions']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid Cassandra role Permissions. A valid List JSON representation is expected.')

if 'Type' not in cassandra_role_definition:
cassandra_role_definition['Type'] = RoleDefinitionType.custom_role

ns.cassandra_role_definition_body = cassandra_role_definition


def validate_cassandra_role_definition_id(ns):
""" Extracts Guid role definition Id """
if ns.role_definition_id is not None:
ns.role_definition_id = _parse_resource_path(ns.role_definition_id, False, "cassandraRoleDefinitions")


def validate_cassandra_role_assignment_id(ns):
""" Extracts Guid role assignment Id """
if ns.role_assignment_id is not None:
ns.role_assignment_id = _parse_resource_path(ns.role_assignment_id, False, "cassandraRoleAssignments")


def validate_mongoMI_role_definition_body(cmd, ns):
""" Extracts role definition body """
from azext_cosmosdb_preview.vendored_sdks.azure_mgmt_cosmosdb.models import RoleDefinitionType
from azure.cli.core.util import get_file_json, shell_safe_json_parse
import os

if ns.mongoMI_role_definition_body is not None:
if os.path.exists(ns.mongoMI_role_definition_body):
mongoMI_role_definition = get_file_json(ns.mongoMI_role_definition_body)
else:
mongoMI_role_definition = shell_safe_json_parse(ns.mongoMI_role_definition_body)

if not isinstance(mongoMI_role_definition, dict):
raise InvalidArgumentValueError(
'Role creation failed. Invalid mongoMI role definition. A valid dictionary JSON representation is expected.')

if 'RoleName' not in mongoMI_role_definition or not isinstance(mongoMI_role_definition['RoleName'], str) or len(mongoMI_role_definition['RoleName']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid mongoMI role name. A valid string role name is expected.')

if 'AssignableScopes' not in mongoMI_role_definition or not isinstance(mongoMI_role_definition['AssignableScopes'], list) or len(mongoMI_role_definition['AssignableScopes']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid MongoMI role definition for AssignableScopes. A valid list of strings is expected.')

if 'Permissions' not in mongoMI_role_definition or not isinstance(mongoMI_role_definition['Permissions'], list) or len(mongoMI_role_definition['Permissions']) == 0:
raise InvalidArgumentValueError(
'Role creation failed. Invalid MongoMI role Permissions. A valid List JSON representation is expected.')

if 'Type' not in mongoMI_role_definition:
mongoMI_role_definition['Type'] = RoleDefinitionType.custom_role

ns.mongoMI_role_definition_body = mongoMI_role_definition


def validate_mongoMI_role_definition_id(ns):
""" Extracts Guid role definition Id """
if ns.role_definition_id is not None:
ns.role_definition_id = _parse_resource_path(ns.role_definition_id, False, "mongoMIRoleDefinitions")


def validate_mongoMI_role_assignment_id(ns):
""" Extracts Guid role assignment Id """
if ns.role_assignment_id is not None:
ns.role_assignment_id = _parse_resource_path(ns.role_assignment_id, False, "mongoMIRoleAssignments")
Loading
Loading