예제 #1
0
    def _run(self, args):
        """Add an OSCAL component/subcomponent to the specified component.

        This method takes input a filename and a list of comma-seperated element path. Element paths are field aliases.
        The method first finds the parent model from the file and loads the file into the model.
        Then the method executes 'add' for each of the element paths specified.
        """
        args = args.__dict__
        if args[const.ARG_FILE] is None:
            raise err.TrestleError(
                f'Argument "-{const.ARG_FILE_SHORT}" is required')
        if args[const.ARG_ELEMENT] is None:
            raise err.TrestleError(
                f'Argument "-{const.ARG_ELEMENT}" is required')

        file_path = pathlib.Path(args[const.ARG_FILE])

        # Get parent model and then load json into parent model
        parent_model, parent_alias = fs.get_contextual_model_type(
            file_path.absolute())
        parent_object = parent_model.oscal_read(file_path.absolute())
        parent_element = Element(
            parent_object,
            utils.classname_to_alias(parent_model.__name__, 'json'))

        # Do _add for each element_path specified in args
        element_paths: list[str] = args[const.ARG_ELEMENT].split(',')
        for elm_path_str in element_paths:
            element_path = ElementPath(elm_path_str)
            self.add(file_path, element_path, parent_model, parent_element)
def test_load_distributed(testdata_dir, tmp_trestle_dir):
    """Test massive distributed load, that includes recusive load, list and dict."""
    # prepare trestle project dir with the file
    test_utils.ensure_trestle_config_dir(tmp_trestle_dir)

    test_data_source = testdata_dir / 'split_merge/step4_split_groups_array/catalogs'

    catalogs_dir = Path('catalogs/')
    mycatalog_dir = catalogs_dir / 'mycatalog'
    catalog_file = mycatalog_dir / 'catalog.json'

    # Copy files from test/data/split_merge/step4
    shutil.rmtree(catalogs_dir)
    shutil.copytree(test_data_source, catalogs_dir)

    actual_model_type, actual_model_alias, actual_model_instance = load_distributed(
        catalog_file)

    expected_model_type, _ = fs.get_contextual_model_type(
        catalog_file.absolute())

    expected_model_instance = Catalog.oscal_read(
        testdata_dir / 'split_merge/load_distributed/catalog.json')

    assert actual_model_type == expected_model_type
    assert actual_model_alias == 'catalog'
    assert len(
        list(dictdiffer.diff(expected_model_instance,
                             actual_model_instance))) == 0
예제 #3
0
    def _list_available_elements(self) -> None:
        """List element paths that can be merged from the current context."""
        current_model, current_alias = fs.get_contextual_model_type(Path.cwd())

        current_filename = f'{current_alias}.json'

        self._list_options_for_merge(Path.cwd(), current_alias, current_model, current_filename)
예제 #4
0
    def _run(self, args) -> None:
        """Validate an OSCAL file in different modes."""
        if args.file is None:
            raise TrestleError(f'Argument "-{const.ARG_FILE_SHORT}" is required')

        if args.mode is None:
            raise TrestleError(f'Argument "-{const.ARG_MODE_SHORT}" is required')
        mode = args.mode
        if mode != const.VAL_MODE_DUPLICATES:
            raise TrestleError(f'Mode value "{mode}" is not recognized.')

        if args.item is None:
            raise TrestleError(f'Argument "-{const.ARG_ITEM_SHORT}" is required')
        item = args.item

        file_path = pathlib.Path(args.file).absolute()
        model_type, _ = fs.get_contextual_model_type(file_path)
        model: OscalBaseModel = model_type.oscal_read(file_path)

        loe = validator.find_values_by_name(model, item)
        if loe:
            nitems = len(loe)
            is_valid = nitems == len(set(loe))
            if is_valid:
                self.out(f'The model is valid and contains no duplicates of item {args.item}')
            else:
                self.out(f'The model is invalid and contains duplicates of item {args.item}')
                raise TrestleValidationError(f'Model {args.file} is invalid with duplicate values of {args.item}')
        else:
            self.out(f'The model is valid but contains no items of name {args.item}')
    def validate(self, args: argparse.Namespace) -> int:
        """Perform the validation."""
        file_path = pathlib.Path(args.file).absolute()
        model_type, _ = fs.get_contextual_model_type(file_path)
        model: OscalBaseModel = model_type.oscal_read(file_path)

        loe = find_values_by_name(model, args.item)
        if loe:
            nitems = len(loe)
            return 0 if nitems == len(set(loe)) else 1
        return 0
def test_load_dict(testdata_dir, tmp_trestle_dir):
    """Test loading of distributed dict."""
    # prepare trestle project dir with the file
    test_utils.ensure_trestle_config_dir(tmp_trestle_dir)

    test_data_source = testdata_dir / 'split_merge/step4_split_groups_array/catalogs'

    catalogs_dir = Path('catalogs/')
    mycatalog_dir = catalogs_dir / 'mycatalog'
    catalog_dir = mycatalog_dir / 'catalog'

    # Copy files from test/data/split_merge/step4
    shutil.rmtree(catalogs_dir)
    shutil.copytree(test_data_source, catalogs_dir)

    actual_model_type, actual_model_alias, actual_model_instance = _load_dict(
        catalog_dir / 'metadata/responsible-parties')

    expexted_model_instance = {
        'contact':
        ResponsibleParty.oscal_read(
            catalog_dir /
            'metadata/responsible-parties/contact__responsible-party.json'),
        'creator':
        ResponsibleParty.oscal_read(
            catalog_dir /
            'metadata/responsible-parties/creator__responsible-party.json')
    }
    assert len(
        list(dictdiffer.diff(actual_model_instance,
                             expexted_model_instance))) == 0
    assert actual_model_alias == 'catalog.metadata.responsible-parties'

    expected_model_type, _ = fs.get_contextual_model_type(
        (catalog_dir / 'metadata/responsible-parties/').absolute())
    assert actual_model_type.__fields__[
        '__root__'].outer_type_ == expected_model_type
예제 #7
0
def test_get_contextual_model_type(tmp_dir):
    """Test get model type and alias based on filesystem context."""
    with pytest.raises(TrestleError):
        fs.get_contextual_model_type(tmp_dir / 'invalidpath') is None

    with pytest.raises(TrestleError):
        fs.get_contextual_model_type(tmp_dir) is None

    create_sample_catalog_project(tmp_dir)

    catalogs_dir = tmp_dir / 'catalogs'
    mycatalog_dir = catalogs_dir / 'mycatalog'
    catalog_dir = mycatalog_dir / 'catalog'
    metadata_dir = catalog_dir / 'metadata'
    roles_dir = metadata_dir / 'roles'
    rps_dir = metadata_dir / 'responsible-parties'
    props_dir = metadata_dir / 'properties'
    groups_dir = mycatalog_dir / 'groups'
    group_dir = groups_dir / f'00000{IDX_SEP}group'
    controls_dir = group_dir / 'controls'

    with pytest.raises(TrestleError):
        assert fs.get_contextual_model_type(catalogs_dir) is None

    assert fs.get_contextual_model_type(mycatalog_dir) == (catalog.Catalog,
                                                           'catalog')
    assert fs.get_contextual_model_type(
        mycatalog_dir / 'catalog.json') == (catalog.Catalog, 'catalog')
    assert fs.get_contextual_model_type(
        catalog_dir / 'back-matter.json') == (catalog.BackMatter,
                                              'catalog.back-matter')
    assert fs.get_contextual_model_type(
        catalog_dir / 'metadata.yaml') == (catalog.Metadata,
                                           'catalog.metadata')
    assert fs.get_contextual_model_type(metadata_dir) == (catalog.Metadata,
                                                          'catalog.metadata')
    assert fs.get_contextual_model_type(roles_dir) == (
        List[catalog.Role], 'catalog.metadata.roles')
    assert fs.get_contextual_model_type(
        roles_dir / '00000__role.json') == (catalog.Role,
                                            'catalog.metadata.roles.role')
    assert fs.get_contextual_model_type(rps_dir) == (
        Dict[str,
             catalog.ResponsibleParty], 'catalog.metadata.responsible-parties')
    assert fs.get_contextual_model_type(
        rps_dir / 'creator__responsible-party.json') == (
            catalog.ResponsibleParty,
            'catalog.metadata.responsible-parties.responsible-party')
    assert fs.get_contextual_model_type(props_dir) == (
        List[catalog.Prop], 'catalog.metadata.properties')
    assert fs.get_contextual_model_type(
        props_dir /
        f'00000{IDX_SEP}prop.json') == (catalog.Prop,
                                        'catalog.metadata.properties.prop')
    assert fs.get_contextual_model_type(groups_dir) == (List[catalog.Group],
                                                        'catalog.groups')
    assert fs.get_contextual_model_type(
        groups_dir / f'00000{IDX_SEP}group.json') == (catalog.Group,
                                                      'catalog.groups.group')
    assert fs.get_contextual_model_type(group_dir) == (catalog.Group,
                                                       'catalog.groups.group')
    assert fs.get_contextual_model_type(controls_dir) == (
        List[catalog.Control], 'catalog.groups.group.controls')
    assert fs.get_contextual_model_type(
        controls_dir / f'00000{IDX_SEP}control.json') == (
            catalog.Control, 'catalog.groups.group.controls.control')
예제 #8
0
def test_get_contextual_model_type(tmp_path: pathlib.Path) -> None:
    """Test get model type and alias based on filesystem context."""
    import trestle.core.utils as cutils
    with pytest.raises(TrestleError):
        fs.get_contextual_model_type(tmp_path / 'invalidpath')

    with pytest.raises(TrestleError):
        fs.get_contextual_model_type(tmp_path)

    create_sample_catalog_project(tmp_path)

    catalogs_dir = tmp_path / 'catalogs'
    mycatalog_dir = catalogs_dir / 'mycatalog'
    catalog_dir = mycatalog_dir / 'catalog'
    metadata_dir = catalog_dir / 'metadata'
    roles_dir = metadata_dir / 'roles'
    rps_dir = metadata_dir / 'responsible-parties'
    props_dir = metadata_dir / 'props'
    groups_dir = mycatalog_dir / 'groups'
    group_dir = groups_dir / f'00000{IDX_SEP}group'
    controls_dir = group_dir / 'controls'

    with pytest.raises(TrestleError):
        assert fs.get_contextual_model_type(catalogs_dir) is None

    assert fs.get_contextual_model_type(mycatalog_dir) == (catalog.Catalog,
                                                           'catalog')
    assert fs.get_contextual_model_type(
        mycatalog_dir / 'catalog.json') == (catalog.Catalog, 'catalog')
    assert fs.get_contextual_model_type(
        catalog_dir / 'back-matter.json') == (catalog.BackMatter,
                                              'catalog.back-matter')
    assert fs.get_contextual_model_type(
        catalog_dir / 'metadata.yaml') == (catalog.Metadata,
                                           'catalog.metadata')
    assert fs.get_contextual_model_type(metadata_dir) == (catalog.Metadata,
                                                          'catalog.metadata')
    # The line below is no longer possible to execute in many situations due to the constrained lists
    # assert fs.get_contextual_model_type(roles_dir) == (List[catalog.Role], 'catalog.metadata.roles') # noqa: E800
    (type_, element) = fs.get_contextual_model_type(roles_dir)
    assert cutils.get_origin(type_) == list
    assert element == 'catalog.metadata.roles'
    assert fs.get_contextual_model_type(
        roles_dir / '00000__role.json') == (catalog.Role,
                                            'catalog.metadata.roles.role')
    assert fs.get_contextual_model_type(rps_dir) == (
        Dict[str,
             catalog.ResponsibleParty], 'catalog.metadata.responsible-parties')
    assert fs.get_contextual_model_type(
        rps_dir / 'creator__responsible-party.json') == (
            catalog.ResponsibleParty,
            'catalog.metadata.responsible-parties.responsible-party')
    (type_, element) = fs.get_contextual_model_type(props_dir)
    assert cutils.get_origin(type_) == list
    assert cutils.get_inner_type(type_) == catalog.Property
    assert element == 'catalog.metadata.props'
    (expected_type, expected_json_path) = fs.get_contextual_model_type(
        props_dir / f'00000{IDX_SEP}property.json')
    assert expected_type == catalog.Property
    assert expected_json_path == 'catalog.metadata.props.property'
    assert cutils.get_origin(type_) == list
    assert fs.get_contextual_model_type(
        groups_dir / f'00000{IDX_SEP}group.json') == (catalog.Group,
                                                      'catalog.groups.group')
    assert fs.get_contextual_model_type(group_dir) == (catalog.Group,
                                                       'catalog.groups.group')
    assert fs.get_contextual_model_type(
        controls_dir / f'00000{IDX_SEP}control.json') == (
            catalog.Control, 'catalog.groups.group.controls.control')
예제 #9
0
    def _run(self, args: argparse.Namespace) -> int:
        """Remove an OSCAL component/subcomponent to the specified component.

        This method takes input a filename and a list of comma-seperated element path. Element paths are field aliases.
        The method first finds the parent model from the file and loads the file into the model.
        Then the method executes 'remove' for each of the element paths specified.
        """
        log.set_log_level_from_args(args)
        args_dict = args.__dict__

        file_path = pathlib.Path(args_dict[const.ARG_FILE])

        # Get parent model and then load json into parent model
        try:
            parent_model, parent_alias = fs.get_contextual_model_type(file_path.absolute())
        except Exception as err:
            logger.debug(f'fs.get_contextual_model_type() failed: {err}')
            logger.error(f'Remove failed (fs.get_contextual_model_type()): {err}')
            return 1

        try:
            parent_object = parent_model.oscal_read(file_path.absolute())
        except Exception as err:
            logger.debug(f'parent_model.oscal_read() failed: {err}')
            logger.error(f'Remove failed (parent_model.oscal_read()): {err}')
            return 1

        parent_element = Element(parent_object, utils.classname_to_alias(parent_model.__name__, 'json'))

        add_plan = Plan()

        # Do _remove for each element_path specified in args
        element_paths: List[str] = str(args_dict[const.ARG_ELEMENT]).split(',')
        for elm_path_str in element_paths:
            element_path = ElementPath(elm_path_str)
            try:
                remove_action, parent_element = self.remove(element_path, parent_model, parent_element)
            except TrestleError as err:
                logger.debug(f'self.remove() failed: {err}')
                logger.error(f'Remove failed (self.remove()): {err}')
                return 1
            add_plan.add_action(remove_action)

        create_action = CreatePathAction(file_path.absolute(), True)
        write_action = WriteFileAction(
            file_path.absolute(), parent_element, FileContentType.to_content_type(file_path.suffix)
        )
        add_plan.add_action(remove_action)
        add_plan.add_action(create_action)
        add_plan.add_action(write_action)

        try:
            add_plan.simulate()
        except TrestleError as err:
            logger.debug(f'Remove failed at simulate(): {err}')
            logger.error(f'Remove failed (simulate()): {err}')
            return 1

        try:
            add_plan.execute()
        except TrestleError as err:
            logger.debug(f'Remove failed at execute(): {err}')
            logger.error(f'Remove failed (execute()): {err}')
            return 1

        return 0