Skip to content

Commit 8146654

Browse files
committed
Add inherence UUID functions
Rationale is documented in case-utils PR 112. References: * casework/CASE-Utilities-Python#112 Signed-off-by: Alex Nelson <alexander.nelson@nist.gov>
1 parent 77a67bb commit 8146654

File tree

1 file changed

+74
-20
lines changed

1 file changed

+74
-20
lines changed

exifread_case/exifread_case.py

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
import hashlib
66
import argparse
77
import logging
8+
import typing
9+
import uuid
10+
import warnings
811

12+
import case_utils.inherent_uuid
913
import case_utils.local_uuid
1014
import exifread
1115
import rdflib
@@ -23,8 +27,31 @@
2327
ns_kb = rdflib.Namespace("http://example.org/kb/")
2428

2529

26-
def get_node_iri(ns: rdflib.Namespace, prefix: str) -> rdflib.URIRef:
27-
node_id = rdflib.URIRef(f"{prefix}{case_utils.local_uuid.demo_uuid()}", ns)
30+
def get_node_iri(
31+
ns: rdflib.Namespace,
32+
prefix: str,
33+
*args: typing.Any,
34+
facet_class: typing.Optional[rdflib.URIRef] = None,
35+
uco_object_node: typing.Optional[rdflib.URIRef] = None,
36+
use_deterministic_uuids: bool = False,
37+
**kwargs: typing.Any
38+
) -> rdflib.URIRef:
39+
node_id: typing.Optional[rdflib.URIRef] = None
40+
if use_deterministic_uuids:
41+
if uco_object_node is None:
42+
warnings.warn("get_node_iri() called requesting deterministic UUIDs, but no UcoObject node was provided.")
43+
else:
44+
if uco_object_node is None:
45+
warnings.warn("get_node_iri() called requesting deterministic UUIDs, but no Facet class node was provided.")
46+
else:
47+
_node_id = case_utils.inherent_id.get_facet_uriref(uco_object_node, facet_class, namespace=ns)
48+
# Swap in the requested prefix value.
49+
_node_uuid = str(_node_id)[-36:]
50+
node_id = ns[f"{prefix}{_node_uuid}"]
51+
52+
if node_id is None:
53+
node_id = ns[f"{prefix}{case_utils.local_uuid.demo_uuid()}"]
54+
2855
return node_id
2956

3057

@@ -72,17 +99,18 @@ def create_exif_dict(tags):
7299
return exif
73100

74101

75-
def n_cyber_object_to_node(graph):
102+
def n_cyber_object_to_node(graph, *args: typing.Any, use_deterministic_uuids: bool = False, **kwargs: typing.Any):
76103
"""
77104
Initial function to create nodes for each of the file's facet nodes
78105
:param graph: rdflib graph object for adding nodes to
79106
:return: The four nodes for each fo the other functions to fill
80107
"""
81108
cyber_object = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="observableobject-"))
82-
n_raster_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="rasterpicture-"))
83-
n_file_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="filefacet-"))
84-
n_content_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="contentfacet-"))
85-
n_exif_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="exiffacet-"))
109+
110+
n_raster_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="rasterpicture-", facet_class=NS_UCO_OBSERVABLE.RasterPictureFacet, uco_object_node=cyber_object_facet, use_deterministic_uuids=use_deterministic_uuids))
111+
n_file_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="filefacet-", facet_class=NS_UCO_OBSERVABLE.FileFacet, uco_object_node=cyber_object_facet, use_deterministic_uuids=use_deterministic_uuids))
112+
n_content_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="contentfacet-", facet_class=NS_UCO_OBSERVABLE.ContentDataFacet, uco_object_node=cyber_object_facet, use_deterministic_uuids=use_deterministic_uuids))
113+
n_exif_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="exiffacet-", facet_class=NS_UCO_OBSERVABLE.EXIFFacet, uco_object_node=cyber_object_facet, use_deterministic_uuids=use_deterministic_uuids))
86114
graph.add((
87115
cyber_object,
88116
NS_RDF.type,
@@ -111,15 +139,14 @@ def n_cyber_object_to_node(graph):
111139
return n_exif_facet, n_raster_facet, n_file_facet, n_content_facet
112140

113141

114-
def filecontent_object_to_node(graph, n_content_facet, file_information):
142+
def filecontent_object_to_node(graph, n_content_facet, file_information, *args: typing.Any, use_deterministic_uuids: bool = False, **kwargs: typing.Any):
115143
"""
116144
Unused: Create a node that will add the file content facet node to the graph
117145
:param graph: rdflib graph object for adding nodes to
118-
:param n_content_facet: Blank node to contain all content facet information
146+
:param n_content_facet: Node to contain all content facet information
119147
:param file_information: Dictionary containing information about file being analysed
120148
:return: None
121149
"""
122-
file_hash_facet = rdflib.URIRef(get_node_iri(ns=ns_kb, prefix="hash-"))
123150
graph.add((
124151
n_content_facet,
125152
NS_RDF.type,
@@ -143,16 +170,38 @@ def filecontent_object_to_node(graph, n_content_facet, file_information):
143170
rdflib.term.Literal(file_information["size"],
144171
datatype=NS_XSD.integer)
145172
))
146-
graph.add((
147-
n_content_facet,
148-
NS_UCO_OBSERVABLE.hash,
149-
file_hash_facet
150-
))
151-
graph.add((
152-
file_hash_facet,
153-
NS_RDF.type,
154-
NS_UCO_TYPES.Hash
155-
))
173+
174+
if "SHA256" in file_information:
175+
hash_method = rdflib.Literal("SHA256", datatype=NS_UCO_VOCABULARY.HashNameVocab)
176+
hash_value = rdflib.Literal(file_information["SHA256"], datatype=NS_XSD.hexBinary)
177+
178+
file_hash: rdflib.URIRef
179+
if use_deterministic_uuids:
180+
file_hash_uuid: uuid.UUID = case_utils.inherent_uuid.hash_method_value_uuid(hash_method, hash_value, namespace=ns_kb)
181+
file_hash = ns_kb["hash-" + str(file_hash_uuid)]
182+
else:
183+
file_hash = get_node_iri(ns=ns_kb, prefix="hash-")
184+
185+
graph.add((
186+
n_content_facet,
187+
NS_UCO_OBSERVABLE.hash,
188+
file_hash
189+
))
190+
graph.add((
191+
file_hash,
192+
NS_RDF.type,
193+
NS_UCO_TYPES.Hash
194+
))
195+
graph.add((
196+
file_hash,
197+
NS_UCO_TYPES.hashMethod,
198+
hash_method
199+
))
200+
graph.add((
201+
file_hash,
202+
NS_UCO_TYPES.hashValue,
203+
hash_value
204+
))
156205

157206

158207
def filefacets_object_to_node(graph, n_file_facet, file_information):
@@ -309,6 +358,11 @@ def main():
309358
"""
310359
parser = argparse.ArgumentParser()
311360
parser.add_argument("file", help="file to extract exif data from")
361+
parser.add_argument(
362+
"--use-deterministic-uuids",
363+
action="store_true",
364+
help="Use UUIDs computed using the case_utils.inherent_uuid module.",
365+
)
312366
args = parser.parse_args()
313367
local_file = args.file
314368
file_info = get_file_info(local_file)

0 commit comments

Comments
 (0)