Skip to content

Commit

Permalink
feat: updates create_new_with_filter with more filter types and mana…
Browse files Browse the repository at this point in the history
…gement operations (#88)

* feat: updates create_new_with_filter with more filter types and management operations

The create_new_with_filter does not support all of the filter types trestle
does. This adds the additional supported types. It also adds optional
markdown creation for continued editing.

Signed-off-by: Jennifer Power <barnabei.jennifer@gmail.com>

* chore: update tests/trestlebot/tasks/authored/test_ssp.py

Co-authored-by: beatrizmcouto <140717802+beatrizmcouto@users.noreply.github.com>

* chore: updates test_ssp.py to use os.path.join()

Signed-off-by: Jennifer Power <barnabei.jennifer@gmail.com>

---------

Signed-off-by: Jennifer Power <barnabei.jennifer@gmail.com>
Co-authored-by: beatrizmcouto <140717802+beatrizmcouto@users.noreply.github.com>
  • Loading branch information
jpower432 and beatrizmcouto authored Dec 6, 2023
1 parent 60e3829 commit fa4d953
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 32 deletions.
2 changes: 1 addition & 1 deletion tests/data/json/test_comp.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"components": [
{
"uuid": "8220b305-0271-45f9-8a21-40ab6f197f70",
"uuid": "8220b305-0271-45f9-8a21-40ab6f197f78",
"type": "Service",
"title": "test_comp",
"description": "test comp",
Expand Down
46 changes: 37 additions & 9 deletions tests/trestlebot/tasks/authored/test_ssp.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_assemble(tmp_trestle_dir: str) -> None:
"""Test to test assemble functionality for SSPs"""
# Prepare the workspace and generate the markdown
trestle_root = pathlib.Path(tmp_trestle_dir)
md_path = f"{markdown_dir}/{test_ssp_output}"
md_path = os.path.join(markdown_dir, test_ssp_output)
args = testutils.setup_for_ssp(trestle_root, test_prof, [test_comp], md_path)
ssp_generate = SSPGenerate()
assert ssp_generate._run(args) == 0
Expand All @@ -72,7 +72,7 @@ def test_assemble_no_ssp_entry(tmp_trestle_dir: str) -> None:
"""Test to trigger failure for missing SSP index"""
# Prepare the workspace and generate the markdown
trestle_root = pathlib.Path(tmp_trestle_dir)
md_path = f"{markdown_dir}/{test_ssp_output}"
md_path = os.path.join(markdown_dir, test_ssp_output)
args = testutils.setup_for_ssp(trestle_root, test_prof, [test_comp], md_path)
ssp_generate = SSPGenerate()
assert ssp_generate._run(args) == 0
Expand Down Expand Up @@ -225,7 +225,7 @@ def test_create_new_with_filter(tmp_trestle_dir: str) -> None:
"""Test to create new SSP with filtering by profile"""
# Prepare the workspace and input ssp
trestle_root = pathlib.Path(tmp_trestle_dir)
md_path = f"{markdown_dir}/{test_ssp_output}"
md_path = os.path.join(markdown_dir, test_ssp_output)
args = testutils.setup_for_ssp(
trestle_root, test_prof, [test_comp, test_comp_2], md_path
)
Expand All @@ -246,28 +246,56 @@ def test_create_new_with_filter(tmp_trestle_dir: str) -> None:
_ = testutils.setup_for_profile(trestle_root, test_prof_filter, test_prof_filter)

ssp_name = "new_ssp"
new_md_path = os.path.join(markdown_dir, ssp_name)
input_ssp = test_ssp_output

# Call create_new_with_filter with new profile
authored_ssp.create_new_with_filter(ssp_name, input_ssp, test_prof_filter, [])
authored_ssp.create_new_with_filter(
ssp_name, input_ssp, markdown_path=new_md_path, profile_name=test_prof_filter
)

ssp_index.reload()

assert ssp_index.get_profile_by_ssp(ssp_name) == test_prof_filter
assert test_comp in ssp_index.get_comps_by_ssp(ssp_name)
_, mpath = load_validate_model_name(
model_path = ModelUtils.get_model_path_for_name_and_class(
trestle_root, ssp_name, ossp.SystemSecurityPlan, FileContentType.JSON
)
assert mpath.exists()
assert model_path.exists()

ssp_name = "new_ssp_2"
new_md_path = os.path.join(markdown_dir, ssp_name)

# Call create_new_with_filter with a single compdef
authored_ssp.create_new_with_filter(ssp_name, input_ssp, "", [test_comp_2])
authored_ssp.create_new_with_filter(
ssp_name, input_ssp, markdown_path=new_md_path, compdefs=[test_comp_2]
)

ssp_index.reload()
assert ssp_index.get_profile_by_ssp(ssp_name) == test_prof
assert test_comp not in ssp_index.get_comps_by_ssp(ssp_name)
assert test_comp_2 in ssp_index.get_comps_by_ssp(ssp_name)

_, mpath = load_validate_model_name(
ssp, model_path = load_validate_model_name(
trestle_root, ssp_name, ossp.SystemSecurityPlan, FileContentType.JSON
)
assert mpath.exists()
assert model_path.exists()

assert len(ssp.system_implementation.components) == 1

component_names = [
component.title for component in ssp.system_implementation.components
]
assert test_comp_2 in component_names
assert test_comp not in component_names

# Check that without markdown path the ssp_index is not updated
ssp_name = "new_ssp_3"
authored_ssp.create_new_with_filter(
ssp_name, input_ssp, implementation_status=["implemented"]
)
ssp_index.reload()
with pytest.raises(
AuthoredObjectException, match="SSP new_ssp_3 does not exists in the index"
):
ssp_index.get_profile_by_ssp(ssp_name)
71 changes: 49 additions & 22 deletions trestlebot/tasks/authored/ssp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
from typing import Any, Dict, List, Optional

from trestle.common.err import TrestleError
from trestle.common.model_utils import ModelUtils
from trestle.core.commands.author.ssp import SSPFilter
from trestle.core.commands.common.return_codes import CmdReturnCodes
from trestle.core.repository import AgileAuthoring
from trestle.oscal.component import ComponentDefinition

from trestlebot.const import COMPDEF_KEY_NAME, LEVERAGED_SSP_KEY_NAME, PROFILE_KEY_NAME
from trestlebot.tasks.authored.base_authored import (
Expand Down Expand Up @@ -251,38 +253,58 @@ def create_new_with_filter(
self,
ssp_name: str,
input_ssp: str,
profile_name: str,
compdefs: List[str],
version: str = "",
markdown_path: str = "",
profile_name: str = "",
compdefs: Optional[List[str]] = None,
implementation_status: Optional[List[str]] = None,
control_origination: Optional[List[str]] = None,
) -> None:
"""
Create new ssp from an ssp with filtering by profile and component definitions
Create new ssp from an ssp with filters.
Args:
ssp_name: Output name for ssp
input_ssp: Input ssp to filter
profile_name: Profile to filter by
compdefs: List of component definitions to filter by
profile_name: Optional profile to filter by
compdefs: Optional list of component definitions to filter by
implementation_status: Optional implementation status to filter by
control_origination: Optional control origination to filter by
markdown_path: Optional top-level markdown path to write to for continued editing.
Notes:
This will generate SSP markdown and an index entry for a new managed SSP.
This will transform the SSP with filters. If markdown_path is provided, it will
also generate SSP markdown and an index entry for a new managed SSP for continued
management in the workspace.
"""

# Create new ssp by filtering input ssp
trestle_root = self.get_trestle_root()
trestle_path = pathlib.Path(trestle_root)
ssp_filter: SSPFilter = SSPFilter()

components_title: Optional[List[str]] = None
if compdefs:
components_title = []
for comp_def_name in compdefs:
comp_def, _ = ModelUtils.load_model_for_class(
trestle_path, comp_def_name, ComponentDefinition
)
components_title.extend(
[component.title for component in comp_def.components]
)

try:
exit_code = ssp_filter.filter_ssp(
trestle_root=trestle_path,
ssp_name=input_ssp,
profile_name=profile_name,
out_name=ssp_name,
regenerate=False,
components=compdefs,
version="",
implementation_status=None,
control_origination=None,
regenerate=True,
components=components_title,
version=version,
implementation_status=implementation_status,
control_origination=control_origination,
)

if exit_code != CmdReturnCodes.SUCCESS.value:
Expand All @@ -294,17 +316,22 @@ def create_new_with_filter(
f"Trestle filtering failed for {input_ssp}: {e}"
)

# Retrieve index information from existing ssp
if not profile_name:
profile_name = self.ssp_index.get_profile_by_ssp(input_ssp)

if not compdefs:
compdefs = self.ssp_index.get_comps_by_ssp(input_ssp)
# If markdown_path is provided, create a new managed ssp.
# this will eventually need to have a JSON to MD recovery to
# reduce manual editing.
if markdown_path:
if not profile_name:
profile_name = self.ssp_index.get_profile_by_ssp(input_ssp)

leveraged_ssp = self.ssp_index.get_leveraged_by_ssp(input_ssp)
if not compdefs:
compdefs = self.ssp_index.get_comps_by_ssp(input_ssp)

# Add new information to index
self.ssp_index.add_new_ssp(ssp_name, profile_name, compdefs, leveraged_ssp)
leveraged_ssp = self.ssp_index.get_leveraged_by_ssp(input_ssp)

# Write out index
self.ssp_index.write_out()
self.create_new_default(
ssp_name,
profile_name,
compdefs,
markdown_path,
leveraged_ssp,
)

0 comments on commit fa4d953

Please sign in to comment.