From 61a954ca9ce996d43db56a6438036a29db746143 Mon Sep 17 00:00:00 2001 From: MonkeyCanCode Date: Sun, 23 Feb 2025 18:51:13 -0600 Subject: [PATCH] CLI: add subcommand access for principals (#1019) * Access subcommand access for principals * Access subcommand access for principals --- .../client/python/cli/command/principals.py | 47 +++++++++++++++++++ regtests/client/python/cli/constants.py | 1 + .../client/python/cli/options/option_tree.py | 3 +- .../unreleased/command-line-interface.md | 19 ++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/regtests/client/python/cli/command/principals.py b/regtests/client/python/cli/command/principals.py index 2de58fde4..20c49374c 100644 --- a/regtests/client/python/cli/command/principals.py +++ b/regtests/client/python/cli/command/principals.py @@ -16,6 +16,7 @@ # specific language governing permissions and limitations # under the License. # +import json from dataclasses import dataclass from typing import Dict, Optional, List @@ -35,6 +36,7 @@ class PrincipalsCommand(Command): Example commands: * ./polaris principals create user + * ./polaris principals access user * ./polaris principals list * ./polaris principals list --principal-role filter-to-this-role """ @@ -48,6 +50,22 @@ class PrincipalsCommand(Command): set_properties: Dict[str, StrictStr] remove_properties: List[str] + def _get_catalogs(self, api: PolarisDefaultApi): + for catalog in api.list_catalogs().catalogs: + yield catalog.to_dict()['name'] + + def _get_principal_roles(self, api: PolarisDefaultApi): + for principal_role in api.list_principal_roles_assigned(self.principal_name).roles: + yield principal_role.to_dict()['name'] + + def _get_catalog_roles(self, api: PolarisDefaultApi, principal_role_name: str, catalog_name: str): + for catalog_role in api.list_catalog_roles_for_principal_role(principal_role_name, catalog_name).roles: + yield catalog_role.to_dict()['name'] + + def _get_privileges(self, api: PolarisDefaultApi, catalog_name: str, catalog_role_name: str): + for grant in api.list_grants_for_catalog_role(catalog_name, catalog_role_name).grants: + yield grant.to_dict() + def validate(self): pass @@ -93,5 +111,34 @@ def execute(self, api: PolarisDefaultApi) -> None: properties=new_properties ) api.update_principal(self.principal_name, request) + elif self.principals_subcommand == Subcommands.ACCESS: + principal = api.get_principal(self.principal_name).to_dict()['name'] + principal_roles = self._get_principal_roles(api) + + # Initialize the result structure + result = { + 'principal': principal, + 'principal_roles': [] + } + + # Construct the result structure for each principal role + for principal_role in principal_roles: + role_data = { + 'name': principal_role, + 'catalog_roles': [] + } + # For each catalog role, get associated privileges + for catalog in self._get_catalogs(api): + catalog_roles = self._get_catalog_roles(api, principal_role, catalog) + for catalog_role in catalog_roles: + catalog_data = { + 'name': catalog_role, + 'catalog': catalog, + 'privileges': [] + } + catalog_data['privileges'] = list(self._get_privileges(api, catalog_data['catalog'], catalog_role)) + role_data['catalog_roles'].append(catalog_data) + result['principal_roles'].append(role_data) + print(json.dumps(result)) else: raise Exception(f"{self.principals_subcommand} is not supported in the CLI") diff --git a/regtests/client/python/cli/constants.py b/regtests/client/python/cli/constants.py index 6dce2b074..1fae549d2 100644 --- a/regtests/client/python/cli/constants.py +++ b/regtests/client/python/cli/constants.py @@ -80,6 +80,7 @@ class Subcommands: VIEW = 'view' GRANT = 'grant' REVOKE = 'revoke' + ACCESS = 'access' class Actions: diff --git a/regtests/client/python/cli/options/option_tree.py b/regtests/client/python/cli/options/option_tree.py index 2510193e0..9d25ec05b 100644 --- a/regtests/client/python/cli/options/option_tree.py +++ b/regtests/client/python/cli/options/option_tree.py @@ -123,7 +123,8 @@ def get_tree() -> List[Option]: Option(Subcommands.UPDATE, args=[ Argument(Arguments.SET_PROPERTY, str, Hints.SET_PROPERTY, allow_repeats=True), Argument(Arguments.REMOVE_PROPERTY, str, Hints.REMOVE_PROPERTY, allow_repeats=True), - ], input_name=Arguments.PRINCIPAL) + ], input_name=Arguments.PRINCIPAL), + Option(Subcommands.ACCESS, input_name=Arguments.PRINCIPAL), ]), Option(Commands.PRINCIPAL_ROLES, 'manage principal roles', children=[ Option(Subcommands.CREATE, args=[ diff --git a/site/content/in-dev/unreleased/command-line-interface.md b/site/content/in-dev/unreleased/command-line-interface.md index c9a6045b9..20078f1ed 100644 --- a/site/content/in-dev/unreleased/command-line-interface.md +++ b/site/content/in-dev/unreleased/command-line-interface.md @@ -256,6 +256,7 @@ The `principals` command is used to manage principals within Polaris. 4. list 5. rotate-credentials 6. update +7. access #### create @@ -372,6 +373,24 @@ polaris principals update --property key=value --property other_key=other_value polaris principals update --property are_other_keys_removed=yes some_user ``` +#### access + +The `access` subcommand retrieves entities relation about a principal. + +``` +input: polaris principals access --help +options: + access + Positional arguments: + principal +``` + +##### Examples + +``` +polaris principals access quickstart_user +``` + ### Principal Roles The `principal-roles` command is used to create, discover, and manage principal roles within Polaris. Additionally, this command can identify principals or catalog roles associated with a principal role, and can be used to grant a principal role to a principal.