Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 26 additions & 1 deletion api/services/metadata_services/metadata_injection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand All @@ -50,19 +70,24 @@ 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 = {}

# 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),
}
)

Expand Down
3 changes: 2 additions & 1 deletion tests/test_metadata_injection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading