Skip to content

Commit

Permalink
Oscal 1.0.0 (#26)
Browse files Browse the repository at this point in the history
Updates SSP and component model schemas for OSCAL 1.0.0. [#20]

Co-authored-by: Tom Wood <[email protected]>
  • Loading branch information
openprivacy and woodt authored Aug 18, 2021
1 parent 205d112 commit 916548e
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 135 deletions.
85 changes: 54 additions & 31 deletions complianceio/oscal/component.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Define OSCAL Component using Component Definition Model v1.0.0
# https://pages.nist.gov/OSCAL/reference/1.0.0/component-definition/json-outline/
from enum import Enum
from typing import Dict
from typing import List
from typing import Optional
from uuid import UUID
from uuid import uuid4

from pydantic import Field

from .oscal import Annotation
from .oscal import BackMatter
from .oscal import Link
from .oscal import MarkupLine
Expand Down Expand Up @@ -35,47 +35,55 @@ class ComponentTypeEnum(str, Enum):


class Statement(OSCALElement):
statement_id: Optional[NCName]
statement_id: NCName
uuid: UUID = Field(default_factory=uuid4)
description: MarkupMultiLine = MarkupMultiLine("")
properties: Optional[List[Property]]
remarks: Optional[MarkupMultiLine]
props: Optional[List[Property]]
links: Optional[List[Link]]
responsible_roles: Optional[List[ResponsibleRole]]
remarks: Optional[MarkupMultiLine]

class Config:
container_assigned = ["statement_id"]
fields = {
"statement_id": "statement-id",
"responsible_roles": "responsible-roles",
}
allow_population_by_field_name = True


class ImplementedRequirement(OSCALElement):
uuid: UUID = Field(default_factory=uuid4)
control_id: str
description: MarkupMultiLine
props: Optional[List[Property]]
annotations: Optional[List[Annotation]]
links: Optional[List[Link]]
responsible_roles: Dict[str, ResponsibleRole] = {}
set_parameters: Dict[str, SetParameter] = {}
statements: Dict[NCName, Statement] = {}
set_parameters: Optional[List[SetParameter]]
responsible_roles: Optional[List[ResponsibleRole]]
statements: Optional[List[Statement]]
remarks: Optional[MarkupMultiLine]

def add_statement(self, statement: Statement):
key = statement.statement_id
if key in self.statements:
if not self.statements:
self.statements = []
elif key in self.statements:
raise KeyError(
f"Statement {key} already in ImplementedRequirement"
" for {self.control_id}"
)
self.statements[NCName(statement.statement_id)] = statement
self.statements.append(statement)
return self

def add_parameter(self, set_parameter: SetParameter):
key = set_parameter.param_id
if key in self.set_parameters:
if not self.set_parameters:
self.set_parameters = []
elif key in self.set_parameters:
raise KeyError(
f"SetParameter {key} already in ImplementedRequirement"
" for {self.control_id}"
)
self.set_parameters[key] = set_parameter
self.set_parameters.append(set_parameter)
return self

class Config:
Expand All @@ -90,11 +98,11 @@ class Config:

class ControlImplementation(OSCALElement):
uuid: UUID = Field(default_factory=uuid4)
description: MarkupMultiLine
source: str
description: MarkupMultiLine
props: Optional[List[Property]]
implemented_requirements: List[ImplementedRequirement] = []
links: Optional[List[Link]]
implemented_requirements: List[ImplementedRequirement] = []

class Config:
fields = {"implemented_requirements": "implemented-requirements"}
Expand All @@ -112,11 +120,11 @@ class Component(OSCALElement):
description: MarkupMultiLine
purpose: Optional[MarkupLine]
props: Optional[List[Property]]
control_implementations: List[ControlImplementation] = []
links: Optional[List[Link]]
annotations: Optional[List[Annotation]]
protocols: Optional[List[Protocol]]
responsible_roles: Optional[List[ResponsibleRole]]
protocols: Optional[List[Protocol]]
control_implementations: List[ControlImplementation] = []
remarks: Optional[MarkupMultiLine]

class Config:
fields = {
Expand All @@ -125,48 +133,63 @@ class Config:
"responsible_roles": "responsible-roles",
}
allow_population_by_field_name = True
container_assigned = ["uuid"]
exclude_if_false = ["control-implementations"]


class IncorporatesComponent(OSCALElement):
component_uuid: UUID
description: str

class Config:
fields = {"component_uuid": "component-uuid"}


class Capability(OSCALElement):
uuid: UUID = Field(default_factory=uuid4)
name: str
description: MarkupMultiLine
control_implementations: Optional[List[ControlImplementation]]
props: Optional[List[Property]]
links: Optional[List[Link]]
annotations: Optional[List[Annotation]]
control_implementations: Optional[List[ControlImplementation]]
incorporates_components: Optional[List[IncorporatesComponent]]

class Config:
fields = {"control_implementations": "control-implementations"}
container_assigned = ["uuid"]
fields = {
"control_implementations": "control-implementations",
"incorporates_components": "incorporates-components",
}


class ImportComponentDefinition(OSCALElement):
href: str # really YRI
href: str # really uri-reference


class ComponentDefinition(OSCALElement):
uuid: UUID = Field(default_factory=uuid4)
metadata: Metadata
components: Dict[str, Component] = {}
components: Optional[List[Component]]
back_matter: Optional[BackMatter]
capabilities: Dict[str, Capability] = {}
capabilities: Optional[List[Capability]]
import_component_definitions: Optional[List[ImportComponentDefinition]]

def add_component(self, component: Component):
key = str(component.uuid)
if key in self.components:
# initialize optional component list
if not self.components:
self.components = []
elif key in self.components:
raise KeyError(f"Component {key} already in ComponentDefinition")
self.components[str(component.uuid)] = component
self.components.append(component)
return self

def add_capability(self, capability: Capability):
key = str(capability.uuid)
if key in self.capabilities:
# initialize optional capability list
if not self.capabilities:
self.capabilities = []
elif key in self.capabilities:
raise KeyError(f"Capability {key} already in ComponentDefinition")
self.capabilities[str(capability.uuid)] = capability
self.capabilities.append(capability)
return self

class Config:
Expand Down
29 changes: 16 additions & 13 deletions complianceio/oscal/oscal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from datetime import datetime
from datetime import timezone
from enum import Enum
from typing import Dict
from typing import List
from typing import Optional
from uuid import UUID
Expand Down Expand Up @@ -141,7 +140,7 @@ def dict(self, *args, **kwargs):
del d[key]
if hasattr(self.Config, "exclude_if_false"):
for key in self.Config.exclude_if_false:
if not d.get(key, False):
if not d.get(key, False) and key in d:
del d[key]
return d

Expand Down Expand Up @@ -216,6 +215,11 @@ class Config:
allow_population_by_field_name = True


class DocumentId(OSCALElement):
scheme: str # really, URI
identifier: str


class PartyTypeEnum(str, Enum):
person = "person"
organization = "organization"
Expand Down Expand Up @@ -285,34 +289,34 @@ class ResponsibleParty(OSCALElement):
role_id: RoleIDEnum
party_uuids: List[UUID]
props: Optional[List[Property]]
annotations: Optional[List[Annotation]]
links: Optional[List[Link]]
remarks: Optional[MarkupMultiLine]

class Config:
fields = {"party_uuids": "party-uuids"}
allow_population_by_field_name = True
container_assigned = ["role_id"]


class Metadata(OSCALElement):
title: str
published: datetime = datetime.now(timezone.utc)
last_modified: datetime = datetime.now(timezone.utc)
version: str
oscal_version: str = OSCAL_VERSION
revisions: Optional[List[Revision]]
published: datetime = datetime.now(timezone.utc)
last_modified: datetime = datetime.now(timezone.utc)
document_ids: Optional[List[DocumentId]]
props: Optional[List[Property]]
annotations: Optional[List[Annotation]]
links: Optional[List[Link]]
roles: Optional[List[Role]]
locations: Optional[List[Location]]
parties: Optional[List[Party]]
responsible_parties: Dict[str, ResponsibleParty] = {}
responsible_parties: Optional[List[ResponsibleParty]]
remarks: Optional[MarkupMultiLine]

class Config:
fields = {
"oscal_version": "oscal-version",
"document_ids": "document-ids",
"last_modified": "last-modified",
"responsible_parties": "responsible-parties",
}
Expand All @@ -322,22 +326,21 @@ class Config:

class SetParameter(OSCALElement):
param_id: NCName
values: List[str]
values: List[str] = []
remarks: Optional[MarkupMultiLine]

class Config:
fields = {"param_id": "param-id"}
allow_population_by_field_name = True
container_assigned = ["param_id"]


class ResponsibleRole(OSCALElement):
role_id: str
role_id: RoleIDEnum
props: Optional[List[Property]]
annotations: Optional[List[Annotation]]
links: Optional[List[Link]]
party_uuids: Optional[List[UUID]]
remarks: Optional[MarkupMultiLine]

class Config:
fields = {"party_uuids": "party-uuids"}
allow_population_by_field_name = True
container_assigned = ["role_id"]
Loading

0 comments on commit 916548e

Please sign in to comment.