diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f35ee5..ab5146a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.9.0] - 2026-03-12 + +### Added +- Add `ndp_creator_md5` field for catalog alignment with official NDP catalog + ## [0.8.0] - 2026-03-03 ### Added diff --git a/api/services/metadata_services/metadata_injection.py b/api/services/metadata_services/metadata_injection.py index c204070..1ffc82c 100644 --- a/api/services/metadata_services/metadata_injection.py +++ b/api/services/metadata_services/metadata_injection.py @@ -24,6 +24,26 @@ def hash_user_id(user_info: Dict[str, Any]) -> str: return hashlib.sha256(user_sub.encode()).hexdigest()[:16] +def calculate_md5(input_string: str) -> str: + """ + Generate MD5 hash for catalog alignment. + + This function replicates the hashing algorithm used by the official + NDP catalog (ckanext-ndpcatalogadditions) to ensure metadata alignment. + + Parameters + ---------- + input_string : str + The string to hash (typically the user's 'sub' field from Keycloak). + + Returns + ------- + str + A 32-character hexadecimal MD5 hash. + """ + return hashlib.md5(input_string.encode('utf-8')).hexdigest() + + def inject_ndp_metadata( user_info: Dict[str, Any], extras: Dict[str, Any] = None ) -> Dict[str, Any]: @@ -50,7 +70,8 @@ def inject_ndp_metadata( ----- The following fields are automatically injected: - ndp_group_id: Organization name from configuration - - ndp_user_id: Hashed user identifier (16-character hex) + - ndp_user_id: Hashed user identifier (16-character hex, SHA-256) + - ndp_creator_md5: MD5 hash for catalog alignment (32-character hex) """ if extras is None: extras = {} @@ -58,11 +79,15 @@ def inject_ndp_metadata( # Create a copy to avoid modifying the original dictionary updated_extras = extras.copy() + # Get user sub for hashing + user_sub = user_info.get("sub", "unknown") + # Add NDP metadata fields updated_extras.update( { "ndp_group_id": swagger_settings.organization, "ndp_user_id": hash_user_id(user_info), + "ndp_creator_md5": calculate_md5(user_sub), } ) diff --git a/tests/test_metadata_injection.py b/tests/test_metadata_injection.py index 3292779..77e3f8d 100644 --- a/tests/test_metadata_injection.py +++ b/tests/test_metadata_injection.py @@ -130,7 +130,8 @@ def test_inject_metadata_empty_extras_dict(self, mock_settings): assert "ndp_group_id" in result assert result["ndp_group_id"] == "empty-org" - assert len(result) == 2 # Only NDP fields + assert "ndp_creator_md5" in result + assert len(result) == 3 # NDP fields: ndp_group_id, ndp_user_id, ndp_creator_md5 @patch("api.services.metadata_services.metadata_injection.swagger_settings") def test_inject_metadata_user_without_sub(self, mock_settings):