def create_policy_sid_namespace(service, access_level, resource_type_name, condition_block=None): """ Simply generates the SID name. The SID groups ARN types that share an access level. For example, S3 objects vs. SSM Parameter have different ARN types - as do S3 objects vs S3 buckets. That's how we choose to group them. Arguments: service: `ssm` access_level: `Read` resource_type_name: `parameter` condition_block: `{"condition_key_string": "ec2:ResourceTag/purpose", "condition_type_string": "StringEquals", "condition_value": "test"}` Returns: String: A string like `SsmReadParameter` """ # Sanitize the resource_type_name; otherwise we hit some list conversion # errors resource_type_name = re.sub("[^A-Za-z0-9]+", "", resource_type_name) # Also remove the space from the Access level, if applicable. This only # applies for "Permissions management" access_level = re.sub("[^A-Za-z0-9]+", "", access_level) sid_namespace_prefix = ( capitalize_first_character(strip_special_characters(service)) + capitalize_first_character(access_level) + capitalize_first_character(resource_type_name)) if condition_block: condition_key_namespace = re.sub( "[^A-Za-z0-9]+", "", condition_block["condition_key_string"]) condition_type_namespace = condition_block["condition_type_string"] condition_value_namespace = re.sub("[^A-Za-z0-9]+", "", condition_block["condition_value"]) sid_namespace_condition_suffix = ( f"{capitalize_first_character(condition_key_namespace)}" f"{capitalize_first_character(condition_type_namespace)}" f"{capitalize_first_character(condition_value_namespace)}") sid_namespace = sid_namespace_prefix + sid_namespace_condition_suffix else: sid_namespace = sid_namespace_prefix return sid_namespace
def get_rendered_policy(self, minimize=None): """ Get the JSON rendered policy Arguments: minimize: Reduce the character count of policies without creating overlap with other action names Returns: Dictionary: The IAM Policy JSON """ statements = [] # Only set the actions to lowercase if minimize is provided all_actions = get_all_actions(lowercase=True) # render the policy sids_to_be_changed = [] for sid in self.sids: temp_actions = self.sids[sid]["actions"] if len(temp_actions) == 0: logger.debug(f"No actions for sid {sid}") continue actions = [] if self.exclude_actions: for temp_action in temp_actions: if temp_action.lower() in self.exclude_actions: logger.debug(f"\tExcluded action: {temp_action}") else: if temp_action not in actions: actions.append(temp_action) else: actions = temp_actions # temp_actions.clear() match_found = False if minimize is not None and isinstance(minimize, int): logger.debug("Minimizing statements...") actions = minimize_statement_actions(actions, all_actions, minchars=minimize) # searching in the existing statements # further minimizing the the output for stmt in statements: if stmt["Resource"] == self.sids[sid]["arn"]: stmt["Action"].extend(actions) match_found = True sids_to_be_changed.append(stmt["Sid"]) break logger.debug(f"Adding statement with SID {sid}") logger.debug(f"{sid} SID has the actions: {actions}") logger.debug( f"{sid} SID has the resources: {self.sids[sid]['arn']}") if not match_found: statements.append({ "Sid": sid, "Effect": "Allow", "Action": actions, "Resource": self.sids[sid]["arn"], }) if sids_to_be_changed: for stmt in statements: if stmt['Sid'] in sids_to_be_changed: arn_details = parse_arn(stmt['Resource'][0]) resource_path = arn_details.get("resource_path") resource_sid_segment = strip_special_characters( f"{arn_details['resource']}{resource_path}") stmt['Sid'] = create_policy_sid_namespace( arn_details['service'], "Mult", resource_sid_segment) policy = {"Version": POLICY_LANGUAGE_VERSION, "Statement": statements} return policy