From cc7353ec6c36ab7f5e1d22f09bf94f429337d903 Mon Sep 17 00:00:00 2001 From: Pelayo Torres Date: Mon, 24 Aug 2026 10:23:46 +0200 Subject: [PATCH 1/4] Added open discover url in getAuth endpoint --- services/register/register_service/core/register_operations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/services/register/register_service/core/register_operations.py b/services/register/register_service/core/register_operations.py index 2244d30..9aeb18a 100644 --- a/services/register/register_service/core/register_operations.py +++ b/services/register/register_service/core/register_operations.py @@ -69,6 +69,7 @@ class RegisterOperations: ccf_publish_url="published-apis/v1//service-apis", ccf_onboarding_url="api-invoker-management/v1/onboardedInvokers", ccf_discover_url="service-apis/v1/allServiceAPIs?api-invoker-id=", + ccf_open_discover_url="open-api-disc/v1/service-apis", ccf_security_url="capif-security/v1/trustedInvokers/"), 200 except Exception as e: -- GitLab From 2c56a28b3e99182642172cd1e3f9f2d776deb923 Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Mon, 24 Aug 2026 11:20:19 +0200 Subject: [PATCH 2/4] Added new test for Open Discover --- .../__init__.robot | 2 + .../capif_api_service_open_discover.robot | 46 +++++++++++++++++++ tests/libraries/common/types.json | 36 +++++++++++++++ tests/libraries/helpers.py | 35 ++++++++++++++ tests/resources/common.resource | 1 + 5 files changed, 120 insertions(+) create mode 100644 tests/features/CAPIF Api Open Discover Service/__init__.robot create mode 100644 tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot diff --git a/tests/features/CAPIF Api Open Discover Service/__init__.robot b/tests/features/CAPIF Api Open Discover Service/__init__.robot new file mode 100644 index 0000000..3a72d04 --- /dev/null +++ b/tests/features/CAPIF Api Open Discover Service/__init__.robot @@ -0,0 +1,2 @@ +*** Settings *** +Force Tags capif_api_open_discover_service \ No newline at end of file diff --git a/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot b/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot new file mode 100644 index 0000000..2afd191 --- /dev/null +++ b/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot @@ -0,0 +1,46 @@ +*** Settings *** +Resource /opt/robot-tests/tests/resources/common.resource +Resource /opt/robot-tests/tests/resources/api_invoker_management_requests/apiInvokerManagementRequests.robot +Resource ../../resources/common.resource +Library /opt/robot-tests/tests/libraries/bodyRequests.py + +Suite Teardown Reset Testing Environment +Test Setup Reset Testing Environment +Test Teardown Reset Testing Environment + + +*** Variables *** +${API_INVOKER_NOT_REGISTERED} not-valid + + +*** Test Cases *** +Open Discover Published service APIs by Authorised Entity + [Tags] capif_api_open_discover_service-1 + + # Register APF + ${register_user_info}= Provider Default Registration + + # Publish one api + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + + # Register user for invoker + ${register_user_info}= Register User At Jwt Auth + ... username=${INVOKER_USERNAME} role=${INVOKER_ROLE} + + # Test + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + Check Response Variable Type And Values ${resp} 200 OpenDiscoveryResp + + # Check Results + Dictionary Should Contain Key ${resp.json()} discApis + Should Not Be Empty ${resp.json()['discApis']} + Length Should Be ${resp.json()['discApis']} 1 + # Convert Service API Description to Open API Details + ${open_api_details}= Create Open API Details From Service API Description ${service_api_description_published} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details} diff --git a/tests/libraries/common/types.json b/tests/libraries/common/types.json index 076d833..03b2da2 100644 --- a/tests/libraries/common/types.json +++ b/tests/libraries/common/types.json @@ -413,6 +413,42 @@ "suppFeat": "SupportedFeatures" } }, + "OpenDiscoveryResp": { + "mandatory_attributes": { + "discApis": "OpenAPIDetails" + }, + "optional_attributes": { + "suppFeat": "SupportedFeatures" + } + }, + "OpenAPIDetails": { + "mandatory_attributes": { + "apiName": "string" + }, + "optional_attributes": { + "apiId": "string", + "apiStatus": "ApiStatus", + "description": "string", + "serviceAPICategory": "string", + "apiSuppFeats": "SupportedFeatures", + "apiProvName": "string", + "aefProfiles": "OpenAefProfile" + } + }, + "OpenAefProfile": { + "mandatory_attributes": {}, + "optional_attributes": { + "aefId": "string", + "versions": "Version", + "protocol": "Protocol", + "dataFormat": "DataFormat", + "aefLocation": "AefLocation", + "serviceKpis": "ServiceKpis" + }, + "regex_attributes": { + "^vendorSpecific-(.*)": "VendorSpecificObject" + } + }, "ServiceSecurity": { "mandatory_attributes": { "securityInfo": "SecurityInformation", diff --git a/tests/libraries/helpers.py b/tests/libraries/helpers.py index e739347..b078eed 100644 --- a/tests/libraries/helpers.py +++ b/tests/libraries/helpers.py @@ -177,3 +177,38 @@ def filter_users_by_prefix_username(users, prefix): if user['username'].startswith(prefix): filtered_users.append(user['username']) return filtered_users + + +def create_open_api_details_from_service_api_description( + service_api_description): + filtered_fields = ['apiName', 'apiId', 'apiStatus', 'description', + 'serviceAPICategory', 'apiSuppFeats', 'apiProvName', + 'aefProfiles'] + + open_api_details = {} + + for field in filtered_fields: + if field in service_api_description: + if field == 'aefProfiles' and field in service_api_description: + open_aef_profiles = [] + for aef_profile in service_api_description[field]: + open_aef_profile = create_open_aef_profile_from_aef_profile(aef_profile) + open_aef_profiles.append(open_aef_profile) + print('open_aef_profile=' + str(open_aef_profile)) + open_api_details[field] = open_aef_profiles + else: + open_api_details[field] = service_api_description[field] + return open_api_details + + +def create_open_aef_profile_from_aef_profile(aef_profile): + filtered_fields = ['aefId', 'versions', 'protocol', 'dataFormat', + 'aefLocation', 'serviceKpis'] + + open_aef_profile = {} + + for field in filtered_fields: + if field in aef_profile: + open_aef_profile[field] = aef_profile[field] + + return open_aef_profile diff --git a/tests/resources/common.resource b/tests/resources/common.resource index 119db76..ac5a61d 100644 --- a/tests/resources/common.resource +++ b/tests/resources/common.resource @@ -43,6 +43,7 @@ ${NOTIFICATION_DESTINATION_URL} ${MOCK_SERVER_URL} ${DISCOVER_URL} /service-apis/v1/allServiceAPIs?api-invoker-id= +${OPEN_DISCOVER_URL} /open-api-disc/v1/service-apis *** Keywords *** -- GitLab From 8067cc3c2af115ebb25e36117b51204bac14b6bd Mon Sep 17 00:00:00 2001 From: Pelayo Torres Date: Mon, 24 Aug 2026 15:14:50 +0200 Subject: [PATCH 3/4] OpenDiscover tests --- .../core/open_discover_operations.py | 27 ++- services/nginx/maps/95-auth-error.conf | 2 +- .../nginx/policies/open-discover-token.conf | 2 +- .../capif_api_service_open_discover.robot | 203 ++++++++++++++++++ 4 files changed, 228 insertions(+), 6 deletions(-) diff --git a/services/TS29222_CAPIF_Open_Discover_Service_API/openapi_server/core/open_discover_operations.py b/services/TS29222_CAPIF_Open_Discover_Service_API/openapi_server/core/open_discover_operations.py index 29fe6e9..88d6cd8 100644 --- a/services/TS29222_CAPIF_Open_Discover_Service_API/openapi_server/core/open_discover_operations.py +++ b/services/TS29222_CAPIF_Open_Discover_Service_API/openapi_server/core/open_discover_operations.py @@ -464,14 +464,33 @@ class OpenDiscoverOperations(Resource): if param == "api_supported_features": if isinstance(query_params[param], dict): - for api_name, api_supp_feat in query_params[param].items(): - my_params.append({"$and": [{"api_name": api_name}, {"api_supp_feats": api_supp_feat}]}) + # Each (api_name, api_supp_feat) pair is an independent + # alternative match, not a joint requirement on a single + # document - combine with $or, not $and. + feature_conditions = [ + {"$and": [{"api_name": api_name}, {"api_supp_feats": api_supp_feat}]} + for api_name, api_supp_feat in query_params[param].items() + ] + if len(feature_conditions) == 1: + my_params.append(feature_conditions[0]) + elif feature_conditions: + my_params.append({"$or": feature_conditions}) else: my_params.append({query_params_name[param]: query_params[param]}) continue - for entry in self._ensure_list(query_params[param]): - my_params.append({query_params_name[param]: entry}) + # api_names, api_ids, api_cats, api_prov_names: scalar fields. + # A single document cannot equal more than one value at once, + # so multiple requested values must be combined with $in + # (any of these), not one $and clause per value (impossible + # to satisfy, always zero results). + values = self._split_form_values(query_params[param]) + if not values: + continue + if len(values) == 1: + my_params.append({query_params_name[param]: values[0]}) + else: + my_params.append({query_params_name[param]: {"$in": values}}) if my_params: my_query = {"$and": my_params} diff --git a/services/nginx/maps/95-auth-error.conf b/services/nginx/maps/95-auth-error.conf index c18fc86..01f0b8d 100644 --- a/services/nginx/maps/95-auth-error.conf +++ b/services/nginx/maps/95-auth-error.conf @@ -1,6 +1,6 @@ map "$service:$endpoint:$method:$has_token:$has_cert:$role" $auth_error { - default '{"status":401,"title":"Unauthorized","detail":"Operation not allowed","cause":"Access denied by policy"}'; ~^open-discover-service:.*:.*:0:0:.*$ '{"status":401, "title":"Unauthorized" ,"detail":"Access token not present", "cause":"Bearer token is required for this API route"}'; + default '{"status":401,"title":"Unauthorized","detail":"Operation not allowed","cause":"Access denied by policy"}'; ~^.*:.*:.*:0:0:.*$ '{"status":401, "title":"Unauthorized" ,"detail":"Certifcate not present", "cause":"Certificate is required for this API route"}'; ~^helper:.*:.*:0:1:(invoker|apf|aef)$ '{"status":401, "title":"Unauthorized" ,"detail":"Role not authorized for this API route", "cause":"User role must be superadmin"}'; ~^invoker-management:.*:.*:0:1:(amf|apf|aef|ccf)$ '{"status":401, "title":"Unauthorized" ,"detail":"Role not authorized for this API route", "cause":"User role must be invoker"}'; diff --git a/services/nginx/policies/open-discover-token.conf b/services/nginx/policies/open-discover-token.conf index 668e460..0392ea7 100644 --- a/services/nginx/policies/open-discover-token.conf +++ b/services/nginx/policies/open-discover-token.conf @@ -1,4 +1,4 @@ map "$endpoint:$method" $open_discover_token_policy { default DENY; - open_discover_exact:GET ALLOW; + ~^open_discover_exact:GET ALLOW; } diff --git a/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot b/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot index 2afd191..4b4c375 100644 --- a/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot +++ b/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot @@ -40,7 +40,210 @@ Open Discover Published service APIs by Authorised Entity # Check Results Dictionary Should Contain Key ${resp.json()} discApis Should Not Be Empty ${resp.json()['discApis']} + #Length Should Be ${resp.json()['discApis']} 1 + # Convert Service API Description to Open API Details + ${open_api_details}= Create Open API Details From Service API Description ${service_api_description_published} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details} + + +Open Discover Published service APIs by Non Authorised Entity + [Tags] capif_api_open_discover_service-2 + + # Register APF + ${register_user_info}= Provider Default Registration + + # Publish one api + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + + # Register user for invoker + ${register_user_info}= Register User At Jwt Auth + ... username=${INVOKER_USERNAME} role=${INVOKER_ROLE} + + # Test + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + + + Check Response Variable Type And Values ${resp} 401 ProblemDetails + ... title=Unauthorized + ... status=401 + ... detail=Access token not present + ... cause=Bearer token is required for this API route + +Open Discover Published service APIs when no APIs are published + [Tags] capif_api_open_discover_service-3 + + # Register APF + ${register_user_info}= Provider Default Registration + + # Register user for invoker + ${register_user_info}= Register User At Jwt Auth + ... username=${INVOKER_USERNAME} role=${INVOKER_ROLE} + + # Test + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + # Check Results + Check Response Variable Type And Values ${resp} 404 ProblemDetails + ... title=Not Found + ... status=404 + ... detail=No API Published accomplish filter conditions + ... cause=No API Published accomplish filter conditions + +Open Discover Published service APIs filtered by api-names with 1 result + [Tags] capif_api_open_discover_service-4 + + # Register APF + ${register_user_info}= Provider Default Registration + + ${api_name_1}= Set Variable service_1 + ${api_name_2}= Set Variable service_2 + + # Publish 2 apis + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + ... ${api_name_1} + ${service_api_description_published_2} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + ... ${api_name_2} + + # Register user for invoker + ${register_user_info}= Register User At Jwt Auth + ... username=${INVOKER_USERNAME} role=${INVOKER_ROLE} + + # Request all the APis + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + Check Response Variable Type And Values ${resp} 200 OpenDiscoveryResp + + # Check Results + Dictionary Should Contain Key ${resp.json()} discApis + Should Not Be Empty ${resp.json()['discApis']} + # Convert Service API Description to Open API Details + ${open_api_details}= Create Open API Details From Service API Description ${service_api_description_published} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details} + ${open_api_details_2}= Create Open API Details From Service API Description ${service_api_description_published_2} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details_2} + + # Request api 1 + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL}?api-names=${api_name_1} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + Check Response Variable Type And Values ${resp} 200 OpenDiscoveryResp + + # Check Results + Dictionary Should Contain Key ${resp.json()} discApis + Should Not Be Empty ${resp.json()['discApis']} Length Should Be ${resp.json()['discApis']} 1 # Convert Service API Description to Open API Details ${open_api_details}= Create Open API Details From Service API Description ${service_api_description_published} List Should Contain Value ${resp.json()['discApis']} ${open_api_details} + +Open Discover Published service APIs filtered by api-names with no match + [Tags] capif_api_open_discover_service-5 + + # Register APF + ${register_user_info}= Provider Default Registration + + ${api_name_1}= Set Variable service_1 + ${api_name_2}= Set Variable service_2 + + # Publish 2 apis + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + ... ${api_name_1} + ${service_api_description_published_2} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + ... ${api_name_2} + + # Register user for invoker + ${register_user_info}= Register User At Jwt Auth + ... username=${INVOKER_USERNAME} role=${INVOKER_ROLE} + + # Request all the APis + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + Check Response Variable Type And Values ${resp} 200 OpenDiscoveryResp + + # Check Results + Dictionary Should Contain Key ${resp.json()} discApis + Should Not Be Empty ${resp.json()['discApis']} + # Convert Service API Description to Open API Details + ${open_api_details}= Create Open API Details From Service API Description ${service_api_description_published} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details} + ${open_api_details_2}= Create Open API Details From Service API Description ${service_api_description_published_2} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details_2} + + ${api_name_x}= Set Variable service_x + + # Request other api + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL}?api-names=${api_name_x}, + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + Check Response Variable Type And Values ${resp} 404 ProblemDetails + ... title=Not Found + ... status=404 + ... detail=No API Published accomplish filter conditions + ... cause=No API Published accomplish filter conditions + +Open Discover Published service APIs not filtered + [Tags] capif_api_open_discover_service-6 + + # Register APF + ${register_user_info}= Provider Default Registration + + ${api_name_1}= Set Variable service_1 + ${api_name_2}= Set Variable service_2 + + # Publish 2 apis + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + ... ${api_name_1} + ${service_api_description_published_2} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info} + ... ${api_name_2} + + # Register user for invoker + ${register_user_info}= Register User At Jwt Auth + ... username=${INVOKER_USERNAME} role=${INVOKER_ROLE} + + # Request all the APis + ${resp}= Get Request Capif + ... ${OPEN_DISCOVER_URL} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... access_token=${register_user_info['access_token']} + + Check Response Variable Type And Values ${resp} 200 OpenDiscoveryResp + + # Check Results + Dictionary Should Contain Key ${resp.json()} discApis + Should Not Be Empty ${resp.json()['discApis']} + # Convert Service API Description to Open API Details + ${open_api_details}= Create Open API Details From Service API Description ${service_api_description_published} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details} + ${open_api_details_2}= Create Open API Details From Service API Description ${service_api_description_published_2} + List Should Contain Value ${resp.json()['discApis']} ${open_api_details_2} + + -- GitLab From 2beea56ca25861fb66f46ce9e3e96886b0a9d51e Mon Sep 17 00:00:00 2001 From: Pelayo Torres Date: Wed, 26 Aug 2026 12:51:48 +0200 Subject: [PATCH 4/4] Added smoke open discover tests --- .../capif_api_service_open_discover.robot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot b/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot index 4b4c375..ac11ac6 100644 --- a/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot +++ b/tests/features/CAPIF Api Open Discover Service/capif_api_service_open_discover.robot @@ -15,7 +15,7 @@ ${API_INVOKER_NOT_REGISTERED} not-valid *** Test Cases *** Open Discover Published service APIs by Authorised Entity - [Tags] capif_api_open_discover_service-1 + [Tags] capif_api_open_discover_service-1 smoke # Register APF ${register_user_info}= Provider Default Registration @@ -154,7 +154,7 @@ Open Discover Published service APIs filtered by api-names with 1 result List Should Contain Value ${resp.json()['discApis']} ${open_api_details} Open Discover Published service APIs filtered by api-names with no match - [Tags] capif_api_open_discover_service-5 + [Tags] capif_api_open_discover_service-5 smoke # Register APF ${register_user_info}= Provider Default Registration -- GitLab