def i_expect_the_result_is_operator_than_number(_step_obj, operator, number, _stash=EmptyStash): values = _step_obj.context.stash if _stash is EmptyStash else _stash if type(values) is list: for value_set in values: i_expect_the_result_is_operator_than_number(_step_obj, operator, number, _stash=value_set) elif type(values) is dict: i_expect_the_result_is_operator_than_number(_step_obj, operator, number, values.get('values', Null)) elif type(values) is int or type(values) is str: values = int(values) if operator in ("more", "greater", "bigger"): assert values > number, "{} is not more than {}".format(values, number) elif operator in ("more and equal", "greater and equal", "bigger and equal"): assert values >= number, "{} is not more and equal than {}".format(values, number) elif operator in ("less", "lesser", "smaller"): assert values < number, "{} is not less than {}".format(values, number) elif operator in ("less and equal", "lesser and equal", "smaller and equal"): assert values <= number, "{} is not less and equal than {}".format(values, number) elif operator in ("equal",): assert values == number, "{} is not equal to {}".format(values, number) else: raise TerraformComplianceNotImplemented('Invalid operator: {}'.format(operator)) elif type(values) is Null: raise TerraformComplianceNotImplemented('Null/Empty value found on {}'.format(_step_obj.context.type))
def i_expect_the_result_is_operator_than_number(_step_obj, operator, number, _stash=EmptyStash): def fail(assertion, message): try: assert assertion, 'Failed' except AssertionError as e: raise Failure('for {} on {}. {}.'.format( _step_obj.context.address, _step_obj.context.property_name, message)) values = _step_obj.context.stash if _stash is EmptyStash else _stash if type(values) is list: for value_set in values: i_expect_the_result_is_operator_than_number(_step_obj, operator, number, _stash=value_set) elif type(values) is dict: _step_obj.context.property_name = values.get('type') _step_obj.context.address = values.get('address') i_expect_the_result_is_operator_than_number(_step_obj, operator, number, values.get('values', Null)) elif type(values) is int or type(values) is str: values = int(values) if operator in ('more', 'greater', 'bigger'): fail(values > number, '{} is not more than {}'.format(values, number)) elif operator in ('more and equal', 'greater and equal', 'bigger and equal'): fail(values >= number, '{} is not more and equal than {}'.format(values, number)) elif operator in ('less', 'lesser', 'smaller'): fail(values < number, '{} is not less than {}'.format(values, number)) elif operator in ('less and equal', 'lesser and equal', 'smaller and equal'): fail(values <= number, '{} is not less and equal than {}'.format(values, number)) elif operator in ('equal', ): fail(values == number, '{} is not equal to {}'.format(values, number)) else: raise TerraformComplianceNotImplemented( 'Invalid operator: {}'.format(operator)) elif type(values) is Null: raise TerraformComplianceNotImplemented( 'Null/Empty value found on {}'.format(_step_obj.context.type))
def i_action_them(_step_obj, action_type): if action_type == "count": # WARNING: Only case where we set stash as a dictionary, instead of a list. if isinstance(_step_obj.context.stash, list): # This means we are directly started counting without drilling down any property # Thus, our target for the count is stash itself. if _step_obj.context.property_name in types_list: _step_obj.context.stash = dict( values=len(_step_obj.context.stash)) else: if isinstance(_step_obj.context.stash[0], dict): if _step_obj.context.stash[0].get('values'): _step_obj.context.stash = seek_key_in_dict( _step_obj.context.stash, 'values') count = 0 for result in _step_obj.context.stash: count += len(result.get( 'values', {})) if result.get('values') else 1 _step_obj.context.stash = dict(values=count) else: _step_obj.context.stash = dict( values=len(_step_obj.context.stash)) else: raise TerraformComplianceNotImplemented( 'Invalid action_type in the scenario: {}'.format(action_type))
def encryption_is_enabled(_step_obj): for resource in _step_obj.context.stash: if type(resource) is dict: prop = encryption_property.get(resource['type'], None) if not prop: raise TerraformComplianceNotImplemented( 'Encryption property for {} ' 'is not implemented yet.'.format(resource['type'])) encryption_value = seek_key_in_dict( resource.get('values', {}), encryption_property[resource['type']]) if len(encryption_value): encryption_value = encryption_value[0] if type(encryption_value) is dict: encryption_value = encryption_value[encryption_property[ resource['type']]] if not encryption_value: raise Failure( 'Resource {} does not have encryption enabled ({}={}).'. format(resource['address'], prop, encryption_value)) return True
def its_value_condition_contain(_step_obj, condition, value, _stash=EmptyStash): if condition not in ('must', 'must not'): raise TerraformComplianceNotImplemented('Condition should be one of: `must`, `must not`') values = _step_obj.context.stash if _stash is EmptyStash else _stash # TODO: Update here for checking values in a list or dict. if isinstance(values, list): for elem in values: values = its_value_condition_contain(_step_obj, condition, value, elem) if isinstance(values, (int, bool, str, float)): values = dict(values=values, address=_step_obj.context.address if hasattr(_step_obj.context, 'address') else _step_obj.context.addresses) found_values = seek_value_in_dict(value, values) condition = condition == 'must' if condition and not found_values: if isinstance(values, list): objects = [] for elem in values: objects.append(elem.get('address', '???')) objects = ', '.join(objects) else: objects = values.get('address') Error(_step_obj, '{} could not found in {}.'.format(value, objects)) elif not condition and found_values: Error(_step_obj, '{} found in {}.'.format(value, get_resource_name_from_stash(found_values).get('address'))) return values
def i_action_them(_step_obj, action_type): if action_type == "count": # WARNING: Only case where we set stash as a dictionary, instead of a list. _step_obj.context.stash = {"values": len(_step_obj.context.stash)} else: raise TerraformComplianceNotImplemented( "Invalid action_type in the scenario: {}".format(action_type))
def its_key_condition_be_value(_step_obj, key, condition, value, stash=Null, depth=0): if condition not in ('must', 'must not'): raise TerraformComplianceNotImplemented( 'This step only accepts "must" and "must not" as a condition.') if stash is Null: stash = _step_obj.context.stash if not stash or stash is Null: Error( _step_obj, 'No entities found for this step to process. Check your filtering steps in this scenario.' ) return False found_values = [] for entity in stash: if isinstance(entity, dict): found_values.extend( seek_regex_key_in_dict_values(entity, key, value)) elif isinstance(entity, list): for element in entity: found_values.extend( its_key_condition_be_value(_step_obj, key, condition, element, entity, depth + 1)) elif isinstance(entity, (str, int, bool)) and (str(entity).lower == key.lower or str(entity) == value.lower): found_values.append(entity) # Return the values to the parent call. if depth > 0: return found_values condition = condition == 'must' found_values = [values for values in found_values if values is not None] obj_address = _step_obj.context.name if hasattr(_step_obj.context, 'address'): obj_address = _step_obj.context.address elif hasattr(_step_obj.context, 'addresses'): obj_address = ', '.join(_step_obj.context.addresses) if found_values and not condition: Error( _step_obj, 'Found {}({}) in {} property of {}.'.format( value, ', '.join(found_values), key, obj_address)) elif not found_values and condition: Error( _step_obj, 'Can not find {} in {} property of {}.'.format( value, key, obj_address)) return True
def its_value_condition_contain(_step_obj, condition, value, _stash=EmptyStash): values = _step_obj.context.stash if _stash is EmptyStash else _stash if isinstance(values, list): for value_set in values: its_value_condition_contain(_step_obj, condition, value, value_set) elif isinstance(values, dict): _its_value_condition_contain(_step_obj, condition, value, values.get('values', Null)) elif type(values) is Null: raise TerraformComplianceNotImplemented('Null/Empty value found on {}'.format(_step_obj.context.type))
def its_key_condition_be_value(_step_obj, key, condition, value, stash=Null, depth=0): match = _step_obj.context.match seek_regex_key_in_dict_values = match.seek_regex_key_in_dict_values if condition not in ('must', 'must not'): raise TerraformComplianceNotImplemented('This step only accepts "must" and "must not" as a condition.') condition = condition == 'must' if stash is Null: stash = _step_obj.context.stash if not stash or stash is Null: Error(_step_obj, 'No entities found for this step to process. Check your filtering steps in this scenario.') return False obj_address = _step_obj.context.name if hasattr(_step_obj.context, 'address'): obj_address = _step_obj.context.address elif hasattr(_step_obj.context, 'addresses'): obj_address = ', '.join(_step_obj.context.addresses) found_values = [] for entity in stash: if isinstance(entity, dict): found_value = seek_regex_key_in_dict_values(entity, key, value) if not found_value and condition: Error(_step_obj, 'Can not find {} in {} property of {}.'.format(value, key, match.get(entity, 'address', obj_address))) Error(_step_obj, 'Can not find {} in {} property of {}.'.format(value, key, obj_address)) # legacy error message elif found_value and not condition: Error(_step_obj, 'Found {}({}) in {} property of {}.'.format(value, ', '.join(found_values), key, obj_address)) found_values.extend(found_value) elif isinstance(entity, list): dict_entity = {f'not_{str(key)}': entity} found_value = seek_regex_key_in_dict_values(dict_entity, key, value) if not found_value and condition: Error(_step_obj, 'Can not find {} in {} property of {}.'.format(value, key, obj_address)) elif found_value and not condition: Error(_step_obj, 'Found {}({}) in {} property of {}.'.format(value, ', '.join(found_values), key, obj_address)) found_values.extend(found_value) elif isinstance(entity, (str, int, bool)): # raise error because you don't have a {key: value} if (match.equals(entity, key) or match.equals(entity, value)) and condition: Error(_step_obj, 'Value {} found in {} property of {}, but is not in {{key: value}} format.'.format(value, key, obj_address)) if condition: Error(_step_obj, 'Can not find {} in {} property of {}.'.format(value, key, obj_address)) _step_obj.context.stash = found_values return True
def i_action_them(_step_obj, action_type): if action_type == "count": # WARNING: Only case where we set stash as a dictionary, instead of a list. if type(_step_obj.context.stash) is list: if type(_step_obj.context.stash[0]) is dict(): if _step_obj.context.stash.get('values'): _step_obj.context.stash = seek_key_in_dict(_step_obj.context.stash, 'values') count = 0 for result in _step_obj.context.stash: count += len(result.get('values', {})) if result.get('values') else 1 _step_obj.context.stash = {'values': count} else: _step_obj.context.stash = {'values': len(_step_obj.context.stash)} else: raise TerraformComplianceNotImplemented('Invalid action_type in the scenario: {}'.format(action_type))
def i_expect_the_result_is_operator_than_number(_step_obj, operator, number): # TODO: Maybe iterate over the stash if it is a list and do the execution per each member ? value = int(_step_obj.context.stash.get('values', 0)) if operator == "more": assert value > number, "{} is not more than {}".format(value, number) elif operator == "more and equal": assert value >= number, "{} is not more and equal than {}".format( value, number) elif operator == "less": assert value < number, "{} is not less than {}".format(value, number) elif operator == "less and equal": assert value <= number, "{} is not less and equal than {}".format( value, number) else: raise TerraformComplianceNotImplemented( 'Invalid operator: {}'.format(operator))