from abc import ABC, abstractmethod from enum import Enum from typing import TYPE_CHECKING, Dict, List, Optional, Type from models.affected_element import ( AffectedElement, ArrayAffectedElement, AuthAffectedElement, BizEnumAffectedElement, DigitAffectedElement, EnumAffectedElement, LeafAffectedElement, NonLeafAffectedElement, ObjectAffectedElement, PathParamAffectedElement, QueryParamAffectedElement, StringAffectedElement, ) if TYPE_CHECKING: from models.enhanced_operation import EnhancedOperation from models.openapi import Schema type_map: Dict[str, Type[AffectedElement]] = { "string": StringAffectedElement, "integer": DigitAffectedElement, "boolean": EnumAffectedElement, "array": ArrayAffectedElement, "object": ObjectAffectedElement, } class CategoryInfo(ABC): def __init__(self, position: str, description: str): self.name: Optional[str] = None self.position = position self.description = description @abstractmethod def get_affected_elements( self, operation: "EnhancedOperation" ) -> List[AffectedElement]: pass # Add more category info classes as needed class AuthCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[AuthAffectedElement]: key_parts = [] if operation.get_security_type() == "basic": key_parts = ["user", "password"] elif operation.get_security_type() == "oauth2": key_parts = ["token"] elif operation.get_security_type() == "apiKey": key_parts = ["token"] # elif "digest authentication" in operation.spec.info.description: # info not in operation # key_parts = ["public_key", "private_key"] else: key_parts = ["public_key", "private_key"] # return [StringAffectedElement(f"auth.{key}", Schema(type="string")) for key in key_parts] return [ AuthAffectedElement(f"auth.{key}", Schema(type="string")) for key in key_parts ] class RBACCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[BizEnumAffectedElement]: return [ BizEnumAffectedElement( "rbac_limit", Schema( type="string", enum=[ "ORG_ADMIN", ], ), ) ] class ClusterStatusCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[BizEnumAffectedElement]: return [ BizEnumAffectedElement( "cluster_status", Schema( type="string", enum=[ "CREATING", "ACTIVE", "RESTORING", "MAINTENANCE", "INACTIVE", "MODIFYING", "PAUSING", "PAUSED", "RESUMING", ], ), ) ] class TenantStatusCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[BizEnumAffectedElement]: return [ BizEnumAffectedElement( "tenant_status", Schema( type="string", enum=[ "free", "poc", "on-demand", ], ), ) ] class CMEKCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[BizEnumAffectedElement]: return [ BizEnumAffectedElement( "cmek", Schema( type="boolean", enum=[ True, False, ], ), ) ] class CloudProviderCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[BizEnumAffectedElement]: return [ BizEnumAffectedElement( "cloud_provider", Schema( type="string", enum=[ "AWS", "AZURE", "GCP", "ON_PREMISE", ], ), ) ] class InvalidPathParameterCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[PathParamAffectedElement]: path_params = operation.get_path_parameters() return [ PathParamAffectedElement(f"path_params.{path_param.name}", path_param) for path_param in path_params ] class InvalidQueryParameterCategoryInfo(CategoryInfo): def get_affected_elements( self, operation: "EnhancedOperation" ) -> List[AffectedElement]: query_params = operation.get_query_parameters() affected_elements: List[AffectedElement] = [] for param in query_params: if isinstance(param.schema, Schema): type = param.schema.type # format = param.schema.format affected_elements.append( type_map[type](f"query_params.{param.name}", param) # type: ignore ) # TODO: change to QueryParamAffectedElement using kv pairs return [ QueryParamAffectedElement(f"query_params.{query_param.name}", query_param) for query_param in query_params ] class InvalidBodyValueCategoryInfo(CategoryInfo): def get_affected_elements( self, operation: "EnhancedOperation" ) -> List[AffectedElement]: enums, non_enums, _, _, _ = operation.get_body_parameters() affected_elements: List[AffectedElement] = [] for item in enums: affected_elements.append(EnumAffectedElement(item[0], item[1])) for item in non_enums: if isinstance(item[1], Schema): type = item[1].type # format = item[1].format affected_elements.append(type_map[type](item[0], item[1])) # type: ignore return affected_elements class InvalidBodyKeyCategoryInfo(CategoryInfo): def get_affected_elements( self, operation: "EnhancedOperation" ) -> List[AffectedElement]: _, _, _, leaf_nodes, non_leaf_nodes = operation.get_body_parameters() affected_elements: List[AffectedElement] = [] for item in leaf_nodes: affected_elements.append(LeafAffectedElement(item[0], item[1])) for item in non_leaf_nodes: affected_elements.append(NonLeafAffectedElement(item[0], item[1])) # if len(affected_elements) > 0: # affected_elements.append( # NonLeafAffectedElement("body", Schema(type="object")) # ) # body name should be ignored, so the body is implemented in openapi return affected_elements class InvalidMethodCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[BizEnumAffectedElement]: return [ BizEnumAffectedElement( "method", Schema( type="string", enum=[ "get", "post", "put", # "delete", "patch", "head", "options", "trace", "connect", ], ), ) ] class NonExistentPathCategoryInfo(CategoryInfo): def get_affected_elements( # type: ignore self, operation: "EnhancedOperation" ) -> List[PathParamAffectedElement]: resources = operation.get_path_resources() return [ PathParamAffectedElement( f"path.{resource}", Schema(type="string", default=resource) ) for resource in resources ] class PositiveCategoryInfo(CategoryInfo): def get_affected_elements( self, operation: "EnhancedOperation" ) -> List[AffectedElement]: return [] class Category(Enum): # Environments AUTH = AuthCategoryInfo("environment", "Invalid Key Provided") RBAC = RBACCategoryInfo("environment", "Different RBAC Roles") CLUSTER_STATUS = ClusterStatusCategoryInfo("environment", "Cluster Status") CMEK = CMEKCategoryInfo("environment", "CMEK") CLOUD_PROVIDER = CloudProviderCategoryInfo("environment", "Cloud Provider") # Parameters INVALID_PATH_PARAMETERS = InvalidPathParameterCategoryInfo( "request", "Invalid Path Parameters" ) INVALID_QUERY_PARAMETERS = InvalidQueryParameterCategoryInfo( "request", "Invalid Query Parameters" ) INVALID_BODY_PARAMETERS_VALUE = InvalidBodyValueCategoryInfo( "request", "Invalid Body Parameters" ) INVALID_BODY_PARAMETERS_KEY = InvalidBodyKeyCategoryInfo( "request", "Invalid Body Parameters Key" ) INVALID_METHOD = InvalidMethodCategoryInfo("request", "Invalid Method") NON_EXISTENT_PATH = NonExistentPathCategoryInfo("request", "Non Existent Path") # Operations POSITIVE = PositiveCategoryInfo("operation", "Positive Case")