From 974e9a6d0b7bde9c9ad6c6e81e2a36f21084cda8 Mon Sep 17 00:00:00 2001
From: Magsen ABBE <magsen.abbe@thalesgroup.com>
Date: Wed, 7 Sep 2022 17:16:12 +0200
Subject: [PATCH] fix: spec_version & version from modified/created

---
 .gitignore                          |  1 +
 opentaxii/persistence/sqldb/api.py  | 21 ++++++++++++++---
 opentaxii/server.py                 |  8 +++----
 opentaxii/taxii2/entities.py        |  8 +++----
 tests/taxii2/test_taxii2_object.py  | 26 ++++++++++-----------
 tests/taxii2/test_taxii2_objects.py | 36 ++++++++++++++---------------
 6 files changed, 58 insertions(+), 42 deletions(-)

diff --git a/.gitignore b/.gitignore
index e42b1ae0..73ce85c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ var/
 *.egg-info/
 .installed.cfg
 *.egg
+venv/
 
 # PyInstaller
 #  Usually these files are written by a python script from a template
diff --git a/opentaxii/persistence/sqldb/api.py b/opentaxii/persistence/sqldb/api.py
index af9a5b1e..b9e43b39 100644
--- a/opentaxii/persistence/sqldb/api.py
+++ b/opentaxii/persistence/sqldb/api.py
@@ -977,9 +977,24 @@ def add_objects(
         self.db.session.commit()
         job_details = []
         for obj in objects:
-            version = datetime.datetime.strptime(
-                obj["modified"], DATETIMEFORMAT
-            ).replace(tzinfo=datetime.timezone.utc)
+            version = None
+            if "modified" in obj:
+                version = datetime.datetime.strptime(
+                    obj["modified"], DATETIMEFORMAT
+                ).replace(tzinfo=datetime.timezone.utc)
+            elif "created" in obj:
+                version = datetime.datetime.strptime(
+                    obj["created"], DATETIMEFORMAT
+                ).replace(tzinfo=datetime.timezone.utc)
+            else:
+                # If a STIX object is not versioned (and therefore does not have a modified
+                # timestamp) then this version parameter MUST use the created timestamp. If
+                # an object does not have a created or modified timestamp or any other
+                # version information that can be used, then the server should use a value for
+                # the version that is consistent to the server.
+                # -- TAXII 2.1 specification --
+                raise ValueError("STIX object MUST have `modified` or `created` timestamp "
+                                 "in order to create version")
             if (
                 not self.db.session.query(literal(True))
                 .filter(
diff --git a/opentaxii/server.py b/opentaxii/server.py
index 82cd067a..b93fa82e 100644
--- a/opentaxii/server.py
+++ b/opentaxii/server.py
@@ -538,7 +538,7 @@ def collections_handler(self, api_root_id):
             response["collections"] = []
             for collection in collections:
                 data = {
-                    "id": collection.id,
+                    "id": str(collection.id),
                     "title": collection.title,
                     "can_read": collection.can_read(context.account),
                     "can_write": collection.can_write(context.account),
@@ -567,7 +567,7 @@ def collection_handler(self, api_root_id, collection_id_or_alias):
         if context.account is None and not collection.can_read(context.account):
             raise Unauthorized()
         response = {
-            "id": collection.id,
+            "id": str(collection.id),
             "title": collection.title,
             "can_read": collection.can_read(context.account),
             "can_write": collection.can_write(context.account),
@@ -655,7 +655,7 @@ def objects_get_handler(self, api_root_id, collection_id_or_alias):
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in objects
@@ -738,7 +738,7 @@ def object_get_handler(self, api_root_id, collection_id_or_alias, object_id):
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in versions
diff --git a/opentaxii/taxii2/entities.py b/opentaxii/taxii2/entities.py
index 867b2a6e..5eedc302 100644
--- a/opentaxii/taxii2/entities.py
+++ b/opentaxii/taxii2/entities.py
@@ -63,14 +63,14 @@ def can_read(self, account: Optional[Account]):
         return self.is_public or (
             account
             and (
-                account.is_admin or "read" in set(account.permissions.get(self.id, []))
+                account.is_admin or "read" in account.permissions.get(str(self.id), [])
             )
         )
 
     def can_write(self, account: Optional[Account]):
         """Determine if `account` is allowed to write to this collection."""
         return account and (
-            account.is_admin or "write" in set(account.permissions.get(self.id, []))
+            account.is_admin or "write" in account.permissions.get(str(self.id), [])
         )
 
 
@@ -185,7 +185,7 @@ def __init__(
 
     def as_taxii2_dict(self):
         """Turn this object into a taxii2 dict."""
-        response = {"id": self.stix_id, "version": taxii2_datetimeformat(self.version)}
+        response = {"id": str(self.stix_id), "version": taxii2_datetimeformat(self.version)}
         if self.message:
             response["message"] = self.message
         return response
@@ -243,7 +243,7 @@ def __init__(
     def as_taxii2_dict(self):
         """Turn this object into a taxii2 dict."""
         response = {
-            "id": self.id,
+            "id": str(self.id),
             "status": self.status,
             "request_timestamp": taxii2_datetimeformat(self.request_timestamp),
             "total_count": self.total_count,
diff --git a/tests/taxii2/test_taxii2_object.py b/tests/taxii2/test_taxii2_object.py
index a0266c1c..2a7c9fb3 100644
--- a/tests/taxii2/test_taxii2_object.py
+++ b/tests/taxii2/test_taxii2_object.py
@@ -43,7 +43,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0]]
@@ -70,7 +70,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0]]
@@ -104,7 +104,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[2]]
@@ -177,7 +177,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
@@ -210,7 +210,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
@@ -242,7 +242,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0], STIX_OBJECTS[2]]
@@ -269,7 +269,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0]]
@@ -341,7 +341,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[2]]
@@ -386,7 +386,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
@@ -413,7 +413,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0]]
@@ -444,7 +444,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[2]]
@@ -473,7 +473,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0], STIX_OBJECTS[2]]
@@ -514,7 +514,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
diff --git a/tests/taxii2/test_taxii2_objects.py b/tests/taxii2/test_taxii2_objects.py
index 4c2b074c..a8f0bf17 100644
--- a/tests/taxii2/test_taxii2_objects.py
+++ b/tests/taxii2/test_taxii2_objects.py
@@ -49,7 +49,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -79,7 +79,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -111,7 +111,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[1:2]
@@ -176,7 +176,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
@@ -206,7 +206,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -236,7 +236,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -291,7 +291,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[1:2]
@@ -338,7 +338,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0]]
@@ -368,7 +368,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -396,7 +396,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in [STIX_OBJECTS[0]]
@@ -426,7 +426,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -454,7 +454,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
@@ -484,7 +484,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]
@@ -516,7 +516,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[1:3]
@@ -546,7 +546,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:3]
@@ -580,7 +580,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:3]
@@ -608,7 +608,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:1]
@@ -642,7 +642,7 @@
                     {
                         "id": obj.id,
                         "type": obj.type,
-                        "spec_version": obj.type,
+                        "spec_version": obj.spec_version,
                         **obj.serialized_data,
                     }
                     for obj in STIX_OBJECTS[:2]