From 2ce3538b447b59a12d0909c61dab319eee363305 Mon Sep 17 00:00:00 2001 From: gruebel Date: Sat, 9 Sep 2023 21:45:31 +0200 Subject: [PATCH] add type hints to writing folder --- MANIFEST.in | 1 + policy_sentry/bin/cli.py | 4 ++-- policy_sentry/py.typed | 0 policy_sentry/writing/sid_group.py | 27 +++++++++++++++------------ policy_sentry/writing/template.py | 15 +++++++++------ policy_sentry/writing/validate.py | 12 ++++++++---- pyproject.toml | 7 ++++++- setup.py | 15 ++++++++------- 8 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 policy_sentry/py.typed diff --git a/MANIFEST.in b/MANIFEST.in index 13441203e..1328f5e37 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ +include policy_sentry/py.typed recursive-include policy_sentry/shared *.txt *.html *.yml *.json diff --git a/policy_sentry/bin/cli.py b/policy_sentry/bin/cli.py index b097df7c7..d7a0e08c6 100755 --- a/policy_sentry/bin/cli.py +++ b/policy_sentry/bin/cli.py @@ -9,7 +9,7 @@ @click.group() @click.version_option(version=__version__) -def policy_sentry(): +def policy_sentry() -> None: """ Policy Sentry is a tool for generating least-privilege IAM Policies. """ @@ -21,7 +21,7 @@ def policy_sentry(): policy_sentry.add_command(command.query.query) -def main(): +def main() -> None: """Policy Sentry is a tool for generating least-privilege IAM Policies.""" policy_sentry() diff --git a/policy_sentry/py.typed b/policy_sentry/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/policy_sentry/writing/sid_group.py b/policy_sentry/writing/sid_group.py index 1ff17b720..7c56e84e7 100644 --- a/policy_sentry/writing/sid_group.py +++ b/policy_sentry/writing/sid_group.py @@ -60,17 +60,17 @@ class SidGroup: def __init__(self) -> None: # Dict instead of list # sids instead of ARN - self.sids = {} - self.universal_conditions = {} - self.skip_resource_constraints = [] - self.exclude_actions = [] - self.wildcard_only_single_actions = [] + self.sids: dict[str, dict[str, Any]] = {} + self.universal_conditions: dict[str, Any] = {} + self.skip_resource_constraints: list[str] = [] + self.exclude_actions: list[str] = [] + self.wildcard_only_single_actions: list[str] = [] # When a user requests all wildcard-only actions available under a service at a specific access level - self.wildcard_only_service_read = [] - self.wildcard_only_service_write = [] - self.wildcard_only_service_list = [] - self.wildcard_only_service_tagging = [] - self.wildcard_only_service_permissions_management = [] + self.wildcard_only_service_read: list[str] = [] + self.wildcard_only_service_write: list[str] = [] + self.wildcard_only_service_list: list[str] = [] + self.wildcard_only_service_tagging: list[str] = [] + self.wildcard_only_service_permissions_management: list[str] = [] def get_sid_group(self) -> dict[str, dict[str, Any]]: """ @@ -172,7 +172,7 @@ def add_sts_actions(self, sts_actions: dict[str, list[str]]) -> None: self.sids[sid_namespace] = temp_sid_dict def add_requested_service_wide( - self, service_prefixes: str, access_level: str + self, service_prefixes: list[str], access_level: str ) -> None: """ When a user requests all wildcard-only actions available under a service at a specific access level @@ -226,7 +226,7 @@ def get_rendered_policy(self, minimize: int | None = None) -> dict[str, Any]: Returns: Dictionary: The IAM Policy JSON """ - statements = [] + statements: list[dict[str, Any]] = [] # Only set the actions to lowercase if minimize is provided all_actions = get_all_actions(lowercase=True) @@ -330,6 +330,9 @@ def add_by_arn_and_access_level( resource_type_name = get_resource_type_name_with_raw_arn( raw_arn_format ) + if resource_type_name is None: + continue + sid_namespace = create_policy_sid_namespace( service_prefix, access_level, resource_type_name ) diff --git a/policy_sentry/writing/template.py b/policy_sentry/writing/template.py index bd6570aec..e9abc6bd1 100644 --- a/policy_sentry/writing/template.py +++ b/policy_sentry/writing/template.py @@ -1,6 +1,9 @@ """Templates for the policy_sentry YML files. These can be used for generating policies """ +from __future__ import annotations + +from typing import Any ACTIONS_TEMPLATE = """mode: actions name: '' @@ -71,8 +74,8 @@ "skip-resource-constraints": [], "exclude-actions": [], "sts": { - "assume-role": [], - "assume-role-with-saml": [], + "assume-role": [], + "assume-role-with-saml": [], "assume-role-with-web-identity": [] } } @@ -80,21 +83,21 @@ ACTIONS_TEMPLATE_DICT = {"mode": "actions", "name": "", "actions": []} -def create_crud_template(): +def create_crud_template() -> str: """Generate the CRUD YML Template """ return CRUD_TEMPLATE -def create_actions_template(): +def create_actions_template() -> str: """Generate the Actions YML template""" return ACTIONS_TEMPLATE -def get_crud_template_dict(): +def get_crud_template_dict() -> dict[str, Any]: """Generate the CRUD template in dict format""" return CRUD_TEMPLATE_DICT -def get_actions_template_dict(): +def get_actions_template_dict() -> dict[str, Any]: """Get the Actions template in dict format.""" return ACTIONS_TEMPLATE_DICT diff --git a/policy_sentry/writing/validate.py b/policy_sentry/writing/validate.py index 5df748176..9718e4b17 100644 --- a/policy_sentry/writing/validate.py +++ b/policy_sentry/writing/validate.py @@ -1,13 +1,17 @@ """ Validation for the Policy Sentry YML Templates. """ +from __future__ import annotations + import logging +from typing import Any + from schema import Optional, Schema, And, Use, Regex, SchemaError logger = logging.getLogger(__name__) -def check(conf_schema, conf): +def check(conf_schema: Schema, conf: dict[str, Any]) -> bool: """ Validates a user-supplied JSON vs a defined schema. @@ -60,7 +64,7 @@ def check(conf_schema, conf): ) -def check_actions_schema(cfg): +def check_actions_schema(cfg: dict[str, Any]) -> bool: """ Determines whether the user-provided config matches the required schema for Actions mode """ @@ -75,7 +79,7 @@ def check_actions_schema(cfg): ) -def check_crud_schema(cfg): +def check_crud_schema(cfg: dict[str, Any]) -> bool: """ Determines whether the user-provided config matches the required schema for CRUD mode """ @@ -90,7 +94,7 @@ def check_crud_schema(cfg): ) -def validate_condition_block(condition_block): +def validate_condition_block(condition_block: dict[str, Any]) -> bool: """ Validates the format of the condition block that should be supplied in the template. diff --git a/pyproject.toml b/pyproject.toml index 3a26577f7..21d446760 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,9 +6,14 @@ pretty = true exclude = [ '^policy_sentry/bin', '^policy_sentry/command', - '^policy_sentry/writing', ] +[[tool.mypy.overrides]] +module = [ + "schema" +] +ignore_missing_imports = true + [tool.pytest.ini_options] testpaths = [ "test", diff --git a/setup.py b/setup.py index 595c6c3a3..f7297c8b9 100644 --- a/setup.py +++ b/setup.py @@ -57,15 +57,16 @@ def get_description(): install_requires=REQUIRED_PACKAGES, project_urls=PROJECT_URLS, classifiers=[ - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Typing :: Typed", ], entry_points={"console_scripts": "policy_sentry=policy_sentry.bin.cli:main"}, zip_safe=True,