added versioning test for embedded_object
replaced VersioningError with RevokeError and UnmodifiablePropertyError added __deepcopy__ to base class to handle embedded_objectsstix2.1
parent
c2d628db50
commit
5b8585b392
|
@ -7,7 +7,7 @@ import json
|
|||
|
||||
|
||||
from .exceptions import ExtraFieldsError, ImmutableError, InvalidValueError, \
|
||||
MissingFieldsError, VersioningError
|
||||
MissingFieldsError, RevokeError, UnmodifiablePropertyError
|
||||
from .utils import format_datetime, get_timestamp, NOW
|
||||
|
||||
__all__ = ['STIXJSONEncoder', '_STIXBase']
|
||||
|
@ -102,32 +102,32 @@ class _STIXBase(collections.Mapping):
|
|||
return "{0}({1})".format(self.__class__.__name__,
|
||||
", ".join(["{0!s}={1!r}".format(k, v) for k, v in props]))
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
# Assumption: we can ignore the memo argument, because no object will ever contain the same sub-object multiple times.
|
||||
new_inner = copy.deepcopy(self._inner, memo)
|
||||
cls = type(self)
|
||||
return cls(**new_inner)
|
||||
|
||||
# Versioning API
|
||||
|
||||
def new_version(self, **kwargs):
|
||||
unchangable_properties = []
|
||||
if self.revoked:
|
||||
raise VersioningError("Cannot create a new version of a revoked object")
|
||||
raise RevokeError("new_version")
|
||||
new_obj_inner = copy.deepcopy(self._inner)
|
||||
properties_to_change = kwargs.keys()
|
||||
if "type" in properties_to_change:
|
||||
unchangable_properties.append("type")
|
||||
if "id" in properties_to_change:
|
||||
unchangable_properties.append("id")
|
||||
if "created" in properties_to_change:
|
||||
unchangable_properties.append("created")
|
||||
if "created_by_ref" in properties_to_change:
|
||||
unchangable_properties.append("created_by_ref")
|
||||
for prop in ["created", "created_by_ref", "id", "type"]:
|
||||
if prop in properties_to_change:
|
||||
unchangable_properties.append(prop)
|
||||
if unchangable_properties:
|
||||
raise VersioningError("These properties cannot be changed when making a new version: " + ", ".join(unchangable_properties))
|
||||
raise UnmodifiablePropertyError(unchangable_properties)
|
||||
if 'modified' not in kwargs:
|
||||
kwargs['modified'] = get_timestamp()
|
||||
new_obj_inner.update(kwargs)
|
||||
|
||||
cls = type(self)
|
||||
return cls(**new_obj_inner)
|
||||
|
||||
def revoke(self):
|
||||
if self.revoked:
|
||||
raise VersioningError("Cannot revoke an already revoked object")
|
||||
raise RevokeError("revoke")
|
||||
return self.new_version(revoked=True)
|
||||
|
|
|
@ -51,8 +51,27 @@ class ImmutableError(STIXError, ValueError):
|
|||
super(ImmutableError, self).__init__("Cannot modify properties after creation.")
|
||||
|
||||
|
||||
class VersioningError(STIXError, ValueError):
|
||||
"""Execption while using the Versioning API"""
|
||||
class UnmodifiablePropertyError(STIXError, ValueError):
|
||||
"""Attempted to modify an unmodifiable property of object when creating a new version"""
|
||||
|
||||
def __init__(self, msg):
|
||||
super(VersioningError, self).__init__(msg)
|
||||
def __init__(self, unchangable_properties):
|
||||
super(UnmodifiablePropertyError, self).__init__()
|
||||
self.unchangable_properties = unchangable_properties
|
||||
|
||||
def __str__(self):
|
||||
msg = "These properties cannot be changed when making a new version: {0}."
|
||||
return msg.format(", ".join(self.unchangable_properties))
|
||||
|
||||
|
||||
class RevokeError(STIXError, ValueError):
|
||||
"""Attempted to an operation on a revoked object"""
|
||||
|
||||
def __init__(self, called_by):
|
||||
super(RevokeError, self).__init__()
|
||||
self.called_by = called_by
|
||||
|
||||
def __str__(self):
|
||||
if self.called_by == "revoke":
|
||||
return "Cannot revoke an already revoked object."
|
||||
else:
|
||||
return "Cannot create a new version of a revoked object."
|
||||
|
|
|
@ -23,6 +23,34 @@ def test_making_new_version():
|
|||
assert campaign_v1.modified < campaign_v2.modified
|
||||
|
||||
|
||||
def test_making_new_version_with_embedded_object():
|
||||
campaign_v1 = stix2.Campaign(
|
||||
id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||
created="2016-04-06T20:03:00.000Z",
|
||||
modified="2016-04-06T20:03:00.000Z",
|
||||
name="Green Group Attacks Against Finance",
|
||||
external_references=[{
|
||||
"source_name": "capec",
|
||||
"external_id": "CAPEC-163"
|
||||
}],
|
||||
description="Campaign by Green Group against a series of targets in the financial services sector."
|
||||
)
|
||||
|
||||
campaign_v2 = campaign_v1.new_version(external_references=[{
|
||||
"source_name": "capec",
|
||||
"external_id": "CAPEC-164"
|
||||
}])
|
||||
|
||||
assert campaign_v1.id == campaign_v2.id
|
||||
assert campaign_v1.created_by_ref == campaign_v2.created_by_ref
|
||||
assert campaign_v1.created == campaign_v2.created
|
||||
assert campaign_v1.name == campaign_v2.name
|
||||
assert campaign_v1.description == campaign_v2.description
|
||||
assert campaign_v1.modified < campaign_v2.modified
|
||||
assert campaign_v1.external_references[0].external_id != campaign_v2.external_references[0].external_id
|
||||
|
||||
|
||||
def test_revoke():
|
||||
campaign_v1 = stix2.Campaign(
|
||||
id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||
|
@ -55,7 +83,7 @@ def test_versioning_error_invalid_property():
|
|||
description="Campaign by Green Group against a series of targets in the financial services sector."
|
||||
)
|
||||
|
||||
with pytest.raises(stix2.exceptions.VersioningError) as excinfo:
|
||||
with pytest.raises(stix2.exceptions.UnmodifiablePropertyError) as excinfo:
|
||||
campaign_v1.new_version(type="threat-actor")
|
||||
|
||||
str(excinfo.value) == "These properties cannot be changed when making a new version: type"
|
||||
|
@ -73,7 +101,7 @@ def test_versioning_error_new_version_of_revoked():
|
|||
|
||||
campaign_v2 = campaign_v1.revoke()
|
||||
|
||||
with pytest.raises(stix2.exceptions.VersioningError) as excinfo:
|
||||
with pytest.raises(stix2.exceptions.RevokeError) as excinfo:
|
||||
campaign_v2.new_version(name="barney")
|
||||
|
||||
str(excinfo.value) == "Cannot create a new version of a revoked object"
|
||||
|
@ -91,7 +119,7 @@ def test_versioning_error_revoke_of_revoked():
|
|||
|
||||
campaign_v2 = campaign_v1.revoke()
|
||||
|
||||
with pytest.raises(stix2.exceptions.VersioningError) as excinfo:
|
||||
with pytest.raises(stix2.exceptions.RevokeError) as excinfo:
|
||||
campaign_v2.revoke()
|
||||
|
||||
str(excinfo.value) == "Cannot revoke an already revoked object"
|
||||
|
|
Loading…
Reference in New Issue