Esempio n. 1
0
class TestTemplateProperty(unittest.TestCase):
    def test_single_parameter(self):
        self.add_parameter(create_parameter_model('param'))
        property = self.create_property('Hello, ${param}!')

        self.set_value('param', 'world')
        self.assertEqual('Hello, world!', property.get())

    def test_single_parameter_when_no_value(self):
        self.add_parameter(create_parameter_model('param'))
        property = self.create_property('Hello, ${param}!')

        self.assertIsNone(property.get())

    def test_single_parameter_with_value_on_init(self):
        self.add_parameter(create_parameter_model('param'))
        self.set_value('param', 'world')
        property = self.create_property('Hello, ${param}!')

        self.assertEqual('Hello, world!', property.get())

    def test_no_dependencies(self):
        self.add_parameter(create_parameter_model('param'))
        property = self.create_property('Test value')

        self.assertEqual('Test value', property.get())

    def test_multiple_parameters(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))

        property = self.create_property('Hello, ${p1}, ${p2} and ${p3}!')

        self.set_value('p1', 'John')
        self.set_value('p2', 'Mary')
        self.set_value('p3', 'world')

        self.assertEqual('Hello, John, Mary and world!', property.get())

    def test_multiple_parameters_when_one_missing(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))

        property = self.create_property('Hello, ${p1}, ${p2} and ${p3}!')

        self.set_value('p1', 'John')
        self.set_value('p3', 'world')

        self.assertIsNone(property.get())

    def test_multiple_parameters_when_one_becomes_missing(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))
        self.set_value('p1', 'John')
        self.set_value('p2', 'Mary')
        self.set_value('p3', 'world')
        property = self.create_property('Hello, ${p1}, ${p2} and ${p3}!')

        self.set_value('p3', None)

        self.assertIsNone(property.get())

    def test_multiple_parameters_when_one_repeats(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        property = self.create_property('Hello ${p1}, ${p1}, ${p2} and ${p1}!')

        self.set_value('p1', 'John')
        self.set_value('p2', 'Mary')

        self.assertEqual('Hello John, John, Mary and John!', property.get())

    def test_multiple_parameters_get_required(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))
        property = self.create_property('Hello ${p1}, ${p3} and ${p2}!')

        self.assertCountEqual(['p1', 'p2', 'p3'], property.required_parameters)

    def test_value_without_parameter(self):
        self.set_value('p1', 'John')
        property = self.create_property('Hello, ${p1}!')

        self.assertEqual('Hello, ${p1}!', property.get())

    def test_late_add_single_parameter(self):
        self.set_value('p1', 'John')
        property = self.create_property('Hello, ${p1}!')

        self.add_parameter(create_parameter_model('p1'))

        self.assertEqual('Hello, John!', property.get())

    def test_late_remove_single_parameter(self):
        parameter = create_parameter_model('p1')
        self.add_parameter(parameter)
        self.set_value('p1', 'John')
        property = self.create_property('Hello, ${p1}!')

        self.parameters.remove(parameter)

        self.assertEqual('Hello, ${p1}!', property.get())

    def setUp(self):
        super().setUp()

        self.parameters = ObservableList()
        self.values = ObservableDict()

    def create_property(self, template):
        return _TemplateProperty(template, self.parameters, self.values)

    def add_parameter(self, config):
        self.parameters.append(config)

    def set_value(self, name, value):
        self.values[name] = value
Esempio n. 2
0
class ConfigModel:

    def __init__(self,
                 config_object,
                 path,
                 username,
                 audit_name,
                 pty_enabled_default=True,
                 ansi_enabled_default=True,
                 parameter_values=None):
        super().__init__()

        short_config = read_short(path, config_object)
        self.name = short_config.name
        self._ansi_enabled_default = ansi_enabled_default
        self._pty_enabled_default = pty_enabled_default
        self._config_folder = os.path.dirname(path)

        self._username = username
        self._audit_name = audit_name

        self.parameters = ObservableList()
        self.parameter_values = ObservableDict()

        self._original_config = config_object
        self._included_config_path = _TemplateProperty(config_object.get('include'),
                                                       parameters=self.parameters,
                                                       values=self.parameter_values)
        self._included_config_prop.bind(self._included_config_path, self._path_to_json)

        self._reload_config()

        self._init_parameters(username, audit_name)

        if parameter_values is not None:
            self.set_all_param_values(parameter_values)
        else:
            for parameter in self.parameters:
                self.parameter_values[parameter.name] = parameter.default

        self._reload_parameters({})

        self._included_config_prop.subscribe(lambda old, new: self._reload(old))

    def set_param_value(self, param_name, value):
        parameter = self.find_parameter(param_name)
        if parameter is None:
            LOGGER.warning('Parameter ' + param_name + ' does not exist in ' + self.name)
            return
        validation_error = parameter.validate_value(value, ignore_required=True)

        if validation_error is not None:
            self.parameter_values[param_name] = None
            raise InvalidValueException(param_name, validation_error)

        self.parameter_values[param_name] = value

    def set_all_param_values(self, param_values):
        original_values = dict(self.parameter_values)
        processed = {}

        anything_changed = True
        while (len(processed) < len(self.parameters)) and anything_changed:
            anything_changed = False

            for parameter in self.parameters:
                if parameter.name in processed:
                    continue

                required_parameters = parameter.get_required_parameters()
                if required_parameters and any(r not in processed for r in required_parameters):
                    continue

                value = parameter.normalize_user_value(param_values.get(parameter.name))
                validation_error = parameter.validate_value(value)
                if validation_error:
                    self.parameter_values.set(original_values)
                    raise InvalidValueException(parameter.name, validation_error)

                self.parameter_values[parameter.name] = value
                processed[parameter.name] = parameter
                anything_changed = True

            if not anything_changed:
                remaining = [p.name for p in self.parameters if p.name not in processed]
                self.parameter_values.set(original_values)
                raise Exception('Could not resolve order for dependencies. Remaining: ' + str(remaining))

        for key, value in param_values.items():
            if self.find_parameter(key) is None:
                LOGGER.warning('Incoming value for unknown parameter ' + key)

    def list_files_for_param(self, parameter_name, path):
        parameter = self.find_parameter(parameter_name)
        if not parameter:
            raise ParameterNotFoundException(parameter_name)

        return parameter.list_files(path)

    def _init_parameters(self, username, audit_name):
        original_parameter_configs = self._original_config.get('parameters', [])
        for parameter_config in original_parameter_configs:
            parameter = ParameterModel(parameter_config, username, audit_name,
                                       lambda: self.parameters,
                                       self.parameter_values,
                                       self.working_directory)
            self.parameters.append(parameter)

        self._validate_parameter_configs()

    def _reload(self, old_included_config):
        self._reload_config()
        self._reload_parameters(old_included_config)

    def _reload_config(self):
        if self._included_config is None:
            config = self._original_config
        else:
            config = merge_dicts(self._original_config, self._included_config, ignored_keys=['parameters'])

        self.script_command = config.get('script_path')
        self.description = config.get('description')
        self.working_directory = config.get('working_directory')

        required_terminal = read_bool_from_config('requires_terminal', config, default=self._pty_enabled_default)
        self.requires_terminal = required_terminal

        ansi_enabled = read_bool_from_config('bash_formatting', config, default=self._ansi_enabled_default)
        self.ansi_enabled = ansi_enabled

        self.output_files = config.get('output_files', [])

    def _reload_parameters(self, old_included_config):
        original_parameters_names = {p.get('name') for p in self._original_config.get('parameters', [])}

        if old_included_config and old_included_config.get('parameters'):
            old_included_param_names = {p.get('name') for p in old_included_config.get('parameters', [])}
            for param_name in old_included_param_names:
                if param_name in original_parameters_names:
                    continue

                parameter = self.find_parameter(param_name)
                self.parameters.remove(parameter)

        if self._included_config is not None:
            included_parameter_configs = self._included_config.get('parameters', [])
            for parameter_config in included_parameter_configs:
                parameter_name = parameter_config.get('name')
                parameter = self.find_parameter(parameter_name)
                if parameter is None:
                    parameter = ParameterModel(parameter_config, self._username, self._audit_name,
                                               lambda: self.parameters,
                                               self.parameter_values,
                                               self.working_directory)
                    self.parameters.append(parameter)

                    if parameter.name not in self.parameter_values:
                        self.parameter_values[parameter.name] = parameter.default
                    continue
                else:
                    LOGGER.warning('Parameter ' + parameter_name + ' exists in original and included file. '
                                   + 'This is now allowed! Included parameter is ignored')
                    continue

    def find_parameter(self, param_name):
        for parameter in self.parameters:
            if parameter.name == param_name:
                return parameter
        return None

    def _validate_parameter_configs(self):
        for parameter in self.parameters:
            parameter.validate_parameter_dependencies(self.parameters)

    def _path_to_json(self, path):
        if path is None:
            return None

        path = file_utils.normalize_path(path, self._config_folder)

        if os.path.exists(path):
            try:
                file_content = file_utils.read_file(path)
                return json.loads(file_content)
            except:
                LOGGER.exception('Failed to load included file ' + path)
                return None
        else:
            LOGGER.warning('Failed to load included file, path does not exist: ' + path)
            return None
Esempio n. 3
0
class ConfigModel:
    def __init__(self,
                 config_object,
                 path,
                 username,
                 audit_name,
                 pty_enabled_default=True,
                 ansi_enabled_default=True,
                 parameter_values=None):
        super().__init__()

        short_config = read_short(path, config_object)
        self.name = short_config.name
        self._ansi_enabled_default = ansi_enabled_default
        self._pty_enabled_default = pty_enabled_default
        self._config_folder = os.path.dirname(path)

        self._username = username
        self._audit_name = audit_name
        self.schedulable = False

        self.parameters = ObservableList()
        self.parameter_values = ObservableDict()

        self._original_config = config_object
        self._included_config_path = _TemplateProperty(
            config_object.get('include'),
            parameters=self.parameters,
            values=self.parameter_values)
        self._included_config_prop.bind(self._included_config_path,
                                        self._path_to_json)

        self._reload_config()

        self.parameters.subscribe(self)

        self._init_parameters(username, audit_name)

        if parameter_values is not None:
            self.set_all_param_values(parameter_values)
        else:
            for parameter in self.parameters:
                self.parameter_values[parameter.name] = parameter.default

        self._reload_parameters({})

        self._included_config_prop.subscribe(
            lambda old, new: self._reload(old))

    def set_param_value(self, param_name, value):
        parameter = self.find_parameter(param_name)
        if parameter is None:
            LOGGER.warning('Parameter ' + param_name + ' does not exist in ' +
                           self.name)
            return
        validation_error = parameter.validate_value(value,
                                                    ignore_required=True)

        if validation_error is not None:
            self.parameter_values[param_name] = None
            raise InvalidValueException(param_name, validation_error)

        self.parameter_values[param_name] = value

    def set_all_param_values(self, param_values):
        original_values = dict(self.parameter_values)
        processed = {}

        anything_changed = True
        while (len(processed) < len(self.parameters)) and anything_changed:
            anything_changed = False

            for parameter in self.parameters:
                if parameter.name in processed:
                    continue

                required_parameters = parameter.get_required_parameters()
                if required_parameters and any(r not in processed
                                               for r in required_parameters):
                    continue

                value = parameter.normalize_user_value(
                    param_values.get(parameter.name))
                validation_error = parameter.validate_value(value)
                if validation_error:
                    self.parameter_values.set(original_values)
                    raise InvalidValueException(parameter.name,
                                                validation_error)

                self.parameter_values[parameter.name] = value
                processed[parameter.name] = parameter
                anything_changed = True

            if not anything_changed:
                remaining = [
                    p.name for p in self.parameters if p.name not in processed
                ]
                self.parameter_values.set(original_values)
                raise Exception(
                    'Could not resolve order for dependencies. Remaining: ' +
                    str(remaining))

        for key, value in param_values.items():
            if self.find_parameter(key) is None:
                LOGGER.warning('Incoming value for unknown parameter ' + key)

    def list_files_for_param(self, parameter_name, path):
        parameter = self.find_parameter(parameter_name)
        if not parameter:
            raise ParameterNotFoundException(parameter_name)

        return parameter.list_files(path)

    def _init_parameters(self, username, audit_name):
        original_parameter_configs = self._original_config.get(
            'parameters', [])
        for parameter_config in original_parameter_configs:
            parameter = ParameterModel(parameter_config, username, audit_name,
                                       lambda: self.parameters,
                                       self.parameter_values,
                                       self.working_directory)
            self.parameters.append(parameter)

        self._validate_parameter_configs()

    def _reload(self, old_included_config):
        self._reload_config()
        self._reload_parameters(old_included_config)

    def _reload_config(self):
        if self._included_config is None:
            config = self._original_config
        else:
            config = merge_dicts(self._original_config,
                                 self._included_config,
                                 ignored_keys=['parameters'])

        self.script_command = config.get('script_path')
        self.description = replace_auth_vars(config.get('description'),
                                             self._username, self._audit_name)
        self.working_directory = config.get('working_directory')

        required_terminal = read_bool_from_config(
            'requires_terminal', config, default=self._pty_enabled_default)
        self.requires_terminal = required_terminal

        ansi_enabled = read_bool_from_config(
            'bash_formatting', config, default=self._ansi_enabled_default)
        self.ansi_enabled = ansi_enabled

        self.output_files = config.get('output_files', [])

        if config.get('scheduling'):
            self.schedulable = read_bool_from_config('enabled',
                                                     config.get('scheduling'),
                                                     default=False)

        if not self.script_command:
            raise Exception('No script_path is specified for ' + self.name)

    def _reload_parameters(self, old_included_config):
        original_parameters_names = {
            p.get('name')
            for p in self._original_config.get('parameters', [])
        }

        if old_included_config and old_included_config.get('parameters'):
            old_included_param_names = {
                p.get('name')
                for p in old_included_config.get('parameters', [])
            }
            for param_name in old_included_param_names:
                if param_name in original_parameters_names:
                    continue

                parameter = self.find_parameter(param_name)
                self.parameters.remove(parameter)

        if self._included_config is not None:
            included_parameter_configs = self._included_config.get(
                'parameters', [])
            for parameter_config in included_parameter_configs:
                parameter_name = parameter_config.get('name')
                parameter = self.find_parameter(parameter_name)
                if parameter is None:
                    parameter = ParameterModel(parameter_config,
                                               self._username,
                                               self._audit_name,
                                               lambda: self.parameters,
                                               self.parameter_values,
                                               self.working_directory)
                    self.parameters.append(parameter)

                    if parameter.name not in self.parameter_values:
                        self.parameter_values[
                            parameter.name] = parameter.default
                    continue
                else:
                    LOGGER.warning(
                        'Parameter ' + parameter_name +
                        ' exists in original and included file. ' +
                        'This is now allowed! Included parameter is ignored')
                    continue

    def find_parameter(self, param_name):
        for parameter in self.parameters:
            if parameter.name == param_name:
                return parameter
        return None

    def on_add(self, parameter, index):
        if self.schedulable and parameter.secure:
            LOGGER.warning(
                'Disabling schedulable functionality, because parameter ' +
                parameter.str_name() + ' is secure')
            self.schedulable = False

    def on_remove(self, parameter):
        pass

    def _validate_parameter_configs(self):
        for parameter in self.parameters:
            parameter.validate_parameter_dependencies(self.parameters)

    def _path_to_json(self, path):
        if path is None:
            return None

        path = file_utils.normalize_path(path, self._config_folder)

        if os.path.exists(path):
            try:
                file_content = file_utils.read_file(path)
                return json.loads(file_content)
            except:
                LOGGER.exception('Failed to load included file ' + path)
                return None
        else:
            LOGGER.warning(
                'Failed to load included file, path does not exist: ' + path)
            return None
Esempio n. 4
0
class TestTemplateProperty(unittest.TestCase):
    def test_single_parameter(self):
        self.add_parameter(create_parameter_model('param'))
        property = self.create_property('Hello, ${param}!')

        self.set_value('param', 'world')
        self.assertEqual('Hello, world!', property.get())

    def test_single_parameter_when_no_value(self):
        self.add_parameter(create_parameter_model('param'))
        property = self.create_property('Hello, ${param}!')

        self.assertIsNone(property.get())

    def test_single_parameter_with_value_on_init(self):
        self.add_parameter(create_parameter_model('param'))
        self.set_value('param', 'world')
        property = self.create_property('Hello, ${param}!')

        self.assertEqual('Hello, world!', property.get())

    def test_no_dependencies(self):
        self.add_parameter(create_parameter_model('param'))
        property = self.create_property('Test value')

        self.assertEqual('Test value', property.get())

    def test_multiple_parameters(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))

        property = self.create_property('Hello, ${p1}, ${p2} and ${p3}!')

        self.set_value('p1', 'John')
        self.set_value('p2', 'Mary')
        self.set_value('p3', 'world')

        self.assertEqual('Hello, John, Mary and world!', property.get())

    def test_multiple_parameters_when_one_missing(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))

        property = self.create_property('Hello, ${p1}, ${p2} and ${p3}!')

        self.set_value('p1', 'John')
        self.set_value('p3', 'world')

        self.assertIsNone(property.get())

    def test_multiple_parameters_when_one_becomes_missing(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))
        self.set_value('p1', 'John')
        self.set_value('p2', 'Mary')
        self.set_value('p3', 'world')
        property = self.create_property('Hello, ${p1}, ${p2} and ${p3}!')

        self.set_value('p3', None)

        self.assertIsNone(property.get())

    def test_multiple_parameters_when_one_repeats(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        property = self.create_property('Hello ${p1}, ${p1}, ${p2} and ${p1}!')

        self.set_value('p1', 'John')
        self.set_value('p2', 'Mary')

        self.assertEqual('Hello John, John, Mary and John!', property.get())

    def test_multiple_parameters_get_required(self):
        self.add_parameter(create_parameter_model('p1'))
        self.add_parameter(create_parameter_model('p2'))
        self.add_parameter(create_parameter_model('p3'))
        property = self.create_property('Hello ${p1}, ${p3} and ${p2}!')

        self.assertCountEqual(['p1', 'p2', 'p3'], property.required_parameters)

    def test_value_without_parameter(self):
        self.set_value('p1', 'John')
        property = self.create_property('Hello, ${p1}!')

        self.assertEqual('Hello, ${p1}!', property.get())

    def test_late_add_single_parameter(self):
        self.set_value('p1', 'John')
        property = self.create_property('Hello, ${p1}!')

        self.add_parameter(create_parameter_model('p1'))

        self.assertEqual('Hello, John!', property.get())

    def test_late_remove_single_parameter(self):
        parameter = create_parameter_model('p1')
        self.add_parameter(parameter)
        self.set_value('p1', 'John')
        property = self.create_property('Hello, ${p1}!')

        self.parameters.remove(parameter)

        self.assertEqual('Hello, ${p1}!', property.get())

    def setUp(self):
        super().setUp()

        self.parameters = ObservableList()
        self.values = ObservableDict()

    def create_property(self, template):
        return _TemplateProperty(template, self.parameters, self.values)

    def add_parameter(self, config):
        self.parameters.append(config)

    def set_value(self, name, value):
        self.values[name] = value