Skip to content

Commit 713f3b1

Browse files
committed
add service data classes
1 parent ca0e924 commit 713f3b1

File tree

2 files changed

+265
-2
lines changed

2 files changed

+265
-2
lines changed

cyclonedx/model/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class DataFlow(str, Enum):
6060
This is our internal representation of the dataFlowType simple type within the CycloneDX standard.
6161
6262
.. note::
63-
See the CycloneDX Schema: https://cyclonedx.org/docs/1.4/xml/#type_dataFlowType
63+
See the CycloneDX Schema: https://cyclonedx.org/docs/1.6/xml/#type_dataFlowType
6464
"""
6565
INBOUND = 'inbound'
6666
OUTBOUND = 'outbound'

cyclonedx/model/service.py

Lines changed: 264 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
from .._internal.compare import ComparableTuple as _ComparableTuple
3535
from ..schema.schema import SchemaVersion1Dot3, SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6
36-
from . import DataClassification, ExternalReference, Property, XsUri
36+
from . import DataClassification, DataFlow, ExternalReference, Property, XsUri
3737
from .bom_ref import BomRef
3838
from .contact import OrganizationalEntity
3939
from .dependency import Dependable
@@ -381,3 +381,266 @@ def __hash__(self) -> int:
381381

382382
def __repr__(self) -> str:
383383
return f'<Service bom-ref={self.bom_ref}, group={self.group}, name={self.name}, version={self.version}>'
384+
385+
386+
@serializable.serializable_class
387+
class OrganizationOrIndividualType:
388+
"""
389+
This is our internal representation of the organizationOrIndividualType complex type within the CycloneDX standard.
390+
391+
.. note::
392+
See the CycloneDX Schema: https://cyclonedx.org/docs/1.6/xml/#type_organizationOrIndividualType
393+
"""
394+
395+
def __init__(
396+
self, *,
397+
organization: Optional[OrganizationalEntity] = None,
398+
individual: Optional[OrganizationalEntity] = None,
399+
) -> None:
400+
self.organization = organization
401+
self.individual = individual
402+
403+
# Property for organization
404+
@property
405+
@serializable.xml_sequence(1)
406+
@serializable.xml_name('organization')
407+
def organization(self) -> Optional[OrganizationalEntity]:
408+
return self._organization
409+
410+
@organization.setter
411+
def organization(self, organization: Optional[OrganizationalEntity]) -> None:
412+
self._organization = organization
413+
414+
# Property for individual
415+
@property
416+
@serializable.xml_sequence(2)
417+
@serializable.xml_name('individual')
418+
def individual(self) -> Optional[OrganizationalEntity]:
419+
return self._individual
420+
421+
@individual.setter
422+
def individual(self, individual: Optional[OrganizationalEntity]) -> None:
423+
self._individual = individual
424+
425+
426+
@serializable.serializable_class
427+
class DataGovernance:
428+
"""
429+
This is our internal representation of the dataGovernance complex type within the CycloneDX standard.
430+
431+
.. note::
432+
See the CycloneDX Schema: https://cyclonedx.org/docs/1.6/xml/#type_dataGovernance
433+
"""
434+
435+
def __init__(
436+
self, *,
437+
custodian: Optional[OrganizationOrIndividualType] = None,
438+
steward: Optional[OrganizationOrIndividualType] = None,
439+
owner: Optional[OrganizationOrIndividualType] = None,
440+
) -> None:
441+
self.custodian = custodian
442+
self.steward = steward
443+
self.owner = owner
444+
445+
# Property for custodian
446+
@property
447+
@serializable.xml_sequence(1)
448+
@serializable.xml_name('custodian')
449+
def custodian(self) -> Optional[OrganizationOrIndividualType]:
450+
return self._custodian
451+
452+
@custodian.setter
453+
def custodian(self, custodian: Optional[OrganizationOrIndividualType]) -> None:
454+
self._custodian = custodian
455+
456+
# Property for steward
457+
@property
458+
@serializable.xml_sequence(2)
459+
@serializable.xml_name('steward')
460+
def steward(self) -> Optional[OrganizationOrIndividualType]:
461+
return self._steward
462+
463+
@steward.setter
464+
def steward(self, steward: Optional[OrganizationOrIndividualType]) -> None:
465+
self._steward = steward
466+
467+
# Property for owner
468+
@property
469+
@serializable.xml_sequence(3)
470+
@serializable.xml_name('owner')
471+
def owner(self) -> Optional[OrganizationOrIndividualType]:
472+
return self._owner
473+
474+
@owner.setter
475+
def owner(self, owner: Optional[OrganizationOrIndividualType]) -> None:
476+
self._owner = owner
477+
478+
479+
@serializable.serializable_class
480+
class Data:
481+
"""
482+
This is our internal representation of the service.data complex type within the CycloneDX standard.
483+
484+
.. note::
485+
See the CycloneDX Schema: https://cyclonedx.org/docs/1.6/xml/#type_service
486+
"""
487+
# @serializable.xml_string(serializable.XmlStringSerializationType.STRING)
488+
489+
def __init__(
490+
self, *,
491+
flow: DataFlow,
492+
classification: str,
493+
name: Optional[str] = None,
494+
description: Optional[str] = None,
495+
governance: Optional[DataGovernance] = None,
496+
source: Optional[Iterable[Union[BomRef, XsUri]]] = None,
497+
destination: Optional[Iterable[Union[BomRef, XsUri]]] = None
498+
) -> None:
499+
self.flow = flow
500+
self.classification = classification
501+
self.name = name
502+
self.description = description
503+
self.governance = governance
504+
self.source = source
505+
self.destination = destination
506+
507+
@property
508+
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
509+
def name(self) -> Optional[str]:
510+
"""
511+
The name of the service data.
512+
513+
Returns:
514+
`str` if provided else None
515+
"""
516+
return self._name
517+
518+
@name.setter
519+
def name(self, name: Optional[str]) -> None:
520+
self._name = name
521+
522+
@property
523+
@serializable.xml_attribute()
524+
def flow(self) -> DataFlow:
525+
"""
526+
Specifies the flow direction of the data.
527+
528+
Valid values are: inbound, outbound, bi-directional, and unknown.
529+
530+
Direction is relative to the service.
531+
532+
- Inbound flow states that data enters the service
533+
- Outbound flow states that data leaves the service
534+
- Bi-directional states that data flows both ways
535+
- Unknown states that the direction is not known
536+
537+
Returns:
538+
`DataFlow`
539+
"""
540+
return self._flow
541+
542+
@flow.setter
543+
def flow(self, flow: DataFlow) -> None:
544+
self._flow = flow
545+
546+
@property
547+
@serializable.xml_name('.')
548+
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
549+
def classification(self) -> str:
550+
"""
551+
Data classification tags data according to its type, sensitivity, and value if altered, stolen, or destroyed.
552+
553+
Returns:
554+
`str`
555+
"""
556+
return self._classification
557+
558+
@classification.setter
559+
def classification(self, classification: str) -> None:
560+
self._classification = classification
561+
562+
# description property
563+
564+
@property
565+
@serializable.xml_sequence(2) # Assuming order after name
566+
@serializable.xml_string(serializable.XmlStringSerializationType.STRING)
567+
def description(self) -> Optional[str]:
568+
"""
569+
The description of the service data.
570+
571+
Returns:
572+
`str` if provided else None
573+
"""
574+
return self._description
575+
576+
@description.setter
577+
def description(self, description: Optional[str]) -> None:
578+
self._description = description
579+
580+
# governance property
581+
@property
582+
@serializable.xml_sequence(3) # Assuming order after description
583+
def governance(self) -> Optional[DataGovernance]:
584+
"""
585+
Governance information for the service data.
586+
587+
Returns:
588+
`DataGovernance` if provided else None
589+
"""
590+
return self._governance
591+
592+
@governance.setter
593+
def governance(self, governance: Optional[DataGovernance]) -> None:
594+
self._governance = governance
595+
596+
# source property
597+
@property
598+
@serializable.xml_sequence(4) # Assuming order after governance
599+
def source(self) -> Optional[Iterable[Union[BomRef, XsUri]]]:
600+
"""
601+
The source(s) of the service data.
602+
603+
Returns:
604+
Iterable of `BomRef` or `XsUri` if provided else None
605+
"""
606+
return self._source
607+
608+
@source.setter
609+
def source(self, source: Optional[Iterable[Union[BomRef, XsUri]]]) -> None:
610+
self._source = source
611+
612+
# destination property
613+
@property
614+
@serializable.xml_sequence(5) # Assuming order after source
615+
def destination(self) -> Optional[Iterable[Union[BomRef, XsUri]]]:
616+
"""
617+
The destination(s) of the service data.
618+
619+
Returns:
620+
Iterable of `BomRef` or `XsUri` if provided else None
621+
"""
622+
return self._destination
623+
624+
@destination.setter
625+
def destination(self, destination: Optional[Iterable[Union[BomRef, XsUri]]]) -> None:
626+
self._destination = destination
627+
628+
def __eq__(self, other: object) -> bool:
629+
if isinstance(other, DataClassification):
630+
return hash(other) == hash(self)
631+
return False
632+
633+
def __lt__(self, other: object) -> bool:
634+
if isinstance(other, DataClassification):
635+
return _ComparableTuple((
636+
self.flow, self.classification
637+
)) < _ComparableTuple((
638+
other.flow, other.classification
639+
))
640+
return NotImplemented
641+
642+
def __hash__(self) -> int:
643+
return hash((self.flow, self.classification))
644+
645+
def __repr__(self) -> str:
646+
return f'<DataClassification flow={self.flow}>'

0 commit comments

Comments
 (0)