Browse Source

Fixity check Python API.

Stefano Cossu 6 years ago
parent
commit
18fcd8e3db
2 changed files with 72 additions and 1 deletions
  1. 40 0
      lakesuperior/api/admin.py
  2. 32 1
      tests/1_api/test_admin_api.py

+ 40 - 0
lakesuperior/api/admin.py

@@ -1,7 +1,11 @@
+import hashlib
 import logging
 import logging
 
 
 from lakesuperior import env
 from lakesuperior import env
 from lakesuperior.config_parser import parse_config
 from lakesuperior.config_parser import parse_config
+from lakesuperior.dictionaries.namespaces import ns_collection as nsc
+from lakesuperior.exceptions import (
+        ChecksumValidationError, IncompatibleLdpTypeError)
 from lakesuperior.migrator import Migrator
 from lakesuperior.migrator import Migrator
 from lakesuperior.store.ldp_nr.default_layout import DefaultLayout as FileLayout
 from lakesuperior.store.ldp_nr.default_layout import DefaultLayout as FileLayout
 
 
@@ -54,3 +58,39 @@ def integrity_check():
     """
     """
     with env.app_globals.rdfly.store.txn_ctx():
     with env.app_globals.rdfly.store.txn_ctx():
         return set(env.app_globals.rdfly.find_refint_violations())
         return set(env.app_globals.rdfly.find_refint_violations())
+
+
+def fixity_check(uid):
+    """
+    Check fixity of a resource.
+
+    This calculates the checksum of a resource and validates it against the
+    checksum stored in its metadata (``premis:hasMessageDigest``).
+
+    :param str uid: UID of the resource to be checked.
+
+    :rtype: None
+
+    :raises: lakesuperior.exceptions.ChecksumValidationError: the cecksums
+        do not match. This indicates corruption.
+    :raises: lakesuperior.exceptions.IncompatibleLdpTypeError: if the
+        resource is not an LDP-NR.
+    """
+    from lakesuperior.api import resource as rsrc_api
+    from lakesuperior.model.ldp_factory import LDP_NR_TYPE
+
+    rsrc = rsrc_api.get(uid)
+    if LDP_NR_TYPE not in rsrc.ldp_types:
+        raise IncompatibleLdpTypeError()
+
+    ref_digest_term = rsrc.metadata.value(nsc['premis'].hasMessageDigest)
+    ref_digest_parts = ref_digest_term.split(':')
+    ref_cksum = ref_digest_parts[-1]
+    ref_cksum_algo = ref_digest_parts[-2]
+
+    calc_cksum = hashlib.new(ref_cksum_algo, rsrc.content.read()).hexdigest()
+
+    if calc_cksum != ref_cksum:
+        raise ChecksumValidationError(uid, ref_cksum, calc_cksum)
+
+    logger.info(f'Fixity check passed for {uid}.')

+ 32 - 1
tests/1_api/test_admin_api.py

@@ -1,12 +1,16 @@
 import pdb
 import pdb
 import pytest
 import pytest
 
 
+from io import BytesIO
+from uuid import uuid4
+
 from rdflib import Graph, URIRef
 from rdflib import Graph, URIRef
 
 
 from lakesuperior import env
 from lakesuperior import env
 from lakesuperior.api import resource as rsrc_api
 from lakesuperior.api import resource as rsrc_api
 from lakesuperior.api import admin as admin_api
 from lakesuperior.api import admin as admin_api
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
+from lakesuperior.exceptions import ChecksumValidationError
 
 
 
 
 @pytest.mark.usefixtures('db')
 @pytest.mark.usefixtures('db')
@@ -48,7 +52,34 @@ class TestAdminApi:
         assert {trp[2] for trp in check_trp} == {brk_uri}
         assert {trp[2] for trp in check_trp} == {brk_uri}
         assert (nsc['fcres']['/'], nsc['ldp'].contains, brk_uri) in check_trp
         assert (nsc['fcres']['/'], nsc['ldp'].contains, brk_uri) in check_trp
         assert (
         assert (
-                nsc['fcres']['/test_refint2'], 
+                nsc['fcres']['/test_refint2'],
                 URIRef('http://ex.org/ns#p1'), brk_uri) in check_trp
                 URIRef('http://ex.org/ns#p1'), brk_uri) in check_trp
 
 
 
 
+    def test_fixity_check_ok(self):
+        """
+        Verify that fixity check passes for a non-corrupted resource.
+        """
+        content = BytesIO(uuid4().bytes)
+        uid = f'/{uuid4()}'
+
+        rsrc_api.create_or_replace(uid, stream=content)
+        admin_api.fixity_check(uid)
+
+
+    def test_fixity_check_corrupt(self):
+        """
+        Verify that fixity check fails for a corrupted resource.
+        """
+        content = BytesIO(uuid4().bytes)
+        uid = f'/{uuid4()}'
+
+        _, rsrc = rsrc_api.create_or_replace(uid, stream=content)
+
+        with open(rsrc.local_path, 'wb') as fh:
+            fh.write(uuid4().bytes)
+
+        with pytest.raises(ChecksumValidationError):
+            admin_api.fixity_check(uid)
+
+