Skip to content

Commit

Permalink
Merge pull request #323 from intuitem/remove/_equirement_levels
Browse files Browse the repository at this point in the history
Remove requirement level
  • Loading branch information
nas-tabchiche authored Apr 25, 2024
2 parents d610f5f + 43407b6 commit 3bb9e83
Show file tree
Hide file tree
Showing 10 changed files with 2 additions and 104 deletions.
2 changes: 0 additions & 2 deletions backend/app_tests/api/test_api_requirement_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
REQUIREMENT_NODE_URN = "urn:test:req_node.t:1"
REQUIREMENT_NODE_PARENT_URN = "urn:test:req_node.t"
REQUIREMENT_NODE_ORDER_ID = 1
REQUIREMENT_NODE_LEVEL = 2
REQUIREMENT_NODE_REFERENCE = "test ref"


Expand Down Expand Up @@ -57,7 +56,6 @@ def test_get_requirement_nodes(self, test):
"urn": REQUIREMENT_NODE_URN,
"parent_urn": REQUIREMENT_NODE_PARENT_URN,
"order_id": REQUIREMENT_NODE_ORDER_ID,
"level": REQUIREMENT_NODE_LEVEL,
"assessable": True,
"folder": test.folder,
"framework": Framework.objects.all()[0],
Expand Down
1 change: 0 additions & 1 deletion backend/core/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@
"view_framework",
"delete_framework",
"view_requirementnode",
"view_requirementlevel", # Permits to see the object on api by an admin
"view_library",
"add_library",
"delete_library",
Expand Down
27 changes: 0 additions & 27 deletions backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,6 @@ def library_entry(self):
if requirement_nodes:
res["requirement_nodes"] = requirement_nodes

requirement_levels = self.get_requirement_levels()
if requirement_levels:
res["requirement_levels"] = requirement_levels

return res

def get_requirement_nodes(self):
Expand All @@ -385,28 +381,6 @@ def process_node(self, node):
]
return node_dict

def get_requirement_levels(self):
levels_queryset = self.requirement_levels.all()
if levels_queryset.exists():
return [model_to_dict(level) for level in levels_queryset]
return []


class RequirementLevel(ReferentialObjectMixin):
framework = models.ForeignKey(
Framework,
on_delete=models.CASCADE,
null=True,
blank=True,
verbose_name=_("Framework"),
related_name="requirement_levels",
)
level = models.IntegerField(null=False, blank=False, verbose_name=_("Level"))

class Meta:
verbose_name = _("Requirements level")
verbose_name_plural = _("Requirements levels")


class RequirementNode(ReferentialObjectMixin):
threats = models.ManyToManyField(
Expand All @@ -433,7 +407,6 @@ class RequirementNode(ReferentialObjectMixin):
max_length=100, null=True, blank=True, verbose_name=_("Parent URN")
)
order_id = models.IntegerField(null=True, verbose_name=_("Order ID"))
level = models.IntegerField(null=True, verbose_name=_("Level"))
maturity = models.IntegerField(null=True, verbose_name=_("Maturity"))
assessable = models.BooleanField(null=False, verbose_name=_("Assessable"))

Expand Down
10 changes: 0 additions & 10 deletions backend/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,16 +417,6 @@ class FrameworkWriteSerializer(FrameworkReadSerializer):
pass


class RequirementLevelReadSerializer(BaseModelSerializer):
class Meta:
model = RequirementLevel
fields = "__all__"


class RequirementLevelWriteSerializer(RequirementLevelReadSerializer):
pass


class RequirementNodeReadSerializer(BaseModelSerializer):
reference_controls = FieldsRelatedField(many=True)
threats = FieldsRelatedField(many=True)
Expand Down
3 changes: 0 additions & 3 deletions backend/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@
basename="compliance-assessments",
)
router.register(r"requirement-nodes", RequirementViewSet, basename="requirement-nodes")
router.register(
r"requirement-levels", RequirementLevelViewSet, basename="requirement-levels"
)
router.register(
r"requirement-assessments",
RequirementAssessmentViewSet,
Expand Down
10 changes: 0 additions & 10 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1077,16 +1077,6 @@ class RequirementNodeViewSet(BaseModelViewSet):
search_fields = ["name", "description"]


class RequirementLevelViewSet(BaseModelViewSet):
"""
API endpoint that allows requirement levels to be viewed or edited.
"""

model = RequirementLevel
filterset_fields = ["framework"]
search_fields = ["name"]


class RequirementViewSet(BaseModelViewSet):
"""
API endpoint that allows requirements to be viewed or edited.
Expand Down
1 change: 0 additions & 1 deletion backend/library/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ def import_requirement_node(self, framework_object: Framework):
annotation=self.requirement_data.get("annotation"),
provider=framework_object.provider,
order_id=self.index,
level=self.requirement_data.get("level"),
name=self.requirement_data.get("name"),
description=self.requirement_data.get("description"),
maturity=self.requirement_data.get("maturity"),
Expand Down
27 changes: 1 addition & 26 deletions documentation/architecture/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ erDiagram
REQUIREMENT_ASSESSMENT }o--o{ EVIDENCE : is_proved_by
APPLIED_CONTROL }o--o| REFERENCE_CONTROL : implements
REQUIREMENT_NODE }o--o{ THREAT : addresses
FRAMEWORK ||--o{ REQUIREMENT_LEVEL : contains
FRAMEWORK ||--o{ REQUIREMENT_NODE : contains
APPLIED_CONTROL }o--o{ EVIDENCE : is_proved_by
RISK_ASSESSMENT }o--|| RISK_MATRIX : applies
Expand Down Expand Up @@ -154,18 +153,6 @@ erDiagram
string provider
}
REQUIREMENT_LEVEL {
string urn
string locale
boolean default_locale
string ref_id
string name
string description
string annotation
int level
}
REQUIREMENT_NODE {
string urn
string locale
Expand All @@ -177,7 +164,6 @@ erDiagram
urn parent_urn
int order_id
int level
int maturity
boolean assessable
}
Expand Down Expand Up @@ -432,7 +418,6 @@ ReferentialObjectMixin <|-- Threat
ReferentialObjectMixin <|-- ReferenceControl
ReferentialObjectMixin <|-- RiskMatrix
ReferentialObjectMixin <|-- Framework
ReferentialObjectMixin <|-- RequirementLevel
ReferentialObjectMixin <|-- RequirementNode
ReferentialObjectMixin <|-- Mapping
NameDescriptionMixin <|-- Assessment
Expand Down Expand Up @@ -506,18 +491,12 @@ namespace ReferentialObjects {
+is_deletable() bool
}
class RequirementLevel {
+Framework framework
+IntegerField level
}
class RequirementNode {
+Threat[] threats
+ReferenceControl[] REFERENCE_CONTROLs
+Framework framework
+CharField parent_urn
+IntegerField order_id
+IntegerField level
+IntegerField maturity
+BooleanField assessable
}
Expand Down Expand Up @@ -688,11 +667,7 @@ Assets are of category primary or support. A primary asset has no parent, a supp
## Frameworks

The fundamental object of CISO Assistant for compliance is the framework. It corresponds to a given standard, e.g. ISO27001:2013. It mainly contains requirements nodes. A requirement node can be assessable or not (e.g. title or informational elements are not assessable). Assessable requirement nodes can be simply called "requirements".
The structure (tree) of requirements is defined by the level and requirement node objects. The *parent_urn* of a requirement node can either be the URN of another requirement node or null for top-level objects. This allows to simply define the structure of a framework. An assessable requirement node can be the child of another assessable requirement node, which is very convenient for frameworks that have lists of conditions attached to a requirement.

The requirement level objects of a framework optionally provide the naming of each level from 1 to n, when applicable. Requirement nodes have a nullable *level* field to refer to the corresponding requirement level. If requirement nodes are set at a defined level, the term "requirement" is replaced by the name of the correponding level (e.g. "subcategory" for CSF).

If no level information is provided, requirement nodes will be displayed without reference to a notion of level, only as a tree containing requirement nodes. This can address potential frameworks with branches of various depths.
The structure (tree) of requirements is defined by the requirement node objects. The *parent_urn* of a requirement node can either be the URN of another requirement node or null for top-level objects. This allows to simply define the structure of a framework. An assessable requirement node can be the child of another assessable requirement node, which is very convenient for frameworks that have lists of conditions attached to a requirement.

The maturity field describes the maturity level of the requirement node, when this is relevant (e.g. for CMMC or CIS).

Expand Down
12 changes: 0 additions & 12 deletions tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ This will produce a file name your_library_file.yaml

## Format of Excel files

This is documented in the header of the python file. Please note the notion of level is not yet implemented in CISO Assistant.

```
Conventions:
| means a cell separation, <> means empty cell
Expand All @@ -38,20 +36,11 @@ Conventions:
framework_description | <description>
reference_control_base_urn | <base_urn> | id
threat_base_urn | <base_urn> | id
tab | <tab_name> | levels
tab | <tab_name> | requirements | <section_name>
tab | <tab_name> | threats | <base_urn>
tab | <tab_name> | reference_controls | <base_urn>
For levels:
A "levels" tab enumerates levels. If it exists, it shall be placed before the correponding framework.
The first line is a header, with the following possible fields (* for required):
- level(*)
- ref_id(*)
- name
- description
- annotation
For requirements:
If no section_name is given, no upper group is defined, else an upper group (depth 0) with the section name is used.
The first line is a header, with the following possible fields (* for required):
Expand All @@ -60,7 +49,6 @@ Conventions:
- ref_id
- name
- description
- level
- maturity
- threats
- reference_controls
Expand Down
13 changes: 1 addition & 12 deletions tools/convert_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,11 @@
framework_description | <description>
reference_control_base_urn | <base_urn> | id
threat_base_urn | <base_urn> | id
tab | <tab_name> | levels
tab | <tab_name> | requirements | <section_name>
tab | <tab_name> | threats | <base_urn>
tab | <tab_name> | reference_controls | <base_urn>
For levels:
A "levels" tab enumerates levels. If it exists, it shall be placed before the correponding framework.
The first line is a header, with the following possible fields (* for required):
- level(*)
- ref_id(*)
- name
- description
- annotation
For requirements:
If no section_name is given, no upper group is defined, else an upper group (depth 0) with the section name is used.
The first line is a header, with the following possible fields (* for required):
Expand All @@ -41,7 +32,6 @@
- ref_id
- name
- description
- level
- maturity
- threats
- reference_controls
Expand Down Expand Up @@ -154,7 +144,6 @@ def read_header(row):
name = row[header['name']].value if 'name' in header else None
description = row[header['description']].value if 'description' in header else None
annotation = row[header['annotation']].value if 'annotation' in header else None
level = row[header['level']].value if 'level' in header else None
maturity = row[header['maturity']].value if 'maturity' in header else None
ref_id_urn = ref_id.lower().replace(' ', '-') if ref_id else f"node{counter}"
urn = f"{root_nodes_urn}:{ref_id_urn}"
Expand All @@ -169,7 +158,7 @@ def read_header(row):
elif depth <= current_depth:
pass
else:
error(f"wrong level in requirement (tab {title}) {urn}")
error(f"wrong depth in requirement (tab {title}) {urn}")
current_node_urn = urn
parent_urn = parent_for_depth[depth]
current_depth = depth
Expand Down

0 comments on commit 3bb9e83

Please sign in to comment.